ESP8266 DCC Controller




Introduction: 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 simultantously 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.


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

Step 1: 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.

Step 2: 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 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.

Step 3: 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, 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. ThenodeMCUis 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 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.

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 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 will update 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. 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.

Step 4: 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.

ESP board library. Works with version 2.7.4 does not work with 3.0.0 or higher (will compile but ESP fails to provide an IP addess to clients and they cannot connect.)

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 but you want version 5x only

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


remember to configure Global.h for the type of power board and I2C backpacks you are using.

I updated the Github software recently, the system now correctly supports 8 locos.

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 will update the software on gitHub to support both. But both types have a hardware limitation due to the behaviour of GPIO15 on the ESP12.

Step 5: 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 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 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.

Step 6: 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.

Step 7: 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.

Microcontroller Contest

Participated in the
Microcontroller Contest

1 Person Made This Project!


  • Microcontroller Contest

    Microcontroller Contest
  • Eggs Challenge

    Eggs Challenge
  • Backyard Contest

    Backyard Contest



Question 27 days ago on Introduction

Hi, this is a great project, thank you for all the hard work you have done and I’ve almost got my first train running! I have built the “bare bones ‘ version running on an esp8266 dev module and the L298 H bridge with the INA219 power monitor. Software runs on my iPad and I’m using WIThrottle full version. How do I read CV values? I can’t see a hardware mechanism to read data off the track? Is there something I need to add to the above?


Answer 26 days ago

Hi. Step 6 in the operating manual, section "Part two: web interface"
you will see instructions on how to open a web browser from your ipad to default IP address i.e. open safari and navigate to this assumes you already connected the ipad to the DCC_ESP wifi hotspot. At this point it should bring up various web pages to let you set up a loco roster and there's also a page to read CVs off the track, send CVs and also support for POM mode.

Engine driver/ WiThrottle don't support CVs directly, but Engine Driver (Android) does have a web-window inside of it that will navigate to these web pages. I don't think WiThrottle does have this kind of in-app web-browser support but I only tested with the free version.

See attached image. For some reason Safari does not render correctly despite me using viewpoint in the HTML, so its oversize on the screen.

The system uses the INA219 to read the values, you have that installed so it will be able to read CVs from the track.


Reply 26 days ago

Hi and thank you for your rapid reply👍 Yes I have access to the web pages and can see all the different pages, when I try to read CV’s I just get ??? Back. I can get a few functions to work like bells and whistles but the train won’t move. I suspect it’s not making good contact with the track, I’m using the Marklin center track system and the train is a modified Hornby OO series which would normally use just a two rail system. I’ve converted it to use the center track but maybe not a good enough contact. Of course you have answered my question that the INA219 device reads the data from the track.
the paid version of WIThrottle does allow you to access the web pages directly.
many thanks for your help, I’ll concentrate on getting a good connection and let you know how it goes.


Reply 26 days ago

Hi. If you can see functions such as bells/lights working then it proves that the decoder is picking up the DCC signal and that is has contact with the track.
I suggest you check you have the motor wired correctly to the decoder.

When you send a request to read a CV, you should see the loco make little movements along the track as it responds. Per the DCC specification for Service Mode, to respond, the decoder needs to assert current pulses on the track and the way most decoders do this is to momentarily energise the motor.

If you see no movement / hear nothing at all, it suggests no-track contact, or
the motor itself is not connected. If you hear some pulses/see some movement but still read ??? this suggests dirty track. if you see ??? and hear nothing, this
suggests no motor in-circuit or there is zero track contact (but being able to turn lights on/off indicates this is not the case)

worse case the decoder motor output could be blown. is the decoder new? was it working with another controller previously?


Reply 21 days ago

Hi again,
I have reconfigured the global.h file and recompiled with trace enabled data as below.

If connected to withrottle (train works as before!) I see the heart beat every 1/2 second as shown below however led on the nodencu does not light:- (this may be just a pin issue in the file, I will check later)

11:35:10.495 -> data from client
11:35:10.495 -> element *
11:35:10.495 -> heartbeat
11:35:10.975 ->
11:35:10.975 -> data from client
11:35:10.975 -> element *
11:35:10.975 -> heartbeat
11:35:11.323 ->

If I switch back to the web server I get :-

11:37:10.170 -> }handleFileRead: /index.htm
11:37:31.329 -> [0] Disconnected!
11:37:31.329 -> [1] Disconnected!
11:37:31.363 -> {"type":"dccUI","cmd":"hardware","SSID":"DCC_ESP","pwd":"","version":20211213,"wsPort":12080,"IP":"","action":"poll","STA_SSID":"YOUR_SSID","STA_pwd":"none","uptime":406,"clients":0,"volt":9.74,"quiescent":1.95,"busmA":5.85,"base":5.85,"AD":3,"heap":39496}[0] Connected from url: /
11:37:34.383 -> [1] Connected from url: /
11:37:52.087 -> handleFileRead: /service.htm
11:37:52.225 -> [0] Disconnected!
11:37:52.225 -> [1] Disconnected!
11:37:52.225 -> {"type":"dccUI","cmd":"hardware","SSID":"DCC_ESP","pwd":"","version":20211213,"wsPort":12080,"IP":"","action":"poll","STA_SSID":"YOUR_SSID","STA_pwd":"none","uptime":427,"clients":0,"volt":8.956,"quiescent":1.95,"busmA":5.85,"base":5.85,"AD":3,"heap":39496}[0] Connected from url: /
11:37:52.293 ->
11:37:52.293 -> from WS: {"type":"dccUI","cmd":"service","action":"enter"}
11:37:55.260 -> [1] Connected from url: /
11:37:55.260 ->
11:37:55.260 -> from WS: {"type":"dccUI","cmd":"service","action":"enter"}
11:37:56.189 ->
11:37:56.189 -> from WS: {"type":"dccUI","cmd":"power","mA":5,"V":0,"trip":false,"track":false,"mA_limit":"","V_limit":""}
11:37:56.223 -> {"type":"dccUI","cmd":"power","mA":5,"V":0,"trip":false,"track":false,"mA_limit":1000,"V_limit":28,"SM":true}
11:38:05.510 -> from WS: {"type":"dccUI","cmd":"power","mA":5,"V":0,"trip":false,"track":true,"mA_limit":"","V_limit":""}
11:38:05.510 -> {"type":"dccUI","cmd":"power","mA":5,"V":0,"trip":false,"track":true,"mA_limit":1000,"V_limit":28,"SM":true}

And if I try to read the cv I get:-

11:43:20.554 -> from WS: {"type":"dccUI","cmd":"service","action":"read","cvReg":"1"}
11:43:22.630 -> verify fail 0
11:43:22.630 ->
reg 1 val -1
11:43:22.664 ->

For some reason its not reading a voltage from the INA219 although it seems to read it in the withrottle case above (quiescent 9.74). I have enabled the only one side of the bridge option in global.h

Hope this helps


Reply 21 days ago

If you boot the unit with the USB serial port dumping to your laptop, you should see the I2C addresses it finds at boot. Your INA219 address should appear there. If it does, this confirms the INA219 is active and the ESP can talk to it.

Next, check the INA219 is wired correctly in the circuit. Its a high-side device in my design, which means 12v comes into it and then from there goes to the H-bridge.

If you are reading zero volts it means the INA219 is either not in circuit or the I2C address is incorrect so the ESP is not actually reading it.

Your readings are on the low side.e.g. these are mine with the controller not even connected to the track

i.e. its reading 8mA and just under 14V.
Your boot message is also reading low

when the unit first boots it checks the current draw (i.e. base level current that the INA itself draws plus the H bridge plus the ESP if its being powered off that 12v supply via the L298 onboard regulator). i.e. quiescent current should be higher than the 1.95mA your message states. Again points to the INA not talking to the ESP or the INA not wired up correctly.
You are reading 8.9V so i assume you are using a 9V DC supply.

BTW here's a tip for monitoring the websocket messages without using serial. On your laptop, open Chrome and then download "Simple Web Socket Client for Chrome". This is a chrome extension. Then power up the ESP, and connect your laptop Wifi to ESP-DCC. open the WS chrome extension (click the blue WS icon, see encl graphic) and then enter the WS address as
ws:// and click OPEN. After a couple of seconds you should see the regular broadcast messages from the ESP.
The above message states 8mA and 13.4 track voltage. These messages are used to update the web interface when you have it open.

Note: another test is to put a resistor across the track. e.g. a 500ohm resistor with 9v track voltage wil draw 18mA. so you should see the mA reading move up/down depending on whether the resistor is there/not.

And of course the L298 needs to be wired up correctly so that there is a DCC signal on the track. A multimeter should read 9V AC and maybe 2V DC. if you read zero AC it means no DCC signal is present. You'd need a logic analyser or scope of some sort to see the actual DCC signal. But I think you said you were able to control the lights on your loco and that means that DCC is present on the track and you were sending messages to the correct loco address.

Good luck. its difficult to diagnose this over email :-) Note that the build instructions do cover all of this including the I2C communications. some INA modules are set to a different default address than others so do check the INA is actually working.

Note that CV reading relies on current pulses, the voltage is not relevant. Per the DCC spec, the loco needs to assert current pulses of 64mA above quiescent on the track, and its these that the INA picks up.

Note: if your laptop is corrected to DCC-ESP it cannot also browse the internet!

simple WS client.png

Reply 21 days ago

Many thanks again,
ok so I have the INA219 connected from the H bridge to the track, so after the H bridge rather than before it, logic here Im powering the ESP via the H bridge so didn't want that interfering with the track measurement. Vin comes from the H bridge (L298) and Vout goes to the track.
the INA is certainly picked up ok at address x40.
There are plenty of volts on the track I'm measuring 15.3 with an ac multimeter, the H bridge is being fed via an 18v psi. Steady state the INA reads 9.8 volts
reading in the Home Screen with power on.
if I put a 470 Ohm resistor across the track the current goes up to 4 mA and the voltage drops to 8.7V
I still can't get the heartbeat led to flash, its defined as GPIO 16 / D0.
The power led is fine but inverted as expected.
I think there must be an issue in my config file but I can't find it.
Also is there a way to configure the loco function keys so that WIThrottle can display them?
sorry to be a pain!



Reply 20 days ago

No! don't put the INA219 on the leads to the track. It is not an AC capable device and the readings will be junk. The DCC signal is effectively a 10kHz AC squarewave.

Put it on the high side to the unit from the 15v DC supply. That is how I designed it. I also designed the system to measure the quiescent current (i.e. no locos running) and subtract this from the instantaneous measurement. This gives the true track current.


Reply 20 days ago

Good morning, that did the trick, quiescent current is about 54mA and 18.4 volts.
I can now read the cvs without a problem.
I will try and get WiThrottle to see the web pages and let you know.
the heartbeat still isn’t showing but I will check the led configuration.
last question, how do I define the function keys for each loco? I think this is done in jmri and passed to WIThrottle?
many thanks for all your help and for this project👍👍👍


Reply 20 days ago

ok good to hear you have CVs working now. 18.4 track volts is rather high, most DCC decoders have 16v rated components in them, so you will be stressing them with 18v. NMRA recommends 14v for HO/OO scale.

What ESP board are you using? Only the nodeMCU supports heartbeat on GPIO16. From memory the WeMOS D1R1 won't support a heartbeat LED, as its LED is not wired to GPIO16. Global.h is where you select which board combination you are using. If you compiled using WEMOS_D1R1_AND_L298_SHIELD as the active config, this intentionally does not define PIN_HEARTBEAT

Function keys. The software always sends 16 of them to the throttle. Its the throttle itself that needs to be configured to determine which are/not displayed, read the WiThrottle documentation, or the EngineDriver documentation.


Reply 20 days ago

Hello again and thank you for your advice. I’m using Marklin C track and they supply an 18v power unit with their controller, I know the turnouts don’t work on 12V but I have a converter so I can try 14 V👍
The documentation on WIThrottle is not great, it states you can hide function keys which are not defined but I can see anywhere where you can define them. I’ll check engine driver.
im using a NodeMCU E-12 dev board V3 which has two useable leds.
I’ve stripped out all variants in global.h as I want to know what I’m doing so the D1R1 section has gone but the pin definitions look ok.
again many thanks for your support, I will let you know what I find👍


Reply 21 days ago

Here is an odd one, monitoring the web pages with my iPhone and running WIThrottle on my iPad I see this,
if I apply the 470 Ohm resistor to the track the web page shows 8.7 V and 4mA.
however, if I remove the resistor and start the train moving from WIThrottle i see no current on the web page????


Reply 21 days ago

If I use the 15v seen by the meter I should draw about 40ma so it looks like it’s a factor of 10 out?


Reply 22 days ago

Hello again, sorry for the delay, my workshop is in the attic and it’s been far too hot to work up there, so today I moved my work on to the kitchen table and progress has been made.
on the track, using the loco and turn out roster to feed WIThrottle I can now control the train. Forward/reverse and function calls all work. However, I still cannot recall the CV’s on a programming track, I can see the motor move as you say but nothing is read. Also the heartbeat monitor does nothing. The on board power on led is reversed, on - no power, off - power on track. This can also be controlled from WIThrottle.
I have also tried to access the jmri web pages from within WIThrottle but no luck, I’ve tried http:/ and and combinations without front and address 12090 but nothing works„
if you can give me any pointers I would be happy to try anything!
many thanks.

Anderson Vinicius
Anderson Vinicius

9 months ago

You have the VSC schedule for importing into ESP32 ?


Reply 9 months ago

What is a VSC schedule? I have not ported this to an ESP32 yet. Will do so by year-end, time permitting. I have figured out the DCC signal generation, but need to check whether all the other libraries will also work in the ESP32 environment. I also have a Wemos D1 R2 and R32 on order (arduino form factor ESP12 and ESP32) along with an arduino motor shield. The idea is to compete with the DCC++EX project.

Meanwhile, my latest software is on github. This works reliably, and now supports the nodeMCU motor shield. Plus, it can now also operate as a conventional DC PWM controller, supporting a single loco (number 3, with 28 speed steps).


Tip 11 months ago on Step 5

LCD library problems?

I used the library
"NewLiquidCrystal". You cannot obtain this library in through arduino library manager, you will need to download and save it to your libraries folder. It is confusing, lots of people took the original arduno library and tweaked for specific back-packs but NewLiquidCrystal is best because you can soft-configure to any back-pack of choice.

Personally I compile using vMicro, but I did run the exact same code back through the Arduino IDE version 1.8.13 and it compiled fine.

Finally look at the arduino IDE config screenshot, select these options;
You need to select board=Node MCU 1.0 ESP-12. Flash=4M (1M spiffs).


Reply 11 months ago

Hi, I have used the library that you suggested and that has cured the previous LCD compile errors. I now have the following:-
By the way the PCB came today, thanks.
Arduino: 1.8.15 (Windows 7), Board: "NodeMCU 1.0 (ESP-12E Module), 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:2MB OTA:~1019KB), 2, v2 Lower Memory, Disabled, None, Only Sketch, 115200"
In file included from C:\Users\Paul\Documents\Arduino\ESP_DCC_Controller-main\ESP_DCC_Controller\ESP_DCC_Controller.ino:32:0:
C:\Users\Paul\Documents\Arduino\libraries\ESP_DCC_Controller-main/DCCcore.h:182:24: error: 'MAX_TURNOUT' was not declared in this scope
extern TURNOUT turnout[MAX_TURNOUT];
C:\Users\Paul\Documents\Arduino\libraries\ESP_DCC_Controller-main/DCCcore.h:183:18: error: 'MAX_LOCO' was not declared in this scope
extern LOCO loco[MAX_LOCO];
C:\Users\Paul\Documents\Arduino\ESP_DCC_Controller-main\ESP_DCC_Controller\ESP_DCC_Controller.ino: In function 'void loop()':
ESP_DCC_Controller:188:5: error: 'loco' was not declared in this scope
loco[0].debug = true;
exit status 1
'loco' was not declared in this scope
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.


Reply 11 months ago

The arduino IDE is not a true cpp compiler. For this reason it may not behave when compared to a proper compiler such as Microsoft Visual Stuido with vMicro installed, which is what I used to develop this software.

I went back to basics, created a new windows account to emulate a new user, I downloaded the ESP_DCC_Controller from github and fired up the Arduino IDE 1.8.13

I deleted the reference to <ESP8266WebServerSecure.h> in the .INO file. It is not required.
I loaded the following libraries through Arduno library manager;
<websockets> from Markus Sattler v2.1
<Adafruit_INA219> and its dependencies
<arduinoJson> is from library manager but you want version 5x only

and I loaded these two libraries by pulling them from github and saving them unzipped into the Arduino libraries folder.
note that you need to remove the -master ending on their folder names.

and finally I added this include in WiThrottle.h just below the <ESPAsyncTCP.h> include.
#include <string>
which is a reference to the std::string library

The code then successfully compiled inside the Arduino IDE. i.e. without VSstudio/vMicro touching it.

I cannot recreate your error, but it appears your IDE cannot find a reference to MAX_LOCO, which is in Global.h (make sure the Global.h file is present)
This might be because your IDE didn't load things in the right order, and in fact that is one of the limitations of the Arduino IDE.

BUG WARNING: Keep MAX_LOCO=4. There's a bug with the Json buffer size that means setting it to 8 will cause the mobile phone controllers such as WiThrottle to fail (they won't see the roster etc).

Anyway, as I outline above, it is possible for a new user to pull the code from Github and compile just using the Ardunio IDE.

One other thing you can do is delete the entire ESP_DCC_Controller folder, then follow all my steps above.


Reply 11 months ago

Hi, thanks for your support. I'm away for a few days so will certainly try that library when I get back and let you know how I get on.