Repository navigation
Hive Rail Node Board
Status: concept (scope widened 2026-10-31); schematic draft and parts list:
hardware/board-v2/in the repository; plan: phases B0 to B8 (13). The board is a full DCC-EX command station with the Hive-Rail extras (LocoNet, multiMAUS, the Hive-Rail node), usable at home and at a FREMO meeting (1, 2). Board v2 has two ESP32s: one runs stock DCC-EX, the other the Hive-Rail lite node (4). Built so far, on the single-ESP32 v1 prototype (the 2021 "ESP32 LocoNet Command Station"): stock DCC-EX 5.6.6 with a Hive-Rail add-on (OLED, LEDs, buttons, LocoNet buffer), and the DCC-EX driver in Hive-Rail (DCC-EX Adapter).
Contents
- Goal and scope
- Five ways to use the board
- Features
- Board v2: two ESP32s
- Who does what
- Throttles
- Stop and go buttons
- Status LEDs, OLED and link heartbeat
- District transfer relays and sensor
- Firmware
- v1 prototype: the 2021 board
- Existing projects and parts sources
- Phases
- Open questions
One board that is a complete command station on its own, and a Hive-Rail node when it joins a meeting.
- Command station: stock DCC-EX (EX-CommandStation) with main and programming track outputs. It is useful without any other Hive-Rail part, at home on a layout of any size that one command station can run.
-
Hive-Rail extras on the same board:
- LocoNet (LocoNet-T for throttles and module devices, LocoNet-B with RailSync for boosters);
- multiMAUS support over XpressNet, and Roco's Z21 app and wlanMAUS over Wi-Fi (6);
- the Hive-Rail lite node: the district's part of the Hive-Rail mesh, without a Pi.
- Cheap and easy to install: one board per district, powered from the district's track supply, with optional wired Ethernet.
Must-haves on every board:
| Connection | Why |
|---|---|
| Main track output | The district's own track, or the part of it the board powers itself |
| Programming track output | Reading and writing decoder CVs at home and at the meeting |
| LocoNet-T | 12 V for throttles (FREDs) and module devices |
| LocoNet-B | RailSync with DCC for LocoNet boosters, such as Digitrax or Uhlenbrock Power |
| CDE booster output | DCC for CDE boosters (Lenz, Tams, Uhlenbrock and others), with the booster's short-circuit line back (E) |
Larger districts get their power from external boosters on LocoNet-B or CDE; the board's own main output serves a small district or the station area.
Why two ESP32s: DCC-EX supports only the original ESP32 (ESP32-WROOM, ESP32-D0WD), which generates DCC with its RMT hardware. Putting the Hive-Rail extras on a second ESP32 keeps DCC-EX completely stock and updatable, keeps DCC timing apart from networking, and lets the node chip be chosen freely (4).
This widens Hive-Rail's scope: it was a set of nodes for FREMO meetings, with a board to make one district cheap. Now the board is also a product of its own, for home layouts, and the meeting use is one of its modes.
| Mode | Where | What runs | Who drives |
|---|---|---|---|
| 1. Command station | Home | DCC-EX, and the node chip as LocoNet and throttle interface. No PC needed | multiMAUS, FREDs and LocoNet throttles, the Z21 app or wlanMAUS, WiThrottle apps (Engine Driver) |
| 2. With Rocrail or JMRI | Home | As 1, with a PC program on the network | Rocrail over RocNet (UDP), or Rocrail or JMRI over LocoNet over TCP or DCC-EX's own protocol. Throttles as in 1 |
| 3. Lite node | FREMO meeting | The node chip joins the Hive-Rail mesh itself (zenoh-pico): district power, loco register, train tracking, transfer zone, boundary signal | Throttles as in 1, under Hive-Rail's one-controller rule; the dispatcher sees the district |
| 4. Full node | FREMO meeting | A Raspberry Pi (or PC) runs the full Hive-Rail node; the board is its command station and its LocoNet and XpressNet interface | As 3, plus everything only a full node has: web view, dispatcher, layout tools, LocoNet over TCP clients |
| 5. Booster | Home | The board is a booster behind another command station, such as a Z21 or an Intellibox: DCC-EX in booster mode amplifies the master's DCC from the booster input, and reports a short back on the input's short line | The master's throttles; the board adds track power, nothing else |
Rules across the modes
- One command station per district. In every mode, DCC-EX on the board is the district's only command station; Hive-Rail never adds a second one.
- The one-controller rule applies at meetings (3, 4): one throttle at a time per loco. DCC-EX's own WiThrottle server bypasses it and is switched off there; WiThrottle apps then connect through the node, like every other throttle.
- A meeting needs at least one full node, better two (open, NB-Q12). Lite nodes run their districts, but the shared roles (throttle number allocator, fast clock master), the dispatcher's web view and the layout tools need a full node. Two full nodes keep the shared roles alive if one fails, in line with "keeps working until the last mesh node dies". Whether lite nodes may take a shared role when no full node is left is still to be decided.
- Several boards at home (one per booster district): either one Pi as a full node with the boards as lite nodes, or lite nodes meshing among themselves (if zenoh-pico's peer mode is good enough, NB-Q13).
- Booster mode (5): for someone who already has a Z21 or an Intellibox at home and wants more track power. Details below.
A lite node (the board's node chip, or the M5StickC test device) reaches the mesh through a lite server: a full node or the dispatcher started with --lite-server, which runs Zenoh as a router. Lite nodes find lite servers by Zenoh scouting (routers only), so no addresses are configured; with two or more on the mesh they fail over to another when theirs stops (tested 2026-10-31, without a missed heartbeat). At a meeting: the dispatcher's computer, a dedicated Raspberry Pi, or both. A lite server with --layout also hands lite nodes their district's part of the layout. Later: load balancing, with each lite server publishing how many lite nodes it carries.
Z21 / Intellibox ──DCC──> [optocoupler] ──┬──> DCC-EX chip ──> track outputs (booster)
└──> node chip: decodes DCC
accessory packets ──> LocoNet OPC_SW_REQ ──> LocoNet accessory decoders
DCC-EX chip: overload ──> [optocoupler] ──> short line back to the master (B-BUS pin 4, CDE E)
- The master's DCC comes in on the booster input: the Z21's B-BUS, or an Intellibox's CDE or LocoNet-B output. A fast optocoupler keeps the two systems apart.
-
DCC-EX passes it through in hardware: with
BOOSTER_INPUTset, a track inBOOSTmode gets the input pin routed to its driver inside the ESP32's GPIO matrix (DCC-EXTrackManager.cpp), with no software in the path. EX-RAIL'sONRAILSYNCONswitches the tracks toBOOSTwhen the signal appears. DCC-EX keeps measuring current and cuts its own outputs on a short. DCC-EX's booster mode on the ESP32 is still beta (DCC-EX 5.4). -
The short line back: EX-RAIL's
ONOVERLOADsets an output that pulls the master's short line through an optocoupler, so the Z21 or Intellibox sees the booster's short as from any of its own boosters. -
The DCC-to-LocoNet bridge: the node chip also gets the master's DCC (one extra trace), decodes the accessory packets and sends them as
OPC_SW_REQon the board's LocoNet. Turnouts, lights and signals on LocoNet accessory decoders then follow the Z21's multiMAUS or app. The plain Z21 (white) and the z21start have no LocoNet, so for them the board is a booster and a LocoNet bridge in one.- One message per change: the master repeats each packet; the node sends one LocoNet message per real change.
- Address offset: Roco, the Intellibox and the NMRA count accessory addresses differently (Roco by 4 for the same decoder); the offset is a setting.
- Off when the master already has LocoNet on the same bus (an Intellibox, a black Z21): every command would arrive twice. On by default only on a separate LocoNet.
- One way only: DCC has no replies; feedback from LocoNet does not reach the master this way.
- Signal aspects: DCC's extended accessory packets have no direct LocoNet equivalent; basic accessory packets (turnouts, lights, simple signals) first, an aspect mapping later.
-
Later: the same decoder sees the master's loco packets (address, speed, functions). A district at a meeting that runs a Z21 or an Intellibox, with the board as booster and bridge, could put its locos into Hive-Rail's loco register from what it sees on the track: Hive-Rail with any DCC central. DCC-EX's own sniffer already decodes loco and accessory packets from the booster input (
DCCDecoder.cpp), which is worth comparing with the node's.
| Feature | Chip | Notes |
|---|---|---|
| DCC-EX, main and programming track (must) | DCC-EX ESP32 | Stock, unchanged. Current sensing per output |
| LocoNet-T and LocoNet-B (must) | Node ESP32, RailSync from DCC-EX | T: 12 V for throttles and devices. B: RailSync carries DCC for boosters |
| CDE booster output (must) | DCC-EX ESP32 | C and D: the DCC signal for the boosters. E: a booster pulls it on a short; the board turns that into a district short (power off and report) |
| Z21 B-BUS (open) | – | Roco's booster bus. Only if its electrical spec is available (NB-Q16) |
| multiMAUS over XpressNet | Node ESP32 | RS-485 port; the node is the XpressNet master (6) |
| Z21 app and wlanMAUS | Node ESP32 | Roco's Z21 LAN protocol over Wi-Fi (6) |
| Hive-Rail lite node | Node ESP32 | Modes 3 and 1 to 2 at home with several boards |
| RocNet to Rocrail | Node ESP32 | Mode 2 |
| Wired Ethernet (optional) | Node ESP32 | W5500; recommended at meetings |
| District transfer relays and sensor | Node ESP32 | For the Transition Zone (9) |
| Stop and go buttons | DCC-EX ESP32 | Work even when the node chip is down (7) |
| Status LEDs and OLED | Node ESP32 | Power, link, layout; fast clock and district on the screen (8) |
| Booster input | DCC-EX ESP32 | Booster mode (2, mode 5): DCC in through a fast optocoupler, short line back to the master through a second optocoupler |
flowchart LR
subgraph board["Hive-Rail board v2"]
subgraph cs["ESP32-WROOM-32E: DCC-EX (stock)"]
DCC["DCC generation (RMT)"]
end
subgraph node["Node ESP32 (S3 or C6): Hive-Rail lite node"]
LN["LocoNet"]
XN["XpressNet master"]
NET["Wi-Fi, Ethernet (W5500)<br/>RocNet, Z21, zenoh-pico"]
IO["Relays, LEDs, OLED"]
end
node <-->|"UART: DCC-EX commands<br/>+ reset and boot lines"| cs
DRV["2× DRV8874<br/>main + programming"]
RS["TC4427<br/>LocoNet-B RailSync"]
cs --> DRV
cs -->|"DCC signal"| RS
CDE["CDE driver<br/>C, D out, E in"]
cs <-->|"DCC signal, short"| CDE
BTN["Stop and go buttons"] --> cs
end
TRACK["Track"] --- DRV
LNB["LocoNet-B (boosters)"] --- RS
BOOST["CDE boosters"] --- CDE
LNT["LocoNet-T (throttles, devices)"] --- LN
MAUS["multiMAUS"] --- XN
PI["Pi (mode 4), Rocrail or JMRI (mode 2)"] --- NET
MESH["Hive-Rail mesh (modes 3, 4)"] --- NET
Build style: through-hole first (decision 2026-10-31). Every part can be soldered by hand: the ESP32s, the regulators, Ethernet and the OLED are plug-in modules on pin headers, the track driver is an Arduino Uno motor shield (optional), and the interface chips sit in DIP sockets, so whatever blows is swapped in seconds. Everything else is through-hole, as on the 2021 board. A machine-assembled SMD variant can follow for batches. The full draft, with a first review, PCB current and track-width rules and the parts list, is in hardware/board-v2/.
Parts, by block. Taken from three designs that work: the EX-CSB1 (DCC-EX's own ESP32 board, 2025), positron96's LocoNet command station (2021, CERN-OHL) and our own 2021 board. We use their component choices, not their layouts.
| Block | Choice | From |
|---|---|---|
| DCC-EX chip | ESP32-DevKitC (ESP32-WROOM-32E) in a socket | EX-CSB1 (the chip DCC-EX supports) |
| Track outputs | Arduino Uno R3 shield headers, wired as DCC-EX's ESP32 shield definitions expect: the owner plugs in an EX-MotorShield8874 (2× DRV8874, 5 A) or an Arduino Motor Shield R3 (L298P, 2 A), swapped in seconds if blown. Optional: a district powered only by boosters needs no shield. A header for an IBT-2 module on the main track; a third DCC-EX track (no driver) feeds the booster buses, so they switch off with the district | DCC-EX shields; EX-CSB1 also has Uno headers |
| Booster input (mode 5) | DCC in through a 6N137 fast optocoupler with a constant-current input (any level from 5 to 24 V, C and D or one DCC wire and ground); short line out through a PC817 to the master's E or overload input. Screw terminals plus the Roco 4-pole B-BUS socket (NB-Q16) | 6N137 instead of EX-CSB1's SMD TLP2361 |
| Node chip | ESP32-S3-DevKitC-1 in a socket: the node needs three UARTs (LocoNet, XpressNet, the DCC-EX link); the ESP32-C6 has two plus a limited low-power one (NB-Q10) | New |
| LocoNet | LM393 comparator receiver, 2N3904 transmitter, pull-up jumper, 2 to 3 RJ12 jacks (T and B) | positron96 and our 2021 board (same circuit, IO16/IO17 on v1) |
| LocoNet-B RailSync | TC4427 driver (DIP) fed by DCC-EX's booster-bus track, 150 Ω and a polyfuse per pin | Our 2021 board; current limit from positron96's v2 goals |
| CDE booster output | Driver for C and D from DCC-EX's DCC signal; E through an optocoupler to a DCC-EX input, which an EX-RAIL handler turns into power off. Screw terminals | New; levels from Lenz's booster documents (NB-Q16) |
| XpressNet (optional) | 3.3 V RS-485 transceiver with direction control, connector to match the multiMAUS (NB-Q14) | New |
| Ethernet (optional) | Socket for the common blue W5500 module (about a quarter of a WIZ850io's price) | Our 2021 board |
| Link between the chips | UART: DCC-EX's SERIAL2_COMMANDS takes commands on GPIO16/17 next to USB. The node can reset DCC-EX |
New |
| USB | On the development boards: each has its own USB port, USB-serial chip and auto-reset. No USB circuit on the board | – |
| Power | 5 A fuse, P-MOSFET reverse-polarity protection, TVS, 12 V and 5 V switching regulators in a 3-pin 7805 footprint (36 V input). VIN 15 to 22 V | Review 2026-10-31 |
| Transfer relay driver | Two coil drivers per double-pole changeover latching relay (9) | Transition Zone |
| Buttons, LEDs, display | Stop and go buttons, RGB status LEDs, 128x64 OLED, STEMMA QT I²C | Our 2021 board, EX-CSB1 |
Rule: isolate where a device with its own power supply and its own ground connects; protect, without isolating, where a bus shares the board's ground by design.
| Connection | Isolate? | How |
|---|---|---|
| Booster input (mode 5) | Yes | DCC in: fast optocoupler (TLP2361 or 6N137). A slow PC817 takes several microseconds to switch and skews DCC's 58 µs half-bits. Short line out: optocoupler transistor |
| E line from CDE boosters, overload line from B-BUS boosters | Yes | The booster has its own supply. A PC817 is fast enough |
| CDE and B-BUS DCC outputs to boosters | Not on our side | Boosters have an optocoupler at their input; we drive it through a current-limiting resistor, protected against shorts |
| Sensors under the module (transfer zone, anything that touches track voltage) | Yes | Optocoupler input. Dry contacts (reed, IR modules on our 5 V): RC filter, protection diode, Schmitt trigger |
| LocoNet-T and LocoNet-B | No: LocoNet needs a common ground for all devices | Series resistors, a TVS diode on the data line, the LM393 input divider, current-limited RailSync and 12 V, so a miswired cable cannot reach the ESP32 |
| XpressNet (multiMAUS) | No: the board powers the multiMAUS | RS-485 TVS (for example SM712), fail-safe biasing, the 12 V pin current-limited |
| Transfer relays | The relay isolates | Flyback diodes on the coils |
| Ethernet | Already isolated | The magnetics of the jack or the W5500 module |
| USB | No | ESD protection (USBLC6). USB joins the laptop's ground to the board's: a laptop on two boards in different districts joins their grounds. At meetings the boards talk over Ethernet; debug one board at a time over USB, or use a USB isolator |
| Power input | – | Reverse-polarity MOSFET, TVS, fuse or polyfuse |
| Between the two ESP32s | No | Same board, same ground |
The optocouplers, TVS diodes and USB protection cost a few euros in all.
Rule: the chips do everything timing-critical; the decisions are made by the node, the lite node on the node ESP32 or the full node on the Pi.
| DCC-EX ESP32 | Node ESP32 | Pi (mode 4) |
|---|---|---|
| DCC generation, programming track, overload and short detection, booster short line (E) | LocoNet bit timing and collisions; XpressNet polling | Full Hive-Rail node: mesh, web view, dispatcher, layout tools |
| Stop and go buttons (power off and on without anything else) | Relay pulse timing, sensor debounce, LEDs, OLED | Transition zone state machine and handoff (in mode 3: the node ESP32) |
| Booster mode (booster input, mode 5) | Modes 1 to 3: slot master, loco register, throttles, RocNet, Z21, lite node | Slot master, loco register, routing to other districts |
| Throttle | How it connects | Notes |
|---|---|---|
| FRED, Fredi and other LocoNet throttles | LocoNet-T | The node is the only slot master on the board's LocoNet; a FRED keeps its loco across modules (FRED design) |
| multiMAUS (Roco, Fleischmann) on a cable | XpressNet on RS-485 | See below |
| wlanMAUS, Z21 app | Wi-Fi, Roco's Z21 LAN protocol (UDP) | Roco publishes the protocol. Software only: can be built on the Pi node first, without new hardware |
| WiThrottle apps (Engine Driver) | Wi-Fi | At home DCC-EX's own server; at meetings through the node (2) |
The multiMAUS speaks XpressNet (Roco's X-BUS). XpressNet is RS-485 at 62.5 kbit/s with 9-bit characters. The command station is the bus master and polls each handheld in turn by its bus address; a handheld only speaks when asked. With Roco's own amplifier a "master" multiMAUS is the command station itself; on another central it is an ordinary handheld, as on this board.
- The multiMAUS keeps its own loco library (name and address) and selects the loco address itself; nothing assigns it, unlike a FRED. Every XpressNet command carries the loco address directly; there are no slots.
- It fits the loco register, which is also keyed by address. The node passes the multiMAUS's commands to the register, tells it when another controller has the loco (XpressNet has a message for that), and forwards commands to another district when the loco has moved there, as it does for LocoNet throttles.
- 9-bit characters on the ESP32: its UART has no native 9-bit mode. The usual way is to set the parity per byte so that the parity bit is the ninth bit (NB-Q14).
- Stop (red): district power off directly in DCC-EX, so it works even when the node chip, the Pi or the network is down.
- Go (green): district power on (MAIN only), also directly in DCC-EX.
- Implemented on v1 as an EX-RAIL script,
firmware/node-board/myAutomation.h:ONBUTTON(15) POWEROFFandONBUTTON(13) POWERON. Tested on the 2021 board on USB power: each press switches DCC-EX, the OLED follows, Hive-Rail shows the district as reported by DCC-EX and sendsOPC_GPON/OPC_GPOFFto its LocoNet clients. On v2 the buttons stay on the DCC-EX chip. - DCC-EX broadcasts the power change; the node follows power changes it did not request, reports them to every node and tells its LocoNet clients.
-
Long press on stop: a global emergency stop. The node turns it into the global stop (halt on
rn/cs/all), because it must reach the other districts.
Proposal for three RGB LEDs:
| LED | Shows | Source |
|---|---|---|
| 1 Power | green on, dark off, yellow emergency stop, red blinking short | DCC-EX state |
| 2 Hive-Rail link | green connected, red blinking link lost | The node's heartbeat |
| 3 Layout | green all neighbours fine, yellow a neighbour stale or without power, red global stop | The mesh |
Link lost: if the node is lost (on v1: Hive-Rail on the Pi; on v2: also the node chip, NB-Q2), the board puts the district in a safe state (emergency stop, value to be decided) and shows it on LED 2. LocoNet throttles need the node as slot master, so they stop working without it anyway.
On v1 (implemented 2026-10-29): LED 1 in myHiveRail.cpp, not yet seen lit (the WS2812s run on the board's +5 V rail, which needs the 12 V supply): dim green MAIN on, dim red off, orange blinking short, at about 10 % brightness, read from DCC-EX's track manager every 50 ms, driven on RMT channel 6 (DCC-EX uses 0 to 3).
▓Hive-Rail RN101 ★▓ title bar: module name (HIVE_RAIL_NAME), RocNet ID, ★ = allocator and clock master
06:42 ON large: fast clock, district power (ON / OFF / STOP / SHORT, SHORT blinks)
fast x4 district
─────────────────────────
Mesh 4 nodes locos ok or blinking LOCOS !! when the loco registers differ
Loco 1234 ▶ 40 F0 last loco driven on this module (or locos and slots taken)
IP 192.168.4.1
Before Hive-Rail connects (Waiting for Hive-Rail) and after the link is lost (blinking HIVE-RAIL LINK LOST), the bottom lines show the WiFi name and, every 3 s in turn, the password and the IP address.
How it works on v1, without changing DCC-EX's files:
-
A HAL device (
halSetup(), DCC-EX'smyHal.cpphook) gets_loop()calls from DCC-EX's main loop. The screen, the status LEDs and the reads of DCC-EX's track state run there. One changed 128-byte page is sent per call, about 3 ms at 400 kHz. -
myFilter()sees every command. It answers<U 0>with<u 1>(the add-on and its protocol), takes Hive-Rail's status line, and copies the fast clock from<JC minutes rate>. -
Status line from Hive-Rail, once a second:
<U 1 rocnet nodes locos slots flags address speed dirf>. DCC-EX accepts at most 9 parameters, sodirfpacks F0 to F4 (bits 0 to 4) and forward (bit 7); flags are allocator (1), loco register in sync (2), emergency stop (4), register compared (8). Hive-Rail only sends it after the board answered<U 0>; a stock DCC-EX answers<X>once and gets nothing more. The status line is also the link heartbeat: without it for 3 s the screen and LED 2 show the link as lost. - Our own 5x7 font and SSD1306 driver;
HIVE_RAIL_OLED_SH1106selects the 2-column offset of an SH1106 display.
On v2 the screen and the LEDs move to the node chip, which has all of this information itself, and DCC-EX needs no add-on.
- One double-pole changeover latching relay per transfer block: it switches both rails, so two districts never share a conductor, and a changeover contact always breaks before it makes. DCC currents (2 to 5 A at 14 to 22 V) are well within a small relay's rating.
- The relay pulse is timed on the board (node chip), never over the network.
- The entry and clear sensors are read on the board.
- The state machine runs in the node: on the node chip in mode 3, on the Pi in mode 4 (Transition Zone Specification).
| Chip | Firmware |
|---|---|
| DCC-EX ESP32 |
Stock EX-CommandStation, with only the user files DCC-EX provides for configuration: config.h (board definition) and myAutomation.h (stop and go buttons). Updated the normal DCC-EX way, or by the node chip through its reset and boot lines |
| Node ESP32 |
Hive-Rail lite node: LocoNet, XpressNet, Z21, RocNet, zenoh-pico, relays, LEDs, OLED, the modes of 2. Language and framework to be decided (NB-Q10): Rust (esp-hal, sharing no_std protocol crates with the Pi node) or C/C++ on ESP-IDF |
Decision (2026-10-29): LocoNet is not ported into DCC-EX. On the single-ESP32 prototype, a LocoNet buffer runs next to stock DCC-EX: one firmware image, two independent services that do not know about each other.
| Link | DCC-EX | LocoNet buffer |
|---|---|---|
| WiFi or Ethernet | TCP 2560 (--dccex host:2560, works now) |
TCP 1234, LbServer (standard LocoNet over TCP) |
| USB | The board's USB serial port (works now) | A second USB-serial chip on another ESP32 UART |
Implemented (2026-10-29), not yet tested on a powered bus: myHiveRailLocoNet.cpp runs the LocoNet buffer in its own ESP32 task: UART2 at 16 666 baud (RX IO16 not inverted, TX IO17 inverted for the 2N3904, both configurable), waiting for a quiet bus (carrier detect plus priority delay), comparing the echo byte by byte and sending a break on a collision, and an LbServer on TCP 1234 (SEND → RECEIVE echo to all clients and SENT OK, or SENT ERROR no LocoNet power / bus busy). The OLED shows LN n (clients) or LN off when the bus line stays low (no LocoNet power, the board's 12 V). On the Hive-Rail side, --loconet-connect <board>[:1234] is an LbServer client that reconnects by itself and drops the echo of its own messages; the bus's slots belong to the node by default (--loconet-connect-slots node), for FREDs.
- The board is the LbServer server, because it sits on the bus; Hive-Rail connects as a client, as JMRI or Rocrail would. The reverse would loop: Hive-Rail echoes each message to its sender, and a bus bridge would put it back on the bus.
-
Fallback if only one serial link is available: tunnel LocoNet through DCC-EX's command hook (
myFilter) as<U …>/<u …>frames.
Firmware options considered for v1 (NB-Q8):
| Option | Strength | Weakness |
|---|---|---|
| A. DCC-EX + LocoNet buffer next to it | Works today, large community, mature DCC on the ESP32, DCC-EX unchanged | Two services on one chip; text protocol not made as a machine interface (shorts only as diagnostics, no reply without tracks, no acknowledgements, <c> crash); its own WiThrottle controllers bypass the one-controller rule; does not run the fast clock |
| B. positron96 LocoNetControlStation (MIT) | LocoNet-first, same LocoNet circuit, LocoNet over TCP built in | Its own slot server conflicts with Hive-Rail as slot master; small one-person project |
| C. Own firmware in Rust (esp-rs) | Exactly the board's needs; reuses Hive-Rail's code | Most work: DCC timing, programming track, overload handling of our own |
Board v2 answers most of A's weaknesses without option C: DCC-EX stays stock on its own chip, and everything else moves to the node chip.
Build and flash v1 (the add-on lives in the Hive-Rail repo under firmware/node-board/; DCC-EX is an unmodified checkout next to it):
git clone https://github.com/DCC-EX/CommandStation-EX.git ../CommandStation-EX
pip3 install --user platformio
firmware/node-board/build.sh # build
firmware/node-board/build.sh --upload /dev/cu.usbserial-0001 # build and flashStep 1 result (2026-10-29), DCC-EX 5.6.6 on an ESP32 DevKit with OLED: 15 % RAM, 69 % flash. DCC-EX reports HIVERAIL_ESP32 with tracks A MAIN and B PROG, the add-on starts and runs its own task on core 0. With tracks defined, <1>/<0> answer <p1>/<p0>, the OLED shows PWR On/PWR Off, and Hive-Rail shows the district power as confirmed by DCC-EX.
Board definition in DCC-EX's config.h for v1:
// A = MAIN: DCC to LocoNet-B RailSync via MIC4426, no current sense (boosters measure)
// B = PROG: L6202, second input through a 74HC04, no usable sense
#define HIVE_RAIL_TEST_BOARD F("HIVERAIL_ESP32"), \
new MotorDriver(32, 25, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 1.0, 5000, UNUSED_PIN), \
new MotorDriver(33, 26, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 1.0, 250, UNUSED_PIN)
#define MOTOR_SHIELD_TYPE HIVE_RAIL_TEST_BOARDA negative enable pin (-32) makes the enable active-low; which polarity switches the RailSync DCC off on <0> is tested on the board.
"ESP32 LocoNet Command Station" (Eagle 9.6.2, January 2021): ESP32 DevKit V1, LocoNet-T and LocoNet-B sockets, LM393 LocoNet receiver, MIC4426 RailSync driver, L6202 programming track, W5500 Ethernet, OLED, three WS2812 LEDs, red and green buttons. CAN is on the board but not needed.
| Function | GPIO |
|---|---|
| LocoNet RX / TX | IO16 / IO17 |
| DCC main signal / enable (MIC4426) | IO25 / IO32 |
| Programming track signal / enable / sense (L6202) | IO26 / IO33 / IO39 |
| OLED SDA / SCL | IO21 / IO22 |
| W5500 MOSI / MISO / SCK / CS / reset | IO23 / IO19 / IO18 / IO5 / IO34 |
| Red / green button | IO15 / IO13 |
| WS2812 LEDs | IO4 |
IO34 and IO35 are input-only on the ESP32, so the W5500 reset on IO34 cannot be driven by firmware.
| Project | Relation |
|---|---|
| EX-CSB1 (KiCad files) | DCC-EX's own ESP32 command station and booster. Source of the DCC-EX side's parts: ESP32-WROOM-32E, DRV8874, TLP2361 RailSync input, USB-C with CH340C and USBLC6, AO4407C and TPS54302 power. The repository has no licence file; we use its part choices only |
| positron96/LocoNetControlStation (firmware MIT, PCB CERN-OHL) | ESP32 LocoNet command station with its own DCC. Same LocoNet circuit (LM393, 2N3904, LM317L-limited RailSync, RJ12, pull-up jumper); v2 plans a socketed DevKitC, USB-PD, a switching regulator, a DIN-rail case and spare headers |
| IoTT RedHat++ shield and IoTT Stick | Adds LocoNet to a DCC-EX Arduino with a second ESP32: the same two-chip idea |
| DCC-EX itself | No LocoNet or XpressNet support |
Each phase ends with something that works and can be used. Mode 1 (a command station at home) comes first: it is useful on its own, and everything later builds on it.
| Phase | Goal | Done when |
|---|---|---|
| B0. Specs and measurements (zenoh-pico test passed 2026-10-31) | Close the open hardware questions before the PCB: CDE and B-BUS levels and connectors (NB-Q16), the X-BUS cable and the 9-bit trick (NB-Q14), LocoNet-T supply and bus pull-up current, the node chip (NB-Q10) | Measurements on a Z21, a multiMAUS, a Lenz or Digikeijs booster and an Intellibox are in this page; a zenoh-pico heartbeat from the chosen node chip shows as a district on the dispatcher |
| B1. Breadboard prototype | Both chips on development boards: an ESP32 DevKit with DRV8874 breakouts on EX-CSB1's pins running stock DCC-EX, an ESP32-S3 DevKit as the node with the LocoNet circuit of the 2021 board. Lite node firmware skeleton: the DCC-EX link (SERIAL2_COMMANDS), LocoNet buffer, OLED |
A loco runs on main, CVs read on programming, a FRED drives through the node as slot master |
| B2. Schematic and PCB rev A | KiCad schematic and layout from the draft, reviewed; five boards assembled at JLCPCB | Boards delivered |
| B3. Bring-up: mode 1 | Every block of rev A tested: power, both tracks, LocoNet-T and -B, CDE and B-BUS out with a real booster, stop and go, LEDs, OLED, USB flashing of both chips | The board runs a home layout as its only command station |
| B4. Throttles | Z21 LAN protocol (Z21 app, wlanMAUS), then the multiMAUS on XpressNet, both into the loco register with the one-controller rule | A multiMAUS and the Z21 app drive locos next to a FRED |
| B5. Booster mode and the bridge: mode 5 | Booster input with the short line back; DCC-to-LocoNet bridge for accessory packets | The board boosts a Z21 and an Intellibox; a Z21 switches a LocoNet turnout decoder; a booster short shows on the master |
| B6. Home with a PC: mode 2 | RocNet to Rocrail, LocoNet over TCP for Rocrail and JMRI, from the node chip | Rocrail and JMRI each run a home layout through the board |
| B7. Meetings: modes 3 and 4 | Lite node on the mesh (zenoh-pico): district power, loco register, transfer relays and sensors, boundary signal. Full node with a Pi using the board as command station and interface | Two boards and a Pi run a bench layout across a district border, with a train handed over |
| B8. Rev B and builders' documents | Fixes from B3 to B7 in a second PCB revision; assembly and setup guides for module owners | A small batch for a FREMO trial |
The software phases of the Hive-Rail roadmap (README) run alongside: train tracking (Phase T) and the transfer zone are needed for B7; the Z21 protocol (8.10) can start on the Pi node before B4.
| # | Question | How it will be answered |
|---|---|---|
| NB-Q1 | Is the stop button power off or emergency stop? | Decide with FREMO operators |
| NB-Q2 | What does the board do when the node (Pi or node chip) is lost: emergency stop, power off, or keep running? | Decide; start with emergency stop |
| NB-Q3 | Do the LocoNet buffer and its TCP server run reliably next to DCC-EX on one chip (v1)? | Prototype on the 2021 board |
| NB-Q4 | Ethernet: W5500 on the node chip only, or also for DCC-EX? | v2: the node chip has the network; DCC-EX needs none |
| NB-Q5 | Pi separate, or mounted on the board (header for a Pi Zero 2 W)? | Decide when the PCB is designed |
| NB-Q6 | How are the mode, module name, district and RocNet ID set without a laptop? | Web page on the node chip, or OLED and buttons |
| NB-Q7 | Heartbeat and LED message format between node and board | Decide with the v2 firmware |
| NB-Q8 | v1 firmware: DCC-EX with an add-on, or own Rust firmware? | Answered for v2 by the second chip (10) |
| NB-Q9 | Does a FRED get slot replies fast enough through the node? | Measure with a real FRED (README roadmap 8.5) |
| NB-Q10 | Node chip: ESP32-S3 (two cores, PSRAM, three UARTs; Rust needs Espressif's compiler fork) or ESP32-C6 (RISC-V, standard Rust, but two UARTs plus a low-power one)? Rust or C/C++? | The draft uses the S3 for its three UARTs. B0 test passed (2026-10-31), now our test device: Node-Lite on the M5StickC on an original ESP32 (M5StickC) with zenoh-pico 1.10.1 in C++ (Arduino): it joins the mesh as a district, about 210 KB heap free, 1.2 MB flash (results). Rust is still open |
| NB-Q11 |
SERIAL2_COMMANDS opens Serial2 on GPIO16/17 at 115 200 baud (DCC-EX SerialManager.cpp) |
– |
| NB-Q12 | Must a meeting have a full node (Pi), or may lite nodes take the shared roles when none is left? |
Shown to matter (2026-10-31): in the B0 test the ESP32 became allocator and clock master, because the roles go to the lowest id. Implemented (2026-10-31): the heartbeat flag lite, and full nodes leave lite nodes out of the shared roles. Still to decide: may lite nodes take them when no full node is left? |
| NB-Q13 | Is zenoh-pico's peer mode good enough for lite nodes to mesh without a Pi (several boards at home)? | Test |
| NB-Q14 | multiMAUS: connector and pinout, bus address handling, loco library size; 9-bit characters on the node chip (parity trick) | Read Roco's and Lenz's XpressNet documents; test with a multiMAUS |
| NB-Q15 |
|
– |
| NB-Q16 | Booster outputs: CDE signal levels and the E line's behaviour (Lenz's booster documents); is Roco's B-BUS documented, and does a Märklin 6017-style booster output matter for 3-rail districts? | Read the documents; ask FREMO and 3-rail users which boosters they bring |
| NB-Q17 | Booster mode: does DCC-EX pass the signal in hardware? Who drives the short line back? |
Answered (2026-10-31): hardware (GPIO matrix, TrackManager.cpp); the short line from EX-RAIL's ONOVERLOAD. To test in B5 |