Skip to main content

I am developing a python project to issue Lionel TMCC1 and Legacy commands via the LCS SER2 interface. the project is up on GitHub, and the link to the repository is here. I've run the code on a Raspberry Pi 5 as well as on an Apple Mac M1 (where I'm doing the development).

I need to flesh out README.md to let folks know how to install the project. I will try to get that in place over the weekend.

So far, I have CLI commands to issue route and switch directives. Next up is support for accessories.

My goal is to control parts of my layout with physically buttons that will trigger TMCC/Legacy commands, using the Raspberry Pi as the controller.

More later!

  -- Dave

Last edited by cdswindell
Original Post

Replies sorted oldest to newest

Sounds like an exciting project Dave!

One thing that I've asked Lionel for and suggested previously on this Forum, is some provision for RANDOMNESS.  As far as I know, the built-in macro recording capability of the Legacy base, and even Lionel's LCS lack any such provision, and that's a shame!

To my way of thinking: if you build a layout with multiple routes, passing sidings or hidden staging that has the ability to store and stage trains, adding random selection of trains and routes turns the whole thing into a kind of "mechanical fish tank" that visitors could watch for several minutes without seeing the same train on the same track twice.  Code for semi-autonomous operation might make train layouts a fixture in doctors' offices, hotel lobbies, even store windows!  When you take into account the ongoing costs of maintenance, a train display would probably cost less than an ACTUAL fish tank!

One thing I am picky about is realistic starting and stopping.  Smooth starts and stops are also much easier on rubber tires and gears.  So the code should contain provisions for gradual stops and starts, perhaps by sending multiple speed commands.  I'm not sure how you're sensing train position, but to make it reliable and repeatable, care has to be taken to make sure the train stops within a staging block, not fouling other routes / trains.

Can't wait to see what comes of your efforts!  Please keep us posted (and a video demo would be great!)  Thanks for sharing!

I’ve made some progress over the weekend and now have Python code working to control switches, operate accessories, fire routes and run engines and trains. The code supports both TMCC1 and TMCC2. (Legacy) syntax, and provides a means to specify the command format you want from the command line.

The code can be found on GitHub here:https://github.com/cdswindell/PyLegacy.

TMCC2 provides far more options for engine/train control than does the older TMCC1 syntax. I have about 50% of the TMCC1 engine commands implemented, along with their corresponding TMCC2 counterparts. Of course, this means I’ve only implemented about 10-15% of the TMCC2 engine commands. However, as in TMCC2 land, engine and train commands are the same, except for the first byte of the command, this means I also have a bunch of train commands working!


The CLI layer calls into a set of command generation classes that construct the byte sequences to send to the SER2. This layer is relatively clean and uses a set of Python enums that use the bit patterns associated with each Lionel command to uniquely define them. The address of the entity being controlled can then be logically ORed to this bit string to build the command bytes. Frankly, the hard part is working out a sensible CLI command syntax for the TMCC2 engine/train commands. The Python library ArgParser is definitely my friend!


After I complete the implementation of the TMCC1 and 3-byte TMCC2 engine commands, I will turn to writing unit tests and packaging the code. Then, I’ll move on to connecting physical button presses on my Raspberry Pi to my command architecture.

  — Dave

It was gratifying to see the code up and working on my little Pi this morning. There were some surprises:

  • The 'Ring Bell' commands (both TMCC1 and 2) actually turn the bell ringing on and off (like sliding and holding the bell slider on the Cab 2 remote)
  • Unclear why the "Bell Slider Command [2 - 5]" didn't seem to do anything
  • For the "Bell One-shot Ding [0 - 3]" command, the numeric argument controls the bells volume
  • The TMCC1 engine commands work on Legacy (tmcc2) engines and carry out the corresponding functionality.
  • Not sure what "Blow Horn 2" does; none of my engines responded to it. Is this reserved for the Vision Line series?
  • Seems to be a slight difference between the TMCC1 and TMCC2 Halt commands. Both stop all engines dead on their tracks (pun intended), but the TMCC1 command shuts down enabled power districts on my LCS BP2 whereas the TMCC2 command left the power districts on.


I still have several commands left to implement, but as I said in my last message, I want to get some unit tests in place before I go too much further. Everything is checked in, and the README.md file contains instructions on how to grab my code and get it to compile. Although that said, if you are not familiar with Python, it will be a challenge...

  -- Dave

FYI, I've implemented most all of the TMCC1 and TMCC2 commands on pages 4, 5, 6, 8, and 9 of the LCS Partner Documentation Legacy Command Protocol, rev 1.21. The commands I've omitted are those that add/remove engines and rail sounds cars from consists/trains.

The Python code is checked in at github.com/cdswindell/PyLegacy. There are a series of command line tools (CLI) you can use to control an engine from a shell window of a Linux/Mac/Windows PC, as well as a series of classes to issue commands programatically. With respect to the cli commands, the engine command was the most challenging, because there are so many options!! Typing the command:

(legacyEnv) davids@Sprucewood-iMac PyLegacy (master) % src/cli/engine.py -h         

usage: Control specified engine/train (1 - 99) [-h] [-a | -s | -fwd | -rev | -t | -fc | -rc | -r | -b |
                                               -boost | -bl [0 - 7] | -brake | -kl [0 - 7] |
                                               -tb [0 - 7] | -sui | -sud | -sdi | -sdd] [-tr]
                                      [-tmcc | -legacy]
                                      [-baud BAUDRATE] [-p PORT]
                                      Engine/Train {speed,sp,bell,be,horn,ho,momentum,mo,sound,so}

positional arguments:
  Engine/Train          Engine/Train to control
  {speed,sp,bell,be,horn,ho,momentum,mo,sound,so}

  Engine/train sub-commands
    speed (sp)                    Speed of engine/train
    bell (be)                       Bell operations
    horn (ho)                     Horn operations
    momentum (mo)       Momentum operations
    sound (so)                   Sound operations

options:

-h, --help            show this help message and exit

-a, --set_address     Set engine address

-s, --stop_immediate Stop immediate

-fwd, --forward_direction

                        Set forward direction

-rev, --reverse_direction

                        Set reverse direction

-t, --toggle_direction

                        Toggle direction

-fc, --front_coupler Open front coupler

-rc, --rear_coupler Open rear coupler

-r, --ring_bell       Ring bell

-b, --blow_horn       Blow horn

-boost, --boost_speed

                        Brake speed

-bl [0 - 7], --boost_level [0 - 7]

                        Boost level

-brake, --brake_speed

                        Boost speed

-kl [0 - 7], --brake_level [0 - 7]

                        Brake level

-tb [0 - 7], --train_brake [0 - 7]

                        Train brake

-sui, --start_up_immediate

                        Start up immediate

-sud, --start_up_delayed

                        Start up delayed (prime mover)

-sdi, --shutdown_immediate

                        Shutdown Immediate

-sdd, --shutdown_delayed

                        Shutdown delayed with announcement

-tr, --train          Direct command to addressed train rather than engine (for TMCC2 commands)

-tmcc, --tmcc1        Use TMCC1 command syntax.

-legacy, --tmcc2      Use TMCC2/Legacy command syntax.

-baud BAUDRATE, --baudrate BAUDRATE

                        Baud Rate (9600)

-p PORT, --port PORT Serial Port (/dev/ttyUSB0)

provides a list of command options that can be applied to the specified option. For example the command:

src/cli/engine.py 71 speed 65

sets the absolute speed of the Legacy engine with the Engine ID of 71 to 65.

The command:

src/cli/engine.py 71 -r

turns the bell on (or off) on Engine #71.

I've added tests for the TMCC1 commands. I need to think a bit about how to test the TMCC2 commands, as it is tedious to code the bit patterns from the Lionel doc into source code, but that is most likely what I need to do...

Next up is to implement a serial communications module that allows a single instance to take commands from multiple threads and meter them out to the serial input of the LCS SER2 (so its buffer doesn't overflow).

To follow are implementing the multi-word Legacy commands, the Raspberry Pi code to respond to button presses, complete unit tests, better documentation, and packaging the project into a downloadable project on PyPi.

    -- Dave

Today's checkins include support for a comms buffer that sends command bytes to the LCS SER2 with buffering and throttling. The buffer can hold up to 2,048 commands (3 or 9 byte packets) and then sends them to the SER2 no faster than one every 50ms. This behavior is transparent to the client command code, meaning programmers just have to think about what they want their trains to do.

I also added support for the Lionel's "official railroad speeds". For example:

davids@Sprucewood-iMac PyLegacy (master *) % src/cli/engine.py 10 sp limited

sets the speed of the Legacy/TMCC2 engine with TMCC ID#10 to "Limited", which corresponds to[ponds to the absolute speed step of 118. Appropriate speed tables have been implemented for TMCC1 as well (where only speed steps of 0 - 31 are supported)

I've also added a repetition option, which sends the same command multiple times. For example:

davids@Sprucewood-iMac PyLegacy (master *) % src/cli/engine.py 12 -re 10 -boost

sends the "boost" command 12 times to Engine # 10. Why you may ask? Because this is essentially what happens when you press/roll up repeatedly on the multi-controller wheel on the Cab 2. Sending the command multiple times corresponds to holding the "boost" function wheel for a period of time. You could also use the -(re)peat option to ring the bell, say, 5 times or blow the whistle 3 times, etc.

I have been a bit surprised to find that the "Train Brake" functionality actually sends along absolute speed commands as you move the slider control on a Cab 2. In add-on to sending the bytes for the train brake command, the Cab 2 sends multiple decreasing absolute speed commands along as well. It appears that it is these speed commands that actually slow the train down. When you push the slider back up (no brake), a series of increasing speed commands are sent as well. Huh.

Tests are coming along as well. Tests have been added for all of the TMCC1 and TMCC2 3 byte commands. I've done this by entering them in my constants file as hex digits. The test classes use binary representation ("1"'s and "0"'s) taken directly from the documentation and compare values. Not perfect, but it means I had to make the same transcription mistake twice in two different formats.

Here is a summary of the CLI commands that exist today:

  • src/cli/acc.py: issue accessory commands
  • src/cli/engine.py: issue engine/train commands
  • src/cli/halt.py: issue an emergency stop/halt command
  • src/cli/route.py: fire routes
  • src/cli/switch.py: throw switches

The engine, halt, and route commands can issue either TMCC1 or TMCC2 command sequences. Python classes have been developed (and are in /src/protocol) that are used by the CLI and can be incorporated into other python programs to build up more elaborate command flows (start up a train, ramp up speed over time, ring bells and blow horns, fire routes, etc.)

I will next add the remaining sound effects controls that are defined for the TMCC2 multi-word commands and then on to physical buttons.

  -- Dave

I had a really productive day today with the project. Using the gpiozero library by Ben Nuttall and Dave Jones, I connected the command generation and sending code I've developed to physical button presses on a Raspberry Pi (4)! For example, by writing this one line of code:

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

I can fire Route 10 (using  TMCC2/Legacy command syntax) when I press a button connected to my Pi's GPIO pin 21!

The gpiozero library really made the integration easy. For example, with these two lines:

from gpiozero import Button

button = Button(21)

you create a "Button" object tied to GPIO pin 21.

Then, with this line:

button.when_pressed = CommandReq.as_action(TMCC2RouteOption.ROUTE, 10)

you assign a callback function to call whenever the button is pressed.

Here's a picture of the setup:

Raspberry Pi 4

The red wire is attached to one of the Pi's Gnd (ground) pins, and the brown wire is attached to GPIO pin 21. You push the big red button and the byte string 0xFA14FD is sent to my LCS SER2 and route #10 is fired!!

  -- Dave

Attachments

Images (1)
  • Raspberry Pi 4

It is exciting to see your progress.  Thanks for making the project available on github.

For me, I'd love to implement this on a microcontroller running circuit python.  Think of the possibilities if it could also receive and interpret commands on the serial bus.  Very nice to have this as an alternative to the whole LCS partner shebang.

I don't know how many Python fans there are in our community, but it is definitely a +1 from me.

I've been busy the last few days on doing a fairly major restructuring of the code. The end result is that all of the logic to apply address and data values to the TMCC1, TMCC2, and a good portion of the TMCC2 Parameter commands. I've also unit tested all of this code (which isn't as fun as building new features, but, alas, it is critical to producing production-quality software).

The CLI and the classes that back end it (and implement the command syntax to make things happen on a layout) all now use this new code and benefit from the unit testing of the underlying subsystem. These classes are CommandReq and ParameterCommandReq. They can be used directly, are used by the CLI, and support several class methods to issue directives to a layout (via the LCS SER2) without creating intermediate CommandReq objects.

I've also implemented a command buffer which receives byte strings from the CommandReq fire or send methods and queues them for dispatch to the LCS SER2. Per other posts I've read on this forum, output to the SER2 is throttled so no more than one byte sequence is sent every 40 milliseconds (configurable, of course).

For others to be able to use this code, I really need to write more documentation and to use a python package manager to bundle everything up. Doing the packaging is a little new to me, but the gpiozero package I'm using to connect my work to physical button presses gives me some ideas on how to do that. The packaging will probably be my next step.

I've also signed up for the Lionel Partner Program and am looking forward to participating there as well.

  -- Dave

p.s. I do, on occasion run trains too. Below is a picture of the layout I'm building.IMG_6024

Attachments

Images (1)
  • IMG_6024

Just checked in code to issue Legacy/TMCC2 Lighting Effects commands, as documented on page 18 of the Lionel Partner Program documentation. These are part of the 3-word commands, and use 9 bytes to express a command. The Lighting control effects include support for:

  • Dog house lights (on/off)
  • Hazard lights (on/off/auto)
  • Ditch lights (on/on pulse off with horn/off pulse on with horn/off)
  • Loco/engine marker lights (on/off)
  • Tender marker lights (on/off)
  • Ground lights (on/off/auto)
  • Strobe lights (on/double flash/off
  • Mars lights (on/off)
  • Cab lights (on/off/auto)
  • Rule 17 (on/off/auto)
  • Car cabin lights (on/off/auto)

Support includes the cli script lighting.py, and a command class.

I also built a cli that reads requests from the command line and, using the parsers developed for each of the cli commands, parses the input and sends the appropriate command to the LCS SER2. The program continues to process your input until you enter "q" or hit <ctrl-c>.

Here is an example:

(legacyEnv) davids@Sprucewood-iMac PyLegacy (master) % src/cli/control.py 
PyLegacy train controller, Ver 0.1
>> eng 88 speed 50 # sets the absolute speed of engine 88 to 50
>> li 88 ditch -on # turns ditch lights on
>> eng 88 -b -re 4 -de 0.5 # blow horn 4 times w/delay of 0.5 seconds between


You can also get help on command options like so:

>> eng 1 sp -h
usage: control.py Engine/Train speed [-h] [-a | -r]
Engine/Train speed: 0 - 199 (Legacy) or 0 - 31 (TMCC) or roll, restricted,
slow, medium, limited, normal, or highball

positional arguments:
Engine/Train speed: 0 - 199 (Legacy) or 0 - 31 (TMCC) or roll, restricted,
slow, medium, limited, normal, or highball

options:
-h, --help show this help message and exit
-a, --absolute Set absolute speed
-r, --relative Set relative speed speed (-5 to 5)
>>


The Effects controls are next up (smoke on/off, stock car options, pantograph control, etc).

Enjoy!

  -- Dave

Last edited by cdswindell

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

Here is an example screen capture of a client control program sending commands to a server. Again, the server has the serial connection to an LCS SER2. The server is in the top window and the client is in the bottom window:

In the top window, the control program is launched in the normal way:

(legacyEnv) davids@Sprucewood-iMac PyLegacy (master) % src/cli/control.py


In the bottom window, the control program is launched with the special argument -server, which tells it to use client/server mode and send all commands to the program on the server 'localhost':

% src/cli/control.py -server localhost


In practice, you would specify the IP address of your server on your local network, like so:

% src/cli/control.py -server 192.168.3.35


Here, the IP Address 192.168.3.35 is the address of my development machine, an older iMac (from 2021).

This is all checked in, although I still haven't done any packaging nor distributed the code on PyPi. The GitHub sources are here: https://github.com/cdswindell/PyLegacy.

  -- Dave

Attachments

Images (2)
  • mceclip0
  • mceclip1
Last edited by cdswindell

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.

Last edited by BlueFeather
@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.

Thanks!! I haven’t taken on reading serial input yet. I just got the documentation from the Lionel partners program yesterday. I definitely want to be able to read some serial commands, as I wanna be able to set the state of things when my programs power on, and the only way to do that is to read state  via the Ser2.

I hadn’t thought about key emulation, but you could very easily do that by wiring your own buttons to a raspberry pi!!

  — Dave

@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 about 25% of the Rail Sounds Dialog controls. This one was fun, as I can now trigger engineer to tower and tower to engineer dialogs. Combined with the speed commands, this means one can now trigger "ok to change speed" "yes, change speed", change the speed, and then ack back to the tower the speed change!

  -- 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.
×
×