Page 1 of 1

Sending log messages over CAN (not CAN logging)

Posted: Sun Sep 07, 2025 7:22 pm
by chrskly
Hi all,

Anyone know of a protocol or standard for sending log messages over CANbus? Turns out this exact thing is hard to search for and it seems like there should be already something for doing this.

What I'm trying to do might make more sense if I give you and example. I have lots of code that does a printf to a tty which is fine for debugging, but in the longer term, I want to be able to record all of that information in one central place. It's all well and good capturing all of the CAN traffic between all of the devices in the car, but that only really gives you the 'what', not the 'why'.

For example, I have functions like this:

Code: Select all

bms.enable_charge_inhibit("[S09] too cold to charge", R_TOO_COLD);
This is part of the BMS and disallows charging. Right now, the string "[S09] too cold to charge" gets printed to the serial port and the 'reason' (R_TOO_COLD) gets saved for later reference. But, there could be other reasons why charging was disallowed. E.g. battery too hot, battery full, etc. The BMS broadcasts it's state, so I can figure out the why (usually), but it would be much easier to debug if I had the sort of narrative that logging gives you.

I was thinking of implementing it the following way. First, assign each log message a unique id and have a global mapping, across all of the devices, of id to log message. E.g.

Code: Select all

enum canLogMessage {
    BMS_BOOT,
    BMS_BOOT_FROM_WATCHDOG,
    ...
    BMS_CHARGE_INHIBIT_TOO_COLD,
    BMS_CHARGE_INHIBIT_TOO_HOT,
    BMS_CHARGE_INHIBIT_BATTERY_FULL,
    ...
    AC_CHARGER_CHARGE_INITIATED,
    AC_CHARGER_CHARGE_TERMINATED,
    ...
}
Then pick some universal id that all devices send their log messages on, say, 0x999, or whatever. The payload of the message is the log message id from the enum above. With 8 bytes, you have 2^64 possible log messages.

The logging device need only record those 8 bytes to permanent storage (sd card or what have you). So it's quite space efficient.

The device that displays the logs will just need a mapping from id to the human readable version. Something like this ...

Code: Select all

const std::map<canLogMessageId, std::string> canLogMessageIdToString = {
    { BMS_BOOT,  "BMS booting up" },
    { BMS_BOOT_FROM_WATCHDOG,   "BMS has been rebooted by watchdog" },
    ...
    { BMS_CHARGE_INHIBIT_TOO_COLD, "BMS : Charge inhibit enabled : reason = battery too cold" },
    { BMS_CHARGE_INHIBIT_TOO_HOT, "BMS : Charge inhibit enabled : reason = battery too hot" },
    { BMS_CHARGE_INHIBIT_BATTERY_FULL, "BMS : Charge inhibit enabled : reason = battery full" },
    ...
    { AC_CHARGER_CHARGE_INITIATED, "AC Charger : charge started" },
    { AC_CHARGER_CHARGE_TERMINATED, "AC Charger : charge stopped" },
    ...
};
This feels like something that should exist already and I'd rather not reinvent the wheel.

Thanks!

Re: Sending log messages over CAN (not CAN logging)

Posted: Mon Sep 08, 2025 11:35 am
by davefiddes
OBD-II has a structured methods for transferring diagnostic codes. There's also UDS that layers on top of that. This is what the OEMs use.

CANopen SDO has mechanisms for transferring simple strings and structured binary data. It also has bulk upload/download capability for larger data transfers. There's support in libopeninv which is somewhat standards compliant but there are other FOSS libraries out there that stick more closely to the standard and are suitable for various MCU platforms.

Hopefully that'll give you some bits of string to pull on.

Re: Sending log messages over CAN (not CAN logging)

Posted: Mon Sep 08, 2025 6:50 pm
by chrskly
davefiddes wrote: Mon Sep 08, 2025 11:35 am OBD-II has a structured methods for transferring diagnostic codes. There's also UDS that layers on top of that. This is what the OEMs use.

CANopen SDO has mechanisms for transferring simple strings and structured binary data. It also has bulk upload/download capability for larger data transfers. There's support in libopeninv which is somewhat standards compliant but there are other FOSS libraries out there that stick more closely to the standard and are suitable for various MCU platforms.

Hopefully that'll give you some bits of string to pull on.
That's brilliant. Thanks a lot for that Dave. I'll dig into all of that this evening.