Tech: Programming an Arduino to talk Seatalk

Warning: This blog post contains code!
SmartConroller test bench.
Recently my Raymarine "Smart Controller" remote control stopped working. It simply refused to connect to the wireless base station, complaining "ALARM: NO LINK" every time it was turned on. Fortunately it was under warranty so I promptly sent it back to Raymarine to be fixed. When it came back I thought it would be a good idea to test it before going to trouble to drive to my boat and re-install it.

The SmartController wireless base station and remote control are Seatalk devices. I cobbled together a simple test bench using a prototyping breadboard, supplied 12V and connected both devices. Voila! The remote control now connected to the base station.

To confirm full functionality though, I needed a way of sending Seatalk data to the remote. Previously I've done a bit of NMEA 0183 programming with my Arduino, testing my MR-350 GPS. Both NMEA 0183 and Seatalk are serial protocols but that is where the similarities end. NMEA 0183 is a simple text-based protocol, whereas Seatalk is binary protocol. Seatalk is a proprietary protocol developed by Raymarine, which has been reverse engineered. SeaTalk is weird in that it uses 9-bit symbols instead of the usual 8-bit bytes. The most-significant bit (0x100) is set to 1 for the first symbol of every new message, and 0 thereafter. To learn more, read Thomas Knauf's Seatalk reference, the Bible of Seatalk.

The good news is that the Arduino Uno's UART supports 9-bit serial transmission. The bad news is that this support is not available in any standard library. Fortunately this forum post pointed to a modified version of the Arduino HardwareSerial code by Nick Gammon. I could not find any Arduino code for sending Seatalk messages though so I decided to write my own.

Note: I also used the handy XScopes Xprotolab mini oscilloscope (bottom right in photo) for measuring voltages and waveforms.

Here's the gist of how my code works:

First, I defined all the field types used by Seatalk as follows:

  enum FieldType {
    Null =   0,     // terminates a datagram spec
    Cmd =    1,     // command byte
    Att =    2,     // attribute byte
    Nibble = 3,     // data nibble (4 bits)
    Byte =   4,     // data byte (8 bits)
    Int =    5,     // data int (16 bits)
    Int10 =  6      // data int scaled by 10 (NOT 10 ints)
  };

Next, I defined Seatalk datagram specifications as arrays of alternating field types and field values, terminated by Null.

For example, Speed Through Water (STW) is defined as follows:

  uint16_t stwSpec[] = { Cmd, 0x20, Att, 0x01, Int10, 0, Null };
  #define STW_SPEED 5

The #define is the position in the array of the speed value.

Depth Below Transducer (DBT) requires 3 fields:
  uint16_t dbtSpec[] = { Cmd, 0x00, Att, 0x02, Nibble, 0, Nibble, 0, Int10, 0, Null };
  #define DBT_Y 5
  #define DBT_Z 7
  #define DBT_DEPTH 9

The first nibble at position 5 (DBT_Y) defines the units. The second nibble at position 7 (DBT_Z) optionally defines alarms.

Compass Heading (HDG) is even more complicated:

  uint16_t hdgSpec[] = { Cmd, 0x89, Nibble, 0, Nibble, 0x2, Byte, 0, Byte, 0,
                         Nibble, 0x2, Nibble, 0, Null };
  #define HDG_U  3
  #define HDG_VW 7
  #define HDG_XY 9
  #define HDG_Z 13

The heading value is calculated from 3 different fields as follows:
  heading = (HDG_U & 0x3) * 90 + (HDG_VW & 0x3F) * 2 + (HDG_U & 0xC) / 2

I case you're wondering where the "U", "VW", "XY", etc. all come from, I'm using the same symbols as in Knauf's Seatalk reference.

The following function does the work of transmitting any Seatalk datagram:

  boolean writeDatagram(uint16_t data[]) {
    // send a datagram; last element of data must be Null
    for (int ii = 0; ; ii += 2) {
      switch (data[ii]) {
      case Null:
        return true;

      case Cmd:
        // NB: set command bit
        Serial.write9bit(data[ii + 1] | 0x0100);     
        break;

      case Nibble:
        // NB: nibbles come in pairs, with most significant first
        if (data[ii + 2] != Nibble) return false;
        Serial.write9bit((data[ii + 1] << 4) | (data[ii + 3] & 0x0f));
        ii += 2; // skip over next nibble which we've already consumed
        break;

      case Att:
      case Byte:
        Serial.write9bit(data[ii + 1] & 0x00ff); // mask high byte
        break;

      case Int:
      case Int10:
        Serial.write9bit(data[ii + 1] & 0x00ff); // LSB first
        Serial.write9bit(data[ii + 1]  >> 8);    // MSB second
        break;

      default:
        return false;
      }
    }
    return true;
  }

Finally, here's the code to send Seatalk messages for depth, speed and heading:

    // DBT = 8.1 m
    seatalk::dbtSpec[DBT_Y] = 4; // specify meters
    seatalk::dbtSpec[DBT_DEPTH] = (uint16_t)(M_TO_FT(8.1) * 10);
    seatalk::writeDatagram(seatalk::dbtSpec);

    // STW = 6.2 knots
    seatalk::stwSpec[STW_SPEED] = (uint16_t)(6.2 * 10);           
    seatalk::writeDatagram(seatalk::stwSpec);

    // HDG = 291 (= 3 * 90 + 10 * 2 + 2/2)   
    seatalk::hdgSpec[HDG_U] = 0xB;
    seatalk::hdgSpec[HDG_VW] = 10;
    seatalk::writeDatagram(seatalk::hdgSpec);

The full source code for my Arduino program ("sketch") along with the modified HardwareSerial files can be found here.  Copy HardwareSerial files over those in your Arduino installation (in cores/arduino) then restart the Arduino IDE.

The most satisfying thing about coding, is the joy of working code.  I even have photos to prove it!

 

Regarding the hardware interface, note that the Seatalk bus rests at 12V, whereas Arduino uses 5V. A non-inverting 5V~12V level shifter, such as this, is therefore advisable.

OVER.

PS More Arduino Arriba blog posts and an Arduino-based autopilot simulator.

Comments

  1. Hello,
    i downloaded all files (incl. Hardwareserial... copyed in core/arduino directory) but whe i try to compile i get following message!

    "
    :
    :
    Using board 'nano' from platform in folder: C:\Program
    Using core 'arduino' from platform in folder: C:\Program
    Detecting libraries used...
    "C:\\Users\\JSz\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino5/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10810 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR "-IC:\\Program Files\\Arduino\\hardware\\arduino\\avr\\cores\\arduino" "-IC:\\Program Files\\Arduino\\hardware\\arduino\\avr\\variants\\eightanaloginputs" "C:\\Users\\JSz\\AppData\\Local\\Temp\\arduino_build_873228\\sketch\\SeatalkTest.ino.cpp" -o nul
    :
    :
    Sketch wird kompiliert...
    :
    :
    :
    C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial - original.cpp:117:6: error: prototype for 'void HardwareSerial::begin(long unsigned int, byte)' does not match any in class 'HardwareSerial'

    void HardwareSerial::begin(unsigned long baud, byte config)

    ^~~~~~~~~~~~~~

    In file included from C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\Arduino.h:232:0,

    from C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial - original.cpp:30:

    C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h:121:10: error: candidates are: void HardwareSerial::begin(long unsigned int, uint16_t)

    void begin(unsigned long, uint16_t);

    ^~~~~

    C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h:120:10: error: void HardwareSerial::begin(long unsigned int)

    void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }

    ^~~~~

    C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial - original.cpp:225:8: error: redefinition of 'size_t HardwareSerial::write(uint8_t)'

    size_t HardwareSerial::write(uint8_t c)

    ^~~~~~~~~~~~~~

    In file included from C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\Arduino.h:232:0,

    from C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial - original.cpp:30:

    C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h:133:19: note: 'virtual size_t HardwareSerial::write(uint8_t)' previously defined here

    inline size_t write(uint8_t n) { return write((uint16_t)n); }

    ^~~~~

    "C:\\Users\\JSz\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino5/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10810 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR "-IC:\\Program Files\\Arduino\\hardware\\arduino\\avr\\cores\\arduino" "-IC:\\Program Files\\Arduino\\hardware\\arduino\\avr\\variants\\eightanaloginputs" "C:\\Program Files\\Arduino\\hardware\\arduino\\avr\\cores\\arduino\\HardwareSerial0.cpp" -o "C:\\Users\\JSz\\AppData\\Local\\Temp\\arduino_build_873228\\core\\HardwareSerial0.cpp.o"
    exit status 1
    Fehler beim Kompilieren für das Board Arduino Nano.
    "
    The same when i try UNO or MEGA as Hardware.

    Can you help me?

    Best reguards, Joachim

    ReplyDelete
  2. https://berreizeta.blogspot.com/2017/02/seatalk-35-480x320-tft-monitor.html

    https://berreizeta.blogspot.com/2017/02/seatalk-simulator-arduino.html

    https://berreizeta.blogspot.com/2017/01/seatalk-wireless-remote-controller.html

    More .. ? :

    https://berreizeta.blogspot.com/

    ReplyDelete
    Replies
    1. Hi Berreizeta, I'm trying to test wireless remote at home with a ST60multi repeater display with arduino uno and on IDE 1.8.12 code compiles and uploads without any message but cannot turn on and off the light of repeater, ST60 is connected to UNO 6&7 with a breadboard circuit of Thomas K for seatalk comm. Is my setup not adequate? Thanks & Regards

      Delete
  3. Try my adapters, they work fine

    https://berreizeta.blogspot.com/2016/10/blog-post.html

    https://berreizeta.blogspot.com/2016/09/seatalk-arduino-interface-5v-ttl-for_19.html

    ReplyDelete
  4. SEATALK TO PC BI-DIRECTIONAL INTERFACE

    https://berreizeta.blogspot.com/2016/08/seatalk-to-pc-bi-directional-interface.html

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. ALL WORKS : https://berreizeta.blogspot.com/

    SEATALK SIMULATOR - ARDUINO
    SEATALK 3.5" 480x320 TFT MONITOR
    NEO6 GPS ANTENNA TO RAYMARINE RC425
    SEATALK WIRELESS REMOTE CONTROLLER - ARDUINO
    Seatalk Monitor - Arduino TFT 3.5" 480x320
    Seatalk Monitor - Arduino TFT 320x240
    Seatalk Wireless Remote Control - Keypad PIC16F628A
    SEATALK MONITOR - LCD 20x4
    Seatalk Wireless Remote Controller - Arduino
    SEATALK - ARDUINO BI-DIRECCIONAL ADAPTER
    SEATALK REMOTE CONTROLLER
    SEATALK PINOUT ROUND CONNECTOR
    SEATALK TO ARDUINO INTERFACE
    SEATALK TO PC BI-DIRECTIONAL INTERFACE
    RUDDER INDICATOR - INDICADOR DE TIMON
    RAYMARINE SPORTPILOT PINOUT
    NMEA CHECKSUM
    Seatalk - Concentrador

    ReplyDelete
    Replies
    1. Berreizeta, your code is on google drive and is not accessible without authorization...
      this make all your work useless. Could you provide a solution to make your code accessible (i now google drive had some recent EU problem with google-drive)? Thanks, Davide

      Delete
    2. All links from Blog are downloadable, dont need authorization.

      Delete
  7. Thank, will try Seatalk & Arduino Uno bidirectional for my project.

    ReplyDelete
  8. I am trying to control the screen of a Raymarine St7002 Seatalk autopilot control from an Arduino using your code. Do you happen to have code that allows me to input a decimal heading and returns the [HDG_U] and [HDW_VW] that the Seatalk code uses?

    ReplyDelete
  9. Try this :

    Seatalk Monitor - Arduino
    http://berreizeta.blogspot.com/2016/12/blog-post.html

    ReplyDelete
    Replies
    1. I do not week to have access to the Google Drive link. Can you help me with that?

      Delete
    2. I have no access to your Google Drive files, can you remove the permission lock?

      Delete
  10. Google policies ..... :(
    Email me berreizeta(a)hotmail.com

    ReplyDelete

Post a Comment