Skip to main content

For the past 4-5 years, I've been working on a method for controlling my MTH trains using a computer. I wrote a program titled Remote Train Control or RTC. I first described it in these posts:

https://ogrforum.ogaugerr.com/...onnection-to-the-tiu

https://ogrforum.ogaugerr.com/...onnection-to-the-tiu

https://ogrforum.ogaugerr.com/...omatic-train-running

My goal was to be able to control my layout automatically using a computer. The last post describes a train detection system using RFID tags attached under engines (and cars). Once I had that working, I wrote a program (in C++) which would run three trains around my layout automatically starting, stopping at stations, playing PFA's, blowing their horns and ringing their bells. The program kept close tabs on each engine assuring that even if one ran faster than the others, it would not run into the back of the engine ahead of it. It did all of this automatically. I used it during a recent open house were the computer ran the layout for 4 hours without any train detection/control failures ( I did suffer one derailment).

As my friend, Mike Hewitt (skylar), pointed out - it was a interesting system but it required you to code in C++ and you had to have all of the necessary compilers and programming libraries loaded on your PC.

Mike got me looking around for something called a Scripting Language. I found one and integrated it into RTC. It is called Lua. Lua programs are simple, English like, and can be created/editing using any text editor.

I'll give a brief description here but you can find all of the details and download the beta version of RTC v3.99.2 which supports this scripting language at:   http://www.silogic.com/trains/...ontrol_Language.html

Non-RFID Tag Mode

Here is a script. The RTC program calls three functions which must appear in each script: setup() - called once at the beginning of the run, main() - called once after setup completes and cleanup() - called when the user terminates the script.

Here, setup() turns off the smoke. Then main() turns the whistle on and, 3 seconds later, turns it off. Then, at termination, cleanup() does nothing (pretty simple).

--[[---------------------------------------------------------------------------------------]]
-- two dashes mark the beginning of comment which goes to the end of the line
ON = true;
OFF = false;
NOW = 0 -- Seconds in the future to send the command, values 0 to 9,999 seconds
-- NOW is automatically set to zero at the start of setup(), main() and cleanup()
--[[---------------------------------------------------------------------------------------]]
function setup(Engine, TIU, debug)
-- this function is called once when the [Run Script] button is pushed
Smoke(OFF, NOW, Engine, TIU);
return true;
end
--[[---------------------------------------------------------------------------------------]]
function main(Engine, TIU, debug)
-- this function is called once after setup() completes
Whistle(ON, NOW, Engine, TIU);
Whistle(OFF, NOW+3, Engine, TIU);
return true;
end
--[[---------------------------------------------------------------------------------------]]
function cleanup()
-- this function is called once when the user presses the [STOP] button
return true;
end
--[[---------------------------------------------------------------------------------------]]

 

Its a really simple example. In a more real life script, you would have to startup and shutdown engines, start them moving with a speed command, throw switches and activate accessories. On my web page you can see more complicated examples.

Here is a short list of the commands you can execute from the script by calling a function. The parameters to each function call can be:

  • when is the time in seconds in the future to send the command. It can be 0 to 9,999 seconds.
  • EngineNo is the destination engine number.
  • TIUNo is the destination TIU number.
  • Setting() sends the "ab" command with value iSet (Look at the commands here).
  • Schedule() sends any Command passed to it. For example "u2", "w4", "s010", etc
  • bState and bDIR and bCoupler use the definitions in the "defines.rcl" file. Typically words like ON or OFF, STRAIGHT or DIVERGE, FORWARD or REVERSE, FRONT or REAR.
  • Chan is one of the five sound channels   MASTER_VOLUME,  ENGINE_VOLUME, ACCENT_VOLUME, HORN_VOLUME, BELL_VOLUME
  • Level is the volume level 0 - 100
  • iACC and iDec is the acceleration/deacceleration rate 1-25 Smph/sec
  • iRate is the number of chuffs per revolution - 2 or 4 are the only valid values
  • iSoundNo is the index into the sound file of the sound to play. See the page on the ADPCM program.
  • iMPH is the speed 0-120 Smph
Setting(iSet, when, EngineNo, TIUNo);
SetVolume(Chan, Level, when, EngineNo, TIUNo);
Schedule(Command, when, Command, EngineNo, TIUNo);
ShutDown(when, EngineNo, TIUNo);
-- simple shutting down of an engine.
ShutDown(when, LashUpNo, TIUNo);

-- simple shutting down of a Lashup.
StartUp(when, EngineNo, TIUNo);
-- simple starting up of an engine.
StartUp(when, LashUpNo, TIUNo, LashUpEngineList);
-- simple starting up of a Lashup.
Accessory(bState, iAIUNo, iChan, when, EngineNo, TIUNo);

Switch(bState, iAIUNo, iChan, when, EngineNo, TIUNo);
Whistle(bState, when, EngineNo, TIUNo);
Bell(bState, when, EngineNo, TIUNo);
Markers(bState, when, EngineNo, TIUNo);
Beacon(bState, when, EngineNo, TIUNo);
CabChat(bState, when, EngineNo, TIUNo);
Smoke(bState, when, EngineNo, TIUNo);
ChuffRate(iRate, when, EngineNo, TIUNo);
Rate(iAcc, iDec, when, EngineNo, TIUNo);
OpenCoupler(bCoupler, when, EngineNo, TIUNo);
SetDirection(bDIR, when, EngineNo, TIUNo);
Throttle(iMPH, when, EngineNo, TIUNo);
-- sends only a speed command
SetSpeed(iMPH, when, EngineNo, TIUNo);
-- changes the speed with all of the appropriate
-- bells and whistles (like SFS and SRS)
PlaySound(iSoundNo, when, EngineNo, TIUNo);

Sound(bState, when, EngineNo, TIUNo);
ProtoWhistle(bState, when, EngineNo, TIUNo);
SetQuill(iTone, when, EngineNo, TIUNo);
-- sets the quilling whistle tone, values 0-3
-- Turn on Proto Whistle first, engine must support this feature
SmokeWhistle
(bState, when, EngineNo, TIUNo);
--
engine must support this feature
SwingingBell(bState, when, EngineNo, TIUNo);
--
engine must support this feature

 

RFID Tag Mode

This is the mode that ran my layout during my open house. I installed four tag readers under the track. Each time that a tag was detected, a function in the script was called. The main() function is replaced by the loop() function. Here is a script which blows the horn each time that a tag located on the rear truck of an engine is detected. This is just a simple example, not one that I actually used.

--[[---------------------------------------------------------------------------------------]]
ON = true;
OFF = false;
FORWARD = true; -- Engine = direction
REVERSE = false;
STRAIGHT = true; -- AIU = Switch = State
DIVERGE = false;
NOW = 0 -- Seconds in the future to send the command, values 0 to 9,999 seconds
-- NOW is automatically set to zero at the start of setup(), main() and cleanup()
--[[---------------------------------------------------------------------------------------]]
function setup(Engine, TIU, debug)
-- this function is called once when the [Run Script] button is pushed
MyEngineNo = Engine; -- Engine number selected on the RTC Main window
MyTIUNo = TIU; -- TIU number selected on the RTC Main window
MyDebug = debug; -- debug level, values 0 (off) to 9 (maximum)
StartUp(NOW, MyEngineNo, MyTIUNo);
Smoke(OFF, NOW, MyEngineNo, MyTIUNo);
Markers(ON, NOW + 15, iEng, MyTIUNo); -- Set Marker lights on
-- switches and accessories
Switch(STRAIGHT, 1, 2, NOW + 3, EngineNo, MyTIUNo); -- College East
Switch(STRAIGHT, 1, 3, NOW + 5, EngineNo, MyTIUNo); -- College West
-- Start train moving 20 seconds from NOW
SetSpeed(12, NOW + 20, MyEngineNo, MyTIUNo);
return true;
end
--[[---------------------------------------------------------------------------------------]]
function loop(Detector, Reader, EngineNo, TagLocation, CarType, TagPacket)
-- this function is called each time a tag is detected
if (MyDebug > 4) then PrintString(string.format("Packet %s", TagPacket)); end
-- single toot on engine rear truck detection from all detectors and readers
if (isEngine(EngineNo) and isTagRear(TagLocation)) then
PlaySound(41, NOW, EngineNo, MyTIUNo); -- Short Toot
end
return true;
end
--[[---------------------------------------------------------------------------------------]]
function cleanup()
-- this function is called once when the user presses the [STOP] button
ShutDown(NOW, MyEngineNo, MyTIUNo);
return true;
end
--[[---------------------------------------------------------------------------------------]]

 

The Lua scripting language is full featured and with it, you can write very complex scripts. On the web page, you can see the script that I wrote to run my layout automatically.

This page describes the RTC program:

http://www.silogic.com/trains/RTC_Running.html

This page describes the radio used to communicate with the TIU:

http://www.silogic.com/trains/OOK_Radio_Support.html

This page describes the hardware required for the RFID tag detectors:

http://www.silogic.com/trains/RFID.html

Original Post

Replies sorted oldest to newest

Severn posted:

You need some clock concepts -- absolute time, relative etc...

Plus not to be negative but I think it might be good to have an even more relaxed syntax

The most difficult part of what I did was actually what I didn't have to do.

The person who wrote the Lua parser did the hardest part. He defined the language syntax that his parser could understand so I had to build my program to use his syntax. That saved me having to get a PhD to write my own parser.

Last edited by SanDiegoMark

As I posted on this thread:

https://ogrforum.ogaugerr.com/...onnection-to-the-tiu

I've been able to figure out how to access the RAM memory in the engine and what some of those memory locations mean.

Some words on my web page and a link to the current version of a document that I'm writing.

http://www.silogic.com/trains/RTC_Running.html#RAM

I've added several "Get" functions to my scripting language. Now, from the lua script, you can command the engine to go to a particular speed and then check that it has reached that speed.

I added:

 GetDirection(Engine, TIU);    -- return the direction as an boolean,
-- true =
FORWARD, false = REVERSE

GetVolume(Chan, EngineNo, TIUNo);
-- returns the volume level (0-100) of the channel given by Chan.


GetSpeed(Engine, TIU); -- return the set speed as an integer,
-- engine may be accelerating or decelerating to reach this speed


GetInstSpeed(Engine, TIU); -- return the instantaneous speed of the engine

RAM(address, Engine, TIU); -- returns the byte at the RAM address given as address.

I've created a new Playlist on YouTube with a dozen videos demonstrating version 4.0 of the RTC program. This version is a complete rewrite of the radio communications code. V4.0 also has a completely new scripting feature that lets you write a program-like script to control your trains. The scripting feature works both with and without my RFID train detection scheme. Look at my web pages for more details.

Link to version 4.0 of the RTC Program:

http://www.silogic.com/trains/...ng.html#RTC_Download

YouTube Playlist link:

https://www.youtube.com/watch?...NRe0k5zf-_b0hl6U_hpO

Video Episodes:
0 - Intro
1 - Layout Tour
2 - Radio Hardware
3 - Program Setup
4 - Operations Popup Menu
  - Scene 1
  - Scene 2
5 - Multiple Operations Windows
6 - Sound Controls
7 - Quick Controls
8 - Hot Buttons
9 - LashUps (Consisting)
10 - AIU Control
11 - Routes & Scenes
12 - DCS Setup
13 - Recording and Playback
14 - Program Scripts

Add Reply

Post
The DCS Forum is sponsored by

OGR Publishing, Inc., 1310 Eastside Centre Ct, Suite 6, Mountain Home, AR 72653
800-980-OGRR (6477)
www.ogaugerr.com

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