butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Digital Command and Control (DCC) with pigpio waves

Wed Jun 23, 2021 2:54 am

Getting back into model railroading, I dusted off my old DCC equipment and found it rather dated (RS-232 interface, and all that). So, I started picking at what it would take to make a command station with one of the RPis I had laying around. Bit-banging the DCC signal proved un-scalable, but when I studied the pigpio waveform routines it all fell into place.

My initial implementation, work-in-progress, is here: https://github.com/butcherg/wavedcc

To date, I've only implemented the baseline packets plus a couple of the CV writing packets, along with a rudimentary interface of selected DCC++ commands. There's a command line program as well as a TCP server courtesy of Beej (I literally just took his polling server and stuck the three DCC function calls in the code). Both a direct GPIO interface and an interface through pigpiod are coded, and selected with a pre-processor define, the Makefile has the CFLAGS/LFLAGS for both alternatives.

The waveform engine is based on the example provided in the libpigpio documentation. Basically, a DCC packet is built as a pigpio waveform pulsetrain, and transmitted with the _pad routine. While the transmission is in progress, the next pulsetrain is queued up for transmission, wash and repeat. I implemented commandqueue and roster C++ classes to manage the data feed to the pulsetrain engine.

On my Pi3B, the pigpiod implementation uses about 45% CPU, roughly split between pigpiod and wavedccd. However, the direct GPIO implementation only uses about 11% CPU. In either case, the pulsetrain is continuous, no hiccups or delays.

Right now, I'm driving one of those $6US L298n boards with a bipolar signal right off GPIO 17 and 27, enable on GPIO 22. I'm going to continue implementing packets and commands until I have basic operations and programming track capability.

Thanks @joan and others, for a tool that maps very straightforwardly to a DCC implementation...

User avatar
Gavinmc42
Posts: 5880
Joined: Wed Aug 28, 2013 3:31 am

Re: Digital Command and Control (DCC) with pigpio waves

Wed Jun 23, 2021 6:28 am

CPU 45% seems a bit high.

Use some Picos and control them from a Pi via USB/i2c/rs485.
$10 section controllers ;)
Been looking for an excuse to use Pico's.

Anyone making a Pico PCB with L298s on it?
Handy for robots too.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges


User avatar
Gavinmc42
Posts: 5880
Joined: Wed Aug 28, 2013 3:31 am

Re: Digital Command and Control (DCC) with pigpio waves

Wed Jun 23, 2021 7:11 am

The L298 is a bigger motor controller.
It is pretty much a standard for DCC small power boosters.
Might have to get a bunch at $4 on a PCB.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Wed Jun 23, 2021 3:03 pm

Gavinmc42 wrote:
Wed Jun 23, 2021 7:11 am
The L298 is a bigger motor controller.
It is pretty much a standard for DCC small power boosters.
Might have to get a bunch at $4 on a PCB.
Mine came in a bag-o-four, $9US. Using one for my development, will dig out a second one for the programming track.

Needs modification to do current-sense, necessary for reading/verifying CV values. That, and dual 5V/12V power supply to make this a neat package.

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Wed Jun 23, 2021 3:19 pm

Gavinmc42 wrote:
Wed Jun 23, 2021 6:28 am
CPU 45% seems a bit high.
Did to me also. Quiescent pigpiod ran about 7%; when I started wavedccd and enabled the pulsetrain, it went to pigpiod: 25%, wavedccd 20%

When I compile wavedccd without -DUSE_PIGPIOD_IF, which uses direct GPIO interface through libpigpio, before starting the pulsetrain it runs at 7%, when I enable the DCC pulsetrain it goes to 11%.

All of the above is with idle packets, no processing of throttle commands. The only difference between the two is the use of the TCP network stack between pigpiod and wavedccd. albeit internally through localhost. Early on, I tried running a wavedccd on my Ubuntu desktop, communicating with pigpiod aboard the RPi over the home network (Ethernet out of the desktop box, WiFi to the RPi), and that introduced significant gaps between packet pulsetrains. So, IMHO direct to the GPIOs is the way to go...

User avatar
Gavinmc42
Posts: 5880
Joined: Wed Aug 28, 2013 3:31 am

Re: Digital Command and Control (DCC) with pigpio waves

Thu Jun 24, 2021 3:34 am

My eyes are so old my old 2mm scale narrow gauge stuff is to small.

Might move to 32/45mm track, use a Pico as DDC decoder driving L298 motor driver?
Plenty of room for Pi Zero and camera :D
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
thagrol
Posts: 5265
Joined: Fri Jan 13, 2012 4:41 pm
Location: Darkest Somerset, UK
Contact: Website

Re: Digital Command and Control (DCC) with pigpio waves

Thu Jun 24, 2021 11:57 am

Gavinmc42 wrote:
Thu Jun 24, 2021 3:34 am
My eyes are so old my old 2mm scale narrow gauge stuff is to small.
Glad I'm not the only insane enough for 2mm narrow gauge.

If you can get your hands one one, the gertbot board does DCC. On up to 16 channels (4 per board, maximum 4 boards daisy chained)
I'm a volunteer. Take me for granted or abuse my support and I will walk away

All advice given is based on my experience. it worked for me, it may not work for you.
Need help? https://github.com/thagrol/Guides

User avatar
rpdom
Posts: 18872
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: Digital Command and Control (DCC) with pigpio waves

Thu Jun 24, 2021 12:14 pm

thagrol wrote:
Thu Jun 24, 2021 11:57 am
Gavinmc42 wrote:
Thu Jun 24, 2021 3:34 am
My eyes are so old my old 2mm scale narrow gauge stuff is to small.
Glad I'm not the only insane enough for 2mm narrow gauge.
That's tiny stuff!

My old setup is just British N. That's getting hard for me to see these days.
Unreadable squiggle

User avatar
thagrol
Posts: 5265
Joined: Fri Jan 13, 2012 4:41 pm
Location: Darkest Somerset, UK
Contact: Website

Re: Digital Command and Control (DCC) with pigpio waves

Thu Jun 24, 2021 12:29 pm

rpdom wrote:
Thu Jun 24, 2021 12:14 pm
thagrol wrote:
Thu Jun 24, 2021 11:57 am
Gavinmc42 wrote:
Thu Jun 24, 2021 3:34 am
My eyes are so old my old 2mm scale narrow gauge stuff is to small.
Glad I'm not the only insane enough for 2mm narrow gauge.
That's tiny stuff!

My old setup is just British N. That's getting hard for me to see these days.
It's just N guage on narrower track (usuallly 6.5mm - same a Z). If you want tiny, try T guage. 1/450 on 3mm track. And yes, I have dabbled in it.
I'm a volunteer. Take me for granted or abuse my support and I will walk away

All advice given is based on my experience. it worked for me, it may not work for you.
Need help? https://github.com/thagrol/Guides

User avatar
rpdom
Posts: 18872
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: Digital Command and Control (DCC) with pigpio waves

Thu Jun 24, 2021 12:54 pm

thagrol wrote:
Thu Jun 24, 2021 12:29 pm
rpdom wrote:
Thu Jun 24, 2021 12:14 pm
thagrol wrote:
Thu Jun 24, 2021 11:57 am


Glad I'm not the only insane enough for 2mm narrow gauge.
That's tiny stuff!

My old setup is just British N. That's getting hard for me to see these days.
It's just N guage on narrower track (usuallly 6.5mm - same a Z). If you want tiny, try T guage. 1/450 on 3mm track. And yes, I have dabbled in it.
Yep. I did think about building in Z when I started out, but nowhere local was doing it. Also with the 9mm N track I was able to build a few bits of custom track (back when I had good eyes), including a crossover with a curve crossing a straight track. I was quite proud of that one at the time, especially with all the little bits of tracks being live in the right direction, but isolated from the others. Fiddly soldering of thin wires underneath.
Unreadable squiggle

guym
Posts: 47
Joined: Fri Jun 14, 2019 5:28 pm

Re: Digital Command and Control (DCC) with pigpio waves

Sun Jul 04, 2021 4:12 pm

butchergg wrote:
Wed Jun 23, 2021 3:19 pm
Gavinmc42 wrote:
Wed Jun 23, 2021 6:28 am
CPU 45% seems a bit high.
Did to me also. Quiescent pigpiod ran about 7%; when I started wavedccd and enabled the pulsetrain, it went to pigpiod: 25%, wavedccd 20%

When I compile wavedccd without -DUSE_PIGPIOD_IF, which uses direct GPIO interface through libpigpio, before starting the pulsetrain it runs at 7%, when I enable the DCC pulsetrain it goes to 11%.

All of the above is with idle packets, no processing of throttle commands. The only difference between the two is the use of the TCP network stack between pigpiod and wavedccd. albeit internally through localhost. Early on, I tried running a wavedccd on my Ubuntu desktop, communicating with pigpiod aboard the RPi over the home network (Ethernet out of the desktop box, WiFi to the RPi), and that introduced significant gaps between packet pulsetrains. So, IMHO direct to the GPIOs is the way to go...
Nice to see another application using pigpio padded wave APIs for continuous wave transmission! I think you are correct about the network stack overhead contributing to the (relatively) high cpu utilization.

I took a quick look at your repository and I see that you are polling for the completion of a packet (wave) every millisecond.

Code: Select all

while (wave_tx_at(pigpio_id) == wid) time_sleep(0.001);
Have you tried throttling back a little on the polling rate for a comparison?

One suggestion I can offer is wait just enough time for the current packet to complete its transmission then begin polling. This will only help if your idle packet transmission time is >> your polling period.

Code: Select all

wave_get_micros(pigpio_id); // Use to obtain the initial wait period.

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Mon Jul 05, 2021 1:20 am

guym wrote:
Sun Jul 04, 2021 4:12 pm

Nice to see another application using pigpio padded wave APIs for continuous wave transmission! I think you are correct about the network stack overhead contributing to the (relatively) high cpu utilization.

I took a quick look at your repository and I see that you are polling for the completion of a packet (wave) every millisecond.

Code: Select all

while (wave_tx_at(pigpio_id) == wid) time_sleep(0.001);
Have you tried throttling back a little on the polling rate for a comparison?

One suggestion I can offer is wait just enough time for the current packet to complete its transmission then begin polling. This will only help if your idle packet transmission time is >> your polling period.

Code: Select all

wave_get_micros(pigpio_id); // Use to obtain the initial wait period.
Thanks for looking; I really wondered if this was going to work at all until I played speed/dir packets to my "sacrificial" N-scale diesel locomotive...

The original wait period came from the example in the libpigpio documentation, 0.1sec passed to time_sleep. Playing pulsetrains through piscope clearly showed significant delay between packets, so I just cut the number down until the gap went away for the idle packet stream I was sending. Using wave_get_micros() will be a much better solution, thanks!

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Tue Jul 06, 2021 6:14 pm

guym wrote:
Sun Jul 04, 2021 4:12 pm
One suggestion I can offer is wait just enough time for the current packet to complete its transmission then begin polling. This will only help if your idle packet transmission time is >> your polling period.

Code: Select all

wave_get_micros(pigpio_id); // Use to obtain the initial wait period.
I changed out the loop on time_sleep(0.0001) to a single call to time_sleep with the value from wave_get_micros() / 1000000.0. The CPU utilization went down to pigpiod: ~14% and wavedccd: ~8%. So, it wasn't the network interface...

Thanks, this makes the pigpiod interface a lot more viable. Change pushed to the repo.

guym
Posts: 47
Joined: Fri Jun 14, 2019 5:28 pm

Re: Digital Command and Control (DCC) with pigpio waves

Wed Jul 07, 2021 4:26 pm

I'm glad that worked for you.
So, it wasn't the network interface...
Actually it is since every call to `wave_tx_at` runs a tcp transaction over the socket.

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Wed Jul 07, 2021 7:52 pm

guym wrote:
Wed Jul 07, 2021 4:26 pm
I'm glad that worked for you.
So, it wasn't the network interface...
Actually it is since every call to `wave_tx_at` runs a tcp transaction over the socket.
You're right, after I thought about it a bit, all those libpigpio calls in a loop...

I ran the change this morning with piscope, and delaying for the waveform time yields a 1ms gap in the packets of the pulsetrain. So, I put the loop back in, still use the waveform time, but subtract 2000us from it. Put a counter in the loop, with the idle packet train it almost always runs just once, once in a while runs two times. CPU usage is up, pigpiod: 28%, wavedcc: 18%, but not as bad as with the original loop.

My concern is the arbitrariness of -2000. Good for idle packets, not sure about longer ones, although intuition says the shortest packets should be the most problematic.

Oh, decided to try running wavedcc on my Ubuntu desktop, connected to the RPi through the home network. That was not pretty, 4ms gap between packets... :D

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Fri Jul 09, 2021 2:42 pm

@guym, I've gone back to a hard-coded delay for the time being. I did move off of libpigpio's time_sleep() fuction to the system nanosleep(), removing one burden on the network interface. I still think there's a way to use the pulsetrain timing information provided by libpigpio, but I need to think about it a bit more...

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Thu Jul 15, 2021 2:46 pm

I see traffic on the github repo, so I'll post an update...

I switched out the build system to cmake. The default build is for direct GPIO access, and the pigpiod interface can be built by passing a switch in the cmake .. command. The original Makefile is still there, but I'll probably remove it before I tag a 1.0 release. The README has been updated with the cmake build instructions, a bit more general description of the software, and a limitation section. Right now, wavedcc(d) will handle both baseline and extended speed/direction, function control for F1-F12, ops mode and service mode CV writing.

I should receive today an Adafruit order containing, among other things, two current sense boards. With those, I'll be able to incorporate the last capability I intend for version 1.0, CV reading/verifying (the basic DCC message protocol is one-way, command station -> locomotive, but configuration variables on the locomotive can be read in service mode by looping through DCC verify messages with succeeding values until the locomotive presents a current draw on the track to signify the value to be verified is present in the CV) . After that, my focus will shift to making wavedcc play nice with JMRI, and bug fixes.

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Tue Jul 20, 2021 10:35 pm

If anyone's listening...

Just pushed a couple of commits to start development of power monitoring. This is hard-coded to the INA219 current sense board from Adafruit, a rather daunting board with which to interface. I couldn't get the pigpio word read/write functions to work, so I'm using the device read/write functions to deal with the bus directly. The configure/calibrate registers gave me fits until I found a CircuitPython script that seemed to work; I ran it, then used my program to download the registers' contents. I'll figure out the details later...

The initial implementation starts a separate thread started when wavedcc starts, that just polls the INA219 every millisecond for current and voltage, and posts them to global variables. It also tracks a high-water current, which I eventually will use for CV read/verify. It just runs and runs, until the program is terminated. The wavedccd CPU loading went up just a percent or two, but I'm still a bit worried about the 1 millisecond duty cycle over I2C. I also coded the <c> command, which reports the current; haven't tried it with JMRI yet.

The pigpiod version seems to be working fine, but the direct-GPIO version is broken. I'll may not have that fixed until next week.

User avatar
Gavinmc42
Posts: 5880
Joined: Wed Aug 28, 2013 3:31 am

Re: Digital Command and Control (DCC) with pigpio waves

Wed Jul 21, 2021 1:47 am

This is hard-coded to the INA219 current sense board from Adafruit, a rather daunting board with which to interface. I couldn't get the pigpio word read/write functions to work, so I'm using the device read/write functions to deal with the bus directly. The configure/calibrate registers gave me fits until I found a CircuitPython script that seemed to work; I ran it, then used my program to download the registers' contents. I'll figure out the details later...
Yep fun family of chips to deal with.
I have used the CLI i2c-tools in shell as well.
Need to configure it before you get data too.
I used i2cdump to get a string of data into a buffer then extract the register data from that array.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Thu Jul 29, 2021 2:01 am

Made a bit of a mess for myself in getting current monitoring to work, but it finally does with the most recent commit. @Gavinmc, thanks for the pointers to the i2c utilities, they were a help in getting things debugged.

I've hard-coded the use of the INA219 current sense module from Adafruit, don't see a need right now to make other devices configurable. I use the libpigpio i2c routines to access the module; getting that to work in both pigpiod_if and direct modes was a bit of a pain. I've got a 1/2 second sampling interval implemented, which results in about 40% CPU utilization between pigpiod and wavedcc, about 12% CPU with wavedccd only in direct mode. I've made the interval changeable, as when I build the service mode commands for CV reading I'll need to sample with a single-digit millisecond mode to capture the locomotive's response. Per the DCC specification, the locomotive is to present a 60ma load on the rails for about 6 milliseconds for that response. A ~3 millisecond capture interval drives the pigpiod/wavedcc CPU to 70%, direct wavedccd to about 18%, so there's a definite advantage in running wavedccd as a service.

Now, I can implement the service mode read and verify commands. That should make a sufficiently capable software for an initial release.

User avatar
Gavinmc42
Posts: 5880
Joined: Wed Aug 28, 2013 3:31 am

Re: Digital Command and Control (DCC) with pigpio waves

Thu Jul 29, 2021 8:03 am

thanks for the pointers to the i2c utilities, they were a help in getting things debugged.
Yep, the i2c-tools stuff is so useful for debugging hardware I made baremetal versions.
I need to add i2cdump as well, I generally use i2cget/set.
The advantage of i2c-tools is any language can call the utils.

Been meaning to learn multi-core programming so one core can handle I/O, another sensors, another comms another UI etc.
Not sure how interrupts work on that type of system, more to learn ;)
I think the C0 stepping has some interrupt changes?
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Sat Jul 31, 2021 3:23 am

Just posted a commit with a first cut on CV reading. It continues to amaze me at how well-suited libpigpio is for this application.

The way a configuration variable is read from a locomotive is for the software to send a series of verify messages, one for each value. When the locomotive receives a verify message for the CV that has the value contained in the CV, it presents a current load on the rails of > 60ma for at 6ms +/-1ms Most DCC-equipped locos just turn on the motor for the required time.

Sounds simple, but turns out it's not so easy. Firstly, there's variation in current measurements, in some places such as startup the difference can be significant due to inrush. Also, the difference between the decoder's processing load and the motor load need to be sorted out. And, the sampling interval needs to be able to resolve multiple measurements within the 6ms window of load presentation.

The DCC specification helps with this, stating that a verification operation needs to have a "preamble" of a bunch of reset messages to get the decoder powered up and stable, then 5 identical verify packets, then a small number of reset packets to keep the decoder powered up during the presentation of the ack current for the last verify message. I use libpigpio's wave chain routine to build those with ease.

To catch the ack load, I started with logic in the current monitoring thread to identify a consecutive number of current measurements above the baseline current, which for now I'm hard-coding. That proved to be challenging to align with the previously described wave chain playback, but libpigpio again comes to the rescue. When a wave chain is kicked off for playback, the software is to enter a busy loop that exits when a libpigpio wave-busy function returns false. Ha, what a great place to collect current measurements; I just added a test of the latest current measurement, if > the baseline, increment a counter. When the loop falls through, if the counter is > 1, the CV value being tested is the one contained in the locomotive. So simple, and quite reliable in my afternoon's testing.

DCC CVs are 8-bit registers, so one needs to loop through verifies from 0 to 255 and fall out at the correct verification. If you're reading multiple CVs it can be a long wait, and during that the locomotive can move a significant distance as it acks with pulses of the motor. JMRI's DecoderPro does just that; it has multiple pages of related CVs, with commands to read just single CVs, the whole page, or the whole set.

Right now, the code for interface through the pigpio daemon works, direct doesn't. That'll be remedied in the next few days. I'm also going to take a few pictures of the hardware for posting, a bit more involved now that a I2C current sense board is in the mix. But I also need to budget time for running the locomotive back and forth, ringing the bell, blowing the whistle... :D

butchergg
Posts: 26
Joined: Sun Oct 28, 2012 6:14 pm

Re: Digital Command and Control (DCC) with pigpio waves

Sat Jul 31, 2021 3:34 am

Gavinmc42 wrote:
Thu Jul 29, 2021 8:03 am
thanks for the pointers to the i2c utilities, they were a help in getting things debugged.
Yep, the i2c-tools stuff is so useful for debugging hardware I made baremetal versions.
I need to add i2cdump as well, I generally use i2cget/set.
The advantage of i2c-tools is any language can call the utils.

Been meaning to learn multi-core programming so one core can handle I/O, another sensors, another comms another UI etc.
Not sure how interrupts work on that type of system, more to learn ;)
I think the C0 stepping has some interrupt changes?
Gavin, multi-threaded programming has gotten a lot easier in the past few years. OpenMP lets you multithread any for loop with just a single-line pragma; I used that extensively writing a raw image processor. For wavedcc, the std::thread works nicely; I've found the best way to manage infinite loop std::threads is to loop on a bool that I can set false in my main code, then do a join() to catch it when it falls through the end. std::mutex is trivial to use to protect variable accesses between two threads (oops, reminds me I need to do that for current reading in my CV verify code in wavedcc.... ) If you really need to handle events on GPIOs, libpigpio has functions to set callbacks of your creation on various GPIO state changes.

Gotta love those libraries....

User avatar
Gavinmc42
Posts: 5880
Joined: Wed Aug 28, 2013 3:31 am

Re: Digital Command and Control (DCC) with pigpio waves

Sat Jul 31, 2021 8:41 am

Gavin, multi-threaded programming has gotten a lot easier in the past few years.
Much easier, don't even need a real OS these days.
https://github.com/ultibohub/Examples/t ... 1-MultiCPU
https://github.com/ultibohub/Examples/t ... iThreading

I can run the examples, just have not done anything with them yet.
Need to find the time and an application to try it on.
I really need to make a robot to try this on.
A DCC train might make a good debug platform, not likely to run into things ;)

It was something I thought about years ago.
My Pi coding skills have improved since then :D

The Pi's have a few serial bit stream modes I don't think anyone has used.
The PCM has a 64 x 32 bit Fifo.
DCC 10KHz is pretty slow for audio range hardware.
But this is the sort of stuff a CRO is handy for debugging.
Wonder if my old 10MHz one works?

Probably easier to start with serial controller Ardunio?
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

Return to “Interfacing (DSI, CSI, I2C, etc.)”