Mark DiVecchio's O-Gauge Train Layouts

1992 Layout Page

2006 Layout Page

2009 Layout Page

P&LE Postcards by Howard Fogg 

Plasticville Buildings

Portable Layout Page

Train Clubs

Bing Track

Remote Train Control Program

YouTube Channel

OOK Radio Support

Technical Videos

3D Prints for my Layout

More RTC Videos

ADPCM - Playing clips from .mth sound files

P&LE McKees Rocks Locomotive Shop
3D Printer Project

White Tower Restaurant
3D Printer Project

RFID Train Detection

Engine and Car Operation
Hints and Tricks

RFID Tag Programmer using PN532

RTC Control Language - Scripting

More RFID Tag Videos


RFID on the Layout

This Page last updated on .

I spent time during our COVID-19 self isolation installing the Arduinos and Tag Readers on my layout. With those pieces in place, I can start to do some operational testing. This page first went on-line 11 Aug 2020.

The videos on this page describe what I did and show the tag detection system in operation with my RTC program. In some of these videos, you will see a "Detected Tags Window" which shows all of the tags detected (past and present). Originally, this window displayed whenever a Program Control window was opened. I found this unnecessary during normal operation. Now if you want to see the "Detected Tags Window", you can open it by right clicking the [Prog Ctrl] button and selecting that window from the popup menu.

You can also view these videos in possibly higher resolution from this playlist on YouTube.



Video #1 - Tag Reader Hardware


Here is a video #1 recorded on 10 August 2020. I show the Arduino and PN532 tag reader hardware.

Image loading....

Video #1 - Tag Reader Hardware

Video #2 -  Tag Mounting

Here is video #2 recorded on 10 August 2020. The small tags are fairly easy to attach to the engine and car trucks.

Image loading....

Video #2 - Tag Mounting 



Video #3 - Tag Programming

Here is video #3 recorded on 10 August 2020. This program is used to write 32 bytes of data into each tag.

Image loading....

Video #3 - Tag Programming



Video #4 - Simple Tag Detection Script


Here is video #4 recorded on 10 August 2020. This simple script, written in Lua, uses RTC's program control feature to display information about the engine/car as the tag goes over the undertrack tag reader.

Image loading....

Video #4 - Simple Tag Detection Script

Here is the Lua code for the Simple Tag Detection Script


Here is a example listing of a simple script SimpleTagDetectionTest.lua
(this may not be as up to date as the zip file available on the main RFID page):

--
title = "Tag Detection Simple Test"
--[[---------------------------------------------------------------------------------------]]
function setup(Engine, TIU)
-- this function is called once when the [Run Script] button is pushed
-- Engine : Engine number selected on the RTC Main window
-- TIU : TIU number selected on the RTC Main window
print(string.format("setup() RunTime = %.2f seconds", RunTime()));
return true; -- false=setup failed, true=setup succeeded
end
--[[---------------------------------------------------------------------------------------]]
function tag(Detector, Reader, EngineNo, TagLocation, CarModel, Railroad, CarNumber, TagPacket)
-- This function is called each time a tag programmed with 32 bytes of data is detected (TAGDATABLOCK4)
-- TagPacket contains 8 characters (4 hex digits) of UID and 32 characters
-- of block 4 data from the programmed tag.
if (EngineNo > 0) then -- its an engine
print(string.format("tag(%.2f) : Detector %d Reader %d Eng#%d %s %s #%s %s",
RunTime(), Detector, Reader, EngineNo, GetRailroadName(Railroad), GetEngineName(CarModel), CarNumber, GetTagLocation(TagLocation)));
else -- it's a car
print(string.format("tag(%.2f) : Detector %d Reader %d Car %s %s #%s",
RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));
end
return true; -- true=continue to process tags, false=stop processing tags
end
--[[---------------------------------------------------------------------------------------]]
function cleanup(Engine, TIU)
-- this function is called once when the user presses the [STOP] button
print(string.format("cleanup() Complete at %.2f", RunTime()));
return true; -- false=cleanup failed, true=cleanup succeeded
end
--[[---------------------------------------------------------------------------------------]]





Video #5 - Tags and Train Control


Here is video #5 recorded on 10 August 2020. This demonstrates a Lua script that controls the engine based on detecting its tags.

Image loading....

Video #5 - Tags and Train Control
Here is the Lua code for the Sidings Test Script


Here is a example listing of a simple script Sidings Test - V3.lua
(this may not be as up to date as the zip file available on the main RFID page):

--[=[
---------------------------------------------------------------------------
Remote Train Control Program for Windows

© Copyright 2020 by Mark DiVecchio

This file is part of Remote Train Control.

Remote Train Control is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Remote Train Control is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Remote Train Control. If not, see <http://www.gnu.org/licenses/>.

Home Page : http://www.silogic.com/trains/RTC_Running.html
---------------------------------------------------------------------------]=]
-- Semicolons are not required in Lua code
title = "Sidings Test"
require([[defines]]);
require([[functions]]);

local SD = require([[Distances]]); -- Measured stopping and starting distances in inches

-- Counter Labels
Counter01Label = "Engine Starts";
C1 = COUNTER01;
Counter02Label = "State";
C2 = COUNTER02;
Counter03Label = "#Tags";
C3 = COUNTER03;
Counter04Label = "Target Distance";
C4 = COUNTER04;
Counter05Label = "C5";
C5 = COUNTER05;
Counter06Label = "C6";
C6 = COUNTER06;
-- table to hold all of the tags detected in sequence
Tags = {};
TagCount = 0;
-- starting time
DTOStart = 0.0;
--
-- Siding entrance test
--
local STOPTIME = 15;
local STARTTIME = 10;
BeepOn = false;
SoundOff = false;
SPEEDINCREMENT = 10;

-- state machine states
IDLE = 10;
DONE = 11;
S0 = 0;
S1 = 1;
S2 = 2;
S3 = 3;
S4 = 4;
S5 = 5;
S6 = 6;
-- starting state
SM = IDLE;
StartRun = false;
BASESPEED = 20;
Direction = "WB"; -- "WB" for all of these sidings

-- Switches
local COLLEGE_WEST_AIU = 1;
local COLLEGE_WEST_CHAN = 1;
local COLLEGE_EAST_AIU = 1;
local COLLEGE_EAST_CHAN = 2;

local STRUTHERS_WEST_AIU = 2;
local STRUTHERS_WEST_CHAN = 5;
local STRUTHERS_EAST_AIU = 2;
local STRUTHERS_EAST_CHAN = 6;

local ALIQUIPPA_WEST_AIU = 1;
local ALIQUIPPA_WEST_CHAN = 9;
local ALIQUIPPA_EAST_AIU = 1;
local ALIQUIPPA_EAST_CHAN = 5;

--[[---------------------------------------------------------------------------------------]]
function setup(Engine, TIU)
print(os.date("setup() %d %b %Y %X"));
--
MyEngineNo = Engine;
MyTIUNo = TIU;

SetLED(1, clRed, "LED1");
SetLED(2, clRed, "LED2");
SetLED(3, clRed, "LED3");
SetLED(4, clRed, "LED4");
--
print("setup() : TIU#" .. MyTIUNo .. " Engine#" .. MyEngineNo .. " Debug " .. Debug());
print(string.format("Run Time = %.2f Seconds", RunTime()));
Name, PS = GetName(MyEngineNo);
print("Engine#" .. MyEngineNo .. " " .. Name .. " PS" .. PS);
Title(title .. " - " .. GetName(Engine));

Smoke(OFF, 0, MyEngineNo, MyTIUNo);
StartUp(0, MyEngineNo, MyTIUNo); -- start selected engine

-- aim for the first reader
Target_Station = "College";
Target_Detector = SD.STOP(Target_Station, Direction, "Detector");
Target_Reader = SD.STOP(Target_Station, Direction, "Reader");
if (MyEngineNo == 7 or MyEngineNo == 13) then
Target_Distance = SD.STOP(Target_Station, Direction, "Passenger"); --Passenger StoppingDistance;
else
Target_Distance = SD.STOP(Target_Station, Direction, "Freight"); --Freight StoppingDistance;
end
Direction = "WB";
SetCounter(C4, Target_Distance);

-- let engine startup
Sleep(15);
SM = IDLE;
SetCounter(C2, IDLE);
print(string.format("Run Time = %.2f Seconds", RunTime()));
DTOStart = DTO(Engine, MyTIUNo);
print(string.format("Trip Odometer Start %.2f Smiles", DTOStart));
print(string.format("Run Time = %.2f Seconds", RunTime()));
-- set acceleration and decelleration values
Decell = 1;
Rate(1, Decell, 0, MyEngineNo, MyTIUNo);
print("setup : Set decelleration rate to " .. Decell);
StopForwardScheduled = false;
StopReverseScheduled = false;
print("Please select a target station by pressing a button..........");
return true;
end
--[[---------------------------------------------------------------------------------------]]

function tag(Detector, Reader, EngineNo, TagLocation, CarModel, Railroad, CarNumber, TagPacket)
-- this function is called each time a tag is detected
if (EngineNo ~= MyEngineNo) then return true; end;
if (Debug() and BeepOn) then Beep(); end; -- sound a tone
-- Line below shows a different way to quote a string using brackets
if (EngineNo > 0) then -- it's an engine
print(string.format([[tag(%.2f) : Detector %d Reader %d Eng#%d %s %s #%s %s]],
RunTime(), Detector, Reader, EngineNo, GetRailroadName(Railroad), GetEngineName(CarModel), CarNumber, GetTagLocation(TagLocation)));
else -- it's a car
print(string.format([[tag(%.2f) : Detector %d Reader %d Car %s %s #%s]],
RunTime(), Detector, Reader, GetRailroadName(Railroad), GetCarName(CarModel), CarNumber));
end

-- The complete packet received is in TagPacket
-- (some of the most used fields have already been extracted into the first parameters to tag()
-- First 8 digits are the 4 byte Tag UID in hexidecimal
-- Next 32 digits are the 16 bytes of Block 4 Information read from the Tag in hexidecimal
-- Final 4 digits are the checksum and End of Packet character
if (Debug() > 3) then print(string.format("Packet %s", string.sub(TagPacket, 1, 40))); end
-- index the table Tags[] by tag ID, value is the tag packet itself
Tags[string.sub(TagPacket, 1, 8)] = string.sub(TagPacket, 9, 40); -- remove checksum and EOP
TagCount = TagCount + 1; -- count the number of tags
BumpCounter(C3); -- display the number of tags

--[[---------------------------------------------------------------------------------------]]
-- STATE MACHINE
--[[---------------------------------------------------------------------------------------]]
if (SM == IDLE) then -- do nothing
Sleep(1);
return true; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == DONE) then -- finish up
SetCounter(C2, IDLE);
SM = IDLE; -- go idle
print("Almost Done...");
Stop("DONE");
return false; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == S0) then
-- run forward until the engine tag is detected over the Detector/Reader
if (isEngine(EngineNo) and isDetector(Detector, Target_Detector) and isReader(Reader, Target_Reader)) then
if (isTagFront(TagLocation)) then
-- set stopping time delay to calculated value
if (GetDirection(EngineNo, MyTIUNo) == FORWARD) then
--print("S0 : Tag Detected in Forward");

-- stop the engine
-- time(seconds) = d(in) * 48 Smiles/mile * 3600 sec/hr * / (12 in/ft * 5280 ft/mile * X Smiles/hr)
StopDelay = (Target_Distance - SD.Stopping(EngineNo, EngSpeed)) * (48 / (12 * 5280 * EngSpeed)) * 3600;
print(string.format("Target Distance = %d in - Stopping Distance = %.3f in - Speed %d Smph = Stop Delay %.3f seconds",
Target_Distance, SD.Stopping(EngineNo, EngSpeed), EngSpeed, StopDelay));
EngSpeed = 0;
print("S0 : Set speed to " .. EngSpeed .. " after " .. StopDelay .. " seconds")
Throttle(EngSpeed, StopDelay, EngineNo, MyTIUNo); -- set Speed to EngSpeed
StopForwardScheduled = true;

local StartDelay = StopDelay + STOPTIME;
-- set direction to reverse
SetDirection(REVERSE, StartDelay, EngineNo, MyTIUNo);
-- start the engine moving in reverse
EngSpeed = BASESPEED;
StartDelay = StartDelay + 2;
print("S0 : Set speed to " .. EngSpeed .. " after " .. StartDelay .. " seconds")
Throttle(EngSpeed, StartDelay, EngineNo, MyTIUNo); -- set Speed to EngSpeed
BumpCounter(C1); -- bump number of engine starts
-- stay in S0
return true;
end;
end;

if (isTagRear(TagLocation)) then
if (not StopForwardScheduled) then Stop("S0 : Engine Front tag not detected"); end
StopForwardScheduled = false;
SetCounter(C2, S1); -- go to S1
SM = S1
return true;
end
end;
-- Stay in S0
return true; -- false=stop calling loop(), true=continue to call loop()
end;


if (SM == S1) then
-- run reverse until the engine tag is detected over the Detector/Reader
if (isEngine(EngineNo) and isDetector(Detector, Target_Detector) and isReader(Reader, Target_Reader)) then
if (isTagRear(TagLocation)) then
-- set stopping time delay to fixed value
if (GetDirection(EngineNo, MyTIUNo) == REVERSE) then
--print("S1 : Tag Detected in Reverse");

-- stop the engine
EngSpeed = 0;
local StopDelay = 5;
print("S1 : Set speed to " .. EngSpeed .. " after " .. StopDelay .. " seconds");
Throttle(EngSpeed, StopDelay, EngineNo, MyTIUNo); -- set Speed to EngSpeed
StopReverseScheduled = true;

local StartDelay = 15;
-- set direction to forward
SetDirection(FORWARD, StartDelay, EngineNo, MyTIUNo);
-- start the engine moving forward
EngSpeed = BASESPEED;
StartDelay = StartDelay + 2;
print("S1 : Set speed to " .. EngSpeed .. " after " .. StartDelay .. " seconds");
Throttle(EngSpeed, StartDelay, EngineNo, MyTIUNo); -- set Speed to EngSpeed
BumpCounter(C1); -- bump number of engine starts
-- Stay in S1
return true;
end;
end;

if (isTagFront(TagLocation)) then
if (not StopReverseScheduled) then Stop("S1 : Engine Rear tag not detected"); end
StopReverseScheduled = false;
SetCounter(C2, S0);
SM = S0;
return true;
end;
end;
-- Stay in S1
return true; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == S2) then

return true; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == S3) then

return true; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == S4) then

return true; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == S5) then
Sleep(20);
SM = DONE; -- TBD End the script
return true; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == S6) then
Sleep(20);
SM = DONE; -- TBD End the script
return true; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == S7) then
Sleep(20);
SM = DONE; -- TBD End the script
return true; -- false=stop calling loop(), true=continue to call loop()
end;

if (SM == S8) then
Sleep(20);
SM = DONE; -- TBD End the script
return true; -- false=stop calling loop(), true=continue to call loop()
end;
--[[---------------------------------------------------------------------------------------]]
return true; -- true=continue to process tags, false=stop processing tags
end
--[[---------------------------------------------------------------------------------------]]
function cleanup(Engine, TIU)
-- this function is called once when the user presses the [STOP] button
print(os.date("cleanup() %d %b %Y %X"));
print("cleanup() : TIU#" .. TIU .. " Engine#" .. Engine .. " Debug " .. Debug());
StartRun = false;
Throttle(0, 0, Engine, TIU);
DTOFinish = DTO(Engine, TIU);
-- wait for dispatch list to empty
if (CountDispatchList() > 0) then
ClearDispatchList();
end;
print(string.format("Run Time = %.2f seconds", RunTime()));
print(string.format("Trip Odometer Finish %.2f Smiles", DTOFinish));
Sleep(5);
ShutDown(0, Engine, TIU);
Sleep(5);
if (SoundOff) then
SoundOnButton(Engine, TIU)
end;
ShowTags(Engine, TIU); -- dump out the Tags table
return true; -- false=cleanup failed, true=cleanup succeeded
end;
--[[---------------------------------------------------------------------------------------]]
function B1(Eng, TIU)
-- aim for the this reader
Target_Station = "Aliquippa";
Direction = "WB";
Target_Detector = SD.STOP(Target_Station, Direction, "Detector");
Target_Reader = SD.STOP(Target_Station, Direction, "Reader");
if (MyEngineNo == 7 or MyEngineNo == 13) then
Target_Distance = SD.STOP(Target_Station, Direction, "Passenger"); --Passenger StoppingDistance;
else
Target_Distance = SD.STOP(Target_Station, Direction, "Freight"); --Freight StoppingDistance;
end

SetCounter(C4, Target_Distance);
print("Aliquippa Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
Function01Name, Function01Label = B1, "Aliquippa";
--[[---------------------------------------------------------------------------------------]]
function B2(Eng, TIU)
-- aim for the this reader
Target_Station = "College";
Direction = "WB";
Target_Detector = SD.STOP(Target_Station, Direction, "Detector");
Target_Reader = SD.STOP(Target_Station, Direction, "Reader");
if (MyEngineNo == 7 or MyEngineNo == 13) then
Target_Distance = SD.STOP(Target_Station, Direction, "Passenger"); --Passenger StoppingDistance;
else
Target_Distance = SD.STOP(Target_Station, Direction, "Freight"); --Freight StoppingDistance;
end

SetCounter(C4, Target_Distance);
print("College Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
Function02Name, Function02Label = B2, "College";
--[[---------------------------------------------------------------------------------------]]
function B3(Eng, TIU)
-- aim for the this reader
Target_Station = "Struthers";
Direction = "WB";
Target_Detector = SD.STOP(Target_Station, Direction, "Detector");
Target_Reader = SD.STOP(Target_Station, Direction, "Reader");
if (MyEngineNo == 7 or MyEngineNo == 13) then
Target_Distance = SD.STOP(Target_Station, Direction, "Passenger"); --Passenger StoppingDistance;
else
Target_Distance = SD.STOP(Target_Station, Direction, "Freight"); --Freight StoppingDistance;
end

SetCounter(C4, Target_Distance);
print("Struthers Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
Function03Name, Function03Label = B3, "Struthers";
--[[---------------------------------------------------------------------------------------]]
function B4(Eng, TIU)
-- aim for the this reader
Target_Station = "J&L";
Direction = "WB";
Target_Detector = SD.STOP(Target_Station, Direction, "Detector");
Target_Reader = SD.STOP(Target_Station, Direction, "Reader");
if (MyEngineNo == 7 or MyEngineNo == 13) then
Target_Distance = SD.STOP(Target_Station, Direction, "Passenger"); --Passenger StoppingDistance;
else
Target_Distance = SD.STOP(Target_Station, Direction, "Freight"); --Freight StoppingDistance;
end

SetCounter(C4, Target_Distance);
print("J&L Target Distance = " .. Target_Distance);
StartTheEngine();
return true;
end;
Function04Name, Function04Label = B4, "J&&L"; -- Use "&&" to get one "&" in the label text
--[[---------------------------------------------------------------------------------------]]
function StartTheEngine()
if (StartRun) then return true; end; -- engine already running
StartRun = true;
-- start engine
BumpCounter(C1);
EngSpeed = BASESPEED;
print("StartTheEngine : Set speed to " .. EngSpeed);
Throttle(EngSpeed, 0, MyEngineNo, MyTIUNo); -- set Speed to EngSpeed
BumpCounter(C1);
-- check that the engine has reached speed
local StartTime = STARTTIME * 2;
while (GetSpeed(MyEngineNo, MyTIUNo) ~= BASESPEED) do
if (StartTime == 0) then
EmergencyStop("StartTheEngine : Engine#" .. MyEngineNo .. " Did not Start"); -- failure
return false;
end
Sleep(2);
StartTime = StartTime - 2;
end;

SetCounter(C2, S0);
SM = S0; -- go wait for first tag
end;
--[[---------------------------------------------------------------------------------------]]
function IncTarget(Eng, TIU)
Target_Distance = Target_Distance + 1;
print("Target Distance = " .. Target_Distance);
SetCounter(C4, Target_Distance);
return true;
end;
Function06Name, Function06Label = IncTarget, "Target +1";
--[[---------------------------------------------------------------------------------------]]
function DecTarget(Eng, TIU)
Target_Distance = Target_Distance -1;
print("Target Distance = " .. Target_Distance);
SetCounter(C4, Target_Distance);
return true;
end;
Function07Name, Function07Label = DecTarget, "Target -1";
--[[---------------------------------------------------------------------------------------]]
--[[
function B8(Eng, TIU)
Direction = "EB";
return true;
end;
Function08Name, Function08Label = B8, "East Bound";
--]]
--[[---------------------------------------------------------------------------------------]]
function ShowTags(Eng, TIU)
print("Tags detected:");
--if (#Tags == 0) then
-- print ("none");
-- end
for k,v in pairs(Tags) do -- dump out the Tags table
print(k .. " " .. v);
end
return true;
end;
Function09Name, Function09Label = ShowTags, "Show Tags";
--[[---------------------------------------------------------------------------------------]]
function SoundOffButton(Eng, TIU)
print("Sound Off Button()");
S1 = GetVolume(MASTER_VOLUME, Eng, TIU); -- get Volume
print(" Sound Off - Engine " .. Eng .. " Level was " .. S1);
EngineSoundSave = S1;
SoundOff = true;
SetVolume(MASTER_VOLUME, 0, 0, Eng, TIU);
return true;
end;
Function05Name, Function05Label = SoundOffButton, "Sound Off";
--[[---------------------------------------------------------------------------------------]]
function SoundOnButton(Eng, TIU)
print("Sound On Button()");
if (SoundOff) then
S1 = EngineSoundSave;
print(" Sound On - Engine " .. Eng .. " Level restored to " .. S1);
SoundOff = false;
SetVolume(MASTER_VOLUME, S1, 0, Eng, TIU);
end;
return true;
end;
Function10Name, Function10Label = SoundOnButton, "Sound On";
--[[---------------------------------------------------------------------------------------]]





Video #6 - Running 3 Tag Scripts - Screen Capture (Layout view below)


Here is video #6 recorded on 11 August 2020. Here I am running 3 copies of the same script (Sidings Test - V3.lua) using 3 different engines and 3 different sidings. This video shows only the screen capture of the run. Look at video #7 which shows the layout as the script is run. If you can, run both videos at the same time. Sorry for the error message on the screen right at the end of the video - Dirty Track!

Image loading....

Video #6 -  Running 3 Tag Scripts - Screen Capture


Video #7 - Running 3 Tag Scripts - Layout View (Screen Capture above)


Here is video #7 recorded on 11 August 2020. This video is the layout view when running the 3 scripts shown in Video #6. If you can, run both videos at the same time.

Image loading....

Video #7 - Running 3 Tag Scripts - Layout View


Video #8 - TBD


Here is video #8 - not created yet.

Image loading....

Video #8 -  TBD






This site prepared and maintained by Mark DiVecchio

email :  markd@silogic.com

SD&A HOME
 
 Mark's Home Page

The DiVecchio genealogy home page
The Frazzini genealogy home page

This site will be under construction for a while.