Sending log messages over CAN (not CAN logging)
Posted: Sun Sep 07, 2025 7:22 pm
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:
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.
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 ...
This feels like something that should exist already and I'd rather not reinvent the wheel.
Thanks!
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);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,
...
}
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" },
...
};
Thanks!