Repository navigation
Protocol
CAN 2.0A, standard 11-bit identifier, 250 kbit/s, DLC = 5.
identifier = 0x100 | target_node_id
data[0] target node id (253 = 0xFD, 254 = 0xFE)
data[1] sender node id
data[2] relay number (1 or 2)
data[3] state (0 = off, 1 = on)
data[4] type (0 = CMD, 1 = ACK)
A command 253 → 254 ("relay 1 on") travels as:
can0 1FE [5] FE FD 01 01 00
and the applied-state acknowledgement comes back as:
can0 1FD [5] FD FE 01 01 01
CAN is content-addressed — the identifier field is the address and the priority. We could have put everything in the payload with a fixed ID, and it would have worked on the bench. Two reasons kept it in the ID:
-
Hardware acceptance filters. Each node configures its TWAI filter to
pass only
0x100 | its_own_id. Frames for the other floor are ACKed on the wire (every receiver must ACK — that is how CAN works) and then dropped by the filter without ever interrupting the CPU. - Priority. If a real broadcast ever gets added (an "all off" emergency frame, for example) it gets a low identifier and wins arbitration deterministically. Payload addressing cannot do that.
- Buttons are
INPUT_PULLUP, active-low, 40 ms debounce. Button mapping:B0 → relay1 ON, B1 → relay1 OFF, B2 → relay2 ON, B3 → relay2 OFF. - On a matching CMD frame the node switches the relay and returns an ACK frame so the sender's confirm LED blinks.
- Alerts enabled:
BUS_OFF,BUS_RECOVERED,ERR_PASS,TX_FAILED,RX_DATA. On bus-off the node callstwai_initiate_recovery()and restarts only after the driver reportsBUS_RECOVERED; callingtwai_start()early silently fails (we learned this the annoying way).
sim/can_sim.py is a bus model written to ISO 11898 semantics:
- Builds the real bitstream: SOF, 11-bit ID, RTR/IDE/r0, DLC, data, CRC-15
(polynomial
0x4599), stuff bits, ACK slot, EOF, interframe space. - Arbitration is resolved bit-by-bit — dominant (0) beats recessive (1), losers stop driving and retry next frame.
- The ACK slot is driven by every non-transmitting node, matching hardware.
- TEC +8 on missing ACK, −1 on success; error-passive at ≥128, bus-off at ≥256, timed recovery after.
What it does not model, honestly:
- The receive error counter (REC): the simulated channel is perfect, so receivers never accrue errors.
- Bit-error detection, error frames (6-bit flag + delimiter), form/stuff checking on the receive side.
- The physical layer itself: no rise times, no reflections, no real GPIO.
- Extended 29-bit IDs and remote (RTR) frames; the protocol never uses them.
- Identical-ID simultaneous frames merge arbitrarily rather than continuing arbitration into the data field.
Those are simplifications in our model; the protocol itself still behaves
this way. If you want silicon-level
truth, Espressif's QEMU fork emulates the TWAI peripheral and can bridge to a
Linux vcan interface. Heavier to set up, but it runs the unmodified
firmware.
| Bus | Multi-master | Arbitration / error handling | Cost/node | Fit here |
|---|---|---|---|---|
| CAN | yes | in silicon | ~₹700 | any floor commands any floor |
| RS-485 + Modbus | no (master polls) | firmware | ~₹400 | needs a master + poll loop |
| KNX | yes | full stack, certified | ₹4k+/channel | industry-real, overkill |
| ESP-NOW | yes | WiFi MAC | free | nothing to demonstrate |
RS-485 is cheaper and is what contractors actually install today. The honest defence of CAN here: this project's point is the protocol mechanics. In fact, a babbling node removing itself via bus-off is a demo Modbus cannot give you.
floorCAN
WIT Solapur · E&TC 2026-27