Skip to main content

@cdswindell posted:

So what do you do when your control panel needs more than the 26 GPIO pins a standard Pi makes available? You use the train control program control.py in client-server mode, of course! I just checked in changes that allow multiple Pis to slave off of a server Pi that has the connection to the LCS SER2.

Examples are coming, but this development will allow stand-alone control panels to be built, powered by an inexpensive RaspberryPi with WiF. All wiring, except for power to the Pi, can be managed inside the panel enclosure.

  -- Dave

Sounds like a good place for a canned product.

With tonight's checkin, you can use a potentiometer on a Pi to control the speed of an engine/train. As the Raspberry Pi is a digital device, and by their nature, potentiometers (rheostats) are analog devices, you need to purchase a some additional hardware to make this all work. Specifically, you need a MCP3008 analog to digital chip (available at Amazon at 2 for $14) to convert the analog output of the pot to a digital value the Pi can work with. The chip allows up to 8 pots to be connected to it at a time, BTW.

Below is a picture of my Pi and a development breadboard, which makes prototyping much simpler. The interesting bits are the little black chip (circled in red below), and the pot (the thing to the lower left of the black chip). The chip is the MCP3008. It takes the analog value from the pot (a voltage from 0 to 3.3), and maps it into a float value between 0.0 and 1.0. This value is then scaled from 0 to 199 (for TMCC2/Legacy engines) or 0 to 31 (for TMCC1 engines).

Here's the Python code to make this work (controls the speed of a TMCC1 engine with the ID of #1:

GpioHandler.when_pot(TMCC1EngineCommandDef.ABSOLUTE_SPEED, 1)


Of course, you can also use this command in client-server mode:

GpioHandler.when_pot(TMCC1EngineCommandDef.ABSOLUTE_SPEED, 1, server='192.168.3.35')


This code sends the speed changing commands to a TrainController server running on 192.168.3.35.

  -- Dave

Attachments

Images (1)
  • mceclip0
Last edited by cdswindell

One more checkin. It is becoming clear to me that the "train control" program I developed (src/cli/control.py) is a useful platform to integrate the code to connect Raspberry Pi GPIO pins to physical buttons and devices. Thus, I added an option to the script to execute commands from another Python script at startup. By default, the program looks for a file named buttons.py in the local directory. You can specify a different script via command line argument.

For example, here is the contents of my local buttons.py:

from src.gpio.gpio_handler import GpioHandler
from src.protocol.command_req import CommandReq
from src.protocol.tmcc1.tmcc1_constants import TMCC1AuxCommandDef
from src.protocol.tmcc2.tmcc2_constants import TMCC2RouteCommandDef
from src.protocol.tmcc2.tmcc2_constants import TMCC2EngineCommandDef

"""
Simple examples of how to associate Lionel commands to Raspberry Pi buttons
"""
GpioHandler.when_button_held(26, TMCC2EngineCommandDef.BLOW_HORN_ONE)

GpioHandler.when_button_pressed(21, TMCC2RouteCommandDef.ROUTE, 10)

off = CommandReq(TMCC1AuxCommandDef.AUX2_OPTION_ONE, 9)
on = CommandReq(TMCC1AuxCommandDef.AUX1_OPTION_ONE, 9)
GpioHandler.when_toggle_switch(13, 19, off, on, led_pin=20)
# GpioHandler.when_toggle_button_pressed(19, on, led_pin=20)

GpioHandler.when_pot(TMCC2EngineCommandDef.ABSOLUTE_SPEED, 88)

print("Buttons registered...")


This file does the following:

  • broadcasts the BLOW_HORN_ONE command to all engines when the button connected to pin 26 is held (sends the command to the default address of 99).
  • sends the Fire Route 10 command whenever the button connected to pin 10 is pressed.
  • defines a toggle switch that  simulates the Cab 2's Aux1 and Aux2 buttons on TMCC accessory #9; on my layout, this is an LCS BP2, so track power is turned on when the switch is turned on, and power is turned off when the switch is pressed off. A LED connected to pin 20 is lit when track power is on.
  • controls the absolute speed of Engine #88 via a pot.

Here's what it looks like when I run control.py on my pi:

(legacyEnv) davids@raspberrypi:~/dev/legacyEnv/PyLegacy $ src/cli/control.py
Sending commands directly to LCS Ser2...
Listening for client connections on port 5110...
Loading startup script: buttons.py...
Buttons registered...
PyLegacy train controller, Ver 0.1
>>


On a separate note, what should I call this program? Train Control? I think that is already taken, and it's pretty boring. PyLegacy? Lionel may not be too happy with that. PiTrains? Hmm, potential...

  -- Dave

I've added extensive online help to PyTrain. Here are some examples:

(legacyEnv) davids@Sprucewood-iMac PyLegacy (master) % src/cli/pytrain.py
Sending commands directly to Lionel LCS Ser2...
Listening for client connections on port 5110...
PyTrain, Ver 0.1
>> ?
usage: [h] [accessory | effects | engine | halt | lighting | route | switch | quit]

Valid commands:

options:
h, help show this help message and exit
accessory Issue accessory commands
effects Issue engine or train effects commands
engine Issue engine or train commands
halt Emergency stop
lighting Issue engine or train lighting effects commands
route Fire defined routes
switch Throw switches
quit Quit PyTrain

Commands can be abbreviated, so long as they are unique; e.g., 'en', or 'eng' are the same as typing 'engine'.
Help on a specific command is also available by typing the command name (or abbreviation), followed by '-h',
e.g., 'sw -h'
>> św -h
usage: Fire specified switch (1 - 99) [-h] [-t|-o|-a] Switch Number

positional arguments:
Switch Number switch to throw

options:
-h, --help shows this help message
-t, --through Throw Through
-o, --out Throw Out
-a, --set_address Set switch address
>>


  -- Dave

@cdswindell have you considered interfacing with the LCS system through wifi?  Most raspberry pis have wifi these days and it eliminates the need for the SER2 and a second pi.  Perhaps a second interface option on the back end would be a nice extension.  From the LCS partner documentation, it seems like wifi is the preferred method anyway.

I'm very encouraged by your rapid progress here.  A DIY hardware remote that is powered by a raspberry pi (or other wifi enabled python interpreter) now seems well within reach.  It could be something as simple as a Cab1L with an aux3 and a whistle slider, or even more complicated like a Cab2 with the context sensitive touch screen.  A raspberry pi could easily power a touch screen gui.

@cdswindell have you considered interfacing with the LCS system through wifi?  Most raspberry pis have wifi these days and it eliminates the need for the SER2 and a second pi.  Perhaps a second interface option on the back end would be a nice extension.  From the LCS partner documentation, it seems like wifi is the preferred method anyway.

I'm very encouraged by your rapid progress here.  A DIY hardware remote that is powered by a raspberry pi (or other wifi enabled python interpreter) now seems well within reach.  It could be something as simple as a Cab1L with an aux3 and a whistle slider, or even more complicated like a Cab2 with the context sensitive touch screen.  A raspberry pi could easily power a touch screen gui.

@crazytrain asked the same question. I just gained access to the partner program documentation on Monday. It contains info on the LCS WiFi module. Given that the older LCS app communicates just fine with a Base 3, even though it was designed with the WiFi module in mind, I will have a look at the documentation and see what is involved with making a direct WiFi connection.

  — Dave

@Crazy Train posted:

With the addition of the physical buttons, how much of a leap would it be to make a dedicated controller with physical buttons that communicates with the Pi wirelessly?

With a Pi wirelessly or with the Base 3 wirelessly? The system I built already supports allowing a client Pi to talk to the server Pi (the one connected to the Ser2) wirelessly.

i have to see if I can figure out how to send packets directly to a Base 3 over WiFi…

  — Dave

Just checked in support for "Sequenced Requests" These allow you to send one or more commands with delays in-between. For example, the subclass SpeedReq takes an engine address and a desired speed then sends, in order:

  1. The Tower dialog to request the speed change
  2. Changes the speed
  3. The Engineer acknowledgement that the speed was changed as requested.


This sequence works particularly well when selecting one of the "official rail road speeds" (slow, restricted, limited, etc.). Sequenced commands can be bound to buttons, just like regular commands.

There are other sequenced requests I could think up too, such as an engineer requesting clearance to move out, the tower denying it, wait 30 seconds, have the engineer request clearance again, have the tower grant clearance, and roll out!

I will get back to coding up the remaining RailSounds dialogs...

  -- Dave

I've started reading data from the Lionel Ser2. I can now interpret all of the TMCC1 commands and have unit tests in place verifying that I can construct a command, send it, and recover the exact same command (and address/data) from the transmitted bits. Decoding the 3 byte TMCC2 commands should be straightforward.

Interestingly, the SER2 strips away any of the PDI prefix and suffix bytes. I don't know if this means I can only read TMCC/legacy commands from the SER2 (and not read commands coming from the other LCS devices). I have to do some more experiments.

I just ordered a Raspberry Pi Zero with WiFi to test my code on. These little boards are only $15 and support WiFi. If they have the memory and horsepower to run my stuff, they would be the perfect chip to put into a control panel.

  -- Dave

Just checked in support for interpreting the 3 byte TMCC2/Legacy commands. I have to give some thought to what I want to do here. Writing code to echo the command stream the Ser2 is easy enough. But I am wondering what else we could do with this data?

One thought is to maintain the sensed state of engines, switches, accessories, etc. This would allow control panels to reflect current state (is a switch thrown or through?). But another would be to assist in sending sequenced commands. For example, if the tower sends the "go to limited speed" command (triggers a dialog), the engine should decelerate if it's speed is above this speed step, and accelerate if below it. Interestingly, this is what the Cab2 throttle does when you press one of the "official railroad speed" buttons. The controller doesn't just set an absolute speed, it sends multiple commands to ramp up to/down to this speed. By maintaining the sensed engine speed state, I could mimic this behavior.

It appears Momentum is also handled by the Cab2. For higher momentum settings, the Cab2 accelerates an engine slower by sending more absolute speed commands with smaller deltas.

I'm sure there are other applications as well.

I'm still not sure if I can query other LCS devices via the Ser2. This would be more reliable for things like switch, accessory, and power district state, and would be the preferred way of obtaining state, of course.

  -- Dave

@BlueFeather posted:

Dave, this is awesome.  Really important stuff.

Have you built a CLI mode that listens for commands from BASE2 / BASE3 and displays them in the window -- maybe interprets them too (both raw code and human-readable info)?  This would effectively allow anyone to reverse-engineer the different commands sent by the Legacy Bases when operated by any remote, or sent by your software.

Another potential use I see -- mapping special keypresses or sequences on older less-functional remotes to enable users to access Legacy functionality typically requiring CAB2.  Essentially, someone with a CAB1L could fire off AUX3 on a Legacy-equipped steamer by tapping AUX1 twice within three seconds, for example.  You could even use it to make your own hardware remote, or a software emulator of a CAB2.  Etc.

Simply great work.  Keep it up.

I’ve started work on reading the byte stream from the Ser2. Probably won’t spin this out into its own CLI until next week. I need to learn a bit more on how to code efficient parsers that are just prefix-defined (prefix and length), which the TMCC1 & 2 commands are.

cds

I have command echoing working via the LCS Ser2! Here's the output from a sequence command where the tower requests a full stop, the engine speed is set to zero, and the engineer acknowledges:

[TOWER_SPEED_RESTRICTED ID# 65 Data: 0]
[ABSOLUTE_SPEED ID# 65 Data: 24]
[ENGINEER_SPEED_RESTRICTED ID# 65 Data: 0]


I will add the display of the byte sequence to this output as well.

@Patrick Keistler, @BlueFeather fyi

  -- Dave

Add Reply

Post
×
×
×
×
Link copied to your clipboard.
×
×