
This article will help you build a simple Arduino-based DCC system.
Growing up in the 1980s I was living the dream. I had an HO layout on a 4 x 8 sheet of plywood in my parents’ basement in Reading, Pa., a soldering iron, a nearby RadioShack, and a subscription to Model Railroader. My favorite focus then was where my hobbies of electronics and model railroading intersected.
Today I’m 30-plus years into a career as an electrical engineer with a wife and three great kids. There’s another 4 x 8 layout in the basement, and much of the ’80s vintage HO equipment from my childhood now entertains me and my youngest son.
But memories of my friend’s grandfather working on the 1978 running gear rebuild of Reading Co. T1 4-8-4 No. 2101 of American Freedom Train fame inspired me to buy a model of a T1. Recently, I found myself with the means (and permission from my wife), and I purchased a Broadway Limited HO scale model of a T1, complete with DCC and sound. When I got it home, I ran it on my DC layout and was wowed by the detail, the slow speed operation, the sound, the perfectly synchronized chuffs and smoke. And I thought how cool would it be if I could make the whistle work. And the bell.
Inspiration strikes
As a kid I read the series in Model Railroader about the CTC-16 system [December 1979-April 1980. — Ed.], and recall being amazed by the complexity and possibilities, but also mentally putting it in the “far beyond my means” category back then. Over the years I’ve watched Digital Command Control proliferate in the hobby, but never embraced it since all my equipment pre-dated it, and I thought of it as still beyond my means. Then I bought that T1.
Next thing I knew I was Googling DIY DCC systems and I found a YouTube video showing how to use an Arduino UNO R3 and an L298P motor controller shield to create a DCC base station. I bought the two boards for it, downloaded the sample code, and prepared to tinker.
The control interface from the Arduino to the computer (JMRI) was essentially a serial port over USB, and I could enter cryptic text strings typed in to control my locomotive, which was pretty exciting for less than a $15 investment.
Time to upgrade
But that excitement faded fast, and the EE in me began thinking it would be nice to have a cheap, simple, home brew handheld DCC throttle to control a few locomotives with a display and a few coveted function keys to blow a whistle or sound a bell once in a while.
To make a long story short(er), I added a case, a simple LCD display, 10 switches, and a bit of code to take the tactile inputs and create the commands to the Arduino. The controller is very basic in its capabilities, but for a guy who formerly was operating the layout using a pair of MRC Ampacks from before 1980, it’s pretty exciting. It can control up to 12 locomotives, source 2 amps, control a few locomotive functions, and it cost just under $30 to build (not including shipping). All of the electronic components were bought via AliExpress.com, though ordering from mainland China requires some patience and care in reading the descriptions carefully and then waiting for delivery. All of the parts can also be brought from more local vendors like Amazon and DigiKey, though at higher cost.
Components and construction

This project to build a simple Arduino-based DCC system is based on an Arduino, motor controller, and LCD/keypad shields, fig. 1. The schematic of the complete throttle is shown in fig. 2. Don’t be intimidated by the sheer number of connections between the modules as 90% of these are made by just stacking the shield boards together (thank you Arduino!).
Most of the Arduino I/O (input/outputs) are consumed by the motor and LCD/keypad shields, so I had to get a little creative to connect a number of switches and have them uniquely recognized. This was accomplished by using the Arduino’s analog inputs and voltage dividers (resistor networks) to change the voltage on these analog inputs based on which button is pushed.
The stock LCD keypad does this with a ladder of five resistors and switches on a single analog input, which is great. But you can’t simultaneously push more than one of these buttons or the Arduino gets confused. I used these switches for the overall power enable toggle, throttle up, throttle down, forward/reverse toggle, and F9 (engine startup/shutdown sound toggle on my T1).

On the other three available analog inputs I used a simple single pull-up resistor value to +5V and two different pull-downs on switches to set different voltages, three if you count the possibility of both switches pushed at once. I have not tried, but given the range of the Arduino’s analog inputs I expect one could add many more pull-down resistance “steps” and still accurately detect them if more controls were desired. In the code, these voltages indicating which switch is pressed are defined as ranges to allow for unit-unit variation and component tolerances.
The Arduino UNO accepts a 9V power input and has regulators on board to generate its 5V and 3.3V supplies. On paper, this input can be as high as 12V, but the Arduino’s regulator gets hotter the further you go above that 9V sweet spot. Since I wanted to re-use the old MRC transformer I had as my DC voltage source, input voltages up to 16V DC can be expected. So I separated the track power +12-16 VIN from the Arduino’s VIN and put an LM7809 regulator in the path, U1 in the schematic. I also put a fuse in the power input path for protection.
The case is 3D printed, and is a modified (deeper, and with pre-located switch holes) version of one I found on thingiverse.com (See Trains.com for downloadable files). The button labels are made using a Brother P-Touch label maker. I’ve found the 3D enclosure prints sometimes can be brittle, especially with smaller physical features. I’ve also learned that the Arduino combined printed-circuit-board stack height varies slightly depending on board vendors and their component selection, so some adjustment (snipping shorter) to the locating stub heights in the enclosure bottom was required.
If they snap off completely, they could be replaced by small rubber bumpers to support the PCBs, or short nylon screws up through the bottom of the enclosure to engage the mounting screw holes on the Arduino PCB. Lastly, there were some features on the original enclosure design for a battery holder, not needed here. Those plastic dividers were simply snapped off for the throttle build to free up space.
Connection to the layout is via a large double-pole, double-throw knife switch left over from those RadioShack days; not especially elegant, but my son loves “Frankenstein switches” as he calls them. I basically flip all my old DC block switches on, flip the knife switch to the DCC throttle side, and turn the transformer throttle all the way up to provide my 12-16V DC to the throttle, and I’m in business.
To avoid reverse polarity to the throttle, I placed a bridge rectifier and a filter capacitor under the layout in the path. This way, no matter how the transformer direction switch is set, the throttle sees the correct input polarity. The “Programming Track” power is routed to a simple RCA jack on the side of the throttle case to allow for an easy, yet removable, connection to a piece of track for locomotive programming.
The original open source code by Gregg Berman can be downloaded here. My modified modules are available here. The code modifications were confined to the SerialCommand.cpp and SerialCommand.h files, so you can download the original, swap in my SerialCommand.* files, and be up and running.
I will gladly admit that I am not a programmer (the word “hack” comes to mind). The changes I made could certainly be done more efficiently and elegantly, and I welcome folks improving on my starting point.
Step by step build a simple Arduino-based DCC system

- On the motor controller shield, find the “VIN CONNECT” and “BRAKE DISABLE A, B” traces on the bottom side and cut them to disconnect the motor (input) voltage from the Arduino Vin supply, and to isolate the BRAKE signals from their digital pins. I marked the location in fig. 3.
- Snip the bottom side solder tails from the motor shield’s green power connector to allow a flush fit of the shield on top of the Arduino UNO fig. 4.
- Because the LCD/keypad and motor shields both expect to use some of the same I/O pins, I had to make a few snips and jumps on the shield boards to avoid conflict and alter the connections to match the schematic. They are as follows:
- On the LCD/keypad, snip off pins D5, D10, D12, D13, and A0, essentially disconnecting them from the motor shield and Arduino fig. 4.
- On the LCD/keypad, jump pins D2 and D5 together and pins A0 and A2 together fig. 5.
- On the motor controller, jump pins D5 and D13 together and pins D10 and D12 together. These jumpers are seen as white wires in fig. 3.

4. The LCD/keypad shield needs to have I/O pins cut at D5, D10, D12, and D13. On the other side, remove pin A0. - Place pull-up resistors R1, R2, and R3 on the top side of the LCD/keypad shield as shown in fig. 5. Tuck them in down close to the PCB to avoid interference with the case.
- Attach wires to the switch connections on the LCD/keypad backside fig. 6. These will be used to connect bigger and more reliable push buttons elsewhere in the case. Note the connections need to be on the non-GND side of these switches as shown.
- Assemble the three boards together fig. 7.
At this point, you should be able to load code and see the startup messages on the LCD display (being careful not to short the attached wires to anything). Note that on my LCD display, the contrast potentiometer was initially set such that I saw no characters on the display (minor panic!). They came up once I adjusted it.
- Assemble switches S2 through S10 to the case top panel as shown in fig. 8.
Basically, the switches all have either a ground or a resistor connection to GND from them, so there is a common ground wire routed between and around them all. I used diagonal cutters to remove the normally closed (NC) terminal from all the push-button switches to give myself a little more space to work.

- Separate the LCD/keypad shield from the stack and install it on the case top panel. There are two screw holes on corners of the display to do this. Be careful, as the 3D printed structure can be brittle. The screws don’t need to be torqued down much at all — just tight enough not to move/rattle. At this point, the wires from the keypad switches and wires from the A3 through A5 analog inputs, as well as ground, can be connected to the switches as seen in fig. 9.
- In the bottom part of the enclosure, drill holes for and mount the cable entry and strain relief (lower left), track power/kill switch (SW1, upper right), and the LM7809 regulator and heatsink as shown in fig. 10.
Care should be taken to place the parts so as not to interfere with the stacked boards or top panel switches in the enclosure. The regulator is oversized for its application, so the heatsink will not get anywhere near hot enough to melt/damage the enclosure. The cable is a four-wire telephone wire cable. Maybe a little light gauge for the job, but it works and is readily available.

- The fuse/fuse holder is connected between the input red wire and branches to connect to the input side of the voltage regulator, as well as to the motor controller +VIN terminal. The black wire from the input cable connects to the GND terminal of the motor controller. The orange and tan wires connect from the RCA jack programming track power output back to the motor controller “B” output terminals. The yellow and green wires from the input cable (Track Power) connect to the motor controller “A” output terminals. Note that to connect to the motor controller screw terminals, the motor controller and Arduino must be (carefully) separated from the LCD/keypad shield. These connections are shown in fig. 11.
- The center pin of the LM7809 regulator is ground, and can be bent upward to connect to one of the terminals of the power/kill switch (SW1). The GND from the switch and regulator and the regulator’s 9V output (pink wire) should be soldered to the bottom side of the Arduino board barrel connector as shown in fig. 12. Be careful to get the correct pads on the Arduino PCB as reversing the polarity will cause damage.
- Fold in the wires such that the Arduino and motor controller can fit in the bottom of the case without wires blocking the USB port, then carefully align and attach the lower two boards to the LCD/keypad and slide it all together as shown in fig. 13 on the opposite page.
- To re-use my old transformer as my DC supply, I added a bridge rectifier and a 4700uF, 35V filter cap in the path. These being somewhat larger components, I placed them under the layout benchwork instead of inside the throttle.
The bridge rectifier AC inputs are fed from the DC transformer output, and the bridge rectifier DC output feeds the filter cap and the power input to the throttle. Though this may seem redundant (a rectifier on a DC supply), it’s a handy way to provide reverse polarity protection to the throttle if the direction switch on the old transformer is not set as expected.
The bridge rectifier I used is electrically overrated for the job, but was cheap and physically large/sturdy enough simply zip-tie it under the layout. Also seen in fig. 14 are the yellow and green rail power output from the throttle.
- Last but not least, screw it all together, label the switches, and you’re ready to roll fig. 15!

8. Add the switches to the case, and make the wire connections on the back.
Operation
Not having owned or used a DCC throttle before, the user interface is admittedly tailored to my personal tastes. The unit powers up to a “Splash Screen” that reads “I Like Trains” and then the code version. Knowing the version was helpful when I was writing and debugging my code changes. After a brief pause, the screen displays “Locomotive: 1”. By pressing the Power/Kill switch (SW1), track power is toggled on or off, also indicated on the display along with locomotive speed and direction. The momentary contact toggle switch (SW4) is used to increase or decrease speed between 0 and 126. These were the min/max values in the code so I just carried them over. Pressing the Direction switch (SW3) will set the locomotive’s speed to 0 and toggle its direction, so the locomotive will slow to a stop (based on its programmed momentum variables) and move in the opposite direction when you give it throttle.
The Fine button (SW8) was something I added to give the user two different step speeds for throttle control. In normal operation, holding the throttle toggle for about 5 seconds will take your speed from 0 to 126, which is good for a lot of things.To get a very low speed or fine adjustment, hold the Fine button down while changing the throttle and it changes value at a much slower pace.

Changing which locomotive you are controlling is done by pressing the Select switch, then using the throttle toggle to bump up or down through the locomotives. As you go, you’ll see the locomotive number changing on the display. You can set one locomotive’s speed, then jump to controlling a different locomotive and the other locomotive will continue on its path until you come back to it and change its throttle or direction.
If at any time you see an oops about to happen, pressing the Power/Kill switch will shut down all rail power instantly, and reset all locomotives to FWD, SPD=0.
The remaining switches were set up to control some basic sound functions, though the code can be modified to re-map them to suit specific needs. As currently configured, the Bell (F1) switch toggles the bell off and on when pushed. The Whistle/Coupler (F2/F3) switch is configured to sound the whistle as long as the switch is held. Pushing Select and F2/F3 (simultaneously) sounds the coupler clank. The Mute/Vol switch controls F8. Pressing this button will mute/unmute sound by toggling F8.

On my T1 (Paragon2 Sound), holding Select while repeatedly pushing Mute/Vol will increment/decrement the volume by pulsing F8 on-off-on-off. Pushing the F9/F10 or F13/F14 buttons with or without holding Select will call their assigned sound effects. Because sound/function definitions vary between models and brands, I included a variable in the code that can be written to identify a locomotive’s sound system type and allows these switches to map to alternative functions depending on which loco is being controlled and user preferences, though editing it means editing the code and reloading it to the throttle.
Locomotive programming
I took a very simplistic approach to locomotive programming. I added a “Program” mode to the code, but it requires you to connect a laptop to the throttle via USB (baud rate 115.2K) and use text strings to enter programming mode and set configuration variables in your locomotive. When in programming mode, track power and control is disabled and the throttle display reads “PROGRAM MODE.” The program track power comes out of the RCA jack on the side of the throttle, and it gets connected to a piece of track on my workbench via a homemade cable with an RCA plug at one end and a pair of alligator clips at the other.

Having the throttle powered and simultaneously connected to a computer via USB is safe; the Arduino hardware accommodates it. Below is a simple, commented example of the text commands to set and read back a locomotive’s ID. Programming mode can be exited by command or by power cycling the throttle to bring it back to operating mode.
Programming CV1 example
Throttle status report at startup on USB/Serial:
<iDCC++ BASE STATION FOR ARDUINO UNO / ARDUINO MOTOR SHIELD: V-1.2.1+ / Feb 19 2024 12:27:03><N0: SERIAL>f 1 144
Issue command “<2>” over the serial port to Arduino to put the throttle into Program Mode. It sets all locomotives to forward, speed = 0, and then echoes back “<p2>” response.

<T1 0 1>t 1 1 0 1
<T2 0 1>t 2 2 0 1
<T3 0 1>t 3 3 0 1
<T4 0 1>t 4 4 0 1
<T5 0 1>t 5 5 0 1
<T6 0 1>t 6 6 0 1
<T7 0 1>t 7 7 0 1
<T8 0 1>t 8 8 0 1

<T9 0 1>t 9 9 0 1
<T10 0 1>t 10 10 0 1
<T11 0 1>t 11 11 0 1
<T12 0 1>t 12 12 0 1
<p2>t 12 12 0 1
Issue command “<R 1 555 555>” over the serial port to Read CV1, where the 555 555 portion are just two 3 digit numbers (can be anything) to match the request to its response. The throttle answers with “<r555|555|1 3>” telling us it read the value 3 from CV1, followed by the text of the command as issued.
<r555|555|1 3>R 1 555 555
Issue command “<W 1 8 777 777>” over the serial port to Write CV1 = 8, where the 777 777 portion are just two 3 digit numbers (can be anything) to match the request to its response. The throttle writes 8 to CV1 and then reads it back, responding with “<r777|777|1 8>” telling us it read the value 8 from CV1, followed by the text of the command as issued.
<r777|777|1 8>W 1 8 777 777

Issue command “<0>” over the serial port to exit Program Mode. Throttle returns to normal operation with rail power off and echoes back “<p0>”, followed by the text of the command as issued.
<p0>0
The SerialCommands.cpp portion of the sketch is very well commented by its original author and there are many available commands and their syntax spelled out within.
The code as it is currently written uses the Locomotive Primary Address (CV1) to map which locomotive the throttle is controlling, i.e. the throttle can talk to 12 locomotives, and their addresses must be 1 through 12. Modifying this to enable a wider range of addresses to be mapped into the throttle’s 12 control “slots” is an area for future work (probably for the day I buy/enable a 13th DCC capable locomotive).
Closing thoughts

With its limited current capability, limited number of locomotive IDs, and general simplicity, this throttle will not meet the needs of more advanced users or clubs with multiple users. However, it certainly offers a taste of what’s possible with just a few bucks and a little ingenuity to build a simple Arduino-based DCC system. I can already envision improvements such as a higher current motor controller, or moving to having a base unit hard-wired into the layout with (multiple?) Bluetooth connected wireless handheld control(s). Heck, crossing the Bluetooth bridge could enable a home brew phone app control with some coding and ingenuity. Take what I’ve shown here as a basic recipe, add your own spice and flair, and run with it. The sky is the limit, and all for less than the cost of taking the family to the diner for breakfast.


Hi, what happened to the Arduino code download??
I’m not sure who the EE designer was (another Eric White?), but I know many like you I have worked with – and as always, I am in awe of your ability.