Warning: This blog post contains code!
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.
SmartConroller test bench. |
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.
The first nibble at position 5 (DBT_Y) defines the units. The second nibble at position 7 (DBT_Z) optionally defines alarms.
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
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!
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.
OVER.
PS More Arduino Arriba blog posts and an Arduino-based autopilot simulator.
Hello,
ReplyDeletei 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
https://berreizeta.blogspot.com/2017/02/seatalk-35-480x320-tft-monitor.html
ReplyDeletehttps://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/
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
DeleteTry my adapters, they work fine
ReplyDeletehttps://berreizeta.blogspot.com/2016/10/blog-post.html
https://berreizeta.blogspot.com/2016/09/seatalk-arduino-interface-5v-ttl-for_19.html
SEATALK TO PC BI-DIRECTIONAL INTERFACE
ReplyDeletehttps://berreizeta.blogspot.com/2016/08/seatalk-to-pc-bi-directional-interface.html
This comment has been removed by the author.
ReplyDeleteALL WORKS : https://berreizeta.blogspot.com/
ReplyDeleteSEATALK 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
Berreizeta, your code is on google drive and is not accessible without authorization...
Deletethis 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
All links from Blog are downloadable, dont need authorization.
DeleteThank, will try Seatalk & Arduino Uno bidirectional for my project.
ReplyDeleteI 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?
ReplyDeleteTry this :
ReplyDeleteSeatalk Monitor - Arduino
http://berreizeta.blogspot.com/2016/12/blog-post.html
I do not week to have access to the Google Drive link. Can you help me with that?
DeleteI have no access to your Google Drive files, can you remove the permission lock?
DeleteGoogle policies ..... :(
ReplyDeleteEmail me berreizeta(a)hotmail.com