CAN and CAN FD
Read Distance measurements over CAN, with USB output available at the same time.
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 pin | Signal | Connection |
|---|---|---|
1 | CAN_L | Twisted-pair low |
2 | CAN_H | Twisted-pair high |
3 | +12V | Board power |
4 | GND | Power 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
- Set up a Distance pair. The initiator stays on the standard firmware; only the responder needs the CAN image.
- 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. - Reconnect normally. Send
INFOin the console or a serial terminal; expectfw=twr_resp-can-1and a DIAG line withcan_node=1 can_bps=500000 can_proto=1. Reapply and verify the pair's distance calibration. - 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.
| Byte | Value |
|---|---|
| 0 | Upper nibble: version 1. Bit 0: distance valid. Bit 1: RSSI valid. Bits 2–3 reserved, zero. |
| 1 | UWB sequence, unsigned 8-bit, wraps at 255. |
| 2–5 | Unsigned 32-bit distance in millimetres. 0xFFFFFFFF means invalid. |
| 6–7 | Signed 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.
| Byte | Value |
|---|---|
| 0 | Protocol version: 1. |
| 1 | Bit 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–3 | Unsigned 16-bit age of the last valid range, in milliseconds. 65535 means never seen or saturated. |
| 4–7 | Unsigned 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
| Targets | Nominal | Data | Payload |
|---|---|---|---|
can_tx / can_rx | 500 kbit/s | n/a | 4-byte request, echoed response |
can_fd_tx / can_fd_rx | 1 Mbit/s | 2 Mbit/s with BRS | 64-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 ID | Direction | DLC | Bytes |
|---|---|---|---|
0x123 | TX node → RX node | 4 | CA FE seq 00 |
0x456 | RX node → TX node | 4 | Echo of received request |
These IDs are for the echo test only, separate from the range protocol above.
CAN FD load frame
| Offset | Type | Meaning |
|---|---|---|
0..3 | u32 LE | Wrapping sequence number |
4..7 | u32 LE | Sender embassy_time tick count |
8..63 | bytes | 0xAA 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.
- Power both tags and connect J2 pin 1 to pin 1, pin 2 to pin 2, and pin 4 to pin 4.
- Enable SW3 termination only at the two physical bus ends. With two tags as the complete bus, enable both.
- Flash
can_txto one board andcan_rxto the other over SWD. - Confirm the TX log receives ID
0x456echoes with matching sequence bytes. - Repeat with
can_fd_txandcan_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
| Symptom | Check first |
|---|---|
| No frames, no bus activity | Common ground, CANH/CANL polarity, SWD image, transceiver supply |
| TX retries or no acknowledgement | A second active CAN node at the same nominal bit rate |
| Works at 500 kbit/s, fails in FD | Data-phase timing, BRS support, topology, cable length, termination |
| High sequence loss | RTT 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.