Build the DEJA accessory decoder
A do-it-yourself DCC accessory decoder. Wire an ESP32 or an Arduino Nano to the track through an optocoupler, add hobby servos or LEDs, and the command station moves your turnouts and lights your signals. It costs a few dollars in parts, and DEJA writes the firmware configuration for you.
The page follows the build order: see how it fits together, gather the parts, wire the three parts of the circuit, set it up in DEJA, flash, test.
What it is
A pure DCC accessory decoder. It reads the track, like a DS64 or a Switch-It, and nothing else. It has no WiFi in this release and never reports back, so its turnouts are commanded, not confirmed, like any other third-party decoder. Add a feedback sensor if you want proof.
Each decoder has:
- Up to 16 outputs. Each one is a servo (slow motion, with an optional frog relay), a pulse output (a quarter-second kick for an uncoupler), a steady on/off output, or a signal head. The default build has 4.
- Signal heads of three lamps (red, yellow, green) on plain GPIO pins or on a WS2812 (NeoPixel) strip. Each head is one output, so heads and turnouts share the 16.
- A DCC address you can change from the programming track.
How it fits together
Three parts: the track side (an optocoupler that turns DCC into a logic signal), the board, and whatever the board drives. Power comes from the track through a rectifier and a buck converter, or from a separate 5 V supply.
Parts
| Part | Notes |
|---|---|
| ESP32 DevKit v1 or Arduino Nano | A plain WROOM DevKit is the easy ESP32 choice; WROVER modules reserve GPIO 16 and 17 for PSRAM. |
| Optocoupler | H11L1 (6-pin) for an ESP32, which runs at 3.3 V. 6N137 (DIP-8) for a Nano at 5 V. |
| 1N4148 diode | Protects the opto's LED from the reverse half of the DCC wave. |
| 1 kΩ resistor | Sets the opto's LED current. |
| 10 kΩ resistor | Pull-up on the opto's output. |
| 0.1 µF capacitor | Decoupling, right at the opto. |
| Bridge rectifier (DB107) | Only if you power from the track. |
| Buck converter (MP1584), set to 5.0 V | Only if you power from the track. |
| 470 µF capacitor | One after the rectifier, one on the servo rail. |
| SG90 or MG90S servos | One per turnout. |
| Optional | A 3.3 V-input relay module for frog power, LEDs with resistors, a ULN2003A for lamps on a 5 V rail, or a WS2812 strip. |
1. Wire the DCC input
The optocoupler is the one part that touches the track. Its LED side sees the rails; its transistor side talks to the board. Nothing else crosses that line.
Step by step:
- Rail A → 1 kΩ → pin 1 (anode). Rail B → pin 2 (cathode).
- 1N4148 across pins 1 and 2, stripe towards pin 1. It points the opposite way to the opto's own LED, so the LED sees only one half of the DCC wave and the diode carries the other half.
- Pin 6 (VCC) → 3.3 V from the ESP32's 3V3 pin. Pin 5 → GND. The 0.1 µF capacitor goes across pins 6 and 5, right at the chip.
- Pin 4 (output) → GPIO 34, with the 10 kΩ pull-up from pin 4 to 3.3 V. GPIO 34 to 39 have no internal pull-up, so the resistor is not optional.
On a Nano use the 6N137 instead: rail A → 1 kΩ → pin 2, rail B → pin 3, the diode across pins 2 and 3, pin 8 VCC and pin 7 (enable) to 5 V, pin 5 GND, pin 6 output → D2 with the 10 kΩ pull-up to 5 V. The 6N137 is specified for 4.5 to 5.5 V; it usually works at 3.3 V but is out of spec there, which is why the ESP32 gets the H11L1.
⚠️ ESP32 pins are 3.3 V only. Pull the opto output up to 3.3 V, never 5 V, and never let track voltage near a GPIO.
2. Power it
Pick one of two ways. Both leave the opto listening to the track.
Option A: from the track. Both rails into the bridge rectifier, a 470 µF capacitor across its output, then the buck converter set to 5.0 V. Feed that 5 V to the ESP32's VIN pin or the Nano's 5V pin, and run the servos from the same rail with their own 470 µF capacitor close to them.
Option B: a separate 5 V supply. A USB supply into the board, or a 5 V wall supply into VIN. Join the supply's ground to the board's ground, but never to a rail. With no servos (a signal-only decoder) the whole thing draws well under 200 mA, and this is the simplest build.
Budget the servos. An MG90S can pull close to an amp as it starts to move, so several moving together on a route can overwhelm a small buck converter like the MP1584. A busy decoder wants a bigger 5 V supply.
3. Wire the outputs
Servos and frog relays
- Servo signal lead → the output's GPIO (the pin you assign in DEJA). Power and ground go to the 5 V servo rail, never through the board's 3.3 V pin.
- Frog relay (optional). Assign a second pin as the output's frog pin and drive a relay module from it. On an ESP32 the module must accept a 3.3 V input, or switch it through a transistor. The relay's common goes to the frog; normally-open and normally-closed go to the two rails.
- The firmware detaches a servo once it arrives, so it does not buzz, and remembers each position across a power cycle.
Signal lamps
Each head is three lamps, red, yellow and green, on three pins (or -1 for a lamp you have not wired). The question is where the LEDs get their current. ESP32 pins are 3.3 V and comfortable at 12 mA or less each, so a red, yellow or green LED with a small resistor can hang straight off a pin, but a white or blue LED (forward voltage 3 V or more) cannot, and a resistor sized for a 12 V signal leaves almost nothing.
| LEDs with an in-line resistor of… | From a 3.3 V pin (red / yellow / green) | Do this |
|---|---|---|
| 100 to 220 Ω | 5 to 13 mA, bright | Drive straight from the GPIO, common cathode to GND. |
| 330 to 470 Ω | 2 to 4 mA, dim | Usable in a dark room; better behind a ULN2003A on 5 V. |
| 680 Ω to 1.2 kΩ | 1 to 2 mA, barely glows | ULN2003A on a 12 V rail, or change the resistors. |
| Any value, white / blue / lunar LEDs | ~0 | ULN2003A on 5 V or 12 V. |
The ULN2003A is a seven-channel driver that switches cleanly from 3.3 V logic. It sinks each lamp's cathode to ground, so the LEDs take their current from a 5 V (or 12 V) rail and the ESP32 pins carry almost nothing. It is the recommended way to wire a signal, and it keeps the resistors you already have.
- Direct drive (resistors of 220 Ω or less, red/yellow/green only): each lamp's resistor lead to its pin, all cathodes to GND. That is common cathode, the firmware's default.
- Common anode signals (the common lead is +) cannot be driven directly: the generated firmware drives lamps common cathode, pin high for on. Put them behind a ULN2003A instead. To the firmware that is still common cathode, whichever way the LEDs themselves are wired.
- WS2812 strips take three pixels per head, in the order red, yellow, green, on the WS2812 data pin. A strip pauses the Nano's interrupts for about 30 µs per pixel while it updates, so keep the count small on a Nano and test with a long run of throws; an ESP32 has no such limit and is the better choice for a big gantry.
A seven-lamp signal, say two full heads plus a marker lamp, is three signal outputs: head 1, head 2, and a third head with only its red pin wired. Each head is a separate Signal in DEJA with its own aspect; a prototype "yellow over green" is the two heads set together by a route or an automation.
Pulse outputs
A pulse output drives its pin high for a quarter of a second, then low again, whatever direction was asked for. That suits a single-coil uncoupler. It does not suit a twin-coil switch machine such as a Peco solenoid, which needs a separate drive per direction; convert those to a servo or use an external capacitive-discharge unit. Never use a steady output for a solenoid: it holds the coil on and burns it. And never connect a solenoid straight to a GPIO: let the GPIO switch a driver board.
Pins
These are the defaults. deja deploy builds the sketch with the pins you choose in DEJA.
| Function | ESP32 | Nano |
|---|---|---|
| DCC input (from the opto) | GPIO 34 | D2 (interrupt pin) |
| Servo outputs 1 to 4 | GPIO 13, 14, 16, 17 | D3, D4, D5, D6 |
| Good lamp pins | GPIO 18, 19, 21, 22, 23, 25, 26, 27, 32, 33 | D7 to D10, A0 to A5 (enter A0 to A5 as 14 to 19) |
| WS2812 data | GPIO 5 | D11 |
| Frog relays | the pins you assign | the pins you assign |
DEJA refuses pins that cannot drive an output: on the ESP32, GPIO 6 to 11 (flash), 34 to 39 (input-only) and the strapping pins 0, 2, 12 and 15; on the Nano, D0 and D1 (USB serial) and A6 and A7 (input-only).
The ESP32 pulses GPIO 14 at boot, so a servo on it can twitch once. GPIO 16 and 17 are used by PSRAM on WROVER modules, so pick other pins there.
4. Set it up in DEJA and flash
- Devices → Add device → DCC Accessory Decoder. Command station: your DCC-EX. Decoder: DEJA ESP32 or DEJA Arduino. Base address: the first output of a board, 1, 5, 9, 13, … (4 × n + 1). DEJA will not save another value for these profiles.
- Bind your turnouts and signals to its outputs: the output type and pins on the decoder's page, the lamp pins and wiring on each signal.
- Open the device's Firmware section and read the generated
config.h: output types, pins andDEFAULT_CV1/DEFAULT_CV9should match what you wired. - Take the decoder off the track (or switch track power off). USB and track power must never feed the board at the same time.
- Run
deja deploy <device>. The first run installs the tools and Arduino libraries it needs; see deploying firmware. - Reconnect the track and power up.
Many Nano clones need the older bootloader. If the upload fails, deploy with
deja deploy <device> --board arduino:avr:nano:cpu=atmega328old. The board menu calls this "ATmega328P (Old Bootloader)".
5. First test
From the DCC-EX terminal in DEJA, with the decoder at base address 1 (board 1 → linear addresses 1 to 4):
| Command | Expect |
|---|---|
<a 1 0 1> |
output 1 thrown; <a 1 0 0> closes it |
<a 1 1 1> |
output 2 thrown |
<A 1 0> |
a signal head on output 1 shows red; <A 1 1> yellow, <A 1 2> green, <A 1 3> dark |
Then throw the turnout from its card and set the signal's aspect from its card: the same hardware should move. Power-cycle the decoder; the servos should not sweep, and each head should come back as it was.
Extended signal commands (<A>) need DCC-EX 5.2.34 or newer on the command station. Check with <s>.
Configuration variables (CVs)
You can change a lot of the decoder from the programming track, with no re-flash.
| CV | What it does |
|---|---|
| 1 and 9 | The board address (low and high part). |
| 29 | Compile-time only. Bit 6 chooses board addressing (0) or output addressing (1), and it is set when the sketch is built. A write to it on the track applies only until the next boot, when the decoder rewrites CV29. |
| 33 + 8 × n | Output n (counting from 0): type (1 servo, 2 pulse, 3 steady, 4 signal head), thrown angle, closed angle, speed, frog relay on or off. |
| 161 + 4 × h | Signal head h: which of red, yellow and green are lit for each of the four aspects. |
| 238 and 239 | Which build of the settings the decoder last applied. The decoder writes these itself. Leave them alone. |
| 240 + n | The last position of output n. The decoder writes this itself, after the output has been still for 5 seconds. Leave it alone. |
A value of 255 (the blank default) means "use what the sketch was built with". Angles and speed take effect at once. A change of output type or of the frog relay needs a power cycle before it takes effect.
Re-flashing with changed settings resets the address and output/head CVs to the new config.h values; saved servo positions are kept. CV8 = 8 only resets the address.
Changing the address. Put the decoder alone on the programming track and write CV1 and CV9 (<W 1 v> and <W 9 v>), or use the Set decoder address helper described in Programming the address. For a DEJA decoder the helper writes only CV1 and CV9, does not stop on a -1 reply, and reports "Written (no ACK from this decoder)". Never write <W address>: that changes a locomotive's address, not a decoder's. To switch to output addressing, build with it set instead.
This firmware has no ACK circuit, so DCC-EX reports
-1after each<W>. The write still takes effect: power-cycle and test.
Optional ACK circuit. If you build the sketch yourself, set ACK_PIN in config.default.h (or pass -DACK_PIN=<gpio>) to a free output pin. The decoder drives it high for 6 ms to acknowledge each write. Wire that pin through a 1 kΩ resistor to the base of an NPN transistor (a 2N2222, say), and switch a 150 Ω, 2 W resistor across the rectified track supply with it (about 100 mA at 14 V), so the command station sees the extra 60 mA or more it is listening for.
Troubleshooting
Nothing responds.
- Check the opto's orientation (the dot marks pin 1) and that the 10 kΩ pull-up goes to the board's own logic voltage.
- Check the output lands on the DCC pin (GPIO 34 or D2).
- Check the base address in DEJA matches the decoder's CV1 and CV9.
- For signals, check
<s>reports DCC-EX 5.2.34 or newer.
A servo jitters or the board resets. Almost always power. Give the servos their own 5 V rail with the 470 µF capacitor, and join the grounds.
Every lamp is dim. The in-line resistors were sized for a higher voltage; see the table under Signal lamps.
A lamp is on when it should be off. The signal is wired common anode on a direct drive. Move it behind a ULN2003A (see Signal lamps).
It answers four addresses off. The DEJA decoder uses DCC-EX numbering by construction, so there is no ±4 offset to set for it. Check the base address in DEJA matches CV1 and CV9, and that the command station sending the packets is DCC-EX. If you re-programmed the address on the track, program it again, or write CV8 = 8 to go back to the address it was built with.
The Nano upload fails. Try the old bootloader board setting above.