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.com/...onnection-to-the-tiu
https://ogrforum.com/...onnection-to-the-tiu
https://ogrforum.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: