MILCAN A for Linux
A complete MILCAN A protocol implementation that's just software.
No proprietary gateway box. No vendor SDK. No dedicated hardware. Just a userspace daemon and three command-line tools running on any Linux box with a SocketCAN interface.
It's a Daemon, Not a Box
Most MILCAN A deployments assume dedicated CAN controller hardware and a vendor-supplied stack bolted onto it. milcan-daemon is different: it is pure userspace software. It opens a standard Linux SocketCAN interface a real CAN adapter, or a virtual vcan0 interface for development and CI and implements the full MILCAN A data-link and application-layer behavior itself, in C++20 on top of Qt6.
| Typical MILCAN deployment | MILCAN A for Linux |
|---|---|
| 🔒 Vendor gateway hardware + closed firmware | 🆓 Open userspace daemon, runs on anything Linux runs on |
| 💰 Per-seat vendor tooling to test/inject frames | 🆓 milcan-dump / milcan-play / milcan-send ship in the same .deb |
| 🧪 Hard to test without the physical bus | ✅ First-class vcan0 support — develop and CI-test without any CAN hardware at all |
| 🧩 Bespoke integration per vendor stack | ✅ Local JSON-over-Unix-socket IPC surface any process can consume |
Because it's software, it drops onto anything: a vehicle compute unit, a test bench, a CI runner, or a developer's laptop with vcan0 — with the same binary and the same behavior every time.
Key Features
- ✅ Pure userspace daemon — no kernel module, no vendor hardware, no proprietary SDK
- ✅ Full MILCAN A protocol (MWG-MILA-001 Rev 3): 29-bit identifier decode/encode, message classification, priority-based bus access
- ✅ Compulsory system-mode state machine (Pre-Operational / Operational / System Configuration)
- ✅ Sync Master election with source-address priority arbitration
- ✅ 8-priority-ring transmit queue with "mortal"/TTL frame expiry and the mandatory max-one-per-PTU rule
- ✅ Multi-frame transport with CRC-16/CCITT for payloads beyond 8 bytes
- ✅ Config-driven semantic decoding — name your own
(primaryType, subType)message sets in JSON, no code changes - ✅ Local IPC surface (Unix domain socket, newline-delimited JSON) so any process — DDS bridge, logger, dashboard — can consume decoded traffic
- ✅ Three purpose-built CLI tools, packaged in a single Debian package with the daemon
- ✅ Runs identically on real CAN hardware or
vcan0— same binary, same behavior
The Toolset
MILCAN A for Linux doesn't stop at the daemon — it ships a complete, protocol-aware toolset so you never need to hand-roll cansend invocations or decode 29-bit identifiers by hand.
the node/device"] DU["🔍 milcan-dump
decode & capture"] PL["▶️ milcan-play
replay a capture"] SN["📤 milcan-send
spec-correct injection"] end BUS["CAN Bus / vcan0"] SN -->|inject frames| BUS BUS -->|frames| D BUS -->|frames| DU DU -->|candump -l log| PL PL -->|replay| BUS style Toolset fill:#012141,stroke:#00FFFF,color:#00FFFF style D fill:#0072FF,stroke:#00FFFF,color:#fff style DU fill:#00ff88,stroke:#00FFFF,color:#012141 style PL fill:#00ff88,stroke:#00FFFF,color:#012141 style SN fill:#ffaa00,stroke:#00FFFF,color:#012141
🔍 milcan-dump — see every frame, decoded
A MILCAN-aware candump. Every frame is shown with its priority, protocol bit, primary/sub-type, source address, and classified message type (SyncFrame, Alive, StatusCommand, ...) — plus, with a message-set config, named parameter values decoded to engineering units.
milcan-dump -i vcan0 -c /etc/milcan/milcan-config.json
vcan0 0F730214 [0] pri=3 milcan primary=0x73 sub=0x02 src=0x14 class=Request
vcan0 06710114 [2] 0C 01 pri=1 milcan primary=0x71 sub=0x01 src=0x14 class=StatusCommand
vcan0 06710214 [2] 0C 01 pri=1 milcan primary=0x71 sub=0x02 src=0x14 class=StatusCommand
vcan0 0A720214 [2] 0C 01 pri=2 milcan primary=0x72 sub=0x02 src=0x14 class=StatusCommand
vcan0 0F730214 [0] pri=3 milcan primary=0x73 sub=0x02 src=0x14 class=Request
vcan0 1E310014 [2] 48 49 pri=7 milcan primary=0x31 sub=0x00 src=0x14 class=NonOperational
vcan0 1A620C0C [1] 01 pri=6 milcan primary=0x62 sub=0x0C src=0x0C class=Alive
A real capture of 14 simulated vehicle signals across all 7 priority rings (engine RPM, wheel speed, brake pressure, GPS, diagnostics, and more) — see the bus-saturation-demo walkthrough for the source of this traffic. Note the mix of message classes: Request (no payload, bit set), StatusCommand (periodic signal values), and NonOperational (broadcast beacon) all decoded correctly, at their own priority.
A canplay-alike that replays a candump -l format log back onto SocketCAN, respecting recorded relative timing, with speed control and loop mode — ideal for regression tests and bench demos without a live bus.
milcan-play --interface=vcan0 --loop capture.log
📤 milcan-send — spec-correct frames, no hand-computed IDs
A cansend replacement purpose-built for MILCAN A: every message class the spec defines — Sync Frame, Enter/Exit Configuration Mode, Alive, Request, Non-operational broadcast, Status/Command — is a named subcommand. You never need to hand-compute a 29-bit identifier again.
milcan-send -i vcan0 enter-config --src 5
milcan-send -i vcan0 sync --slot 0
milcan-send -i vcan0 alive --node 7
Architecture
Every piece of the stack — reading, classifying, sequencing, and writing frames — is a plain C++20/Qt6 object running in a normal Linux process. There is no firmware to flash and no kernel driver beyond the standard SocketCAN stack already in your kernel.
System Mode State Machine
MILCAN A mandates a system-mode state machine (§4.2, Fig 4-4) that every node must implement correctly — milcan-daemon implements it exactly as specified:
Sync Master Election
Nodes marked as potential Sync Masters arbitrate for the role using
source-address priority (Fig 4-3):
NodeB defers to NodeA NodeA->>Bus: Sync Frame (slot 1, periodic) Bus->>NodeB: Sync Frame observed Note over NodeA,NodeB: NodeA remains Sync Master
while its Sync Frames keep arriving
Quick Start
# Create a virtual CAN interface — no hardware required
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0
# Install the daemon and all three tools (single package)
sudo apt-get install milcan
# Start the daemon
milcan-daemon --interface=vcan0 --config=/etc/milcan/milcan-config.json
# In another terminal, watch decoded traffic
milcan-dump -i vcan0
# In a third terminal, inject a Sync Frame
milcan-send -i vcan0 sync --slot 0
Full walkthrough: First-Time Setup
Use Cases
🎖️ Vehicle Subsystem Integration Testing
Bring up and exercise a full MILCAN A node/device on a bench or in CI using only vcan0 — no vehicle bus, no adapter, no vendor tooling required.
🧪 Protocol Conformance Verification
Use milcan-send to exercise every message class a real ECU must handle, and milcan-dump to verify the responses, without writing a single line of bespoke test code.
📼 Regression and Demo Capture
Capture representative bus traffic once with milcan-dump -w, then replay it deterministically with milcan-play for demos, regression suites, or offline analysis.
🔌 Bridging to Other Systems
Consume decoded MILCAN A traffic from the local IPC surface in any language that can speak newline-delimited JSON over a Unix domain socket — logging, dashboards, or a bridge into another bus or middleware.
How to get access
MILCAN A for Linux is delivered as part of the ATLAS Digital Crew suite