Documentation / Interfaces / CAN and CAN FD

CAN and CAN FD

Read Distance measurements over CAN, with USB output available at the same time.

ControllerSTM32 FDCAN2
TransceiverTCAN1044VDRQ1
Logic pinsPB12 RX / PB13 TX
CAN-enabled responder: bench-test image

Download firmware · Python reader · DBC file

The implementation and software tests are available. Physical-bus validation is still pending. The standard console firmware is unchanged. Firmware checksum (SHA-256).

Electrical interface

J2 pinSignalConnection
1CAN_LTwisted-pair low
2CAN_HTwisted-pair high
3+12VBoard power
4GNDPower return and bus reference

Connect a common bus ground through J2 pin 4. SW3 closes a 120 Ω termination path through R20. Terminate only the two physical ends of a linear bus, keep stubs short, and use a twisted pair for CANH/CANL.

Read distance over CAN

  1. Set up a Distance pair. The initiator stays on the standard firmware; only the responder needs the CAN image.
  2. Leave CAN disconnected while flashing. Download the CAN firmware above and flash.sh into the same directory. Install dfu-util, connect only the intended responder in DFU mode, and run the command below.
  3. Reconnect normally. Send INFO in the console or a serial terminal; expect fw=twr_resp-can-1 and a DIAG line with can_node=1 can_bps=500000 can_proto=1. Reapply and verify the pair's distance calibration.
  4. Connect the responder to an active CAN adapter or robot controller at 500 kbit/s, Classical CAN. It must acknowledge frames; a listen-only receiver is not enough. Keep the initiator powered.
bash flash.sh responder twr_resp_can.bin

On Linux, bring up your adapter and run the downloaded reader. Replace can0 if your adapter uses a different interface. These commands change that interface's settings; use a dedicated test bus first.

sudo ip link set can0 down
sudo ip link set can0 type can bitrate 500000 restart-ms 100
sudo ip link set can0 up
python3 opentags_can.py --interface can0 --node 1

The reader prints distance in metres and reports stale data after 250 ms without a valid range. It uses Python's standard library and Linux SocketCAN. The existing ROS 2 driver still uses USB; this reader does not add CAN transport to ROS.

More than one CAN device

Give every responder on a bus a unique node ID from 1 to 63. The download defaults to 1. To make another ID survive a reset, download set_can_node.py, personalize the image, then flash the new file:

python3 set_can_node.py twr_resp_can.bin --node 2 --output twr_resp_can-node2.bin
bash flash.sh responder twr_resp_can-node2.bin

The tool changes only the node-ID block and refuses to overwrite an existing file. Verify can_node=2 after a power cycle before joining a shared bus. CAN IDs identify responders; they do not add UWB peer addressing or scheduling between multiple ranging pairs.

For temporary bench changes, send CAN_NODE 2 over USB. CAN OFF and CAN ON disable or enable publication, not the physical transceiver. Runtime settings return to the image defaults at reset. Reserve this protocol's IDs on your bus; it is not CANopen or J1939.

Range frame — protocol v1

Standard 11-bit ID 0x500 + node (default 0x501), DLC 8, no FD or bit-rate switching. A record follows each completed or failed ranging exchange, up to the current 40 Hz target. Multi-byte values are little-endian.

ByteValue
0Upper nibble: version 1. Bit 0: distance valid. Bit 1: RSSI valid. Bits 2–3 reserved, zero.
1UWB sequence, unsigned 8-bit, wraps at 255.
2–5Unsigned 32-bit distance in millimetres. 0xFFFFFFFF means invalid.
6–7Signed 16-bit legacy RSSI / first-path power in 0.1 dBm. 0x7FFF means unavailable.

Example: 501#132AE60B000014FD is node 1, sequence 42, 3.046 m, −74.8 dBm. Check validity flags before using either measurement. Failed exchanges are invalid records, not zero distance; the existing TWR solver still clamps negative corrected distances to zero. Long sequence gaps are ambiguous.

Status heartbeat

Standard ID 0x540 + node (default 0x541), DLC 8, once per second while publication is enabled.

ByteValue
0Protocol version: 1.
1Bit 0: latest observation valid and no older than 250 ms. Bit 1: a CAN transmission was dropped or expired. Bit 2: controller is error-passive. Other bits zero.
2–3Unsigned 16-bit age of the last valid range, in milliseconds. 65535 means never seen or saturated.
4–7Unsigned 32-bit device uptime in milliseconds; wraps after about 49.7 days.

Apply your own 250 ms range timeout. Heartbeats can continue when the UWB peer is silent, and no heartbeat can be delivered over a disconnected bus. The DBC is for node 1; adjust its message IDs for other nodes and always respect the validity signals.

Failure behavior and diagnostics

CAN runs independently of the UWB and USB loops. A single latest-value slot replaces old unsent measurements. Transmit waits are bounded, expired mailboxes are cancelled, and bus-off recovery is enabled. Automatic frame retries are disabled: use the next measurement instead of replaying a backlog.

INFO adds CAN configuration and counters: can_tx counts confirmed transmissions, can_drop counts failed/expired transmissions, and can_coalesced counts replaced pending measurements. can_mode is 0 (active), 1 (passive), or 2 (bus-off); can_tec and can_rec are controller error counters. RESET_STATS clears the software counters, not controller fault state.

Standalone CAN and CAN FD test programs

Example firmware profiles

TargetsNominalDataPayload
can_tx / can_rx500 kbit/sn/a4-byte request, echoed response
can_fd_tx / can_fd_rx1 Mbit/s2 Mbit/s with BRS64-byte load frame

These values describe repository test firmware, not the maximum rating of the transceiver. The TCAN1044 family supports faster signaling, but the complete bus topology and every node's bit timing must be validated before increasing the data phase.

Classical CAN echo contract

CAN IDDirectionDLCBytes
0x123TX node → RX node4CA FE seq 00
0x456RX node → TX node4Echo of received request

These IDs are for the echo test only, separate from the range protocol above.

CAN FD load frame

OffsetTypeMeaning
0..3u32 LEWrapping sequence number
4..7u32 LESender embassy_time tick count
8..63bytes0xAA fill

The frame uses standard ID 0x100, 64-byte DLC, FD format, and bit-rate switching. The receiver reports sequence gaps and aggregate payload throughput. Its “one-way latency” is meaningful only when both boards share a synchronized tick epoch; otherwise treat that field as a diagnostic, not a calibrated latency measurement.

Two-board bring-up

This requires the current source checkout and an SWD probe. The CAN test programs replace the ranging image and use RTT logs; they are not extra modes in the USB distance console. Keep separate power supplies separate—do not join J2 power pins merely to connect the CAN bus.

  1. Power both tags and connect J2 pin 1 to pin 1, pin 2 to pin 2, and pin 4 to pin 4.
  2. Enable SW3 termination only at the two physical bus ends. With two tags as the complete bus, enable both.
  3. Flash can_tx to one board and can_rx to the other over SWD.
  4. Confirm the TX log receives ID 0x456 echoes with matching sequence bytes.
  5. Repeat with can_fd_tx and can_fd_rx, then inspect sequence gaps and throughput.
cd firmware/rust_bringup/stm32g4
DEFMT_LOG=info cargo run --release --bin can_rx
# In a second checkout / terminal with the other probe:
DEFMT_LOG=info cargo run --release --bin can_tx

Fault isolation

SymptomCheck first
No frames, no bus activityCommon ground, CANH/CANL polarity, SWD image, transceiver supply
TX retries or no acknowledgementA second active CAN node at the same nominal bit rate
Works at 500 kbit/s, fails in FDData-phase timing, BRS support, topology, cable length, termination
High sequence lossRTT logging overhead, receiver servicing, wiring reflections, error counters

Source access

To build the CAN-enabled responder from the current firmware checkout, use the dedicated size-optimized profile below. RTT logging is disabled to fit the existing flash layout; USB output and INFO diagnostics remain available. The regular twr_resp build remains USB-only. No firmware source is added to the public website repository by this change.

cd firmware/rust_bringup/stm32g4
mkdir -p out
DEFMT_LOG=off cargo objcopy --profile can-release --features range-can --bin twr_resp -- \
  -O binary out/twr_resp_can.bin

In the supplied checkout, the complete examples are can_tx.rs, can_rx.rs, can_fd_tx.rs, and can_fd_rx.rs under firmware/rust_bringup/stm32g4/src/bin/. No current public source download is linked. Follow the source-access and build prerequisites before attempting these commands.