DCC++ for Model Train Control with Teensy 3.x and Teensy 4.x

mjs513

Senior Member+
Decided to port the CommandStation-EX (https://github.com/mjs513/CommandStation-EX/tree/TeensyBranch...) library that currently only supports Arduino Mega and Arduino Uno boards to the Teensy T3.x and Teensy 4.x (https://www.pjrc.com/teensy/) and is used for controlling locomotives that use dcc++.
What is DCC? DCC stands for Digital Command Control. It is a system where digital commands are sent to the locomotives through the rails. DCC is allows independent control of multiple locomotives without complicated wiring, toggle switches or power packs

With @luni's help, @KurtE's and @PaulStoffregen final fix to EEPROM lib was able to successfully get it ported over.

I used a custom breakout board that supports Arduino shields and used a Arduino Motor shield with a Teensy 3.5. Loaded the modified software onto the Teensy and then used exWebThrottle on a laptop to test the conversion. This video is the result.

Why did I do this, basically wanted to see if it would work with the Teensy. For the test I am using a Walthers EMD-60 modifed for DCC in case you are curious.

In full disclosure I haven't tested with the Teensy 4 yet - not 100% sure the Arduino Motor shield is compatible?


UPDATES 2/1/21:
1. @luni's version of the use of timers has been pushed to the TeensyBranch of the my github repository. Next up issuing a PR back to the authors

2. Saw on youtube that someone created a wireless throttle using HC-12 433Mhz transceivers. So using a Teensy LC that I had I made my own to interface to the CommandStation-EX basestation:

Basically what you need is:
1. Teensy LC
2. 2 HC-12 Transceivers
3. KY-040 Rotary Encoders
4. Nextion 3.2in display

See https://www.youtube.com/watch?v=neMlNHVZWuY for alot more details. I will post my version of the sketch to Github and update this post.
 
Last edited:
This is awesome, I've followed your and Luni's conversation on the TeensyTimerTool threat (not sure if I can include a URL to it)

I stumbled on the CommandStation-EX project a few weeks ago and thought that even the Arduino MEGA would be struggling if all the features are enabled (Ethernet, Wireless and Display), the Teensy 4.1 is a massive improvement in memory size, cpu power and has the build in Ethernet port.

I pulled the GitHup repository today from Luni and got it to compile (only the config.h was missing). I haven't got a Teensy-to-Arduino shield, but working on a DCC controller where I can stick a Teensy 4.1 on with the Ethernet port break out.

BTW if you want to be precise with the waveform, the specs say that a half period of the "1" bit is 58uS and the half period of the "0" bit is 100uS.

The original DCC++ software used an ISR triggered every 58uS and waited for 2 timer interrupts before toggling the power pin to sent a "0". I read an article in Practical Electronics October 2019 (UK magazine) where they saying that the 116uS (2x58uS) is still within the limits for a DCC decoder in a loco. The article discusses a low power DCC programmer for a programming track by Tim Blythman. the magazine URL: https://www.electronpublishing.com/1019-2/
 
Ok a couple things going to be discussed in this post.

@luni
Was working on you changes to the lib but for some reason, either the T4 is hanging or the timers are not starting. I successfully compiled and loaded the sketch but the T4 would not accept commands and did not the initial waveforms I was expecting. As a result I reverted the merge of your changes into into my branch and upload my version. Going to work on it again.

I did verify that the Arduino Motor shield works with the T4 without a problem so was able to test with the loco with my kludge.

@henkk
Thanks for the interest. Wondering if it was only me. As you can see from the above comment to @luni I am having trouble with his version which I would prefer to go to in the long run. As to the config just rename it to config.h from config.example.h and make sure wifi is false if you want to play with it. Also thanks for the added info on the waveform.

EDIT: Also make sure you pull down the latest changes to the EEPROM library. See discussion on this thread: https://forum.pjrc.com/threads/6595...-Errors?highlight=eeprom+multiple+definitions


Here is the link to what I have verified working on the T3.5 and T4.0: https://github.com/mjs513/CommandStation-EX/tree/TeensyBranch
 
@luni
Since it looked like it was hanging I added @Frank B's hardfault mod to core to see what we got and sure enough:
Code:
Hardfault.
Return Address: 0x2A41F0E8
	(IACCVIOL) Instruction Access Violation

Not sure where to go from here with this error.
 
@luni
Since it looked like it was hanging I added @Frank B's hardfault mod to core to see what we got and sure enough:
Code:
Hardfault.
Return Address: 0x2A41F0E8
	(IACCVIOL) Instruction Access Violation

Not sure where to go from here with this error.

Probably best to avoid it ;) :D

For lack of better stuff, I would probably start off with things like adding digitalWriteFast or digitalToggleFast at different places in the code to see if you can get it localized down some.

Don't have the hardware to try it. But could maybe setup anyway?
 
@KurtE
Thanks ... you know me can’t seem to avoid issues like this;)

Actually all you need is T4 and a LA on pins 3 and 12. On start you should see pin 3 low and the waveform on pin 12. That’s it. Think I am done for the night .. maybe.
 
That address will resolve into a calling function in a .lst or whatever file created during the link.

or you could try the GDB setup. If using TSET when assembling the COMPILE.cmd just say yes to debug and this line will be added
>> start "%tools%\GDB.cmd" "%arduino%\hardware\tools\arm\bin\arm-none-eabi-gdb.exe" "%temp1%\%sketchname%.elf"
or hand edit versus
>> REM start "%tools%\GDB.cmd" "%arduino%\hardware\tools\arm\bin\arm-none-eabi-gdb.exe" "%temp1%\%sketchname%.elf"

Last prompt answer 'd' : D :: yes with DEBUG GDB and save to T:\tCode\TIME\GPS_iValTimer\Compile.cmd

I haven't tried it lately but it worked when I added it.

Might have to disable the hardfault catcher code?

GDB should ideally drop into the faulted code to show a stack trace - and maybe var inspection?
 
Was wondering is the trained addressed? Can two such trains sit on the same track and act independently?
 
@mjs513 - It did not build at when I just downloaded your branch. I was able to get it to build was to copy the conifig_example.h to config.h

That does not show up as a updated file in github as config.h is in the .gitignore file.

But not sure if you are using the default settings or if you modified anything.

I Tried to copy in Franks hard fault stuff, so far have not seen anything.

I do have pins 3 and 12 hooked up. So far not seeing anything on 3...

screenshot.jpg

More tomorrow.
 
Ok a couple things going to be discussed in this post.

@luni
Was working on you changes to the lib but for some reason, either the T4 is hanging or the timers are not starting. I successfully compiled and loaded the sketch but the T4 would not accept commands and did not the initial waveforms I was expecting. As a result I reverted the merge of your changes into into my branch and upload my version. Going to work on it again.

I did verify that the Arduino Motor shield works with the T4 without a problem so was able to test with the loco with my kludge.

Sorry, I was busy yesterday and didn't find time to answer. The reason why it hang was one change you did in Timer.cpp which I forgot to undo. Specifically, you assigned TeensyTimer Tool timers to TimerA, TimerB.... The library then tried to call the VirtualTimer Methods on them which can't work of course. I fixed that, cleaned up timer.h and added a while(!Serial) to the setup(). Looks like it works now.

Here the relevant code in timer.cpp:
Code:
....
#elif defined(TEENSYDUINO)

#include "Teensy4X/Timer.h"   
Timer TimerA(1);   // use the timer numbers to select which timer you want to create in timer.h
Timer TimerB(2);
Timer TimerC(3);
#endif

And here a super simple example how to use intervalTimers instead of the AVR timers. I'll prepare an example using the TCK - Software timers. They probably are good enough for the rather long (56µs) signals.
Code:
#pragma once

#include "../VirtualTimer.h"
#include <Arduino.h>

class Timer : public VirtualTimer
{
 public:
    Timer(int timer_num) {} 

    void initialize()
    {       
        if (timer != nullptr) stop();
        timer = new IntervalTimer();
    }

    void setPeriod(unsigned long mu)
    {     
        microseconds = mu;
    }
    void start()
    {     
        timer->begin(isrCallback, microseconds);        
    }
    void stop()
    {     
        if (timer != nullptr)
        {
            timer->end();    //this will release the timer, I assume that the lib doesn't call start after stop without initializing.
            delete timer;
            timer = nullptr; // If so -> start would error. In case the lib wants to stop/start something more elaborate needs to be done here
        }
    }

    void attachInterrupt(void (*isr)())
    {        
        isrCallback = isr;
    }

    void detachInterrupt()
    {
        stop();
        isrCallback = nullptr;
    }

 private:
    uint32_t microseconds;
    IntervalTimer* timer = nullptr;
    void (*isrCallback)();
};

extern Timer TimerA;
extern Timer TimerB;
extern Timer TimerC;
extern Timer TimerD;


Here the generated signals on pin 3 and 12

Screenshot 2021-01-27 105752.jpg

Output on Serial:
Code:
DCC++ EX v3.0.3<iDCC-EX V-3.0.3 / TEENSY / STANDARD_MOTOR_SHIELD G-9db6d36>

LCD1:Ready

I sent you a pull request, but since you changed stuff back, my copy was a bit out of sync and you will have merge conflicts. If you want to try my code, simplest probably is to clone the code from my fork: https://github.com/luni64/CommandStation-EX

Looks like there is a serial interface to control the trains. Is this "human typable" for tests with SerMon?
 
I did find that WebThrottle application to control it. Sending/receiving of commands is nicely logged so it communicates. If I toggle the power off button pin3 gets high. So, looks like everything works as advertised. I can't however judge if the train is doing what it is supposed to do. So, the fun part is with you :)
 
@mjs513 - It did not build at when I just downloaded your branch. I was able to get it to build was to copy the conifig_example.h to config.h

That does not show up as a updated file in github as config.h is in the .gitignore file.

But not sure if you are using the default settings or if you modified anything.

I Tried to copy in Franks hard fault stuff, so far have not seen anything.

I do have pins 3 and 12 hooked up. So far not seeing anything on 3...

View attachment 23471

More tomorrow.

Morning @KurtE - @defragster
The problem I was having with @luni's modifications prior to his latest changes (see post #11). The version I posted was working for me with The T3.x and T4.x. Thanks for checking - at least I now know it works for others as well.

Ok I am bad - i got my pin numbers backwards. Pin 3 is the Power Pin - goes high when you send "<1>" and low when you send <0>. Pin 12 is the signal pin. Sorry - was answering on my cell phone last night.
 
Sorry, I was busy yesterday and didn't find time to answer. The reason why it hang was one change you did in Timer.cpp which I forgot to undo. Specifically, you assigned TeensyTimer Tool timers to TimerA, TimerB.... The library then tried to call the VirtualTimer Methods on them which can't work of course. I fixed that, cleaned up timer.h and added a while(!Serial) to the setup(). Looks like it works now.

Here the relevant code in timer.cpp:
Code:
....
#elif defined(TEENSYDUINO)

#include "Teensy4X/Timer.h"   
Timer TimerA(1);   // use the timer numbers to select which timer you want to create in timer.h
Timer TimerB(2);
Timer TimerC(3);
#endif

And here a super simple example how to use intervalTimers instead of the AVR timers. I'll prepare an example using the TCK - Software timers. They probably are good enough for the rather long (56µs) signals.
Code:
#pragma once

#include "../VirtualTimer.h"
#include <Arduino.h>

class Timer : public VirtualTimer
{
 public:
    Timer(int timer_num) {} 

    void initialize()
    {       
        if (timer != nullptr) stop();
        timer = new IntervalTimer();
    }

    void setPeriod(unsigned long mu)
    {     
        microseconds = mu;
    }
    void start()
    {     
        timer->begin(isrCallback, microseconds);        
    }
    void stop()
    {     
        if (timer != nullptr)
        {
            timer->end();    //this will release the timer, I assume that the lib doesn't call start after stop without initializing.
            delete timer;
            timer = nullptr; // If so -> start would error. In case the lib wants to stop/start something more elaborate needs to be done here
        }
    }

    void attachInterrupt(void (*isr)())
    {        
        isrCallback = isr;
    }

    void detachInterrupt()
    {
        stop();
        isrCallback = nullptr;
    }

 private:
    uint32_t microseconds;
    IntervalTimer* timer = nullptr;
    void (*isrCallback)();
};

extern Timer TimerA;
extern Timer TimerB;
extern Timer TimerC;
extern Timer TimerD;


Here the generated signals on pin 3 and 12

View attachment 23473

Output on Serial:
Code:
DCC++ EX v3.0.3<iDCC-EX V-3.0.3 / TEENSY / STANDARD_MOTOR_SHIELD G-9db6d36>

LCD1:Ready

I sent you a pull request, but since you changed stuff back, my copy was a bit out of sync and you will have merge conflicts. If you want to try my code, simplest probably is to clone the code from my fork: https://github.com/luni64/CommandStation-EX

Looks like there is a serial interface to control the trains. Is this "human typable" for tests with SerMon?

Everyone seems to be a slightly different time zones. Its about 5:30am here in NY so got up when I saw your message. Yes you there are commands you can send via serial monitor but I see you found the exWebThrottlle.

When I did the initial testing of your version I actually had downloaded it from your repository so that was the version I was referring to that was hanging. Anyway - glad its working now and even compiles for T3.5 :) Please no need to apologize for being busy yesterday, things still need to get done :)

As for whether it works. Based on what I am seeing with the waveform and my previous testing it should work. I ordered some new track and should get next week (i hope) and will give it a proper test.

EDIT: Forgot the most important part - going to go ahead and just copy your files into my branch then push it back up. Easier than trying to resolve conflicts.
 
He, he, working on model trains at 5:30 reminds me of those mornings after Christmas some 50 years ago :)

For completeness I uploaded an example showing how to use the TimerTool TCK (software) timers. You can switch between this and the IntervalTimer example in Timer.cpp

Code:
...
#elif defined(TEENSYDUINO)

//#include "Teensy4X/Timer.h"     // using IntervalTimer
#include "TEENSY_TTT/Timer.h"     // using TimerTool TCK timers (software timers)

Timer TimerA(1);   // use the timer numbers to select which timer you want to create in timer.h
Timer TimerB(2);
Timer TimerC(3);

#endif

As expected, the waveforms look quite identical.

The library is quite well designed -> all in all there was not much to change to switch from the AVRs to Teensy with its completely different architecture.
 
Tell me about playing with trains. Used to have (actually still have) a whole lot of Lionel O-gauge trains from the 50's and 60's but takes way too much room to set up. So went to N-gauge for my granddaughter a bunch of years ago. Then I started reading of DCC++ so figured I would try it with a Teensy just because.

Anyways, back to timers. Really cool how you set up Timer.h to interface with TimerTool. Kind of opens it up for other pursposes. And yes = they really did a nice job on library so you could extend it. From here theres all kinds of things you could do. If you want to disconnect the Teensy from the laptop you could put together a separate throttle that uses Bluetooth or even IR if you don't want to rely on Wifi:
1. https://www.youtube.com/watch?v=neMlNHVZWuY
2. https://www.youtube.com/watch?v=4sRVuhFi--I
 
Tell me about playing with trains. Used to have (actually still have) a whole lot of Lionel O-gauge trains from the 50's and 60's but takes way too much room to set up. So went to N-gauge for my granddaughter a bunch of years ago. Then I started reading of DCC++ so figured I would try it with a Teensy just because.
Great you are making progress and having some fun! For your granddaughter ;)

As a kid back in similar time frame had my brother and I had a train set, not sure whatever happened to it. My wife did give me a set 3 or 4 Christmas's ago, which I now often setup during the Holiday season.

I am guessing that not all train setups use DCC? Did you have to buy special engine or the like, or replace transmitter/receivers?
 
Morning @KurtE
You are right not all trains are DCC capable. Some will say DCC ready so its easy to drop in a decoder ($32), which is what I did for one of loco's, digitrak seems to be pretty good. Then of course you can buy loco's that already have them installed but they can be expensive.
 
@luni
That's impressive! Am actually more impressed with the control room - looks like one of those TV control stations or something from NASA.
 
Just updated Post #1 with new video and description of a wireless throttle to interface with the CommandStation-EX.
 
Metro or Grand Central

Hi Mike, I was trying to get back to testing DCC++EX ports and can't find anything on the Metro or Grand Central and know someone told me they had it working on one, the other or both. Was that you?

Fred
DCC-EX
 
Hi Mike, I was trying to get back to testing DCC++EX ports and can't find anything on the Metro or Grand Central and know someone told me they had it working on one, the other or both. Was that you?

Fred
DCC-EX

Hi Fred - finally checked backed on the forum and saw your message. Nope wasn't me. Pretty much been sticking to Teensies these days.

Mike
 
Thought I would post this from the folks over at DCC-EX:
**DCC-EX announces version 4.0 of its DCC++EX Command Station software with EX-RAIL automation and accessory control.**

Operators can now have a fully automated model railroad or a separate accessory control bus for their entire layout and read CVs up to 8 times faster.
February 1, 2022 7am Eastern Standard Time Holly Springs, North Carolina USA – DCC-EX released the next version of its popular free and open source DCC Command Station software today. Version 4.0 of DCC++EX includes the new EX-RAILtm (Extended Railroad Automation Instruction Language) system that revolutionizes the way model railroaders can control and interact with their layouts. This new concept allows users to fully automate the operation of their trains and to manage turnouts, signals, lights, automations, animations, and virtually any kind of sensor, switch, servo, motor, or output, all with a very simple set of commands.

DCC++EX with EX-RAIL runs on inexpensive and readily available hardware like the Arduino and Teensy series of microcontrollers and supports standard switches, servos, and accessory boards.

Concurrent with this rollout, enhancements to the popular JMRI model railroad software and Engine Driver smartphone and tablet app integrate tightly with the new features. Operators can create routes or automation/animation sequences and have them appear as buttons in Engine Driver. Press a button to begin an automation, or drive trains manually and press a button to hand them off to EX-RAIL to run everything unattended. Take back control with another press of a button.

Users no longer have to search for “sketches” and libraries or learn complicated C++ programming. There is no need to modify the Command Station software and risk bringing the whole system down. The only file that needs to be modified is a myAutomation file where simple commands allow the operator to define their devices and tell them what to do when the conditions they specify are triggered. An engine can be sent out from the yard, triggering turnouts to engage for a specific route. Along the way, all the signals and crossings operate automatically in response to sensors along the track or timings or other conditions specified by the operator.

Animations that involve a sequence, like operating cranes, arc welders, station operations, trolleys, etc. are simple to script. To make it even easier, EX-RAIL comes with many standard sequences so that an operator just needs to copy them into their script, load the file into their Command Station and connect their devices. For the first time, everything can be run from the Command Station, without the need for separate devices running custom C++ programs scattered all over the layout.

Interfacing with hardware has never been easier with the new HAL (Hardware Abstraction Layer) feature. Supported hardware such as port expander and servo controller boards are automatically detected. New types of hardware can be added by implementing a driver from a standardized interface without having to write a custom program.
The new DriveAwaytm feature allows users to drive an engine onto a siding or other electrically isolated block used as a programming track. The engine can be programmed and then driven back onto the main section of the layout, all without touching the locomotive.

DCC++EX has enhanced capabilities to read difficult decoders, provide detailed diagnostics, and when using JMRI, improve performance by a factor of 8x when the values of the CVs match what is in the JMRI decoder file. Reading and writing CVs in general, whether using JMRI or another method, is still significantly faster than before.
DCC-EX is a global joint effort of a group of developers and engineers located in the US, UK, Sweden, France, Australia, New Zealand, and India. For more information visit their website at https://www.dcc-ex.com.
 
Back
Top