July 17, 2019  Anchor alarm with Bridge and Button

The new update for the Bridge allows easy calculating of distances between GPS points. This article shows how to create an anchor alarm managed from an MFD screen or hardware button.

Test points for anchor alarm

Figure 1. Test points for anchor alarm (click to enlarge or download KML)

Today we will join the power of the NMEA 2000 Bridge and the NMEA 2000 Alarm Button to create an anchor alarm. Let us start with a simple program (executed by the Bridge), where the anchor alarm is managed from an external button (with an integrated LED) connected to the Alarm Button.

When you press the button, the program saves the current GPS position. The LED of the button will start flashing to show you that alarm is ready. With each position update, the Bridge will calculate the distance to the saved position and run the siren (turning on the channel 1 on the Button) if the position is farther than the pre-programmed safe distance. The next time you press the button, the anchor alarm is cancelled. Also, the program turns on the siren if the GPS position is lost. When a GPS fix is not available, the program will not allow setting the alarm, and it can be easily detected: the button will not flash after being pressed.

The Alarm Button must be configured (see comments in the program's header). With the first and second commands, we reset the settings and switch the Button to "Digital Switching Mode". The next command unlinks the sound from channel 7, so activation of this channel will be silent, but the LED will flash.

And the last command links the button with channel 7. When all channels are off (not active), pressing the button will turn on channel 7. When the Alarm Button has active channels, pressing the button turns off the channel with highest priority (smallest number).

# Anchor Alarm (simplified), version 1.0
# See: http://www.yachtd.com/news/anchor_alarm.html
#
# To configure the Alarm Button, send the following sequence of commands:
# YD:RESET
# YD:MODE DS
# YD:LINK 7 SOUND 0
# YD:CHANNEL 7

FW_CAN1_TO_CAN2=OFF
FW_CAN2_TO_CAN1=OFF

SLOT1 = 000EF20D FF 08 00FDFFFFFFFFFFFF # Turn ON the the anchor alarm (Channel 1)
SLOT2 = 000EF20D FF 08 00FCFFFFFFFFFFFF # Turn OFF the the anchor alarm
SLOT3 = 000EF20D FF 08 0003C0FFFFFFFFFF # Turn off all channels, except Channel 1

# We'll run/stop the alarm once in 3 seconds to avoid fast switches
heartbeat(3000)
{
   if (D > 0) { # Alarm is set (D is the safe distance in meters)
      if (R > D) {  # R is distance to D from current GPS position
        load(SLOT1) # Anchor alarm on
        M = 1       # Mark, that alarm was ON by the Bridge
      }
      else {
        load(SLOT2) # Anchor alarm off
        M = 0       # Alarm was OFF by the Bridge
      }
      send(CAN1)
   }
}

# Position, Rapid Update - calculate the distance from current position
match(CAN1,0x1F80100,0x1FFFF00)
{
   if (get(DATA,INT32) < 0x7FFFFFFF) {
       if (get(DATA+4,INT32) < 0x7FFFFFFF) {      
           X = get(DATA,INT32)     # Latitude, 0.0000001 deg
           Y = get(DATA+4,INT32)   # Longitude, 0.0000001 deg
           T = timer()    # Position is valid, save the time of position fix
           R = 0          # Reset the distance from current position
           if (D > 0) {   # Is the alarm already set?
               # Calculate the distance by Haversine Formula
               W = (cast(X - A,FLOAT)/10000000) * M_PI / 180 # delta Lat
               Z = (cast(Y - B,FLOAT)/10000000) * M_PI / 180 # delta Lon
               W = sin(W/2) 
               Z = sin(Z/2) 
               E = cos((cast(A,FLOAT)/10000000) * M_PI / 180)*Z*Z
               U = W*W + cos((cast(X,FLOAT)/10000000) * M_PI / 180)*E
               if (U != 0) {   
                  U = atan2(sqrt(U),sqrt(1-U))
                  R = 12742000*U    # The distance to (A,B) in meters
               }
           }
       }
   }      
}

# Binary Status Report
match(CAN1,0x1F20D00,0x1FFFF00)
{
   if (get(DATA,UINT8) == 0) {     # Is it Bank 0 message?
      S = get(DATA+1,UINT16)       # Status of Channels 1-8 (2 bit each: 1-ON, 0-OFF)
      if ((S & 3) != 1) {          # Is anchor alarm NOT running?                  
         if ((S >> 12) & 3 == 1) { # Is hardware button was pressed (Channel 7 is set)?
            if (timediff(T) < 2000) { # Is GPS position valid?
   
               if (D == 0) {          # Set new alarm
                  A = X
                  B = Y
                  D = 20              # Safe distance in meters
               }
               else {  # The alarm is already set, canceled by user
                  if (M == 1) {
                     load(SLOT3) # Cancel the alarm
                     send(CAN1)
                     M = 0
                     D = 0
                  }
               }
            }
            else {   # We have no actual position (GPS lost?)
               if (D > 0) {      # Was the alarm set?
                  if (M == 1) {  # Was it canceled by user?   
                     load(SLOT3) 
                     send(CAN1)  # Cancel the alarm
                     M = 0
                     D = 0
                  }
                  else {         # It was not canceled by user
                     R = 1000    # Run the Anchor Alarm!
                     M = 0       # GPS position is lost!
                  }
               }
               else  {        # User tries to set new alarm
                  load(SLOT3) # Switch off all buttons, do not allow
                  send(CAN1)  # to set the alarm without valid position
               }
            }
         }
      }
   }
}

# End of program

When the siren is on (channel 1 is on), pressing the button turns off the siren, but not channel 7 (which is the sign that the anchor alarm is ready). The variable "M" helps the program understand whether the anchor alarm was canceled by the user (channel 1 is off, channel 7 is on) or was not yet turned on in the heartbeat() function, which is called once in 3 seconds.

To calculate the distance more easily, we added one more trigonometric function in Firmware 1.31, the atan2(). It is not the square of arctangents.

To test the program, you will need the CAN Log Viewer with one of our gateways. We recommend adjusting the filter of PGN 0x1F801 to receive only messages sent from the gateway, and increasing the timeout for GPS position from 2000 milliseconds to 20000 milliseconds (or you will need to click the "Send" button too fast!).

NAMELATITUDELONGITUDETO CENTER
(METERS)
NMEA 2000 MESSAGE
CENTER54.6911981420.242790580.009F80100 ED 36 99 20 02 CE 10 0C
NE_64.2354.6915690820.2435567664.239F80100 6B 45 99 20 F0 EB 10 0C
NE_24.2754.6912840220.2431378224.279F80100 48 3A 99 20 92 DB 10 0C
NW_6.8954.6912364720.242706826.879F80100 6D 38 99 20 BC CA 10 0C
SW_9.6854.6911447420.242671599.689F80100 D7 34 99 20 5C C9 10 0C
SE_12.2454.6911614220.2429701712.249F80100 7E 35 99 20 06 D5 10 0C
S_23.9554.6909827920.2427904723.959F80100 84 2E 99 20 01 CE 10 0C
N_20.5954.6913833820.2427916420.609F80100 2A 3E 99 20 0C CE 10 0C

We prepared the set of waypoints (see the Figure 1 at the beginning of the article), sharp coordinates you can find in the KML file. You can copy NMEA 2000 messages with coordinates from the table above and send them with the CAN Log Viewer (menu "Tools" \ "Send CAN Messages...").

Anchor alarm with the control from an MFD

Figure 2. Anchor alarm with the control from an MFD

Now, let's add control from an MFD (see the Figure 2). We'll add five buttons with different safe distances and a button which indicates the state of alarm. To turn off the alarm, you can press any active button.

We'll keep control from the hardware button; it will activate the favorite distance (20m in our example). You will need to download the configuration file for your MFD here (see the example of the settings here) and configure the Alarm Button as shown in the first lines of the code below.

# Anchor Alarm with MFD, version 1.0
# See details: http://www.yachtd.com/news/anchor_alarm.html
#
# To configure the Alarm Button YDAB-01, send the following sequence of commands:
# YD:RESET
# YD:MODE DS
# YD:LINK 2 SOUND 0
# YD:LINK 3 SOUND 0
# YD:LINK 4 SOUND 0
# YD:LINK 5 SOUND 0
# YD:LINK 6 SOUND 0
# YD:LINK 7 SOUND 0
# YD:CHANNEL 7

FW_CAN1_TO_CAN2=OFF
FW_CAN2_TO_CAN1=OFF

SLOT1 = 000EF20D FF 08 00FDFFFFFFFFFFFF # Turn ON the the anchor alarm (Channel 1)
SLOT2 = 000EF20D FF 08 00FCFFFFFFFFFFFF # Turn OFF the the anchor alarm
SLOT3 = 000EF20D FF 08 0003C0FFFFFFFFFF # Turn off all channels, except Channel 1

# We'll run/stop the alarm once in 3 seconds to avoid fast switches
heartbeat(3000)
{
   if (D > 0) {     # Alarm is set (D is the safe distance in meters)
      if (R > D) {  # R is distance to D from current GPS position
        load(SLOT1) # Anchor alarm on
        M = 1       # Mark, that alarm was ON by the Bridge
      }
      else {
        load(SLOT2) # Anchor alarm off
        M = 0       # Alarm was OFF by the Bridge
      }
      send(CAN1)
   }
}

# Position, Rapid Update - calculate the distance from current position
match(CAN1,0x1F80100,0x1FFFF00)
{
   if (get(DATA,INT32) < 0x7FFFFFFF) {
       if (get(DATA+4,INT32) < 0x7FFFFFFF) {      
           X = get(DATA,INT32)   # Latitude, 0.0000001 deg
           Y = get(DATA+4,INT32)   # Longitude, 0.0000001 deg
           T = timer()   # Position is valid, save the time of position fix
           R = 0           # Reset the distance from current position
           if (D > 0) {   # Is the alarm already set?
               # Calculate the distance by Haversine Formula
               W = (cast(X - A,FLOAT)/10000000) * M_PI / 180 # delta Lat
               Z = (cast(Y - B,FLOAT)/10000000) * M_PI / 180 # delta Lon
               W = sin(W/2) 
               Z = sin(Z/2) 
               E = cos((cast(A,FLOAT)/10000000) * M_PI / 180)*Z*Z
               U = W*W + cos((cast(X,FLOAT)/10000000) * M_PI / 180)*E
               if (U != 0) {   
                  U = atan2(sqrt(U),sqrt(1-U))
                  R = 12742000*U    # The distance to (A,B) in meters
               }
           }
       }
   }      
}

# Binary Status Report
match(CAN1,0x1F20D00,0x1FFFF00)
{
   if (get(DATA,UINT8) == 0) {  # Is it Bank 0 message?
      S = get(DATA+1,UINT16)    # Status of Channels 1-8 (2 bit each: 1-ON, 0-OFF)
      if ((S & 3) == 1) {       # Is anchor alarm running?
         if ((S >> 2) & 0x3FF == 0) { # Are all buttons off?
            D = 0            
            M = 0                 
            load(SLOT3)               # User pressed buttons on MFD
            set(DATA+1,UINT8,0)       # screen to cancel alarm
            send(CAN1)                # Cancel the anchor alarm
         }         
      }
      else {    # Anchor alarm is OFF - read MFD buttons
         K = 0
         if ((S >> 10) & 3 == 1) {            
            K = 5  # Channel 6 is ON
            L = 50 # Set 50m alarm
         }                  
         if ((S >> 8) & 3 == 1) {
            K = 4  # Channel 5 is ON
            L = 30 # Set 30m alarm
         }
         if ((S >> 6) & 3 == 1) {            
            K = 3  # Channel 4 is ON
            L = 20 # Set 20m alarm
         }
         if ((S >> 4) & 3 == 1) {            
            K = 2  # Channel 3 is ON
            L = 10 # Set 10m alarm
         }
         # If two buttons pressed on MFD, the smallest
         # distance have priority
         if ((S >> 2) & 3 == 1) { 
            K = 1 # Channel 2 is ON
            L = 5 # Set 5m alarm
         }

         # Is hardware button was pressed? It have priority over MFD
         if ((S >> 12) & 3 == 1) {
            K = 3  # Favorite distance to turn on
            L = 20 # with hardware button (20 meters)
         }

         if (K == 0) {            
            D = 0 # No buttons pressed, clear alarm settings
         }
         else {   # We have buttons pressed
            if (timediff(T) < 2000) { # Is GPS position valid?
               if (D != L) {
                  # New alarm or modification of alarm
                  A = X
                  B = Y
                  D = L # New safe distance, in meters
               }
               else {
                  # Is user canceled the alarm with hardware
                  # button of or by switching the Channel 1 ?
                  if (M == 1) {
                     load(SLOT3) # Cancel the alarm
                     send(CAN1)
                     K = 0
                     M = 0
                     D = 0
                  }
               }               
            }
            else {   # We have no actual position (GPS lost?)
               if (D > 0) {      # Was the alarm set?
                  if (M == 1) {  # Was it canceled by user?   
                     load(SLOT3) 
                     send(CAN1)  # Cancel the alarm
                     K = 0
                     M = 0
                     D = 0
                  }
                  else {  # It was not canceled by user
                     R = 1000    # Run the Anchor Alarm!
                     M = 0
                  }
               }
               else  {        # User tries to set new alarm
                  load(SLOT3) # Switch off all buttons, do not allow
                  send(CAN1)  # to set the alarm without valid position
                  K = 0       
               }
            }
         }
         if (K > 0) { 
            S = get(DATA+1,UINT16)
            K = (S & 0xFFF) | (1 << (K*2))
            if (S != K) # If multiple buttons are pressed, leave on the actual only
            {
               load(SLOT3)
               set(DATA+1,UINT16, K)
               send(CAN1)
            }
         }
      }
   }
}

# End of program

You can see that the code is generally the same; we only added the handling of five "virtual buttons" from the MFD screen. You can also add Switch Control to the system. You will not need to configure it. The first button will turn off the active anchor alarm and its LED will indicate the anchor alarm's state. Three other buttons will turn on/off 5-meter, 10-meter and 20-meter alarms.

The programs above are a bit complicated because they process the state of control buttons. If you need only to sound NMEA 2000 events, the code will be much simpler.

Messages with PGN 127501 (Binary Status Report, with the state of channels) and 127502 (Switch Bank Control, to manage channels) have the same format. The first byte contains the bank number, and the next 7 bytes (56 bits) contain 2-bit states of 28 channels: 0 is "OFF", 1 is "ON", 2 is "Error" (for status) or "Reserved" (for control), 3 is "Unknown" (for status) or "No action" (for control).

You can download the code of a simple program and the code of the program with MFD support. The 1.31 firmware update for the Bridge is available in the Downloads section.

 

Next articles:

Previous articles:

See also: recent news, all news...