User guide · Throttle Bridge
Throttle Bridge guide
Everything the Throttle Bridge does, in the order you will meet it: what to build, first power-on, the network, the inputs, the outputs one by one, the roster, the screen, updates, and what to do when something is off.
What it is
The Throttle Bridge sits between throttles and a layout. On one side it is a server for four throttle protocols — Z21 LAN, WiThrottle, LCC Train Control and MQTT — and on the other it is a client of one layout protocol you choose: a LocoNet bus, LocoNet over TCP or Binary to a DR5000 or YaMoRC, DCC-EX, XpressNet-LAN, an LCC hub or CAN bus, or an MQTT broker. Every throttle drives every loco, and every throttle is told when any of them — or the layout itself — changes something.
The command station sees a single client. Sixteen phones on the Z21 app and eight on Engine Driver still amount to one LocoNet-over-TCP socket, one OpenLCB controller node, one DCC-EX connection. Only the station's loco slots scale with what you drive.
Two products, one setup page. The bridge's web page is the OCU-1's page with different cards: same PIN gate, tabs, switches and Save bar, and every option marked Save or Save & restart. If you have set up an OCU-1 you know your way around already.
What to build
The firmware runs on a Lolin D32 (ESP32-WROOM-32, 4 MB flash) and needs nothing else for most outputs. Add only what your layout calls for:
| Output | Hardware beyond the D32 |
|---|---|
| LocoNet over TCP or Binary (DR5000, YaMoRC, JMRI) | none |
| DCC-EX, XpressNet-LAN, LCC hub, MQTT | none |
| LocoNet bus | comparator + transistor interface on GPIO 16 (RX) / 17 (TX) |
| LCC on the CAN cable | 3.3 V CAN transceiver (SN65HVD230, TJA1051/3) on GPIO 4 (TX) / 13 (RX), CANH/CANL to the LCC cable, terminators as usual |
| Wired network | LAN8720 RMII module on the ESP32's fixed RMII pins, MDC 23 / MDIO 18 |
| Status screen | 0.96 in SSD1306 OLED on SDA 33 / SCL 32, 3V3 |
All the default pins are clear of one another, so you can add any of these later without moving anything. Every pin is changeable on the setup page.
LocoNet bus interface. The standard circuit: the bus into an LM393 or LM311 comparator whose (3.3 V-safe, open-collector) output feeds GPIO 16, and an NPN transistor (BC337 or similar) pulling the bus low from GPIO 17 through a 1 kΩ base resistor. Leave Invert TX on for that driver. If nothing decodes, flip Invert RX — it depends on which comparator input you fed the bus into.
Flashing. Open the ThrottleBridge_x.y.z folder in the Arduino IDE, board ESP32 Dev Module, partition scheme Minimal SPIFFS (1.9 MB APP with OTA) — that one leaves headroom and keeps over-the-air updates working. Install the U8g2 library from the Library Manager (the only one needed; the OCU-1 uses it too). Upload at 460800 baud if 921600 fails on your cable. After this one USB flash, every later update can go through the setup page.
First power-on
With no WiFi saved the bridge starts a hotspot named ThrottleBridge-xxxx — the last four digits of its MAC — with password 12345678. The OLED, if fitted, says so.
- Join
ThrottleBridge-xxxxfrom a phone or laptop. - Open http://192.168.4.1 — type the
http://; a browser that silently upgrades tohttps://will say "refused to connect". Enter the PIN (factory1234). - On the Network tab, tap Scan for networks under the SSID field, pick your layout's WiFi and type the password.
- Tap Save & restart.
The bridge joins your network and from then on is at http://throttlebridge-xxxx.local (same four digits, lower case) or its IP, which the OLED shows. If it cannot join, it starts the hotspot again so you can always get back in.
The System tab is the first thing to look at when anything is odd. Its status card shows the network link, the output and whether its link is up, how many clients are on each input, track power, free memory and uptime, refreshed every two seconds. Most questions are answered there.
The setup page
Five tabs. Every option carries a sentence explaining it and a tag: Save applies as soon as you press Save; Save & restart is stored now and used the next time the bridge starts, so press Save & restart when you are done with those.
- System — status, hostname, serial debug, firmware update, the OLED, the PIN, restart and factory reset.
- Network — layout WiFi (with scan, DHCP or static), the hotspot, Ethernet.
- Inputs — Z21, WiThrottle, LCC throttles, MQTT throttles, turnout behaviour.
- Output — which layout, and its settings.
- Roster — locos, turnouts, routes, and what the station published.
Network addressing. Both the layout WiFi and Ethernet take a DHCP address by default. Addressing → Static on either card takes IP, gateway, mask and an optional DNS (blank = the gateway). Reserve the address on your router as well. A static setting with no IP entered falls back to DHCP rather than leaving the bridge unreachable.
Ethernet. Turn it on under Network → Ethernet with the LAN8720 fitted. WiFi keeps running alongside; the status card shows which link carries the address. RMII takes GPIO 0, 19, 21, 22, 25, 26 and 27 — the default OLED, CAN and LocoNet pins are clear of these.
Hostname and node identity. The hostname you set gets the MAC tail appended, so several bridges can share a layout. The LCC node ID defaults to Traction's range 05.01.01.01.AE.xx with the low byte from the MAC and is editable on the Output tab's LCC card; change it if an OCU-1 on the same network happens to share the low byte.
Inputs
Each input is a server the bridge runs. Enable the ones you use on the Inputs tab; the others cost nothing.
Z21 throttles
The Roco Z21 app, the WLANmaus and Z21 handsets connect to the bridge as if it were a Z21, on UDP port 21105. Point the app at the bridge's address (or let the WLANmaus find it on the hotspot). Locos are driven in 14, 28 or 128 steps as the throttle chooses; F0–F28, turnouts, track power and stop all work.
The card also has the serial number and the reported hardware and firmware the bridge claims to be. These change which features the app offers; they do not change the app's foreign control station notice, which appears with every non-Roco station and is dismissed with a tap (or a one-time unlock).
Traffic log. Switch on Log Z21 traffic and use the throttle: every datagram in (<) and out (>) appears with anything the bridge did not recognise tagged. It is the fastest way to see where a throttle stalls.
WLANmaus. The maus sends its status request with a wrong checksum byte. A real Z21 accepts it and the bridge does too, flagging it in the log as bad XOR, accepted anyway.
WiThrottle throttles
Engine Driver, WiThrottle, the Traction OCU-1 and any JMRI-protocol client, on TCP 12090. The bridge is advertised by mDNS, so these apps list it by name without an address being typed. They get the roster, turnout list and route list from the bridge (see Roster), function labels when they take a loco, and can add a loco by roster name. A throttle that stops sending its heartbeat for 30 seconds has its locos stopped and is dropped.
LCC throttles
With this on, the bridge joins the LCC network — the hub or CAN cable named on the Output tab — and offers a Train Control node for every loco an LCC throttle asks for: the well-known DCC train ID 06.01.00.00.xx.xx, created on a Train Search or a node verify. The throttle assigns itself as controller and drives it; the bridge forwards to whatever the output is. That is how an OCU-1 in LCC mode, or JMRI's LCC throttle, drives a LocoNet layout.
Not available when the output itself is LCC — the bridge would answer its own searches. The page greys the choice out and says why.
MQTT throttles
JMRI throttles on its MQTT connection, Node-RED, Home Assistant or a script publishing JMRI's topics (cab/<addr>/throttle, /direction, /function/<n>, track/power, track/turnout/<n>) drive the layout. The bridge also publishes power and turnout state, so JMRI's MQTT objects follow the layout. Broker, channel prefix and topic profile are on the shared MQTT card, which appears on both the Inputs and Output tabs because it is one setup for both directions. Not available when the output is MQTT.
Custom topics. Choose Custom on the MQTT card to set every topic template ({0} = address or turnout number, {1} = function number, each filling a whole level between slashes), the payload words, and the speed scale (0–100, 0–126 or 0.0–1.0). They apply to the input and the output alike.
Outputs
One output at a time, chosen under Output → Layout. Each has its own card of settings; only the chosen one is used.
LocoNet bus
The physical bus through the comparator interface. The bridge is a LocoNet throttle: it acquires a slot for each loco (OPC_LOCO_ADR, null move, IN_USE), forces it to 128 steps unless you turn that off, sends F0–F8 through the slot and F9–F28 as immediate DCC packets, keeps idle slots alive so the station does not purge them, and paces speed updates so a phone slider cannot flood the bus. Turnouts are OPC_SW_REQ with an automatic off after 250 ms for apps that never send it. Track power and stop are GPON, GPOFF and IDLE.
The Slots section of the LocoNet card covers the timing: acquire timeout and retries, speed spacing, keepalive.
LocoNet over TCP and LocoNet Binary
The same LocoNet logic without any hardware: the bridge connects to a station that already has the bus. LocoNet over TCP is the LbServer text protocol; LocoNet Binary is raw bytes, as from a LocoBuffer. Pick whichever the station has enabled — they are equivalent to the bridge.
Ports: an original DR5000 runs one network protocol at a time, chosen in its configuration tool, always on 5550. A YaMoRC YD7001, or a DR5000 with the YD9401 module, runs them all at once — XpressNet 5550, LbServer 1234, Binary 5560, Z21 21105.
Serving LocoNet over TCP. With any LocoNet output the bridge also serves LocoNet over TCP (LbServer) on port 1234, so JMRI or Rocrail can use the bus through it alongside the throttles. In JMRI choose LocoNet Server (LbServer) with the bridge's address.
DCC-EX
An EX-CommandStation over its native command protocol, port 2560. Locos, functions, accessories, power and stop map directly; the station's <l> and <p> broadcasts keep throttles in step with whatever JMRI or another throttle does. On connect the bridge asks for the station's roster, turnouts and routes (<JR>, <JT>, <JA>) and merges them into what throttles see — station turnouts are thrown by their DCC-EX id, routes and automations started with </ START id>.
XpressNet-LAN
The Lenz LI-LAN protocol, port 5550: Lenz LZV200 and LI-LAN, and the DR5000 and YaMoRC again. Speed, all five function groups (F0–F28), accessories, power and stop. XpressNet's loco-info replies carry no address, so the bridge polls the locos it holds one at a time and attributes each reply to the last one asked; that is how it learns about changes made from another XpressNet throttle. Turnout polarity follows the output 1 = closed switch on the Inputs tab.
LCC Traction
The bridge is an OpenLCB controller, using the same code as the OCU-1: alias allocation, Train Search for the address, the well-known DCC node ID as a fallback, controller assignment, float16 speed at one step per scale mph, functions, heartbeat, and "taken by another throttle" handling. Track power is the emergency-off events; turnouts are DCC accessory events.
Two links: Hub over TCP — GridConnect to a CS-105, an OpenMRN hub or a JMRI LCC hub on port 12021 — or CAN bus, straight onto the LCC cable through a 3.3 V transceiver at 125 kbps. The link choice is shared with the LCC throttle input.
MQTT
The bridge publishes JMRI's topics (or your custom ones) to a broker for MQTT-native locos and decoders, or for JMRI's MQTT layout objects, and subscribes to power and turnout topics so changes made elsewhere reach the throttles. On reconnect it re-publishes power and every held loco. QoS 0, retained. Broker, login, prefix and topic profile are on the shared MQTT card.
Roster, turnouts and routes
The Roster tab is what WiThrottle throttles are shown. Three lists, stored on the bridge separately from the settings:
- Locos — up to 32, each with a name and 29 function labels. A new entry starts with Headlight, Bell and Horn on F0–F2. Engine Driver and the OCU-1 then show Bell instead of F1.
- Turnouts — up to 32 named turnouts by accessory address (1-based, as your decoders are numbered).
- Routes — up to 16, each a name and a list of turnout positions written as
12C, 13T, 14C, thrown in order with 400 ms between.
With a DCC-EX output the station's own roster, turnouts and routes are merged in, shown on the From the station card. A local entry wins over a station entry for the same address. Locos not on any roster still work by address.
Saving a list on the page pushes it to throttles already connected.
The screen
The optional OLED shows, top to bottom: the title band with an ECG-style heartbeat that walks left once a second; the IP address and how it got it (STA, AP or ETH); the output and a dot that is solid when its link is up and blinks when it is down; clients per enabled input; and a footer that rotates between track power with bus traffic, uptime with error count and firmware version, and the network name.
The heartbeat is advanced from the main loop, so a frozen trace means a frozen bridge — that is its job. Brightness is on the System tab; turn it down for a display that is on all day.
Updates
System → Firmware. Export the compiled binary from the Arduino IDE (Sketch → Export Compiled Binary) and upload the file ending in .ino.bin. The page checks it will fit, shows progress, the OLED says do not power off, and the bridge restarts on the new version. Settings, roster, turnouts, routes and the PIN are kept — new settings added by an update take their defaults.
The Firmware card shows the running version, the firmware size against the app slot, and whether an OTA slot exists. Huge APP partition schemes have none; use Minimal SPIFFS (1.9 MB APP with OTA).
When something is off
"Refused to connect" on the setup page. Type http:// in front of the address; browsers silently try https://.
Output shows "down". For the network outputs, the station's address or port on the Output card, or the station's own protocol setting (an original DR5000 offers one at a time). For the LocoNet bus, the wiring: if Bus RX on the status card never counts while a real LocoNet throttle is used, flip Invert RX; if the serial log says no echo on byte 0, the RX side cannot see your own TX — a circuit problem, not firmware.
A throttle connects but nothing moves. Z21: switch on the traffic log and watch for the throttle's drive commands (E4 13 …); if they arrive, the problem is on the output side and the serial log will show the slot request. WiThrottle: check the throttle is on the same network as the bridge's address, not its hotspot.
Turnouts throw the wrong way. Flip Z21 output 1 = closed on the Inputs tab; it also governs XpressNet.
F9 and up do nothing on LocoNet. The immediate-packet high-bit byte differs between command stations; the comment in SlotManager::sendImm says which one line to change.
The Z21 app says the station is not genuine. It says that about every non-Roco station. Tap OK, or use the one-time unlock, or use the WLANmaus, Z21 handsets or a WiThrottle app, none of which check.
Free RAM is falling over days. The status card's largest block figure is the fragmentation indicator. It should hold steady; if it does not, note what was connected and tell us.
Lost the PIN or the WiFi. Hold nothing — there is no button. Flash over USB, or if you can still reach the page, Reset all on the System tab restores the factory defaults and the hotspot.