Go Down

Topic: NMEA 2000 Shield (Read 386500 times) previous topic - next topic


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 .

Code: [Select]

#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:

Code: [Select]


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:
Code: [Select]
or set forward port to SerialUSB by:
Code: [Select]
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.


Well done!

The next step would be to implement library already to RPi with PiCAN2, so you could skip one extra "Arduino" - and save power for sailboaters. I just have not had time to check that. The one problem, which may appear is sending fastpacket messages. I am afraid that Socket CAN just sends frames from three mailboxes, which causes frame order to be mixed, which is not allowed with fastpackets like product information. That is why I had to improve all other can libraries mcp_can, due_can and FlexCan. On the other hand it could be possible to implement mcp_can directly for RPi instead of using Socket CAN. PiCAN2 board just has same chips MCP2515-MCP2551, so it should not be that hard.

I have forgot "Update operating mode" errros. They are simply because NMEA Reader tries to send BEM messages to define some setting to NGT-1 and since I do not response, it shows errors. Also NMEA Simulator has possiblity to enable it to send "enable all PGN output" BEM message, so that it works also with NGT-1. And actually Signal-K should do that too so that it can send data to bus.


- Using FRAM to log data.


I thought using a memory to log some NMEA data like operating hours or some gps points for example.

So I posted a question about what would be the best solution to do so.

The answer was FRAM and since I didn´t know so much about I did some research on it and it sounds like the best solution sincee it is fast, don´t need battery to hold the data and have more write cycles then most other like sd card eeprom and so on. Also what is very important is that there is no cycle / data lost due to high or very low temperatur.

The operating hours should of course also be send on the bus after they are counted and stored so the question is when is the best moment to count and save the data?
Should be an extra board or is that so litle that it doesn´t matter and could be done on the board which generates all the engine data? (I´d like to have all engine data available on the bus so I use one Teensy3.X to do that.)

Any comments and ideas?



Ho can I read PGN 126996 (Product Information) ?

Or, maybe better, how can I check, if one of two similar devices is alive on the bus?


Hopefully you have enough memory on your system. tN2kDeviceList class is for that purpose and there is example DeviceAnalyzer, where that has been used. You just create object:
Code: [Select]

pN2kDeviceList = new tN2kDeviceList(&NMEA2000);

and that automatically catches new devices and device changes on the bus. In well made system you should glue your data to NMEA NAME. You can e.g. have two devices sending "PGN 127505 Fluid Level" information from different tanks. And both may have same instance values. So the only way is to separate them by source. But due to address claiming, source may change. So here you can then use
Code: [Select]
and you can then get tNMEA2000::tDevice, where you have all information - also source.
You have to also periodically check ReadResetIsListUpdated and if that returs true, you check your watch devices source again.

The class does not have any enumeration function, but due to structure it is as effient to to use:
Code: [Select]
for ( size_t i=0; i<=N2kMaxCanBusAddress; i++) {
  const tNMEA2000::tDevice *pDevice=pN2kDeviceList->FindDeviceBySource(i);
  if ( pDevice!=0 ) {
    // Do something

Note that on tNMEA2000::tDevice you can also read PGN:s they transmits and all other information available from device.

I prefer to have at least Due for using tDeviceList. I have tested it on Mega, but on bigger system memory runs out. Again with Teensy 3.2 you can do a lot more.


Timo, thank you for fast reply.
That is, at least at the moment, too complex for me.
I do not understand how the classes are working.
If you can recommend documents to study this stuff, that would be helpful.


What document you mean? About c++ classes, NMEA2000 library or NMEA2000 protocol? For NMEA2000 library there the one inside documents, but there is not much about tDeviceList. For NMEA2000 protocol I have not found good free document. Information is around in small pieces.

But using tDeviceList is far more easier than try to do it yourself from the scratch. And about your original question "if one of two similar devices is alive on the bus", what you actually mean with that? How do you define "similar device"? Do you know their product code? Or do you know the PGN, they should send? For both questions tDeviceList is an simple solution, because it collects all device information automatically just by joining it to NMEA2000 object as I wrote earlier and as it is on DeviceAnalyzer.

I prefer you to study how to use classes. You do not need to write them, but you need to use them.


Timo, my problem is my missing knowledge with c++ classes.
So, first I have to look for a c++ book and do my homework.

Maybe you can help me with the following problem:

I can read  PGN 129284 NavigationInfo with Actisense Listener, all 15 fields have the correct information.

But, with the following sketch all fields have wrong info, for instance, SID is always 255.
PGN 126992 SystemTime is working correct.


There was a bug in ParseN2kPGN129284. I updated the fix to the library.


Timo, thanks a lot,  it is working fine now.

I also tried PGN129285 and PGN130074 to get position of the next waypoint.
But, with this two PGN´s I got a compiling error with the line
if (ParseN2k...

As I can see in N2kMessages.H,  this data is probably stored in a database.

Do yo have an example, how to read such a database?


Unfortunately there is no parser functions for those PGN:s written. Also noticed that I should have been more carefull on accepting merge for those messages, since the writer comments does not match to then parameter list.

There is no database on NMEA2000 protocol. Protocol is just for exchanging data between devices. So the database, all database and route ID:s are manufacturer specific.

Also both of those messages are rather complicate to handle. I do not have NMEA2000 plotter for testing waypoint data exchanging, so I am not how they do it. NMEA2000 fastpacket can carry only few waypoints. So they either send several fastpackets for single route so that you separate data by waypoint start ID. Other possiblity is to use transport protocol, which can move about 1800 bytes data. But even that gets full with long route. And on the other hand currently library does not support large transport protocol packets.

For the 130074 document says "Complex request of this PGN should return the Waypoints of a WP-List". So the devices does not send that automatically. Also other waypoint/route information will be returned with "Complex request" - and I really agree the name of request.

For 129285 document says "It can be requested or may be transmitted without a
request, typically at each Waypoint advance." So also this is rather complex and has same problems as other waypoint/route information PGNs.

Go Up