Class Summary
Header File <Signals3_Serial_Class.h>
Constructor: Signals3_Serial_Class(serial,clk); Pins where serial data and clock are wired
Public Methods: begin( ) No arguments
- assumes external NOR gate to obtain Amber, Positive signals, 4 sets of signals
begin(ext) ext = 1 external gate, ext = 0 no external gate
begin(ext,pol) pol = 1 positive polarity, pol = 0 negative polarity.
begin(ext,pol,no_sigs) no_sigs Number of signals
signal_control(sig,dsp) sig = signal of interest. dsp = state (1/0).
Delay times default to 5 seconds
signal_control(sig,dsp,redWait) set delay of red signal
signal_control(sig,dsp,redWait,amberWait) set delay of amber signal
Abstract/Summary: The project develops a class that takes an input and controls RED-AMBER-GREEN signals that are interfaced to the micro-controller using a serial to parallel convertor.
Possible Audience: Model railway hobbyists interested in electronically controlling signals and programmers interested in examples of classes including inheriting and overloading in the C++ environment. Also software design of state machines. The C++ program is not recommended for beginners.
Keywords: Three aspect signals, C++ Class, C++ overloading, C++ Inheritance, public, private, object arrays, dynamic allocation, state machine, ESP8266
Required Components: Microcontroller (Project tested with ESP8266) 74HC164 Serial to Parallel Converter, a 74HC02 NOR gate, plus LEDs and LDR for on bench testing.
Required Libraries: If desired for testing either LCD_Sensor_Class , LDR_Serial_Sensor_Class, or IR_Sensor_Class
-------------------------------------
The end target for this project is a model railway layout that has several sets of signals that need to be controlled.
Previous projects have developed programs that controlled signals. For example, the LDR_Signals3_RAG_Class used three sensors to give the train position. (Top diagram below). Typically the signal would turn RED when the train passed over the first sensor, AMBER when it reached the second and return to GREEN on reaching the third.
By contrast the project Signals3_Class, second diagram developed a class that included an input method signal_control(int dsp) that turned the signals RED when a train was detected (ie dsp=1). After the train left (dsp=0) the signal would remain RED for 5 seconds, then go AMBER for 5 seconds before returning to GREEN.
This project will duplicate the Signals3_Class but using an external serial to parallel to convertor**.This project, known as Signals3_Serial_Class duplicates the Signals3_Class and develops a C++ class/library that uses a serial to parallel convertor to generate the same signal sequences as the Signals3_Class. (See Test Circuit below)
Ideally end users should not be aware of the circuitry ("black box") between the sensor and the signals. In addition the link or "black box" between the sensors and signals should have a similar "feel". For example, the Signals3_Class has a method signal_control(int dsp). Ideally this new class should also have a public method signal_control(int dsp). This is expanded in the next section.
**A longer term objective is to develop a syatem that uses WiFi to transmit the train location. A possible WiFi micro-controller is the ESP8266 that has only 8 free I/O pins so each Lamp/Led cannot be driven directly. Hence a serial to parallel convertor is required on the output. (The same reason will imply a parallel to serial convertor on the input)
------------------------------------------------------------------
A previous project Signals3_Class developed a class for parallel signals. Ideally this project should "feel" similar to the Signals3_Class.
The Signals3_Class defined the following:
Constructor: Signals3_Class(int red_led, int amber_led, int green_led)
Public Method begin() where the signals were positive by default
Public Method signal_control(int dsp) where the argument dsp controls RED-AMBER-GREEN sequence. Default time 5 seconds
In addition the following overloads were available:
Constructor: Signals3_Class(int red_led, int green_led) -Assumes amber _led generated with external gate
Public Method begin(pol) where pol = 1 for positive logic, pol = 0 for negative logic
Public Method: signal_control(int dsp, int red_wait) red_wait - changes wait time for RED
Public Method: signal_control(int dsp, int red_wait,int amber_wait) amber_wait - changes wait time for AMBER
The objective is if possible to duplicate these methods with the new Signals3_Serial_Class. Looking ahead the differences will be:
Constructor (serial,clock): How the serial interface is wired - not how the leds are wired.
begin ( ): Three overloads - use of logic to generate AMBER, polarity of signals, and number of sets of signals.
signal_control(sig,dsp): Which serial output and how to set. Two overloads-wait times for RED and AMBER
------------------------------------------------------------
The test circuit is shown. The design was tested with an ESP8266 but any microcontroller may be used. The objective of this class is to control 4 sets of 3 aspect signals (hence the "3" in the project title). The circuit will also use a serial input for testing. However the project software will not be restricted to 4 sets of signals. Additional 74HC164's can be wired in serials to give multiples of 8 outputs.
The design firmware will generate the RED and GREEN green signals with the AMBER being generated by a NOR gate. With one serial to parallel chip and one NOR package 4 signal sets can be controlled.
--------------------------
The ESP8266 in the Arduino IDE will be used.
It might be necessary to install the drivers for the ESP8266 (More specially the USB-Serial Interface on the ESP board).I was using a WiFi Mini product code XC3802 from Jaycar) and found my PC recognized the XC3802 I was using so this step was not necessary. If the drivers are not found the WiFi Mini uses a CH340G USB-Serial IC. The drivers for this can be downloaded from the IC manufacturer’s website: http://www.wch.cn/download/CH341SER_ZIP.html
To add board support for the ESP8266 it is recommended to use Arduino IDE version 1.6.4 or later so that the Boards Manager can handle the installation.
1. To install board support for ESP8266, in File>Preferences>Additional Board Manager URLS add: http://arduino.esp8266.com/stable/package_esp8266com_index.json separating from existing entries with a comma.
2: Go to Tools>Boards>Boards Manager and type 'esp' in the search box
3. Install ESP8266 by ESP8266 Community. (Button on lower right) This is about 150MB download and can take a while.
4. Select the desired ESP6266 board. My board was identical to the 'WeMos D1 Mini Lite’
--------------------------------------------
The following three files should be created.
1. In the Arduino IDE use File-->New and give it the name Signals3_Serial_Class. The IDE will give it the extension .ino
2. Use the inverted triangle near top right of IDE and create two new_tabs. Give these the labels Signals3_Serial_Class.cpp and Signals3_Serial_Class.h. Note the extensions.
3. Cut and paste the following snippets to start populating the files.
Notes:
The header file includes the wrapper #ifndef ........#endif to avoid the header file being included more than once.
The header file also includes Arduino.h. This header is included in ordinary programs but has to be specifically included with libraries
Both the implementation and test/client codes include the the Signals3_Serial_Class header file.
The program should compile and run in the target.
---------------------------------------------------
The next step is to define the class, develop the implementation code for the class constructor and declare an instance of the class Signals3_Serial_Class.
The serial to parallel convertor has 2 inputs, the serial data and the clock. The class will have two arguments ser_in and clk. In this design the Master Reset is not used but if required a third argument could be added.
Two private variables are defined which by convention are prefixed with the underscore symbol stressing that they are private. In the implementation file they are assigned to the class arguments.
In the test or client code a new instance/object signal is created where _ser_in and _clk attached to the pins D2 and D1.
A public method begin( ) will need to defined as part of the class.
At this point the implementation code will make the two pins _ser_in and _clk outputs
For the instance of the Signals3_Serial_Class the begin( ) will be invoked as part of the setup() routine:
///Signals3_Serial_Class.ino...... void setup() {signal.begin( );}....
----------------------------------------------------------
In the Signals3_Class there were two options that should be considered duplicating:
1. Using an individual output line for each of the RED, AMBER and GREEN signals, OR using external logic to generate the AMBER from the RED and GREEN.
2. Having the class handle both positive and negative signals.
These options can be included by overloading the begin( ) method. Three options are possible:
begin( ) no argument - use default values
begin(arg1) - one argument - select one parameter but use the default for the other
begin(arg1,arg2) two arguments - select values at run time.
In addition the serial to parallel convertor can be of different lengths. This adds a third argument, the number of signals. In the example there are 4 signals. Other definitions could be used - for example the size of the serial to parallel convertor which is 8 in the test circuit.
The four options are included in the project. arg1 will represent an external logic gate and arg2 the polarity of the logic. That is
The implementation code will become:
Note three private variables have been defined and assigned to the three arguments.
I have chosen as default values using an external gate, signals of positive polarity and 4 signals. Thus in the client code there are four possibilities all of which will give the same result:
begin( );
begin(1);
begin(1,1);
begin(1,1,4);
The dumpArray( ) method may be used to drive the serial to parallel converter.
For testing the RED and GREEN signals were set at '0' and the AMBER at '1'.
As shown below with 4 sets of signals with a red, amber, green signal each there are 12 clock pulses. On the first clock RED=0, on the second AMBER = 1 and on the third GREEN = 0 etc.
When the AMBER signal is generated externally (ie _external==1) there will be only 2 clock pulses per signal (total =8) and the serial output corresponding to the AMBER lamp will be missing. This will be the situation with the example for this project.
If the method dumpArray is called continously the outputs of the serial to parallel convertor will be a pulse train. To "freeze" the outputs at the desired value by inserting the following code the dumpArray( ) will only update the serial to parallel convertor every 100 mSec
updateOP will be declared as a long int and UPDATE_TIME defined as 100 (mSec)
The outputs will now be either a solid high or low but with 8 very short pulses every 100mSec as the data is rippled through.
---------------------------------------------------
At this point the data for each signal mybe represented by two variables _theRed and _theGreen As th eproject progresses there will be the need for two additional variables - one used for generating the 5 seconds in the RED-AMBER-GREEN sequence and one train state. These 4 variables are encapsulated in the data structure illustrated below.
The diagram also shows set/get methods to write/read the individual variables.
A C++ class oneSignal describes the above structure.
Note the set/get methods must be public to be used by the Signals3_Serial_Class.
The whole railway layout will be described by an array of such structures. ie instances of oneSignal. How many will be declared by the begin ( ) method at run time. That is dynamic allocation.
In the implementation file a pointer signalArray is created
oneSignal *signalArray;
and in the begin( ) method in the implementation file an array of oneSignal is declared.
void Signals3_Serial_Class::begin(int external, int polarity,int no_sigs ){
signalArray = new oneSignal[_no_sigs];
}
The Signals3_Serial_Class will inherit the oneSignal class so its definition must be modified:
class Signals3_Serial_Class : public oneSignal { .....};
----------------------------------------------
The begin( ) method can initialise all the red, green and state elements.
An overview of the progrm is shown.
As noted earlier the input signal to the Signals3_Class is signal_control(dsp) where the argument dsp controls RED-AMBER-GREEN sequence. The default time 5 seconds.The input signal for this project will retain the label signal_control( sig , dsp ) but now there needs to be an extra argument to define the signal of interest. Note 0 < sig < no_sigs.
The starting code for signal_control(int sig, int dsp) will be:
The method signal_control( ) is declared as part of Signals3_Serial_Class. That is
The code can be tested using the following in the main or client routine
The serial_control( ) will call the method dumpArray( ) that every 100mSec will output the signals to the serial to parallel convertor. Note dumpArray( ) is non-blocking. The program will open the method. if the time is not up the program leaves the function where other external operations may be performed. Only when the time has expired will the lamps be changed. Once that is completed the program exits.
The resulting waveforms are shown below. The begin method has initialised all the red and green signals to "0". On each pass of the main loop signal.signal_control(3,1) sets sensor 3 to red. With an external gate the signals are rippled out as a RED-GREEN pair. Signal 3 will be the last pair hence on the second last clock the serial input to the 74HC164 is the RED and it has been set high.
The proposed design is illustrated below. The signal will go RED immediately a train is detected. When the train departs the signal will remain RED for a delay time, it will then go AMBER also for a delay time and then GREEN where it waits for the next train. It is proposed that the both delay times default to 5 seconds. However by overloading the signal_control( ) method these values may be varied.
The design will be implemented as a finite state machine using four states defined as a enum in the header file. The state is stored in the object oneSignal and initialiased to wait4train in the begin method:
Also required will be routines to handle the time
void oneSignal::setTime(long tt) {_signalStart= tt;}long oneSignal::getTime( ){return _signalStart; }The design proposes 5 second steps in the time. Hence the constants:
The implementation of the state machine is shown below. The input parameters are the signal of interest "sig" and its state "dsp"
Following reset the state will be wait4train. In the wait4train state
(i) the relevent signal is set to GREEN.
(ii) the input "dsp" is tested and if set the next state is specified as wait4end.
In the wait4end state
(i) the signal is set to RED immediately
(ii) the input "dsp" is tested and if cleared it indicates the train has completely passed over the sensor. The next state is specified as wait4red and a timer is set.
In the wait4red state
(i) the signal remains as RED
(ii) the timer is tested waiting for 5 seconds (non blocking)
(iii) once the time has elapsed the next state is set to wait4amber and the timer reset.
In the wait4amber state
(i) both the red and green "signals" are cleared with the external NOR gate setting the signal to AMBER.
(ii) the timer is tested and when the time has elapsed the next state is changed to wait4train.
Note :signal_control(int sig, int dsp) loops continuously and at the end of each loop the current lamp status is output via the method dumpArray( );
----------------------
The client code was modified to request the red signal for 5 seconds and then red "off" for 15 seconds.
The above code results in three different waveforms
1. As per the waveforms as presented previously where the RED signal is the signal at the second last clock. By observation it was active for 10 seconds. 5 seconds implemented in the above test code and 5 seconds as part of the signal_control( ) method.
2. Waveforms where both the red and green signals are zero. That is the serial data is zero (not shown). This was present for 5 seconds. The external NOR gate would generate an active AMBER light.
3. The waveform as shown below. This will be present for 10 seconds. The red repeats every 25 seconds - its programmed on for 5 seconds plus remains on for another 5. The amber is then active for 5 seconds when the green becomes active. It will remain active until the next red at 25 seconds.
Currently as part of the Signals3_Serial_Class the delay times are initialised to 5000mS.
These times can be modified by overloading the signal_control( ) method.
Implementation code
Client code
void setup() {signal.begin( );signal.signal_control(3,1,2,3);}Notes: In the example the wait time for the red is now 2 seconds and the wait for the amber 3.
The times need only be adjusted once as per the example in the setup routine.
The changes will apply for all sets of signals.
If different times are required the desired times should be applied every time the red signal is cleared.
That is signal_control( sig , 0 ) will start the signal through the RED-AMBER-GREEN sequence with a delay of 5 seconds by default
Adding arguments for example signal_control(sig, 0, 7, 9) will change the red delay to 7 seconds and the amber to 9.
signal_control(sig, 1, 7, 9) will do the same but the times will not come into effect until the red is cleared. signal_control(sig,0). That is when the RED-AMBER-GREEN sequences commences.
To date the design has been tested by monitoring the serial output and clock pins with a Mixed Signal Oscilloscope.
The final product shows the ESP8266 and the 74HC164 Serial to Parallel Convertor and the 74HC02 Nor gate that generates the Amber signal from the red-green combination. Around the board are 4 sets of 4 screw terminals that on th efinal layout will be wired to 4 sets of signals along the track. For on-bench testing Leds are wired into one set of pins. The screw terminals on the left are for wiring a parallel to serial card wired to up to 8 LDRs (See project LDR_Serial_Sensor_Class). Also shown are the headers used for attaching a Mixed Signal Oscilloscope to observe the signals.
The design has been tested using positive signals. For negative signals an active signal will be a low level. Thus in the output method dumpArray( ) each output must be complemented as illustrated.
Note this program has assumed that the two signals _theRed and _theGreen store the state ie active or inactive and the routine dumpArray( ) takes into account if the signals are positive or negative.
If positive signals are used with an external gate the external gate will be a NOR gate. With negative logic the external gate will be a NAND. The NOR and NAND have different pin configurations so cannot be swapped for testing.
------------------------
For the following waveform the begin( ) method is set to begin(0,1,4). That is no external gate, positive logic and 4 sets of signals.
Reading from left to right each clock pulse will correspond to Red(0), Amber(0), Green(0), Red(1), Amber(1), Green(1) etc.
The trace has captured the amber output on signals 0, 1 and 2 and the red output on signal(3) - i.e. third last pulse.
Changing the polarity to negative ie begin(0,1,4) the results are now the complement verifying the program logic.
The best way to implement this code in a new project is to save the (*.cpp and *h) files as a library/class.
1. The files are stored in the subdirectory Signals3_Serial_Class of the sketchbbook location or work folder. (See file-->preferences). Use a progam such as Widows Explorer and combine the *.h and *.cpp files int a *.zip folder.
2. In the Arduino IDE select Sketch -->Include Library --> Add *.Zip Library and select Signals_Serial_Class.This library will be saved in the subdiretory/libraries also under the sketchbook location.
3. To use the library in a following sketch use Sketch --> Include Library --> and select Signal3_Serial_Class from the list of contributed libraries.
The development of the Signals3_Serial_Class has been presented. The project developed two public methods:
begin( ) that performed the initialisation. The default operation assumed an external NOR/NAND gate to generate the AMBER signal from the RED and GREEN. The signals are all positive and the class controls 4 sets of signals.
serial_control( sen, dsp) that set the sen bit to the value dsp. With dsp=1 the red lamp turned on immediately. With dsp = 0 the lights went though a sequence RED, AMBER, GREEN
The begin( ) and serial_control() methods can be overloaded to give other characteristics.
begin( 0, 0, 3) the AMBER is generated internally, the signals use negative logic and there are 3 sets of signals.
serial_control(2,1,4,7) will set signal 2 to 1, there will be a 4 second delay before the AMBER becomes active and a 7 second delay before the GREEN becomes active.
As part of the development a separate class oneSignal was created to save the current values of the signals. At run time as part of the begin method an array of oneSignal was generated. Default was 4 elements.
-------------------------------------
This project focused on the output circuitry, that is the train signals.
A second project will add a parallel to serial convertor to the input to monitor the actual train position.
This project used an ESP8266 micro-controller that has WiFi capability. This will be implemented in a future project.