NMEA 2000 Shield

#include 
#include 

MCP_CAN CAN0(53);                                      // Set CS to pin 53
//MCP_CAN CAN0(2); //for my new prototype shield
unsigned char stmp[8] = {0, 0xA0, 0x41, 0, 0, 0, 0, 0};
unsigned char extpgn[26] = {0, 0xA0, 0x41, 0, 0, 0, 0, 0};
void setup(){
  Serial.begin(115200);
  // init can bus, baudrate: 250k
  if(CAN0.begin(CAN_250KBPS) == CAN_OK) Serial.print("can init ok!!\r\n");
  else Serial.print("Can init fail!!\r\n");
  //testPGN127489();
  //transmitPGN127489(0,55.5,164,171,13.54,15.8,221,11.2,32.1,38,44,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
  testRPM();
}

void loop(){  
  //PGN 127488 binary 011111001000000000
  //for id try 110 + above + 00000000 = 11001111100100000000000000000 = hex 19F20000
  //highest priority = 000 try 00001111100100000000000000000 = hex 1F20000 = dec 32636928
  
  //PGN 127489 binary 011111001000000001
  //for id try 00001111100100000000100000000 = hex 1F20100 = dec 32637184
  
  // send data:  id = 0x00, extended frame, data len = 8, stmp: data buf
  CAN0.sendMsgBuf(32636928L, 1, 8, stmp);  
  
  //testPGN127489();
  //transmitPGN127489(0,55.5,164,171,13.54,15.8,221,11.2,32.1,38,44,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);  //with checkEngine flag set to true
  transmitPGN127489(0,55.5,164,171,13.54,15.8,221,11.2,32.1,38,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);  //with checkEngine flag set to false
  
  delay(1000);                       
}
void testPGN127489(){
  //if an engine parameter is not available, set it to 255
  unsigned char extpgn[26] = {0, 208, 14, 170, 13, 151, 136, 236, 4, 37, 2, 112, 207, 11, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 255, 255};  //decimal values
  unsigned char temp[8] = {0,0,0,0,0,0,0,0};
  //
  
  int cur=0;
  for(int i = 0; i<=3; i++){
      temp[0] = i; //frame counter
      if (i==0){
          temp[1] = 26; //total bytes in fast packet
          //send the first 6 bytes
          for(int j = 2; j<8; j++){  
               temp[j]=extpgn[cur];
               cur++;   
           } 
      } else{
           for(int j = 1; j<8; j++){  
               temp[j]=255;
           }
           //send the next 7 data bytes
           for(int j = 1; j<8; j++){  
               temp[j]=extpgn[cur];
               cur++;   
           } 
      }
      CAN0.sendMsgBuf(32637184L, 1, 8, temp);  
      delay(1);
  }
}
void testRPM(){
  unsigned char stmp[8] = {0, 0xA0, 0x41, 0, 0, 0, 0, 0};
  int rpm;
  for(int i = 0; i<=4200; i=i+100){
      rpm=(i*4);
      stmp[2]=rpm/256;
      stmp[1]=rpm-(stmp[2]*256);
      CAN0.sendMsgBuf(32636928L, 1, 8, stmp);  
      delay(100);
  }
}
void transmitPGN127489(int engineInstance, float engineOilPress, float engineOilTemp, float engineCoolantTemp, float battery, 
  float fuelRate, float engineHours, float engineCoolantPress, float engineFuelPress, float engineLoad, float engineTorque, bool flagCheckEngine, 
  bool flagOverTemp, bool flagLowOilPress, bool flagLowOilLevel, bool flagLowFuelPress, bool flagLowSystemVoltage, bool flagLowCoolantLevel,
  bool flagWaterFlow, bool flagWaterInFuel, bool flagChargeIndicator, bool flagPreheatIndicator, bool flagHighBoostPress, bool flagRevLimitExceeded,
  bool flagEgrSystem, bool flagTPS, bool flagEmergencyStopMode, bool flagWarning1, bool flagWarning2, bool flagPowerReduction, 
  bool flagMaintenanceNeeded, bool flagEngineCommError, bool flagSubThrottle, bool flagNeutralStartProtect, bool flagEngineShuttingDown){
  //if an engine parameter is not available, set it to 255
  unsigned int extpgn[26]; // {0, 208, 14, 170, 13, 151, 136, 236, 4, 37, 2, 112, 207, 11, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 255, 255};  //decimal values
  unsigned char temp[8]; // {0,0,0,0,0,0,0,0};
  float v;
  extpgn[0]=engineInstance;
  
  v=engineOilPress;
  v=v*68.94757;
  //extpgn[2]=v/256;
  //extpgn[1]=v-(extpgn[2]*256);
  extpgn[2]=highByte((int) v);
  extpgn[1]=lowByte((int) v);  //this works well. I should change the code below to use this format.
  
  v=engineOilTemp;
  v=(((v-32)*5/9)+273)*100;
  extpgn[4]=v/256;
  extpgn[3]=v-(extpgn[4]*256);
  
  v=engineCoolantTemp;
  v=(((v-32)*5/9)+273)*100;
  extpgn[6]=v/256;
  extpgn[5]=v-(extpgn[6]*256);
  
  v=battery;
  v=v*100;
  extpgn[8]=v/256;
  extpgn[7]=v-(extpgn[8]*256);
  
  v=fuelRate;
  v=v*37.8541;
  extpgn[10]=v/256;
  extpgn[9]=v-(extpgn[10]*256);
  
  v=engineHours;
  v=v*3600;
  //extpgn[14]=v/16777216;
  //extpgn[13]=(v-(extpgn[14]*16777216))/65536;
  //extpgn[12]=(v-((extpgn[14]*16777216)+(extpgn[13]*65536)))/256;
  //extpgn[11]=v-((extpgn[14]*16777216)+(extpgn[13]*65536)+(extpgn[12]*256));
  int tempVar = (long) v >> 16;
  extpgn[14]=highByte(tempVar);
  extpgn[13]=lowByte(tempVar);
  tempVar = (long) v & 0xFFFF;
  extpgn[12]=highByte(tempVar);
  extpgn[11]=lowByte(tempVar);
  
  v=engineCoolantPress;
  v=v*68.94757;
  extpgn[16]=v/256;
  extpgn[15]=v-(extpgn[16]*256);
  
  v=engineFuelPress;
  v=v*689.4757;
  extpgn[18]=v/256;
  extpgn[17]=v-(extpgn[18]*256);
  
  extpgn[19] = 255; //nmea2000 reserved byte
  
  int engineStatus1P1 = B00000000;
  int engineStatus1P2 = B00000000;
  int engineStatus2 = B00000000;
  if (flagCheckEngine) engineStatus1P1 |= B00000001;
  if (flagOverTemp) engineStatus1P1 |= B00000010;
  if (flagLowOilPress) engineStatus1P1 |= B00000100;
  if (flagLowOilLevel) engineStatus1P1 |= B00001000;
  if (flagLowFuelPress) engineStatus1P1 |= B00010000;
  if (flagLowSystemVoltage) engineStatus1P1 |= B00100000;
  if (flagLowCoolantLevel) engineStatus1P1 |= B01000000;
  if (flagWaterFlow) engineStatus1P1 |= B10000000;
  
  if (flagWaterInFuel) engineStatus1P2 |= B00000001;
  if (flagChargeIndicator) engineStatus1P2 |= B00000010;
  if (flagPreheatIndicator) engineStatus1P2 |= B00000100;
  if (flagHighBoostPress) engineStatus1P2 |= B00001000;
  if (flagRevLimitExceeded) engineStatus1P2 |= B00010000;
  if (flagEgrSystem) engineStatus1P2 |= B00100000;
  if (flagTPS) engineStatus1P2 |= B01000000;
  if (flagEmergencyStopMode) engineStatus1P2 |= B10000000;
  
  if (flagWarning1) engineStatus2 |= B00000001;
  if (flagWarning2) engineStatus2 |= B00000010;
  if (flagPowerReduction) engineStatus2 |= B00000100;
  if (flagMaintenanceNeeded) engineStatus2 |= B00001000;
  if (flagEngineCommError) engineStatus2 |= B00010000;
  if (flagSubThrottle) engineStatus2 |= B00100000;
  if (flagNeutralStartProtect) engineStatus2 |= B01000000;
  if (flagEngineShuttingDown) engineStatus2 |= B10000000;
  
  extpgn[20]=engineStatus1P1;
  extpgn[21]=engineStatus1P2;
  extpgn[22]=engineStatus2;
  extpgn[23]=0;
  
  extpgn[24]=engineLoad;
  
  extpgn[25]=engineTorque;
  
  int cur=0;
  for(int i = 0; i<=3; i++){
      temp[0] = i; //frame counter
      if (i==0){
          temp[1] = 26; //total bytes in fast packet
          //send the first 6 bytes
          for(int j = 2; j<8; j++){  
               temp[j]=extpgn[cur];
               cur++;   
           } 
      } else{
           for(int j = 1; j<8; j++){  
               temp[j]=255;
           }
           //send the next 7 data bytes
           for(int j = 1; j<8; j++){  
               temp[j]=extpgn[cur];
               cur++;   
           } 
      } 
      CAN0.sendMsgBuf(32637184L, 1, 8, temp);  
      delay(1);
  }
  //for debugging
  /*
  Serial.println();
  Serial.print("Data = ");
  for(int j = 0; j<26; j++){  
    if (j<25){
      Serial.print(extpgn[j]);
      Serial.print(", ");
    } else {
      Serial.println(extpgn[j]);
    }
  }
  Serial.print("HexData = ");
  for(int j = 0; j<26; j++){  
    if (j<25){
      Serial.print(extpgn[j],HEX);
      Serial.print(", ");
    } else {
      Serial.println(extpgn[j],HEX);
    }
  }
  */
}

Thank you for your reply. Here is my current code for this part :

 // ++++++++++++++++++++++++++++++++++++ Engine Parameters, Dynamic ++++++++++++++++++++++++++++++++++++
  updateRate = 500; // 2 * / s
  priorite = 5;
  PGN = 127489;  // Paramètres environnementaux bus NMEA 2000 (N2K)
  source = 35;   // Airmar = 35
  adresse = priorite * 67108864 + PGN * 256 + source;
  float tempwc =  95; //TWEng;  // Celsius  Engine Water Temp
  long tempwk =  100 * ( tempwc + 273.15 );
  long tempw1 = ( tempwk & 0b1111111100000000 ) >> 8;  // mise en forme format CAN 16bits
  long tempw2 =   tempwk & 0b0000000011111111;

  float PressEng = 200;  // Lecture capteur pression resolution = 8 bits pour essai.
  long PressEng_ = 20 * PressEng;
  long PressEng1 = ( PressEng_ & 0b1111111100000000 ) >> 8;  // mise en forme format CAN 16bits
  long PressEng2 =   PressEng_ & 0b0000000011111111;

  message.id = adresse;
  message.ide = 1;
  message.rtr = 0;
  message.dlc = 8;
  message.data[0] = 0;   //  SID 
  message.data[1] = 0;   //  Engine instance  8bits    0=Single Engine
  message.data[2] = 0;   //  doit etre  à 0 pour lecture 2 octets plus bas. Oil press = 0 si 255
  message.data[3] = PressEng2; //PressEng2;   
  message.data[4] = PressEng1; //PressEng1;   //  Oil   Press huile 1=0,3  2=0,5  3=0,8  4=1b  28=7,2b
  message.data[5] = 255;   //  ????
  message.data[6] = tempw2;   //  Temperature eau moteur - 16 bits (sonde DS18B20)
  message.data[7] = tempw1;   //  Temperature eau 
 /* Serial.print("TEMPERATURE EAU MOTEUR : ");
 Serial.print(tempw2);  Serial.print(" - ");
 Serial.println(tempw1);
 */
  SendPaquet();

  message.data[1] = 1;  // 1 paquet supplémentaire
  message.data[2] = 0;  // Alternateur
  message.data[3] = 0;  // Alt
  message.data[4] = 0;  // Fuel rate
  message.data[5] = 0;  // Fuel rate
  message.data[6] = 1;  // Hour
  message.data[7] = 0;  // Hour
  message.data[8] = 0;  // Hour

  SendPaquet(); 

  message.data[1] = 2;  // 1 paquet supplémentaire
  message.data[2] = 4;  // Hour
  message.data[3] = 0;  // Coolant press
  message.data[4] = 0;  // Coolant press
  message.data[5] = 0;  // Fuel press
  message.data[6] = 0;  // Fuel press
  message.data[7] = 0;  // reserved
  message.data[8] = 0;  // Discrete Status 1

  SendPaquet();

  message.data[1] = 3;  // 1 paquet supplémentaire
  message.data[2] = 0;  // Discrete Status 1
  message.data[3] = 0;  // Discrete Status 2
  message.data[4] = 0;  // Discrete Status 2
  message.data[5] = 0;  // Percent Engine Load
  message.data[6] = 0;  // Percent Engine Torque

  SendPaquet();

  delay(updateRate);

allen652: I am using non-certified NMEA2000 cabling products that I had manufactured for my current project. I have several available for sale below if interested. See images. 2 meter cable (Male to Female) - $7.5 each T adapter - $7 each Male rear panel mount connector - $5 each

I have found that NEMA2000 is hardware wise follows the older DEVICENET so many parts interchange with out issues.

Compliments, Beautiful Code. But I have to try to translate Arduino uno.

I did not see how you code: address = priority * 67108864 * 256 + PGN + source;

I should probably comment my code better (and more often).

jpjcb66: The first 3 bits of the id is the priority, the next 18 bits are the PGN, and the last 8 bits are the source address.
See the comments in my code above in void loop().

priority = 000
PGN = 011111001000000000
source = 00000000

00001111100100000000000000000 binary = 32636928 decimal

// send data:  id , extended frame, data length = 8, data buffer
  CAN0.sendMsgBuf(32636928L, 1, 8, stmp);

@allen652

I understand ! You put "source" = 0x0 Code Airmar = 0x25 (35dec). There exists an array of manufacturer codes ? For example what is the Volvo Penta code ?

Your code is perfect, thank you infinitely. jp

Hi friends I want to read 2 periodic data from maretron WSO100 module. PGN #130306 for wind data & #130310 for environmental parameters. As I read this PGNs send periodically every 100 ms & 500 ms... I use LPC1768 controller and do this steps to read wind data: 1) set CAN Controller baud rate to 250 kbit/sec 2) set ID to 130306(0x01FD02) in extended format 3) read can receive buff... But I don't receive any data on bus!!! what should I do??? what's wrong??? please help me... Thanks a lot

Hi,

Want to read NEMA2000 stream from a Garmin Depth founder with a Arduino Due.

Just this device on NEMA network.

Did someone have code reference ?

:D

Hi,

I'm about to buy a "triducer", the Airmar DST800. It's going to send depth/temp/speed via NMEA2000. As a java programmer with zero experience with Arduinos (but lot of time to spend on it), do you think I have a chance, using existing resources, to decode these informations and display it on a e-ink display shield ? I don't absolutely need a ready-to-use solution, and a simple "yes you can" would be much appreciated to preserve my motivation ^^ Of course, I'll be back later with more accurate questions ;)

Yes you can.
Only challenge is parsing the NMEA data stream to pick out the pertinent info.
And then sorting out the e-ink interface. Probably just another serial data stream?

Thank you CrossRoads, I have an idea about the parsing challenge, and I guess I can use some existing code that parse GPS datas and adapt it. The e-ink display does not scares me that much, because if I don't succeed, I still have other display options. I'll put my code somewhere on this forum if I reach my goal =) See you !

Hi All,

I have been trying to find NMEA2000 shield without success, so I wrote my own. It can act as listener only and forward data to PC in Actisense or clear text format. Or it can act as Termperature sensor node with automatic address claiming. Or as I am making it as a NMEA0183 combiner/ NMEA200 converter and sensor node.

Library works with Mega board with MCP2551 chip or with Due board with its internal CAN controller.

Anybody interested?

Great timing, I just signed up to this forum and started looking for a library today !

Please count me in.

I am building a LiFePo4 alternator regulator and an active charge/monitor BMS and would love to have a full NMEA2k interface to connect my Maretron/Raymarine network.

Thanks,

Rick

Bonjour

Here is the sketch I have written to display the battery voltage on my boat.
It sends the PGN 127508 thru the NMEA 2000 network.
The voltage (Only one battery, Battery number 1= instance 0) can be read on:

  • Raymarine A78 MFD
  • B&G Triton

The Arduino+Shield (Seeedunio) pack is directly powered by the 12v supply

The voltage is read by the A0 pin through a voltage divider made with 2 resistors

It works well and seems to be reliable, but sometime the INT red led turns on solid. However it keeps on running, the network does’nt hang up.

Amicalement.
François-Xavier

Voltmetre_N2K_V2S.ino (7.29 KB)

Voltmetre NMEA2000 Arduino sur A78 s .jpg

Voltmetre NMEA2000 Arduino sur Triton T41 s.jpg

Hi,

You can find NMEA2000 library and samples on https://github.com/ttlappalainen. The best would be to use Arduino Due, since it needs only MCP2562 tranceiver for connecting to the bus. It also has 8 byte double for better calculations.

My own setup is running and it has also 3 NMEA0183 inputs for combiner and tranfer data to N2kBus. So with Arduino Due and 4 extra chips this replaces 4 devices: NMEA0183 combiner (e.g. Brookhouse), NME0183->N2k (Actisense), N2k->PC (Actisense), MOB device.

Bonjour,

Good job Timo.

I have not understood everything but it is very interesting.

Amicalement. François

Timo,

Can you provide any pictures or other details of your hardware setup? How you hooked everything together. VERY interested!

Paul

timolappalainen: Hi All,

I have been trying to find NMEA2000 shield without success, so I wrote my own. It can act as listener only and forward data to PC in Actisense or clear text format. Or it can act as Termperature sensor node with automatic address claiming. Or as I am making it as a NMEA0183 combiner/ NMEA200 converter and sensor node.

Library works with Mega board with MCP2551 chip or with Due board with its internal CAN controller.

Anybody interested?

Timo,

Hello. Wanted to let you know I have just posted up a very rough cut of a NMEA-2000 / Alternator regulator bridge based on your lib. I made some extensions to include some additional PGNs - and once proofed out a bit will submit a pull back to your get-hub lib.

This bridge is intended to run on a Due, and takes the ASCII status strings from my Arduino based Alternator Regulator and send them out to the NMEA-2000 bus. At this point is is rather unproven as I lack some additional hardware (specifically, the NEMA-2000 display side), am not settled on which PGNs should be broadcasted, and I am not totally sure I have extended your libs correctly :o

Source, extended libs (draft form), as well as a bit more details here: http://arduinoalternatorregulator.blogspot.com/2015/09/very-rough-nmea-2000-bridge-source.html

Looking forward to perhaps getting this a bit more settled.

Hi PogCarr and thomasow,

I'll try to upgrade GitHub with pictures and extension as soon as possible. I have been a bit busy with othe project.

Timo

Hi all,

Now there is upgraded version of NMEA2000 library and some drawings under Documents in https://github.com/ttlappalainen/NMEA2000