ESP8266 DCC Controller
This is a DCC model railroad controller based on the nodeMCU ESP8266 IOT module. It makes use of other modules commonly available on eBay for the Arduino such as a 1602 LCD display, keypad, jogwheel and I2C backpacks.
The controller implements the JMRI protocol over WiFi and you can control your locomotives directly from one or more smartphones. You will need to download the free EngineDriver (Android) or WiThrottle Lite (Apple) App to your phone or tablet.
The system was designed around low cost components and can run 8 locomotives simultaneously drawing a maximum 4 Amps. There are various build options. The lowest cost option is a nodeMCU, an INA219 current monitor and an L298 motor driver module. That's it! Control in this case is via a mobile phone over Wifi.
The local LCD display, keypad and jogwheel are optional. Personally I prefer this kind of tactile interface. Smartphones require you to look at the phone instead of the road...
I designed a system board to hold the nodeMCU, some discrete components, the INA219 monitor and it also gives the option to add an integrated LMD18200T motor controller.
The system can support:
- 8 locomotives, short or long addresses, 126 or 28 speed steps, 16 functions.
- 8 turnouts Holds the loco and turnout roster in EEPROM
- Supports Service Mode programming (prog track) and Program on the Main (POM) for locos and accessory decoders.
- Does NOT support LocoNet.
- Does not support DCC++.
If you want a low-cost entry level DCC controller with Wifi support and quite a lot of features, based on open source Arduino software then give this project some consideration. Thanks!
New option: WeMos D1 with L298 motor shield. This is a simple 2 boards, 1 PSU and no soldering option. See my other instructable.
Supplies
core components
- nodeMCU 1.0 which is the ESP8266, also known as 12-E.
- INA219 current monitoring board.
- 12V dc regulated power supply
- system PCB
- 5V regulator + heatsink
- capacitors, diodes, resistors, headers, dupont jumpers
one of the following motor control boards
- L298
- LMD18200
- IBT2 aka BTS7960B
optionally, for the local hardware user interace DSKY
- 1602LCD with I2C backpack
- 4 x 4 matrix keypad
- I2C GPIO expander PCF8574 to fit the keypad
- red pushbutton for emergency stop, green for mode
- rotary encoder, 5 terminal type
Choose Your Power Hardware
L298. Lowest cost option is the L298 motor board. The ones commonly available on eBay also have a bonus 5v regulator on them which neatly supplies the logic on the system board. Can support 2A, but you can common its outputs for a total 4A power.
LMD18200T as a separate module. Available on eBay as a stand alone H bridge motor controller. It's robust and can support 4A. If you use this device option you will need to put a 5v regulator on the system board.
LMD18200T built into the system board. You can populate the system board with 5v regulator, some discretes and build in an LMD18200T chip. Both the 5v regulator and LMD need a heatsink.
IBT2. Available on eBay. Dual H bridge claims to support 40Amps. That's an insane amout of power which will easily melt things if it shorts! The system INA219 will shut the system down at max 4A anyway (unless you modiify it to say 8A), but realistically with 8 modern sound decoder locos pulling 500mA each, 4A should suffice.
Chose from one of the above. The next two are required elements.
INA219 this is a current and voltage measuring module. It is an essential part of the design. This high-side device can measure up to 4Amps (as typically configured on a module) and uses an I2C bus to communicate with the nodeMCU. The system is capable of detecting shorts / current overloads within a few tens of milliseconds and will immediately shut down power. This protects your locomotives. A common short-circuit scenario is running a loco over a turnout set against it. If power is not shut down quickly, a couple of amps might flow thorugh the loco pickups and wiring. You don't want that.
12V power supply. A laptop power supply is good here. I have a box full of Dell 65 Watt supplies but these push out 17 or 19V. I step these down to 12V with a Buck Converter (eBay again). The DCC specifications suggest 12 to 14V as a suitable track voltage for HO scale. I prefer to keep it close to 12V. Locomotive decoders often have components which are only rated to 16V. If you can find a 12V laptop supply that can push out 2 or 3Amps, or in fact any modern switch mode PSU plug-pack, great, no need for a buck converter. Make sure it is a regulated output. Typically these will have a ripple of only a few mV. DO NOT use an old DC train set transformer. These push out 12V rectified but unregulated and will fry the electronics.
Chose Your Interface Options
Bare bones. No local pushbuttons, display or keypad. No jogwheel. Control is via a mobile phone running EngineDriver (Android) or WiThrottle (IOS). With bare bones, one of the nodeMCU board pushbuttons acts as an emergency stop, or you can wire on a single pushbutton. Or you can hit the emergency stop in the EngineDriver app.
DSKY. Add local pushbuttons for Emergency Stop and Mode. Add a LCD 1602 display and a 4 x 4 matrix telephone style keypad. DSKY = display/keypad.
Jogwheel. If you go the DSKY option, you can also add a Jogwheel. This is my favourite method of control due to the tactile control. Rotate to go faster/slower. Push it down to apply a brake or reverse direction, and even better, if the DSKY is showing the turnout display, you can toggle the turnouts with the keypad AND still drive the active loco through the Jogwheel. Makes shunting operations easy.
Laptop. Some of the system hardware features such as current and voltage trip limits, the IP address and SSID can be set through a web browser. The DCC controller has a default SSID of ESP_DCC and IP of 192.168.6.1. You can also manage the loco and turnout roster through a web browser and programme loco decoders in Service Mode or POM mode. You can also do most of these things through the DSKY. You cannot drive locos through the laptop. It's technically possible with more software, but little point given how good the mobile phone apps are.
Hardware Build
OK, you have chosen your options and rounded up the components. Time to build it. I designed a system board PCB and this supports all the various build options. I have a limited number of these for sale [2022-09-01 update, sorry these are all sold], but I will make the Gerber files available to you so you can order your own. If you build the L298+bare bones option, you could hook all the elements up with dupont wires and avoid using the system board. For any of the more complete builds you really need a system board.
System board. This accepts a 12V power feed. Has an 5v regulator, space for the INA219 current monitor and LD18200 H bridge, some discretes and of course the nodeMCU. It also has various jumper options and provides a hookup for the I2C bus and jogwheel.
DSKY + emergency stop and mode buttons. This runs entirely over the I2C bus. The 1602LCD needs an I2C backpack module. You can buy the 1602 with backback as a complete unit on ebay. Several backpack versions are avaiable and the software allows you to set the configuration for the one you have. The keypad is a 4 x 4 telephone matrix keypad. Conveniently an I2C expansion board module is available which fits the keypad exactly and allows us to scan the key matrix over I2C. The emergency stop and mode buttons are wired as pulldowns onto this expansion pack also, which neatly integrates them. Note that the 1602LCD units typically are 5v units. This means we need to run the I2C bus at 5v. Luckily the I2C backpack, I2C expander for the keypad and the INA219 are all 5v tolerant. The nodeMCU is not, it needs to run at 3v3 and so the I2C bus clamping diodes are essential. 2022-04-01 in fact the nodeMCU pins when used as I2C open-drain/inputs are 5v tolerant, so the clamping diodes are optional.
Side ramble: The system board provides a jumper option to run the I2C bus at 3v3 but you will find the 1602LCD won't display anything. This is because the LCD bias voltage needs to be 4V lower than Vcc, which is actually a negative voltage if you run the LCD at 3v3. You could unsolder one end of the contrast pot and connect this to a -ve voltage bias. In fact I provide for this on the system board. There's a charge pump that generates -3v on a pin next to the I2C bus pins. It does work.... but... the LCD brightness is then poor because the LED backlight is on 3v3 rather than 5V. Save yourself the fuss and run the I2C bus at 5V.
Jogwheel. Also known as a rotary encoder. You need the vanilla 5 pin type. 2 pins are wired to the pushbutton function. The other 3 are wired to the jogwheel itself. These devices detect which way you are rotating them by encoding a pulse on 2 pins vs ground in various phases. They are dreadfully noisy electrically, and the software uses some clever state-engine logic to clean up the signal. IMHO this is the best interface.
Please read the build.pdf for more detail.
Hardware BUGS (update 2022-03-28). See errata schematic. If you build the LMD18200 version on my custom designed circuit board, you will find the system gives a brief DC pulse on the track during boot. This is because the drive pins for the LMD happen to boot high and then correctly go low 0.5 sec later in the boot sequence. To eliminate this, you need to remove the wire link JP6 and replace it with a 15k resistor mounted vertically. Then on the underside of the board, fit a 47uF cap between the LMD pin connecting to this resistor and the 3v3 rail. (the +ve side of the cap to the 3v3 rail). What this does is hold that pin high for about 0.8 sec during power up, before allowing it to go low (JP6 was just a wire link to gnd) and thus enabling the LMD output about 0.8s after power is applied by which time the boot sequence has correctly set the drive pins.
JogWheel BUG 2022-03-28 update. There are two types of rotary encoders available on ebay, the one I used (a free component not mounted on a board and with a splined spindle) will always have its switches open-circuit when on a detent. The second type comes mounted on a board and has a notched spindle, with markings such as HW040. This type alternates between both switches open and closed on alternate detents. I have updated the software on gitHub to support both. But both types have a hardware limitation due to the behaviour of GPIO15 on the ESP12.
Type-1. With type 1, the system will boot from power up (POR), you can programme it via USB and you can reset it via the pushbutton, PROVIDED the switch is not part-rotated between detents. If it is part-rotated then boot will fail. You can fix this by rotating to the next detent. That's fine if you are programming it, but once the unit is built, its annoying that boot occasionally fails (and the unit puts DC on the track) if the jogwheel was knocked to a mid detent position. To fix it so that it always will boot on POR, you need to solder a 47uF cap to the R5 feed to JP5, the other side of the cap is soldered to ground. What this does is modify the R5 (1k) pullup so that it pulses low on POR, and it holds this line low long enough that boot completes. The reason for all this is D8 (GPIO15) must be low during boot. If the switch is mid detent then R5 will pull D8 high and boot will fail. This capacitor fix will make POR reliable for type-1 switches. It does not fix USB programming or the reset button so my advice is to disconnect JP5 if you wish to programme the device.
Type-2. With these rotary encoders, boot definitely will fail on 50% of the detents. To fix: solder a 47uF cap to the R5 feed to JP5, the other side of the cap is soldered to ground. What this does is modify the R5 (1k) pullup so that it pulses low on POR, and it holds this line low long enough that boot completes. Note that USB programming will not work if the rotary encoder is in-circuit, so unplug JP5 (i.e. disconnect the rotary encoder) for programming. The reset button on the board will only work on 50% of the detent positions.
Wiring up type-2 encoders. These come marked with +, Gnd, CK, DT and SW. To wire these up, first swap the polarity. Connect + on the encoder to system GND. Connect Gnd on the encoder to system 3v3. This causes the on-board resistors to act as pull downs on the jogwheel. Connect SW to D0 on the ESP, and CK to D7 and CT to D8. SW when pressed will now pull D0 to system GND.
Proper fix for type-1 and type-2 encoders. Is shown in the PCB errata schematic pdf. It involves a transistor and is more complex, but it will allow the encoder to be in-circuit for USB programming, reset-pushbutton and POR.
Software
Whichever option you build, the software load is the same. You do need to set up global.h to specify the type of LCD backpack and keyboard scanner you are using with the DSKY. If you are not using the DSKY or Jogwheel, no problem, theres no need to set a configuration specially for this, the software just never sees an input from these missing devices.
The nodeMCU can be loaded with the software through the Arduino IDE. Its unlikely you will ever lock yourself out, but if you do the easiest way to fix the system is just reload the software via the IDE.
The software is avaiilable on this github link ESP_DCC_Controller
Lets run through the various modules.
- Global.h this header file contains the hardware configuration such as the 1602 LCD backpack and the I2C address of this and the keypad scanner. It also lets you set the power driver module you intend to use.
- ESP_DCC_Controller.ino is kept to a minimum. It calls various start up routines and runs a few timed activities from loop()
- DCCcore.cpp is the main logic. It manages the loco and turnout roster, generates DCC packets to go to the locomotives, handles the DSKY and current trip.
- DCCweb.cpp handles the web interface and also supports Websockets to make these interactive.
- DCClayer1.cpp is the low level code that generates the DCC signal on the track, driven from the DCC packets passed to it from DCCcore. A big shout out has to go to StefanBruens/ESP8266_new_pwm for his low level ESP code relating to PWM techniques which gave me the means through which I could generate a DCC signal directly from an ESP.
- JogWheel.cpp handles the rotary encoder
- Keypad.cpp handles the keypad matrix
- WiThrottle.cpp implements a JRMI V2.0 server and communicates with the mobile apps over TCP sockets. It leverages the ESPAsyncTCP library.
- You also need to load the Adafruit INA219 library.
See the build document regards the I2C addresses and the LCD backpack configuration. You need to set these in Global.h. After you compile and upload, remember to also select ESP8266 Sketch Data Upload. This will upload the content of the data folder to the ESP SPIFFs memory space. These are the web pages.
2021-12-07 update - read the PDF!
Sometimes getting the Arduino compiler to work can be tricky. Here are the exact libraries you need. This does compile successfully in the Arduino IDE 1.8.13. Do not use Arduino IDE 2.x the latest one because it cannot support uploading files (SPIFFS) to the ESP device. That's a known limitation of ver 2. Stick to 1.8x
ESP board library. The latest version 3xx will work ok.
Through the arduino library manager, load these three libraries;
websockets from Markus Sattler v2.1
Adafruit_INA219 and its dependencies version 1.0.3
arduinoJson is from library manager you want version 7x only. As of 2024-04 the updated software on Github works with version 7x.
and these next libraries must be downloaded from Github and saved after unzipping into your libraries folder. remove the -master ending on the folder name.
me-no-dev / ESPAsyncTCP
mlinares1998 / NewLiquidCrystal
ESPAsyncTCP
remember to configure Global.h for the type of power board and I2C backpacks you are using.
Hardware BUG. If you build the LMD18200 version on my custom designed circuit board, you will find the system gives a brief DC pulse on the track during boot. This is because the drive pins for the LMD happen to boot high and then correctly go low 0.5 sec later in the boot sequence. To eliminate this, you need to remove the wire link JP6 and replace it with a 15k resistor mounted vertically. Then on the underside of the board, fit a 47uF cap between the LMD pin connecting to this resistor and the 3v3 rail. (the +ve side of the cap to the 3v3 rail). What this does is hold that pin high for about 0.8 sec during power up, before allowing to to go low (JP6 was just a wire link to gnd) and thus enabling the LMD output about 0.8s after power is applied by which time the boot sequence has correctly set the drive pins.
New option: WeMos D1 with L298 motor shield. This is a simple 2 boards, 1 PSU and no soldering option. See my other instructable.
JogWheel BUG 2022-02-02. There are two types of rotary encoders available on ebay, the one I used (a free component not mounted on a board with a splined spindle) will always have its switches open-circuit when on a detent. The second type comes mounted on a board and has a notched spindle, with markings such as HW040. This type alternates between both switches open and closed on alternate detents. I have updated the software on gitHub to support both. But both types have a hardware limitation due to the behaviour of GPIO15 on the ESP12. See step 3 for a fix.
Software comment 2024-04-26. All software needs maintenance. NOW UPDATED. Uses LittleFS instead of SPIFFs, and the latest ArduinoJSON library 7x.
Downloads
Integrate and Test
Hardware built, software loaded. I remind you this is ENTIRELY AT YOUR OWN RISK. I take no responsiblity for you damaging any of your locos or decoders. Please double check your soldering and double check the power supply is the correct polarity and voltage is within limits.
First test is just the nodeMCU hanging on its USB cable. This allows us to test WiFi, web pages and the mobile apps.
Through the arduino serial monitor you should see a boot sequence which identifies all the I2C devices connected. It will say none, because there are none right now!
You should be able to see an SSID of ESP_DCC. Connect to this with a laptop and navigate to 192.168.6.1/index.htm. you should see a web page displaying power status and trip thresholds. Power will probably display as tripped, because the INA is missing and the voltage reading tends to jump to a random high value.
Fire up EngineDriver or WiThrottle on your mobile after connecting to ESP_DCC. the server connection is 192.168.6.1 and port is 18090. The mobile app should connect, and you should see a roster of engines 3,4,5 and 6.
Second test is to fit the nodeMCU into the system board and power up with no locos on the track.
CAUTION: If you leave your laptop connected to the nodeMCU via the USB port, make sure the laptop is running on battery only. When you power up the system board from its own supply, this could create a ground loop if your laptop's own supply is still plugged into the wall. Generally USB ports on laptops are protected but don't take the risk. You are warned. Again its at your own risk and I take no responsiblity for damaged laptops. You do not have to keep the laptop connected. Only do so if you wish to observe the serial port trace messages.
If the laptop is connected you will see a message showing the ESP scanning for I2C devices as it searches for the DSKY. Then it will boot and you will see a welcome message on the DSKY assuming you added one. After a few more seconds, if you scan Wifi from your laptop or phone you should see DCC_ESP.
Final test is with a loco on the track.
Same caution as test 2 regards the laptop if you keep this connected. If trace is enabled in Global.h you will see all the various WiThrottle messages being passed as well as websocket broadcasts. Once you are happy it all works, disable trace in global.h and recompile the software and upload it again to the ESP. Running trace messages all the time is not good for a production environment as it does cause delays in the program loop which might result in Wifi throttle drop outs.
Operate and Enjoy
Its time to power up and run some trains!
Please read the operating manual. Remember you can also access the web pages over your mobile phone.
If you have any bug reports, please post them here, or raise them at the Github page.
Downloads
Troubleshooting
DSKY problems
If you added a DSKY but the LCD display does not light up or there are no characters, then check the I2C wiring is correct, check you have set the jumper to the 5v I2C supply position and check you correctly configured the global.h file to match your specific LCD backpack. Also, when the unit boots and is reporting back to the Arduino IDE over the USB link (Serial Monitor) you should see the ESP find your backpack at the nominated address.
Also try adjusting the contrast pot on the backpack, sometimes the contrast is set so low that the characters are not visible.
Wifi problems.
The system will run and connect to laptops and mobiles even when it is just a nodeMCU board dangling on the end of a USB cable. From the IDE serial monitor you should see an I2C scan which will find no devices, and messages indicating start up of the webserver and websockets. At this point the ESC_DCC SSID should appear to your devices.