NMEA 2000 Shield

#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();
    delay(1);
  }
  AverageValue /= MeasurementsToAverage;

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

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

What exactly do you mean by that?

Regards..

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();
    RudderSamplesDone++;
    if ( RudderSendTime < RudderSampleTime ) {
      RudderSendTime += RudderSendPeriod;
      RudderSampleTime = RudderSendTime-RudderSampleCount*RudderSamplePeriod;
      double rudderValue=RudderSampleSum/RudderSamplesDone;
      RudderSampleSum=0;
      RudderSamplesDone=0;
      Serial.print("Rudder value: "); Serial.println(rudderValue);
      SetN2kRudder(N2kMsg, DegToRad(rudderValue));
      NMEA2000.SendMsg(N2kMsg);
     } else {
       RudderSampleTime += RudderSamplePeriod;
     }
  }
}

Hi,

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:

7,129025,1,255,8,61,72,e8,23,40,37,da,0e
3,126992,1,255,8,ff,ff,84,44,70,9d,3b,2a

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:

NMEA2000.EnableForward(false);

or set forward port to SerialUSB by:

#define FORWARD_STREAM SerialUSB

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.

Hi!

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. :slight_smile: 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.

Hello,

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?

Regards..

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:

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

pN2kDeviceList->FindDeviceByName(name);

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:

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.

NMEA2000_Rx_Waypoint_0_0.ino (2.73 KB)

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.

Timo, thank you for new information.
For me PGN 129284 is sufficient.

I have a Lowrance HDS9, and I checkt how PGN´s 129284, 129285, 130074 are working:

129284: HDS is sending this, as far as I navigate to a waypoint. A route is not necessary.

130074: is send automatically, as far as I save a new waypoint.

129285: is send automatically, as far a route is active.