NMEA 2000 Shield

Hey Timo,

nothing to appologize for. I expacted almost a little harder answer already as I posted a lot of times about it and got afraid. That´s why I appologized always already in the question for that post. :wink: Stupid questions, special if they appear so often, deserve sometimes harder answer to reset the brain. And it worked also yesturday evening as I wrote almost the same like you posted now:

double dMap(double val, double in_min, double in_max, double out_min, double out_max) {
    return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

int RudderRead() {
int rudderValue = dMap(rudderSens, 40, 980, -45, 45);
  return rudderValue; // Read here the true temperature e.g. from analog input 

#define RudderSendPeriod 100

void SendRudder() {
  static unsigned long RudderSendTime = millis() + RudderSendPeriod;
  tN2kMsg N2kMsg;
  if ( RudderSendTime < millis() ) {
    RudderSendTime += RudderSendPeriod;
    SetN2kRudder(N2kMsg, rudderValue);

void loop() {

I will compare it carefully to learn the things I do wrong and not just use it as template, promissed!

The debug console from OpenSkipper showed up that it was expacting 6 bit and recieved 8 bit. Not sure if that have something todo with my problem. So I still have 0 as value for the angle. (used my version, not the one you posted now).

I don´t give up so fast. It is now “just” 7-8 days for the rudderfunction I spent. :slight_smile:

What is responsible for the 8 bit or 6 bit in the message?

A nother thing I couldn´t figure out the meaning of it is this one:

tN2kMsg N2kMsg;

First time I got hit because I tried the rudder and got that tN2kMsg was not defiend in this scope. So I just copied this from other examples from you without knowing what it is. ok so far i know it says that tN2kMsg is the same like N2kMsg. I couldn´t exactly find the point in the library files where this is defiend to figure out what this is. I think at least it is the definition of the message to be sent due to "NMEA2000.SendMsg(N2kMsg);.

Could you may reffer to a point in the library files where I can take a look to get more known with the messages which are sent?

Thanks a lot!


The Teensy´s arrived today. I ordered 3.2´s and one 3.5. The 3.5 is also 5 volt compatible and have the sd card slot onboard. Thought cool to use for the planned GPS tracker I lke to build in the future.
One difference to arduino´s is that I have to use the FlexCAN library, something else I have to be carefull with? They are not so cheap as Arduino and I don´t want to burn any. I also got the Linx RXM-GNSS-GM delivered. High performance/ precision GPS (and all other sattelites). Looking forward t see the differences comparing to the “cheaper” once.

I noticed that you have to change on the beginning of my example:

const unsigned long TransmitMessages[] PROGMEM = {127245L, 0};

This gives right information to other devices on the bus.

In your code int ReadRudder you have to defined it to return integer value. Remember that then compiler will do automatic typecasting from double to int. And there is no warning about that, since Arduino IDE does not have good option to turn all warnings as error. So define double ReadRudder(...) Also inside function define double rudderValue=dMap(...)

Have you checked that you have latest 1.6B version of OpenSkipper? And I think you mean 6 bytes.

tN2kMsg N2kMsg; does say that tN2kMsg is N2kMsg. It says that you define variable N2kMsg, which is type of tN2kMsg. It is as you write double val; you define variable val, which is type of double.

It is OpenSkipper 1.6.2016.1217.

I ment 6 vytes not bit yes.

During the test I used allover double, also inside the RudderRead function. The "int rudderValue = dMap(rudderSens, 40, 980, -45, 45);" was just a test.

I did changed the PROGMEM number to 127245L. I found this number from the N2KMessage.h after I compared the number from the Windmonitor with N2KMessages.h. So I use this now all the time. 127245 is the number in the first row of rudder "void SetN2kPGN127245". The "L" behind I just left there, couldn´t find out if that is right there or not. So in your last posted version of the rudder sketch is still the number used from the windmonitor if I understand this correct.

Later I start all over again to be 100% sure.


I am trying to hack together some new electronics capabilities for my sailboat. I currently have a basic NMEA 2000 bus on the boat. I'd like to add low-cost custom sensors and logging capability to the system. My current idea is to install Signal K on a Raspberry Pi Zero W, and cobble the NMEA 2000 bus to the RasPi with an Arduino. Then it would be relatively simple to create "micro-sensors" with an ESP8266 or such. My ideas for sensors would include temperature/humidity/barometer in different locations, engine tachometer (from the RPM gauge signal), electronic compass, gyrometer etc, and so on. Having all those directly on NMEA 2000 would increase the bus complexity and cost quite a bit, hence the wish for simple wireless devices.

I've successfully built the connecting hardware with an Arduino Due and MCP2562 with Timo Lappalainen's excellent instructions. I'm able to eavesdrop to N2k data with ActisenseListener. Similarly, my B&G Vulcan 7 MFD can see the device and receive data when I load MessageSender.ino to the Arduino. Hence, I believe the hardware connectivity is working.

What I don't get is how to use the Arduino as a simple gateway. Did I understand correctly that ActisenseListenerSender should basically act as an Actisense NGT-1 replacement? The code doesn't set any N2k product information, and at least the B&G plotter doesn't see any transmitted data. Should I add the product information data myself? What else might I be missing from a functional gateway?

Of course, I'm not above opening my wallet and simply buying an Actisense NGW-1 or similar, but I quite like the DIY aspect and the challenge of not just throwing some off-the-shelf hardware on the problem. :-)



You do not need NGT-1, exept if you would like to have nice box. With same money you can buy several Teensys. My library does all, what you need.

Also I would not think having devices directly on N2k bus increases costs. You do not need to use expencive N2k connectors. You can simply make your own hub box by using cheap connectors used on DeviceNet (see. e.g. Farnell codes 1717025 and 1717013). Pin order is on Teensy_Actisense_listener_sender_schematics.pdf. With 20 € you can make 5 pos hub with all connectors. I currently have 4 hubs on by boat. The complexity will increase a bit, but then your devices can get power from bus.

ActisenseListenerSender has been designed mostly for testing and logging purposes. It passes messages sent as they are e.g. from my NMEA Simulator (see. kave.fi/Apps). There you can select to send or not the device information. NGT-1 works a bit different way, since it allways shows messages sent from NGT-1, not from Simulator. I can add Gateway example, or you can make it by yourself by combining TemperaturMonitor and ActisenseReader used on ActisenseListenerSender. The other thing I wonder that how did you test that ActisenseListenerSender does not send anything? How did you transmitted data?

Then if you are going to use RPi, it should be possible to integrate library with it by using PiCan2. I have been thinking that, but had no time. Library should compile on Pi and there is NMEA2000_socketCAN for that. As far as I know, SignalK are listen only, so you need anyway code to generate messages. Library has most common message generators on N2kMessages.

Hi all,

Love the work you are doing. I was wondering if the PGN for MOB (I think it is PGN 127233) can be included the library since I want to make a MOB node.

Also i made a PCB design for an atmega 328 (the chip on an Arduino uno) to work with a standard MCP2515 CAN Bus Module. This PCB has I2C or 2 analog connections and a digital connection.

I have used this PCB in 3 applications (a wind monitor, a digital compass and a barometer).


NMEA2kPCB.zip (576 KB)

WindMonitor.zip (2.28 KB)

elcompass.ino (4.71 KB)

baro.ino (2.79 KB)

I have warned that library does not fit well to Arduino Uno . You have use defines to disable all rarely used features and set buffers to minimum. I prefer at minimum Teensy 3.2

Unfortunately I have not found description for PGN 127233. The list of fields does not tell enough - we need bit by bit desrciption. I would use that too. On the other hand, have you checked that some device would listen that message? Older MFD versions does not listen it, so e.g. there is no help for my Garmin GMI 20.


which CAN IC is generally best to use with the Teensy 3.2 one?

I have currently MCP2551, MCP2562, MCP25515 and the SN65HVD234.

I try with the MCP2551 at VUSB or 3.3V but I don´t get any luck. I have set the bandwidth first to default 125K and than I tried 500K but got no traffic.

Is there any "finished" can shield wich works fine with the teensy´s?


I prefer MCP2562. It uses 5V power, but has pin 5 for defining IO-level. Check my example schematics https://github.com/ttlappalainen/NMEA2000/blob/master/Examples/TeensyActisenseListenerSender/Documents/Teensy_Actisense_listener_sender_schematics.pdf

Which badwidth you mean? NMEA2000 library has fixed bandwidth to NMEA 2000 bus speed 250kps.

Check this one: http://skpang.co.uk/catalog/teensy-canbus-breakout-board-include-teensy-32-p-1507.html


I think I was being hasty. I dragged out an old Windows laptop and tried your NMEA-Simulator and was able to get values to the MFD just fine. For a while - after some 15-20 seconds (guesstimate) traffic stops and the devices disappear. I wonder is that a feature or an issue in my hardware? I saw a mention that some Dues have problems sending data. Might that be related? If so, I also have an idle Teensy that I can repurpose for this project.

Now that I understand how the ActisenseListenerSender differs from the actual Actisense hardware, it's fine. I might still do the gateway change because I believe the Signal K plugin and canboat expect actual Actisense style operation. I'll be happy to make a PR for that.

There's now a rudimentary signalk-to-nmea2000 plugin available. Only very few PGNs are supported at the moment, but it doesn't seem to complicated to add any I might need. And I still believe I could get a simpler end result that way. I should get by with a simple ESP8266, a cheapo buck converter, a fuse holder and a piece of perf board as well as any sensor device I might be using. If I went to native N2k, I'd need at least a Teensy+tranceiver for each sensor (or sensor group), right?

Thanks for the quick response!



Using RPi directly would be tempting, but I'm also perfectly happy if I can get the Arduino working. :-D

I got now the MCP2551 working. I don´t know if this have a reason or if this is neccessary, but it didn´t worked until I added the CS pin to the configuration with #define N2k_SPI_CS_PIN 53  // Pin for SPI Can Select. As soon this was part of the config beside the Integer pin 21 and the clock set to 8 mhz it looked like everything is just fine.

By bandwith i ment the setting from the FlexCAN.h FlexCAN(uint32_t baud = 1000000, .... in the public part of the file. If this have no meaning than I guess it is like you said 250K. By default the setting from the flexcan is set to 500K. I just tested a bit with that number since I didn´t got any communication.

Finnally I am now at that point where I can say that I get a bit more familiar to the coding here. That was a long and also expensive journey:


I was so unlucky that I plugged the 12 V into the CAN bus connection so 1 SN65HVD234, 3 MCP2515 shields, 1 MCP2562 and finally one Geniu DUE went to sleep for ever. Was a nice firework. ;D

Another thing:


There exists an automatic steering for the TrimTabs from Bennett and another which also is NMEA2K comapatible to show the position of the tabs.
I am very interessted to build something like this as well as this part was very expensive even without NMEA2k and I have no idicator at all on my hydraulic tab at all.

I thought a bit about it, to indicate the position is no big deel and the orgin sensors are not so expensive as well. But the automasation will be a bit harder I guess. A lot of filtering due to waves and motions on the water which shuld not affect the position like making a turn for example.

Did anybody had something like this in mind also?

define N2k_SPI_CS_PIN 53 is the default setting even you do not use it.

DON´T RUN 12 V ON THE BUS! This is wrong. You can run 12 V. If you look my schema https://github.com/ttlappalainen/NMEA2000/blob/master/Documents/ArduinoDUE_CAN_with_MCP2562.pdf you will see there must be diode from 12 V.

yes I know you can run even more with some IC´s but not how I had it. There was some wrong connections otherwise it haven´t burned all. That was not ment really seriously. I guess it was a shortage of full current on one line of the bus to somewhere so it glowed away.

Hi Matti,

I passed you post yesterday.

Due problems were my fault - for some reason update for NMEA2000_due was not on public GitHub even it showed on my GUI that it has been sended. I have due and it has been working right. I can try to confirm it again with simulator in evening.

Difference between ActisenseListenerSender and original Actisense hw is on configurability. My code just forwards N2k messages to USB in Actisense format and opposite. On Actisense hw you can also send commands to enable specifig PGNs for both directions. These so called BEM messages uses same basic format and my code just skips them, since they have wrong “command” code. So in principle ActisenseListenerSender should work in both directions with any sw, which sends or reads data in Actisense format. If the other program would expect some response to BEM messages and does not continue, because it does not get anything, that can block it. But even Actisense own Actisense NMEA reader works fine with ActisenseListener program.

I may also use ESP8266 for some sensors in future. But for important information like engine and main tanks, I will use wired sensors. It is easier then to turn off WiFi to save power.

Hello Timo,

you ment to do add a noise filtering function to the analog input from the rudder position hall sensor.

Do you mean by that smoothing or averaging the signal?

Otherwise wouldn´t it be better to avoid noice by adding pulldown resistor and capacitor to ground?

The smallest amount of code to do an averaging is this example:

  float AverageValue = 0;
  int MeasurementsToAverage = 16;
  for(int i = 0; i < MeasurementsToAverage; ++i)
    AverageValue += MeasureValue();
  AverageValue /= MeasurementsToAverage;

  float AverageValue = 0;
  int MeasurementsToAverage = 16;
  for(int i = 0; i < MeasurementsToAverage; ++i)
    AverageValue += MeasureValue();
  AverageValue /= MeasurementsToAverage;

I know there are deays part of the code which have to eleminated.

What exactly do you mean by that?


That is not good way to average, since you should allways do something. You should never use delay() function withing loop or any function called on loop. Your example stops other processing for over 16 ms. During that time you can e.g. get 40 frames on N2k bus. It is ok, if you have at least 40 frame imput buffer. You think may that you are just sending data and not worried about incoming frames. But the way is just bad programming for this kind of systems. Situation is completely different on multitasking OS, where “delay” would mean that “I give up my task for a while” and OS takes care of running other tasks.

Also it is almost allways best do filtering with code - not to make it by hw. Hw filter is difficult to change.

The code below calculates average for rudder position without wasting any unnecessary time in averaging. So processor will run full speed all other tasks you call in loop(). Note that RudderSamplePeriod*RudderSampleCount must be < RudderSendPeriod .

#define RudderSendPeriod 100
#define RudderSamplePeriod 1
#define RudderSampleCount 40

// *****************************************************************************
void SendRudder() {
  static unsigned long RudderSendTime = millis() + RudderSendPeriod;
  static unsigned long RudderSampleTime = RudderSendTime-RudderSampleCount*RudderSamplePeriod;
  static double RudderSampleSum=0;
  static size_t RudderSamplesDone=0;
  tN2kMsg N2kMsg;
  if ( RudderSampleTime < millis() ) {
    RudderSampleSum += ReadRudderPosition();
    if ( RudderSendTime < RudderSampleTime ) {
      RudderSendTime += RudderSendPeriod;
      RudderSampleTime = RudderSendTime-RudderSampleCount*RudderSamplePeriod;
      double rudderValue=RudderSampleSum/RudderSamplesDone;
      Serial.print("Rudder value: "); Serial.println(rudderValue);
      SetN2kRudder(N2kMsg, DegToRad(rudderValue));
     } else {
       RudderSampleTime += RudderSamplePeriod;


I indeed tested with Actisense's NMEA Reader, and it was able to receive messages properly. Lots of errors in the beginning, presumedly due to failing mode settings etc.

I made an ArduinoGateway sketch that sets the product and device information and NMEA mode properly. I can see the device on my MFD, but I'm still struggling with the data formats. Would it be possible to get a bit of raw data similar to what NMEA-Simulator sends? I'm not sure where to get valid test data to send to the device. At the moment I'm trying to use Signal K and canboat to send data to the Arduino, and I suppose something is wrong with the data format because at least the MFD doesn't show anything. The data canboat is sending is as follows:


In all cases, the message type is 0x94. It isn't clear to me what its significance is.

I wonder where you did got the error messages? I have never seen any with NMEA Reader.

On your ArduinoGateway remember to use NMEA2000.SendMsg(N2kMsg); not NMEA2000.SendMsg(N2kMsg,-1); -1 Means that library uses source address set to N2kMsg instead of device address, which may vary due to address claim. If you use -1, it may be that your MFD does not like messages, which source is unknown. With Simulator it works, since it sends also device information.

I expect there is some preformatter between your string and final writing to USB. Actisense format is binary format, which starts with Escape (0x10), StartOfText (0x02) and ends to EndOfText (0x03). Data between is binary and also has to have right checksum. You can find full description of format on ActisenseReader.cpp, function GetMessageFromStream. 0x94 is Request command.

I also checked Arduino DUE and NMEA Simulator combination and it works fine. There is also some things you have to remember. If you use ActisenseListenerSender with DUE and Simulator and you do not read out - e.g. with NMEA Reader - data sended by DUE, it gets soon stucked. So you can either disable forwarding:


or set forward port to SerialUSB by:


With either of those settings I run DUE as sender and Mega as listener for over 30 min test with all data enabled on NMEA Simulator.

This is one reason I changed to Teensy, since their port does not get stuck, if it is not read out. They are also smaller and uses less power.


Excellent! The extra -1 argument was the final culprit. Thanks a lot - I'd never have been able to figure it out myself. I made a PR for the ArduinoGateway sketch.

Like you suggested, the messages I quoted indeed weren't the ones written to the serial port. And I believe I earlier ran into issue of Due getting stuck due to unread data. Pretty weird. I think I'll scavenge the Teensy I have and repurpose that into a gateway...

I was still able to get a couple of lines of "Update operating mode" resulting in a timeout on the NMEA Reader Log tab. But the reason for that is by now pretty self-evident.

Again, thanks a lot for your help.

See for yourself: location data from B&G Vulcan 7 is being output via ArduinoGateway on an Arduino Due to Signal K node server and then back. :-) System time originates from Signal K.