Go Down

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

pkeiramo

Has someone been lucky with PGN 127489 and B&G Triton T41? I have an issue with that PGN, T41 does not show it.

Here is my whole code:

Code: [Select]
// NMEA2000 library. Send engine data to the bus.

#include <Arduino.h>
#include <NMEA2000_CAN.h>  // This will automatically choose right CAN library and create suitable NMEA2000 object
#include <N2kMessages.h>
 

// List here messages your device will transmit.
const unsigned long TransmitMessages[] PROGMEM={60928UL,127489UL,127505UL,0};

void setup() {
  // Set Product information
  NMEA2000.SetProductInformation("112233", // Manufacturer's Model serial code
                                 100, // Manufacturer's product code
                                 "Engine monitor",  // Manufacturer's Model ID
                                 "1.1.0.21 (2016-12-31)",  // Manufacturer's Software version code
                                 "1.1.0.0 (2016-12-31)" // Manufacturer's Model version
                                 );
  // Set device information
  NMEA2000.SetDeviceInformation(112233, // Unique number. Use e.g. Serial number.
                                140, // Device function=Engine. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                50, // Device class=Propulsion. See codes on  http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                2040 // Just choosen free from code list on http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf                               
                               );

  // Uncomment 2 rows below to see, what device will send to bus. Use e.g. OpenSkipper or Actisense NMEA Reader                           
  Serial.begin(115200);
  NMEA2000.SetForwardStream(&Serial);
  // If you want to use simple ascii monitor like Arduino Serial Monitor, uncomment next line
  //NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); // Show in clear text. Leave uncommented for default Actisense format.

  // If you also want to see all traffic on the bus use N2km_ListenAndNode instead of N2km_NodeOnly below
  NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly,22);
  //NMEA2000.SetDebugMode(tNMEA2000::dm_Actisense); // Uncomment this, so you can test code without CAN bus chips on Arduino Mega
  //NMEA2000.EnableForward(false); // Disable all msg forwarding to USB (=Serial)
  // Here we tell library, which PGNs we transmit
  NMEA2000.ExtendTransmitMessages(TransmitMessages);
  NMEA2000.Open();
}


void loop() {
  SendN2kTankData();
  SendN2kEngineData();
  SendAddress();
  NMEA2000.ParseMessages();
}

double ReadOilPressure() {
  return mBarToPascal(2870); // fixed value for testing
}

double ReadEngineCoolantTemp() {
  return CToKelvin(85); // fixed value for testing
}

double ReadAlternatorVoltage() {
  return 13.7; // fixed value for testing
}

double ReadFuelRate() {
  return N2kDoubleNA;
}

#define EngineDataUpdatePeriod 500

void SendN2kEngineData() {
  static unsigned long EngineDataUpdated=millis();
  tN2kMsg N2kMsg;

  if ( EngineDataUpdated+EngineDataUpdatePeriod<millis() ) {
    EngineDataUpdated=millis();
    SetN2kEngineDynamicParam(N2kMsg, 0, ReadOilPressure(), N2kDoubleNA, ReadEngineCoolantTemp(), ReadAlternatorVoltage(), ReadFuelRate(), N2kDoubleNA);
    NMEA2000.SendMsg(N2kMsg);   
  }
}

double ReadTankLevel() {
  return 55; // fixed value for testing
}

#define TankDataUpdatePeriod 2500
#define TankCapacity 75

void SendN2kTankData() {
  static unsigned long TankDataUpdated=millis();
  tN2kMsg N2kMsg;

  if ( TankDataUpdated+TankDataUpdatePeriod<millis() ) {
    TankDataUpdated=millis();
    SetN2kFluidLevel(N2kMsg, 0, N2kft_Fuel, ReadTankLevel(), TankCapacity);
    NMEA2000.SendMsg(N2kMsg);   
  }
}

#define IsoAddressClaimUpdatePeriod 30000

void SendAddress() {
  static unsigned long IsoAddressClaimUpdated=millis();
  if ( IsoAddressClaimUpdated+IsoAddressClaimUpdatePeriod<millis() ) {
    IsoAddressClaimUpdated=millis();
    NMEA2000.SendIsoAddressClaim();
  }
}


PGN 127505 is ok, T41 shows it (it is fuel amount).

Do anyone have ideas?

timolappalainen

I have B&G Triton 2 and it reads MessageSender example hard coded data:
    SetN2kEngineDynamicParam(N2kMsg,0,656000,CToKelvin(86.3),CToKelvin(82.1),14.21,5.67,hToSeconds(2137.55),N2kDoubleNA,N2kDoubleNA,N2kInt8NA,N2kInt8NA,true);

There should not be changes, which effect for that, but please download latest library update and try with hard coded data.

An other thing may effect is that your send buffer may be filled up. Depending of board and you sw, increase send buffer at beginning of setup with command:
Code: [Select]
NMEA2000.SetN2kCANSendFrameBufSize(100);
Default size is 40. As I see you do not send that much data, so this should not be the case.

pkeiramo

#812
Oct 21, 2019, 08:21 pm Last Edit: Oct 21, 2019, 08:23 pm by pkeiramo
Timo, thanks for your advices!

I copied your hard coded SetN2kEngineDynamicParam clause from MessageSender example to my code. No success.

I downloaded and installed latest versions from NMEA2000 and NMEA2000_esp32 libraries. No success.

I set NMEA2000.SetN2kCANSendFrameBufSize(100); No success.

I tried your whole MessageSender example. It worked.

I copied NMEA2000.SetProductInformation and NMEA2000.SetDeviceInformation clauses from your MessageSender example to my code. It worked.

I tried with my original SetN2kEngineDynamicParam clause. It worked.

So there is something wrong with my original product and device information. It is also weird that now my mfd does not show my device as source for engine data but it shows right values in right fields.

timolappalainen

Weird.

Please try find some logic for that. I'll expect that only SetDeviceInformation will have effect.  SetDeviceInformation data is just informative.

You could also try add fake sending of Engine rapid data PGN 127488UL. Just set all data fields to NA, but set engine instance field same as on your other engine data.

It is also possible that if tell your function as engine, MFD will request for PGN 127498 "Engine static data" and when it does not get it, it does not listen your engine data. You could try to see requests by setting your device mode to N2km_ListenAndNode and listen your USB with Actisense NMEA Reader. You may see PGN 59904 from MFD with data 127498. The problem is that there will be so many requests that data will change, before you notice it. One way is to put NMEA Reader to record mode File-"Log connection data" and then parse log with Actisense EBL Reader.

Other way is to add use NMEA2000.SetISORqstHandler and write handler function for that. Then if RequestedPGN is 127498, you print a message.

Note that you do not need to have 60928UL on transmit messages, since that is system message and listed as default.

pkeiramo

Now it works :)

It needs also PGN 127488 (Engine Rapid Data) to be sent (can be without values, except engine instance). Then it reads product  and device information right and shows my device as source for engine data in Triton T41.

I set my device mode to N2km_ListenAndNode as you adviced and listened USB with Actisense NMEA Reader. MFD sends PGN 59904 twice and asks for 126996 (Product Information) and 60928 (ISO Address Claim). Device sends them and then everything is ok.

This is a weird world. Thank you, Timo

timolappalainen

I had a bit similar with my Raymarine autopilot. It did not work with True Heading message from my GPS compass, so I programmed my converter box to send Magnetic Heading with Variation correction. Stupid thing to correct wrong direction. Then on other boat I noticed they are sending Variation also on own PGN. I tested that and got it working with HDT message without stupid conversion.

Also in your code you send 60928 ISO Address claim periodically. No device should require that, since it should be automatic and can be requested with 59904 if necessary. So you could test without. I know that some older Garmin did not work without <5 s periodic sending. I have GMI 20 and it works fine and time to time requests it.

skyjumper

Hi Timo and all...

I'm confused about how to use the n2kMsg.Add4ByteUDouble() method.

How should the precision parameter be used? I'm trying to send a float that looks like 7.25 or 15.18 and I have tried using 2e-4 and 10.01 but I don't seem to get a proper result. If I manually convert the float value to a 4 byte hex number and send the bytes one at a time using AddByte() it works.

Thanks...

pkeiramo

Also in your code you send 60928 ISO Address claim periodically. No device should require that, since it should be automatic and can be requested with 59904 if necessary. So you could test without.
Yes, I removed 60928 regular sending and it works fine.

timolappalainen

As in e.g. SetN2kPGN128267 you set N2kMsg.Add4ByteUDouble(DepthBelowTransducer,0.01); You provide DepthBelowTransducer in real value in prefered units - in this case meters. On set you define precision how it will be converted to integer - in this case 0.01 unit. These precisions has been defined by NMEA2000. So if you are sending standard PGN, you have to use right precision.

On read side you use same precision e.g. DepthBelowTransducer=N2kMsg.Get4ByteUDouble(0.01,Index);

Which PGN you are trying to create?

skyjumper

Thanks Timo...

I'm trying to build a fast packet 126720.

Part of the packet is a speed field, transmitted in meters per second. If I use this line of code:


Code: [Select]
float speed = 9.87;
float msSpeed = KnotsToMs(speed);
n2kMsg.Add4ByteUDouble(msSpeed, 0.01);


Same result is I use:

Code: [Select]
n2kMsg.Add4ByteDouble(msSpeed, 0.01);



The device will display 0.00.

This code works:

Code: [Select]
uint8_t *test = (uint8_t*) &msSpeed;
int i = 0;
while(i != 4)
{
   n2kMsg.AddByte(test[i++]);
}



timolappalainen

PGN 126720 and there is no descriptions how each manufacturer uses content of those messages. So they may have also defined that value will be directly float bytes as you are now setting. You need to lock that definition to that specific manufacturer code. See e.g. module N2kMaretron.h/.cpp in library.

Note that float representation is not same over all architectures.

If you get that working, I am interested to see final code, if you want to publish it.

skyjumper

If you get that working, I am interested to see final code, if you want to publish it.

Thanks again Timo, if I get it working I'll give you the code.

pagoda54

Slight shift of emphasis.

What is the experience of using opto and power isolated Can-Bus transceivers to reduce problems with possible ground loops or common-mode voltage problems interfacing between home brew electronics and commercial gear which may be isolated?

Is it worth doing?

timolappalainen

YES.

NMEA2000 certification requires isolation. So if you are generating ground loop, you should use isolated tranceiver (e.g. ISO1050) and either isolated PSU or separate power feed on your device NMEA2000 side. If you do not do isolation you may have funny problems, which are difficult to find. The most critical are those, which start to disturb communication on the bus. Even you do not need certification it is better to think carefully in advance.

Wiki provides good article about ground loop, so there is no need to repeat anything about that subject.

When you desing your device you should check are you going to have ground loop in your system. If you simply measure e.g. temperature with separate isolated sensor,  you can power your device from N2k bus. If you measure e.g. from pressure sensor and it is not isolated from ground, you should have isolation on your N2k side. If you measure digital states, you can use optoisolators on your digital inputs and power your device from N2k side.

On boat all your device will be normally powered from same ground, so there should not be common-mode voltage issues.

skyjumper

#824
Oct 27, 2019, 06:59 am Last Edit: Oct 27, 2019, 07:00 am by skyjumper
Timo, I see there is:

Code: [Select]
void tN2kMsg::Add2ByteInt(int16_t v) {
  SetBuf2ByteInt(v,DataLen,Data);
}


void tN2kMsg::Add3ByteInt(int32_t v) {
  SetBuf3ByteInt(v,DataLen,Data);
}


But not

Code: [Select]
void tN2kMsg::Add4ByteInt(int32_t v)

Is there another, preferred, way to add a signed 32 bit int to the message?

Go Up