Barnsley fern fractal

Thoughts on software architecture and development, and methods and techniques for improving the quality thereof.

David B. Robins (home)


Code Visions: Improving software quality
DCC Train Control with Raspberry Pi and a Joystick

By David B. Robins tags: C++, Development, Embedded, Debugging Sunday, November 20, 2016 16:46 EST (link)

The DCC conversion I planned two weeks ago is going well. (The DC with PWM did not go as well; one locomotive stalled and pulled 1.5 A through the small H-bridge rated for 1 A, with predictable results: magic smoke let out. Fortunately I had a few more, but I decided to focus on the DCC controller.)

I am using the booster circuit here (scroll down), which is built using a Raspberry Pi to control a TI LMD18200 3 A, 55 V H-bridge (with plenty of over-current and other protection built in), and appropriate smoothing capacitors and so forth. I started with the pcDuino V2 rather than the Pi, but the incident with the H-bridge took it out too, and while it ran for a little while it eventually was no more, and I picked up a Raspberry Pi 3 B at Fry's and also installed Arch Linux ARM on it.

The design uses a daemon which I call dccd to send commands to the track, which listens for higher-level commands over a local network socket (for now, speed values from -127 to +127, 0 to brake, sent to default device 3). It uses the pigpio library to handle GPIO, and Poco (after evaluating a few in the space) for sockets and threading. I was originally using nanosleep to handle timing, but the DCC decoder (Digitrax DH126D) didn't like the signal at all. I took it to work, hooked it up to the 'scope, and saw why: the pulses were all way too long: S-9.1, the DCC Electrical Standard, says that each part of a 1-bit should be 58 µs ± a couple and a 0-bit should be 100 µs ± a lot, and it was 60+ µs off. It turns out that nanosleep means that the kernel can do its thing for a while, and for precise waits I needed something like pigpio's gpioDelay. (I had also observed a persistent ringing on the scope, but that was due to having unsynced grounds: I had used a single probe and connected the ground to one H-bridge output and the probe to another; much better with using the board ground, two probes, and plotting the difference. I'm sure someone with an EE background would never have thought of doing such a thing….)

With the square wave going beautifully, the DCC decoder was almost happy, except I had inserted a 5 ms gap between packets, which was unnecessary; I had my idle loop send the idle packet or re-send speeds. Now I was able to hook up the decoder to the H-bridge outputs and a voltmeter to its motor output leads and see reasonable voltage changes corresponding to speed settings—except it never got beyond about 12.5 V after about half speed (on either 28 or 128-speed scale). I figured it was also doing PWM and my meter wasn't averaging it correctly, and hooked that up to he 'scope too and saw reasonable increases (duty cycle changes) all the way to top speed. Now I was ready to install it in a locomotive.

I have three locomotives, two steam, one diesel. One of the steam had been opened before and opened easily to access the motor, so I decided to convert it first. It was in fact a lot of trouble because the motor, as with many older locos, wasn't properly isolated and connected to the pickups through the bottom of the chassis at several points. I covered a couple with electrical tape and that was fine, but I needed to connect to the wheel pickups somehow. The last connector was a brass screw that passed through a hole in the motor frame, and connected through the floor plate to the wheels. I played with that for a while: I needed the brass screw to give me a connection to a wire I'd attach to it, but not to the motor frame it passed through. Much plumber's tape was expended in an attempt to make this work, and it did for a short while, but was unstable. Nylon screws are a recommended solution too, but I wasn't managing to find any the right size (M1.4?) and I'd still need to connect the decoder to that side's wheel pickups another way. Eventually it must have shorted and blown up the decoder, which never came back (so I'll be taking advantage of Digitrax's excellent "no worries warranty" which explicitly includes "accidental customer damage"). I have on order a newer Hornby motor that I think will fit, an X8259 from Peter's Spares; and unsurprisingly the shipping (from the UK) is about twice the price of the motor. The current motor still works—and I can run it through DCC with long overhead wires like a tram!—but it would be far easier with an isolated motor (that's the right size with the right worm gear).

The next conversion went much better: it was my British Rail diesel engine, 37 130, end identification "8H22", and this video was very helpful. My model had some differences: instead of the whole floor plate separating, floor sections at the front and back with wheels (and motor on one) separate from the top/middle piece. The key was separating one metal motor terminal from a track pickup, and then I was able to solder the decoder leads to left/right pickups and motor terminals. Another difference from the video is that he had a separate (accessory—light) pickup lead he could solder to for the one separated from the motor; I had to loop a wire around where the motor terminal had been attached. I used heat-shrink tubing around each of the connections, and when I was done there was plenty of inside space for the decoder (unlike the steam loco where it was a very tight fit). It worked the first time and kept working. The only issue with that loco is that one set of wheels has become stiff and has trouble around curves; a colleague at work suggested graphite and I'll give that a try (plastic on plastic, so oil isn't the best option).

Finally, telnet'ing to the DCC daemon, dccd, wasn't an ideal way to control the train, so I did some work on the other component, Dave's DCC controller (ddcc): I fired up evtest and looked at joystick (Logitech Extreme 3D Pro) events, and wrote a short program to read events from the Y-axis, scale them to -31 to 32 (for now, full speed is unnecessary) and pass them as speed requests to dccd. This worked exactly as planned and I was very happy with it. I may use the joystick's throttle control for speed later, so one doesn't have to hold it in position to keep it moving. It also needs a GUI to be a little more elegant; perhaps Qt (Tk is so ugly)?

Content on this site is licensed under a Creative Commons Attribution 3.0 License and is copyrighted by the authors.