Header "IR_Single_Track_Class.h"
Constructor: IR_Single_Track_Class(int sensor,int red,int amber,int green);
Public methods: void begin(int polarity); - polarity of signals
int my_track(int my_sensor, int your_sensor);
March 2021: This project replaces both the IR_Single_Track and IR_Single_Track_Dual pages. The program as given will duplicate the IR_Single_Track_Dual program but can be used with a layout that only needs to monitor/control one single track. The general layout is illustrated below:
As illustrated the project will simultaneously monitor/control two sections of single track. If developers only wish to monitor/control one section of single track they can omit one set of hardware and either omit some lines of code in the *.ino code or leave the code running in the background generating signals that go no-where. The performance and memory overhead of leaving the unwanted code is minimal. However if the user needs the i/o lines for other tasks then the *.ino file will need to be stripped of those operations impacting the second single track.
Abstract: This page uses IR sensors to monitor and control trains (ie stop) from entering an occupied block of track. The design independently handles either ONE or TWO blocks of single track.
Potential Audience: Model train enthusiasts wishing to control a single block of track. Some programming using Cpp is required.
Required Components: The test circuit used an Arduino NANO although any microcontroller with 12 output and four input pins could be used. To control two blocks of track four IR sensors (See IR sensors page) and four sets of RED-AMBER-GREEN negative polarity signals. Depending on the size of the layout several metres of cable (CAT 5 cable with 4 twisted pairs was available so was used in the prototype). The prototype used 8 each of male-female DB9 connectors. The prototype was powered from the model railway tracks - this required a bridge rectifier, an 8V voltage regulator and several 25ufd capacitors and diodes. (See Power Page). Initially the prototype soldered wires directly onto the prototyping board**. If screw terminals are used 13 will be required. (Two for each signal, four shared for the four sensors and one for power from track).
Keywords: Arduino NANO, IR Sersor, State Machine, Cpp Class/Library, Cpp Inheritance, Non-blocking functions
Required Libraries: IR_Sensor_Class, Signals3_Class
** At a later date a special purpose PCB was developed. (See PCB page)
--------------------
The IR_Single_Track_Class project involves placing three aspect signals (RED-AMBER_GREEN) at both ends of two sets of a single track. The layout for one single track is illustrated below.
This project was retrofitted to an existing layout. This added a number of complications. The IR emitter and detector were removed from their circuit board and extension wiring added which had to be threaded through the baseboard. The signal cabling had to be also extended. These 8 wires were terminated in a small container that contained the original IR curcuit board and a DB9 socket. For ease of soldering etc the wiring needed to be long enough to clear the base board. Trying to wire components under the baseboard was something to be avoided.
The photo shows the signal at one end of the single track.
On the tracks just below the signals are the emitter and detector to detect the presence of a train.
These components were originally part of the IR sensor board . Photo shows a detector before the emitter and detector were removed.
Along with the wires from the signals the sensor wires were threaded under the tracks and the emitter and detector were brought out to a junction box supplied with a DB9 connector for wiring back to the microcontroller.
The first photo shows the cable extensions going out to the left. The wires from the IR-emitter and detector are resoldered to the IR-Circuit board. The three pins (Vcc,Gnd and Out) are soldered directly to the centre three pins of the five on the DB9 connector. The signals are wired to the four pins of the DB9**.
The junction boxes were wired back to the Arduino NANO controller - everything was mounted using velcro under the railway base board. .L405... The photo shows a special PCB for the controller components. In this case a single track is being controlled using objects Entry2 and Entry3 that interface via the two DB9 connectors.++
Since CAT5 cable was available this was used for the main cabling.
** The two outer pins of the five are wired to the track and are used to generate the voltages for the design. Black component is the bridge rectifier feeding the filter capacitor. The 8V regulator is to the left.
++ The PCB was designed for two projects - those unpopulated components are for the second project
----------------------------
All the wiring was relatively fragile so a small test board was used to monitor the IR sensor and control the signals. Photo shows board with 5V power pack. The DIP switches will control the signals while the blue LED will indicate a train_over_sensor.
Further since the code development was undertaken away from the final layout a prototype board was used for testing purposes. With the prototype a hand could be placed across sensor A followed by sensor B. There was no need to walk backwards and forwards between the two sensors on the actual layout.
------------------
The program will use the libraries Signals3_Class and IR_Sensor_Class. If readers do not have these libraries the safest thing to do is to regenerate them. See Appendix.
The Signals3_Class is for 3 aspect signals (ie RED, AMBER,GREEN). The public method signal_control(dsp) will turn the signal to RED when dsp ==1 and when dsp ==0 the signal will remain as RED for 5 seconds, turn AMBER for 5 seconds then GREEN.**
The Signals3_Class can be used with either positive or negative polarity signals The example will define that negative signals are used.
The IR_Sensor_Class returns an integer "train_over_sensor( )" when a train is present.++
** The Signals3_Class may be overloaded to give other timings. Further the AMBER signal may be generated using an external gate. This example will use the default times and assume the AMBER signal is generated by the code.
++ The IR_Sensor_Class contains a one-second filter to eliminate false exits for train couplings above the sensor. The example will use the default value but if needed this filter may be modified.
--------------------------------
Once the wirings have been deternined these should be specified in the class constructors. For my example for two single tracks this gives** ^^:
IR_Single_Track_Class Entry1 (A3,4,3,2);IR_Single_Track_Class Entry2 (A2,7,6,5);IR_Single_Track_Class Entry3 (A1,10,9,8);IR_Single_Track_Class Entry4 (A0,13,12,11);For each object/instance the first argument is the sensor wiring++ while the next three are the signals RED-AMBER-GREEN
**If desired only two objects (eg Entry1 and Entry2) need be created for a single track.
^^ Defining which entry point corresponds to which track is not required at this stage.
++ Pins A0 -A3 are strictly digital pins but with the analog capability they are given the analog label. However they can be programmed exactly the same as the digital pins.
---------------------------
The header file will define a class IR_Single_Track_Class that inherits the two classes IR_Sensor_Class and Signals3_Class. The IR_Single_Track_Class has four arguments: int sensor,int red,int amber,int green.
At this point the *.cpp or implementation file will direct the arguments to the inherited classes. Ie
The *.ino or application file will need to create objects of the class IR_Single_Track_Class**.
The arguments for each object will need to match the wiring.
** For only one single track only two entry point objects are required.
-----------------------------------------------------------
The set up will use the begin() method. This will need to be included as public in the header file. ie
........class IR_Single_Track_Class : public IR_Sensor_Class, public Signals3_Class {public: IR_Single_Track_Class(int sensor,int red,int amber,int green);void begin(int polarity);................The implementation of the begin( ) method in the *.cpp file will invoke the inherited class Signals3_Class with the default arguments RED,AMBER and GREEN.
The begin( ) has the argument (polarity) that will inform the Signals3_Class of the polarity of the signals.
The *.ino code will include the definition of the signal polarity.
--------------------------------
At startup all signals will be set to RED** using the signal_control( ) method from the inherited class Signals3_Class..
Further it will be assumed that all single tracks are initially empty (vacant). This will imply a state machine with three states:
The state machine is described by the enumerated type track_activity. With two single tracks two state variables will be required as illustrated.
The initialisation code is added to the setup() method.
** It is anticipated that the main program will set the signals to GREEN. Using the method signal_control(0) the signals will remain RED for 5 seconds, go AMBER for 5 seconds before going GREEN.
---------------------------------
For each single track there are 3 possible states:
With the vacant state the code will wait until a train from either direction is detected.
With either train1 or train2 the signal at the other end will be set RED while the train that entered the single track will control the operation of the signals at the arrival/entry point and then the exit signals when it departs.
The proposed high level code (in the *.ino file) will be:
track_activity one_track(IR_Single_Track_Class &test_pt1, IR_Single_Track_Class &test_pt2,track_activity track_status){As given on each loop the code will invoke the one_track( ) method**. The arguments are the objects that define each end of the single track plus the current track_activity. one_track( ) will return the new track_activity given by the variables track_status1 or track_status2.
Each object has an associated method my_track( ). When that object is the train in the single track the method my_track( ) will be called++.
**In the example code Entry1 defines one entry to a single track - Entry2 defines the other entry. The second single track is defined by Entry3 at one end and Entry4 at the other.
++ As given if a train enters at Entry1 (At line "case train1") the signals at Entry2 (the exit for train1) are forced to RED (test_pt2.signal_control(1)). If some more fancy signaling is required a method such as "your_track( )" could be included.
---------------------------------------------------------------------
The method my_track(sen1,sen2) tests the two track sensors to control the signals initially at the entry end of the single track. The first argument is the sensor at the entry point while the second is the sensor at the exit.
1. Train detected by sensor 1 invoking routine my_track( ). Signals to remain GREEN while train_over_sensor( ). (Recall at the exit the signals have been set RED)
2. Train passes over the entry sensor. Entry signals turn RED when train has completely passed.
3. Train reaches exit sensor (given by second argument). Signals remain RED.
4. Train completely passes exit sensor signals go through RED-AMBER-GREEN sequence.
The header file
The implementation file
Note: The code as given is NON_BLOCKING. It will test if the train is over a sensor or not and exit the code immediately. The code will not wait for the train to reach (or pass) the sensor. It tests the sensor - sets the next state if necessary and immediately exits back to the one_track( ) method. Similarly the one_track( ) method does not wait for anything to happen, it just reads the status and returns. In the main program loop the one_track( ) method is called for both tracks. Calling the one_track( ) method for one single track will not block the second one_track( ) method.
-------------------------------
If there was ever false triggering, for example an object was waved over the sensors the design could move to the "in_transit" state with signals at both ends of the track RED**. To reset the system a timer was added to the "in_transit" state. This is shown in the above code. If ever the system was in this state for 5 minutes it was reset back to the waiting state.
Possible Start-up Error: Design locks in "RED" state. One cable is not connected or not connected correctly. For example with the code given if the Cable Entry2 is not connected or incorrectly connected the design will freeze in the RED state. If Entry3 and Entry4 are wired corectly these will move to the GREEN state whereas Entry1/2 remain in the RED state.
** Using a flash camera near the sensors also caused false triggering.
----------------------------------
If readers do not have these libraries available the safest thing to do is to regenerate them.
1. In the Arduino create a new file and save as Signals3_Class.
2. From the page Signals3_Class_code load the *.ino file.
3 Use the inverted triangle to create a new tab - give it the label Signals3_Class.cpp and copy/paste the Signals3-Class.cpp code.
4. Repeat for the header file Signals3_Class.h
5. Compile/verify the code - remove any embedded characters that might have been included in the copy/paste.
6. The source code willl be in the folder Signals3_Class under the work or projects directory - In the Arduino see files -->preferences. Use Windows explorer to create a *.zip file of the *.cpp and *.h files. The next file will have the label Signals3_Class.zip
7. In the Arduino select sketch-->include library -->add *.zip library and select Signals3_Class.zip.
8. Repeat for IR_Sensor_Class
--------------------------------