Constructor LDR_Ser_Sig3_Ser_Class ( ); -default wiring
LDR_Ser_Sig3_Ser_Class (int ser_in, int load, int clk_in, int ser_out, int clk_out );
Public Methods: begin (int external, int polarity,int no_sigs); signal wiring
signal_control(3,1); Signal and state - inheriated from Signals3_Serial_Class
train_over_sensor(1); Sensor of interest
Required Components: ESP8266, up to 8 LDR sensors 74HC164 Parallel to Serial Convertor, 74HC164 Serial to Parallel Convertor, 74HC02 NOR gate, up to 4 RED-AMBER-GREEN signals or LEDs
Required Libraries: <Signals3_Serial_Class.h> <LDR_Serial_Sensor_Class.h>
Keywords LDR (Light detecting resistor), Cpp inheritance, Cpp overloading, Scope Resolution Operator ::
Abstract: This project combines/inherits the LDR_Serial_Sensor_Class and Signals3_Serial_Class to detect a model train over LDR (Light detecting resistors) sensor and force signals through a RED-AMBER-GREEN sequence. The project uses both serial input and serial output.
-----------------------------------------------------
I have a model train layout that I wish to simulate the "control" of section of a track. Mounted in the track are LDR (Light Detecting Resistors) that detect the presence of a train. The layout also includes signals that I wish to move through a RED-AMBER-GREEN sequence when a train is detected.**
It is anticipated that a future project will transmit the train status via WiFi. For this the ESP8266 chip is chosen. Since the ESP8266 has only limited I/O pins this project will use serial input and output. Hence the project title LDR_Serial_Signal3_Serial_Class where the input is from a LDR via a serial chip and the output to 3 phase signals (Red-Amber-Green) via a serial chip. The project will develop a C++ Class.
The basic circuit is shown below:
When a train covers a LDR sensor the 74HC165 will detect a high voltage++
The micro-controller will sample the sensor voltage and clock them into the micro-controller. If a train is detected the appropriate signal is rippled into the 74HC164 to set the corresponding signal RED. Once the train passes the sensor the signal remains RED for a small delay, it then moves to AMBER also for a short delay and finally to GREEN indicating the track is clear waiting for the next train.
**This is just a "fun" project - it does not attempt to duplicate real life railway practice.
++ I have found the 4K7 resistor ideal on my layout. This may need to be modified for different environments.
----------------------------------------------------
For bench testing two prototyping boards were used.
The first board was for the sensor input and included the 74HC165 parallel to serial convertor along with screw terminals where the LDRs could be mounted.
A second board contained the ESP8266 micro-controller, the 74HC164 serial to parallel convertor, the 74HC02 Nor gate and screw terminals for attaching the signals (Leds).
--------------------------------
The first step is to create three files LDR_Ser_Sig3_Ser_Class with extensions .ino, .cpp and .h
-------------
//LDR_Ser_Sig3_Ser_Class.cpp Implementation file#include "LDR_Ser_Sig3_Ser_Class.h"Notes:
1. The header file includes a conditional if statement to eliminate multiple includes.
2. The header file includes "Arduino.h" for Arduino definitions. This header is included in normal code by the IDE but it is not included with libraries.
3. The implementation (*.cpp) and client/test (*ino) includes the header file
At this point the code will compile but it will not do anything.
-------------------------------------------
The final product will
(i) use the LDR_Serial_Sensor_Class to read the status of the LDR sensors, and
(ii)control the signals using the Signals3_Serial_Class
These two files must be included in the header code. That is
#include <Signals3_Serial_Class.h>
#include <LDR_Serial_Sensor_Class.h>
The new class LDR_Ser_Sig3_Ser_Class that is being developed in this project will inherit these two classes. The definition for the class LDR_Ser_Sig3_Ser_Class will be:
class LDR_Ser_Sig3_Ser_Class :
public LDR_Serial_Sensor_Class,
public Signals3_Serial_Class {
public: LDR_Ser_Sig3_Ser_Class ( );
}; //semi colon important
Note the sample code includes a comment that the semi colon at the end of the definition is important. Without the semi-colon the compiler will flag errors at other points in the code.
---------------------------------------
As given the new class constructor LDR_Ser_Sig3_Ser_Class has no parameters. This assumes that the definition of the wiring is defined somewhere in the code. In this project it be assumed the wiring is as per the circuit diagram given earlier.
To give the end user the option of using different wiring the constructor can be overloaded to include the wiring as parameters. That is
public: LDR_Ser_Sig3_Ser_Class ( );
LDR_Ser_Sig3_Ser_Class (int ser_in, int load, int clk_in, int ser_out, int clk_out );
}; //semi colon important
In this project the pins will be ser_in (D0), load (D5), clk_in (D6), ser_out ( D2) and clk_out (D1).
Creating an instance with the constructor LDR_Ser_Sig3_Ser_Class ( ); or the constructor LDR_Ser_Sig3_Ser_Class (D0,D5,D6,D2,D1 ) will give identical objects.
It is recommended that private variables be assigned to the parameters. The class definition then becomes:
class LDR_Ser_Sig3_Ser_Class :
public LDR_Serial_Sensor_Class,
public Signals3_Serial_Class {
public: LDR_Ser_Sig3_Ser_Class ( );
LDR_Ser_Sig3_Ser_Class (int ser_in, int load, int clk_in, int ser_out, int clk_out );
private: int _ser_in; int _load; int _clk_in; int _ser_out; int _clk_out;
}; //semi colon important
--------------------------------------------------
The LDR_Ser_Sig3_Ser_Class inherits LDR_Serial_Sensor_Class and Signals3_Serial_Class. The LDR_Ser_Sig3_Ser_Class constructor must create an instance of these two classes. Effectively this means pushing the LDR_Ser_Sig3_Ser_Class parameters to either LDR_Serial_Sensor_Class and Signals3_Serial_Class. Since the first three parameters are associated with the sensor input ser_in, load and clk_in become the parameters for the instance of LDR_Serial_Sensor_Class and ser_out and clk_out the parameters with Signals3_Serial_Class. That is
LDR_Ser_Sig3_Ser_Class::LDR_Ser_Sig3_Ser_Class(int ser_in, int load, int clk_in, int ser_out, int clk_out ):
LDR_Serial_Sensor_Class(ser_in,load,clk_in), Signals3_Serial_Class(ser_out, clk_out){
_ser_in =ser_in; _load = load; _clk_in = clk_in; _ser_out= ser_out; _clk_out= clk_out;};
OR using the default wiring
LDR_Ser_Sig3_Ser_Class::LDR_Ser_Sig3_Ser_Class( ):
LDR_Serial_Sensor_Class(D0,D5,D6), Signals3_Serial_Class(D2, D1){
_ser_in =D0; _load = D5; _clk_in = D6; _ser_out= D2; _clk_out= D1;}
The implementation code will also include assigning the private variables _ser_in, _load etc to the parameters.
An object "train" of class LDR_Ser_Sig3_Ser_Class can be created in the client (*.ino) code with either of the definitions:
LDR_Ser_Sig3_Ser_Class train; //no parenthesis
LDR_Ser_Sig3_Ser_Class train (D0,D5,D6,D2,D1);
Note for the option with no parameters parenthesis should not be used. Using parenthesis will confuse the compiler which will eventually generate a hard to find compile error. (With parenthesis the compiler will think its a function)
----------------------------
For initialisations there will be a LDR_Ser_Sig3_Ser_Class::begin ( ) method. Embedded in this method there will be 3 components/paths:
(i) LDR_Serial_Sensor_Class::begin( ); The existing begin code for the LDR_Serial_Sensor_Class,
(ii) Inialisations restricted to just LDR_Ser_Sig3_Ser_Class, and
(iii) Signals3_Serial_Class::begin( ); The existing begin code for Signals3_Serial_Class.
The Signals3_Serial_Class::begin( ) contained three integer parameters external, polarity and no_sigs. These need to be included in the begin( ) declaration in the LDR_Ser_Sig3_Ser_Class.h file. That is:
class LDR_Ser_Sig3_Ser_Class :
public LDR_Serial_Sensor_Class,
public Signals3_Serial_Class {
public: LDR_Ser_Sig3_Ser_Class ( );
LDR_Ser_Sig3_Ser_Class (int ser_in, int load, int clk_in, int ser_out, int clk_out );
void begin (int external, int polarity,int no_sigs);
private: int _ser_in; int _load; int _clk_in; int _ser_out; int _clk_out;
}; //semi colon important
As shown the three parameters are those for the Signals3_Serial_Class.
In the implementation file the begin method is broken into its various components. That is
void LDR_Ser_Sig3_Ser_Class::begin (int external, int polarity,int no_sigs ){
LDR_Serial_Sensor_Class::begin( );
Signals3_Serial_Class::begin(external, polarity, no_sigs );
};
As shown all the parameters are associated with the Signals3_Serial_Class**.
Since the "begin" label is not unique the scope resolution operator :: is used to define where the begin is attached.
In the client code the begin will be called from the setup( ) method:
void setup() {
train.begin(1,1,4); //external logic, +ve signals, 4 signals
}
** The LDR_Serial_Sensor_Class has assumed a high input for train_over_sensor and there are 8 sensors. The original class could be enhanced to overload to allow a different number of sensors etc. The Signals3_Serial_Class actually has the option for zero, one, two or three parameters. Zero parameters, that is Signals3_Serial_Class::begin( ) will default to Signals3_Serial_Class::begin(1,1,4 )
--------------------------
The Signals3_Serial_Class contains a method signal_control(Signal of interest, State) that may be used to control the signals. All signals can be set to GREEN as part of the begin( ) method. That is
Note the scope resolution operator is not required as signal_control( ) is defined only as part of the Signals3_Serial_Class. The compiler will not be confused.
The signals could be controlled as part of the main program. For example to set signal3 RED the code will be:
void loop() {
train.signal_control(3,1);
}
----------------------------------
In the previous section the signals were controlled using a constatnt value. That is train.signal_control(3,1); set signal 3 to RED. To control the signals from a sensor the constant can be replaced with the train_over_sensor( ) method. For example to control signals 0 from sensor 0 the code will be
void loop( ) {
train.signal_control(0, train.train_over_sensor(0));
}
In the class "train_over_sensor( )" is only defined as part of the LDR_Serial_Sensor_Class so the compiler will not be confused and the scope resolution operator will not be required.
The two inherited classes contain time delays.
(i) train_over_sensor( ) will wait for one second to ensure a train has actually passed the sensor, and
(ii)signal_control( ) will kept the signal RED for 5 seconds and then AMBER for 5 seconds.
In testing the observer must give the signals time to pass through the RED and AMBER delays.
-------------------------------------
The final program to monitor 4 sensors and control 4 sets of signals will be**:
void loop() {
train.signal_control(0, train.train_over_sensor(0));
train.signal_control(1, train.train_over_sensor(1));
train.signal_control(2, train.train_over_sensor(2));
train.signal_control(3, train.train_over_sensor(3));
}
**As given sensor 0 controls signal 0 etc. In my situation I used sensors 0 and 1 for signals 0 and 1 and then sensors 6 and 7 for signals 2 and 3. In the future I anticipate placing a station between sensors 1 and 6 in which case sensors 2 through 5 will be used to monitor the train progress at the station and remotely adjust its speed.
-------------------------------------------
This project has developed a program that will monitor sensors and control signals for a model railway. The signals turn RED when a train is detected and then go through a RED-AMBER-GREEN after the train has departed. The system does not duplicate a real railway where the train has to pass through a track block to initiate the return to GREEN sequence.
An anticipated future project is to use WiFi to send the status of a train. For WiFi the ESP8266 micro-controller would be used. This micro-controller has limited I/O pins that led to the use of a parallel to serial convertor on the sensor inputs and a serial to parallel convertor on the outputs to the signals.
The program demonstrates generating a Cpp class and inheriting previously developed Cpp classes. The final client or user code (file LDR_Ser_Sig3_Ser_Class.ino) is very simple; all the details are hidden in the implementation code (file LDR_Ser_Sig3_Ser_Class.cpp) and the two inherited libraries Signals3_Serial_Class and LDR_Serial_Sensor_Class that are transparent to the end user.