Skip to content

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.