Raw CAN, without CANopen¶
The panel speaks two languages on a CAN bus:
| CANopen | Raw CAN | |
|---|---|---|
| Devices | frequency inverters and I/O modules following CiA 301 / 402 | batteries and BMS, solar inverters, chargers, wallboxes |
| Configuration | an EDS file, node ID, SDO, PDO mapping | a frame ID and a byte layout |
| Who uses it | industry | home automation and energy |
Most devices in home automation do not speak CANopen and never will — they send their own frames with a fixed byte layout. That is why raw CAN is a part of the product in its own right and not an accessory to CANopen.
Both run on the same bus at the same time. The panel can drive an inverter over CANopen while reading a battery from raw frames.
The signal model is DBC¶
A frame has an ID and up to eight bytes. A signal inside it is defined by five things — exactly the five you will find in the manufacturer's DBC file:
| Field | Meaning |
|---|---|
start_bit |
0–63; with little this is the signal's LSB, with big its MSB |
bits |
width, 1–32 |
byte_order |
little (Intel) or big (Motorola) |
signed |
two's complement |
gain / offset |
tag = raw × gain + offset |
A DBC file therefore transcribes one-to-one, and DBC import can be added without changing the package format.
YAML¶
raw_can:
# BMS -> panel
- id: 0x355
dir: rx
dlc: 8
signals:
- { tag: bat_voltage, start_bit: 0, bits: 16, byte_order: little, gain: 0.01 }
- { tag: bat_current, start_bit: 16, bits: 16, byte_order: little, signed: true, gain: 0.1 }
- { tag: bat_soc, start_bit: 32, bits: 8, byte_order: little }
# 29-bit frame (chargers using J1939-like schemes)
- id: 0x18FF50E5
ext: true
dir: rx
dlc: 8
signals:
- { tag: temperature, start_bit: 7, bits: 16, byte_order: big, signed: true, gain: 0.1, offset: -40.0 }
# panel -> BMS, every 100 ms
- id: 0x305
dir: tx
dlc: 2
period_ms: 100
signals:
- { tag: current_setpoint, start_bit: 0, bits: 16, byte_order: little }
dir: rx means the panel receives the frame and unpacks it into tags.
dir: tx means the panel assembles the frame from tags and sends it.
period_ms: 0 means send on change — the tag's deadband has already
filtered the noise, so only a real change goes out.
In the editor this lives in the tree under CAN → Raw CAN, with the same fields and one table per frame.
Product limits¶
| Limit | Value |
|---|---|
| Frames | 32 |
| Signals in total | 128 |
A project over the limit is rejected at compile time.
Traps¶
| Trap | Consequence |
|---|---|
Swapping little and big |
values look almost right — the same class of mistake as word order in Modbus f32. Check against a value you know (a voltage, a temperature), not one that happens to be moving. |
| An ID that collides with the CANopen range | two protocols get in each other's way on one bus. The build refuses such a project and the editor shows it in red on the page. |
A forgotten signed |
a negative current reads as 65 A instead of −1 A |
A truncated frame (dlc shorter than the signal needs) |
the runtime skips it and the tag holds its last value — a truncated frame is normal while a device powers up and must never write nonsense |
Too short a period_ms |
at 500 kbit/s an eight-byte frame is about 130 µs; ten frames every 10 ms is around 13 % of the bus. Send as often as you need, not as often as you can. |
How it works inside¶
The TWAI controller has one receive queue, so only one reader may drain it. Therefore:
- while CANopen is running, its
poll()takes the frames and hands whatever does not belong to CANopen over to raw CAN; - with CANopen switched off, a separate raw CAN task runs on core 1 every 10 ms and reads the queue itself.
Transmit frames are assembled after receiving, in the same cycle, so a value that arrived in this cycle makes it into the reply.
The period is counted in monotonic milliseconds, never in cycles — with cycle jitter the timing would drift.
Diagnostics¶
The serial command VUI! prints rawcan=<received>,<sent>. If the received
counter is not rising, the device is silent or the bus speed does not match. If
it rises but the values are nonsense, byte_order or start_bit is wrong.