-
A DCC+Railcom command station written in Rust on ESP32-C6
09/23/2026 at 05:16 • 0 commentsWho among us has never been fascinated by a little train running on a track? This has been my passion ever since my father gave me toy trains for Christmas. Over the years that passion stayed with me, sometimes dormant, but always there.
In recent years it reawakened when digital technology came to trains too. In the 1980s someone decided to put a microcontroller on a locomotive, and from that moment the world of model trains changed radically. Today we have locomotives (unfortunately very expensive) that can reproduce in miniature everything a real one does: lights, sounds, wheel squeal on curves, pantographs that rise and lower, lighting in the coaches, braking, and puffs of smoke synchronized with the speed of old steam engines.
In the pioneering era of digital trains, Märklin relied on Motorola for its historic and revolutionary 1984 digital system (creating the Märklin-Motorola format). Bernd Lenz originally developed his digital system independently, and in 1989 was commissioned by the Arnold company to miniaturize decoders for N scale. It was this Lenz system that became the basis for DCC.
The Americans later decided to adopt Lenz’s DCC and establish a worldwide standard, which led to the NMRA association publishing the standard. But engineers love complicated things (otherwise what kind of engineers would they be!), and DCC is the result. In practice, commands are sent as bits to the address of a decoder on board a locomotive, and these travel over the track power as a square wave: a 0 must last 100 μs, a 1, 58 μs.
![]()
At some point hobbyists said: we want more — feedback for DCC, which was originally one-way. When we program one of the functions (for example braking inertia) we want to know whether the value has been written or not. Once again Lenz Elektronik developed Railcom, later approved and adopted by the NMRA. Railcom is brilliant, but complex. DCC compatibility could not be broken, so a clever workaround was devised: for a very short time window (453 μs) called the cutout, the DCC command station stops transmitting and short-circuits the rails. At that point the decoder on board the locomotive hears the silence and starts talking, sending the requested value through a tiny current of at most 30mA. This means the hardware must capture this tiny current, filter it, convert it into 0/1 and pass it to a fast UART. At that point the DCC command station knows how to interpret the message.
Here is what a DCC waveform looks like with the cutout and the reply sent by the on-board decoder:
![]()
But today a DCC command station is nothing without remote control. In the model railway world, one of the best-known manufacturers of digital trains is Roco, which has its own sophisticated control system called Z21. It is so popular that they published the UDP protocol that lets a phone control the trains through the DCC command station.
The Z21 specification has been made public here: https://www.z21.eu/en/downloads/manuals
![]()
With this introduction out of the way, being a long-time programmer who loves Rust and microcontrollers, I asked myself: why not build a breadboard board using an ESP32 — which has excellent Rust support — and try to create a DCC+Railcom command station?
I sweated blood over it, but in the end I made it.
In the Rust-for-microcontrollers world there is a crate (a library, to use the old-fashioned term) called Embassy, which provides the functionality a microcontroller without an operating system needs. One feature I find outstanding is its async support (coroutines). Thanks to this, even a single-core microcontroller can handle a great many operations using this cooperative approach. So complex tasks such as Wi-Fi or running a UDP server don’t consume resources, since they stay suspended until Embassy’s async executor wakes them up.
But the heart of this system is the RMT peripheral, a timing unit I used to generate the DCC square wave in real time with the...
Read more »
Cristiano Chieppa


Lutetium