Driving a model railway from Common Lisp



Tags: #commonlisp #networking

Created on April 23rd, 2026.

Model railways and Common Lisp — two subjects that rarely cross paths…

Model railway layout

The Roco Z21 is a command station for digital model railways. It sits on the LAN, listens on UDP port 21105, and speaks a binary protocol. Locomotives, turnouts and track power are all driven by sending it the right bytes.

The Z21 setup — Wi-Fi router, command station, handheld controller, power supply

The protocol is documented, and any language with a UDP socket can drive the layout. Common Lisp seemed a fitting choice — sending an expression from the REPL and watching the train react is exactly the kind of interactive feedback loop for which this language is handy.

cl-z21 is the result: a small 2018 hobby project covering a useful subset of the protocol — system info, track power, turnouts, locomotive drive and functions, CV reading in service mode, and broadcast listening.

A typical session reads like:

(z21-set-track-power-on :verbose t)
(z21-set-loco-drive 3 64 :direction 1 :verbose t)   ; loco 3, forward, speed 64/127
(z21-set-loco-function 3 0 #x01 :verbose t)         ; light on
(z21-switch-turnout 0 :verbose t)

Each form encodes its arguments into a UDP packet, sends it, waits for the reply, and prints the decoded result.

Internally, a small registry of coder structs — one per command — knows how to turn arguments into bytes. The decoding side mirrors it: decoder structs match incoming packets by byte prefix and parse the rest. The function z21-interact ties the two together.

Only runtime dependency: usocket, installed via Quicklisp. Tested under SBCL on Windows.