NMEA2000
NMEA2000 copied to clipboard
Custom PGNs Intermittent Failure
Hi Timo,
I am having a bit of trouble getting a few custom PGNs working reliably using an ESP32 and a couple of MCP chips. While your libraries built in PGN functions work fine the custom PGN functions I created only send on the 2nd attempt and always register a send failure.
In my code I have implemented message constructors for the following PGNS: 127503 (AC Input), 127504 (AC Output), 127509 (Inverter Status), 127510 (Charger Configuration), 127511 (Inverter Configuration), 127512 (AGS Configuration), 127514 (AGS Status), 127496 (Trip Parameters), 130578 (Speed Components).
I have tried increasing both the message buffer size and send frame buffer size to 250, however this seems to have had little effect.
Any suggestions would be appreciated.
// AC Input Status
enum tN2kACLine {
N2kacl_Line1 = 0,
N2kacl_Line2 = 1,
N2kacl_Line3 = 2,
N2kacl_reserved = 3
};
enum tN2kACAcceptability {
N2aca_Bad_Level = 0,
N2aca_Bad_Frequency = 1,
N2aca_Being_Qualified = 2,
N2aca_Good = 3
};
void SetN2kPGN127503(tN2kMsg& N2kMsg, uint8_t instance, uint8_t lineCount, tN2kACLine line, tN2kACAcceptability accceptability, double voltage, double current, double frequency, double breakerSize, double realPower, double reactivePower, double powerFactor) {
// PGN 127503 - AC Input Status
N2kMsg.SetPGN(127503L);
N2kMsg.Priority = 6;
N2kMsg.AddByte(instance);
N2kMsg.AddByte(lineCount);
N2kMsg.AddByte((line & 0x03) | ((accceptability & 0x03) << 2) | (0x0f << 4));
N2kMsg.Add2ByteUDouble(voltage, 0.01);
N2kMsg.Add2ByteUDouble(current, 0.1);
N2kMsg.Add2ByteUDouble(frequency, 0.01);
N2kMsg.Add2ByteUDouble(breakerSize, 0.1);
N2kMsg.Add4ByteUDouble(realPower, 1);
N2kMsg.Add4ByteUDouble(reactivePower, 1);
N2kMsg.Add1ByteDouble(powerFactor, 0.01);
}
// AC Output Status
enum tN2kACWaveform {
N2acw_Sine_Wave = 0,
N2acw_Square_Wave = 1,
N2acw_Error = 6,
N2acw_Unavailable = 7
};
void SetN2kPGN127504(tN2kMsg& N2kMsg, uint8_t instance, uint8_t lineCount, tN2kACLine line, tN2kACWaveform waveform, double voltage, double current, double frequency, double breakerSize, double realPower, double reactivePower, double powerFactor) {
// PGN 127504 - AC Output Status
N2kMsg.SetPGN(127504L);
N2kMsg.Priority = 6;
N2kMsg.AddByte(instance);
N2kMsg.AddByte(lineCount);
N2kMsg.AddByte((line & 0x03) | ((waveform & 0x04) << 3) | (0x0e << 5));
N2kMsg.Add2ByteUDouble(voltage, 0.01);
N2kMsg.Add2ByteUDouble(current, 0.1);
N2kMsg.Add2ByteUDouble(frequency, 0.01);
N2kMsg.Add2ByteUDouble(breakerSize, 0.1);
N2kMsg.Add4ByteUDouble(realPower, 1);
N2kMsg.Add4ByteUDouble(reactivePower, 1);
N2kMsg.Add1ByteDouble(powerFactor, 0.01);
}
// Inverter Status
enum tN2kOperatingState {
N2os_Standby = 0,
N2os_On = 1
};
void SetN2kPGN127509(tN2kMsg& N2kMsg, uint8_t instance, uint8_t acInstance, uint8_t dcInstance, tN2kOperatingState operatingState, tN2kOperatingState inverterEnable) {
// PGN 127509 - Inverter Status
N2kMsg.SetPGN(127509L);
N2kMsg.Priority = 6;
N2kMsg.AddByte(instance);
N2kMsg.AddByte(acInstance);
N2kMsg.AddByte(dcInstance);
N2kMsg.AddByte(((inverterEnable & 0b11) << 4) | (operatingState & 0b1111));
}
// Charger Configuration Status
void SetN2kPGN127510(tN2kMsg& N2kMsg, uint8_t chargerInstance, uint8_t batteryInstance, uint8_t chargerEnable, uint8_t chargeCurrentLimitPercent, uint8_t chargingAlgorithm, uint8_t chargerMode, uint8_t estimatedTemperature, uint8_t equaliseOneTimeEnable, uint8_t overChargeEnable, uint16_t equalisationTime) {
// PGN 127510 - Charger Configuration Status
N2kMsg.SetPGN(127510L);
N2kMsg.Priority = 6;
N2kMsg.AddByte(chargerInstance);
N2kMsg.AddByte(batteryInstance);
N2kMsg.AddByte(chargerEnable & 0b11 | (0b111111 << 2));
N2kMsg.AddByte(chargeCurrentLimitPercent);
N2kMsg.AddByte(((chargerMode & 0b1111) << 4) | (chargingAlgorithm & 0b1111));
N2kMsg.AddByte(((overChargeEnable & 0b11) << 6) | ((equaliseOneTimeEnable & 0b11) << 4) | (estimatedTemperature & 0b1111));
N2kMsg.Add2ByteUInt(equalisationTime);
}
// Inverter Configuration Status
void SetN2kPGN127511(tN2kMsg& N2kMsg, uint8_t inverterInstance, uint8_t acInstance, uint8_t dcInstance, uint8_t inverterEnabled, uint8_t inverterMode, uint8_t loadSenseEnabled, uint16_t loadSenseThreshold) {
// PGN 127511 - Inverter Configuration Status
N2kMsg.SetPGN(127511L);
N2kMsg.Priority = 6;
N2kMsg.AddByte(inverterInstance);
N2kMsg.AddByte(acInstance);
N2kMsg.AddByte(dcInstance);
N2kMsg.AddByte(((loadSenseEnabled & 0b11) << 6) | ((inverterMode & 0b1111) << 2) | (inverterEnabled & 0b11));
N2kMsg.Add2ByteUInt(loadSenseThreshold);
N2kMsg.Add2ByteUInt(0); // Load Sense Interval???
}
// AGS Configuration Status
void SetN2kPGN127512(tN2kMsg& N2kMsg, uint8_t agsInstance, uint8_t generatorInstance, uint8_t agsMode) {
// PGN 127512 - AGS Configuration Status
N2kMsg.SetPGN(127512L);
N2kMsg.Priority = 6;
N2kMsg.AddByte(agsInstance);
N2kMsg.AddByte(generatorInstance);
N2kMsg.AddByte(agsMode);
}
// AGS Status
void SetN2kPGN127514(tN2kMsg& N2kMsg, uint8_t agsInstance, uint8_t generatorInstance, uint8_t agsOperatingState, uint8_t generatorState, uint8_t generatorOnReason, uint8_t generatorOffReason) {
// PGN 127514 - AGS Status
N2kMsg.SetPGN(127514L);
N2kMsg.Priority = 6;
N2kMsg.AddByte(agsInstance);
N2kMsg.AddByte(generatorInstance);
N2kMsg.AddByte(((generatorState & 0b1111) << 4) | (agsOperatingState & 0b1111));
N2kMsg.AddByte(generatorOnReason);
N2kMsg.AddByte(generatorOffReason);
}
// Trip Parameters Vessel
void SetN2kPGN127496(tN2kMsg& N2kMsg, double timeToEmpty, double distanceToEmpty, uint16_t estimatedFuelRemaining, double tripRuntime) {
// PGN 127496 - Trip Parameters Vessel
N2kMsg.SetPGN(127496L);
N2kMsg.Priority = 5;
N2kMsg.Add4ByteUDouble(timeToEmpty, 0.001);
N2kMsg.Add4ByteUDouble(distanceToEmpty, 0.01);
N2kMsg.Add2ByteUInt(estimatedFuelRemaining);
N2kMsg.Add4ByteUDouble(tripRuntime, 0.001);
}
// Vessel Speed Components
void SetN2kPGN130578(tN2kMsg& N2kMsg, double longSpeedWaterReferenced, double transSpeedWaterReferenced, double longSpeedGroundReferenced, double transSpeedGroundReferenced, double sternSpeedWaterReferenced, double sternSpeedGroundReferenced) {
// PGN 130578 - Vessel Speed Components
N2kMsg.SetPGN(130578L);
N2kMsg.Priority = 2;
N2kMsg.Add2ByteDouble(longSpeedWaterReferenced, 0.001);
N2kMsg.Add2ByteDouble(transSpeedWaterReferenced, 0.001);
N2kMsg.Add2ByteDouble(longSpeedGroundReferenced, 0.001);
N2kMsg.Add2ByteDouble(transSpeedGroundReferenced, 0.001);
N2kMsg.Add2ByteDouble(sternSpeedWaterReferenced, 0.001);
N2kMsg.Add2ByteDouble(sternSpeedGroundReferenced, 0.001);
}
Have you read the document: https://github.com/ttlappalainen/NMEA2000/blob/master/Documents/Connecting_hardware_to_NMEA2000.pdf chapter "7 Most common hardware errors"
If you receive "Send failed", it means that CAN controller can not send data to the bus. If you increase Tx buffer, there is jus more room to buffer and it takes longer time to get "Send failed" message.
Hi Timo,
I have seen that section, although it would seem not to be a hardware issue as sending the built-in PGNs it work completely fine. I had the message sender example running for quite some time without any errors.
If you build message wrong, just data content is wrong. It does not effect sending.
Do you call NMEA2000.ParseMessages within your loop? You should call it on every loop not e.g., every 100ms as I saw in some example made by other person. And your loop or any code/library called in it should not have any delay(xx) calls.
I am not currently calling that function and I don't have any delays in my code.
~~The loop function of my code currently has a simple serial menu for choosing a PGN function - this hangs for a period of time. Would it be suitable to make the call after each PGN has been transmitted?~~
Just read the function description, I will offload the NMEA2000.ParseMessages()
call to an xTask and get back to you with a result.
Okay I have changed my setup in-line with your suggestions, creating an xTask that periodically calls NMEA2000.ParseMessages()
. I now get a heartbeat transmission.
My PGN's are now working every time. Thanks for the help & quick response.
If you create xTask for NMEA2000.ParseMessages(); you need to take care that you do not call it at same time with NMEA2000.SendMsg(); So if you call NMEA2000.SendMsg(); on other task, you have to use mutexes to lock calls. E.g.,
void N2kParseTask() {
while (true) {
LockN2k();
NMEA2000.ParseMessages();
Yield();
ReleaseN2k();
}
}
void loop() {
LockN2k();
SendMyN2kMessages();
ReleaseN2k();
}