Header File "Track_Train_ESP_Class.h"
Constructor Track_Train_ESP_Class (D0,D5,D6,D2,D1); Pins for Ser_In, Load, In_Clk, Ser_Out, Out_Clk
Public Methods: begin(ext,pol,sets .) external gate used for AMBER, polarity of signals, number of signals
monitor_train( ); Returns train state: where is train
Public Type enum where_is_train {waiting, coming0, approaching0, arriving0, station0, departing0, leaving0, going0, coming1, approaching1, arriving1, station1, departing1, leaving1, going1};
-------------------------------
Abstract: This project will convert the project Track_Train_ESP into a C++ class/library. The Track_Train_ESP project tracked the position of a train along a section of track. Due to the limited number of pins on the ESP8266 the input and output signals will use serial devices.
Possible Audience: Programmers interested in developing C++ classes and libraries.
Keywords: C++ class/library, scope resolution operator, ESP8266, Arduino IDE Program Manager, LDR Light Detecting Resistors, enum type, state machine, watchdog timer
Components Required: ESP8266 Microcontroller, 74HC164, 74HC165 and 74HC02 chips. 4 LDR plus resistors. 4 sets of RED,AMBER,GREEN Leds for laboratory testing.
Required Software: Arduino IDE with ESP8266 Add on. Libraries from previous projects: Signals3_Serial_Class and LDR_Serial_Sensor_Class
The Track_Train_ESP project tracked the locaton of a model train as it moved along a section of track. The position of the train was detected using LDR (Light Detecting Resistors). As the train passes each sensor the signals will go through a RED-AMBER-GREEN sequence. Four states, {coming,arriving,departing,going}are defined when the train is over a sensor and three states, approaching,station,leaving when the train is between the sensors. The other state is waiting when there are no trains present.
The proposed next project was to add WiFi. From an educational perspective while trying to develop the WiFi code it is better to have all the details of monitoring the train hidden so it was proposed to convert the Train_Track_ESP project into a C++ class where all the details are hidden in the implementation file. The new project will be known as Track_Train_ESP_Class.
------------------------------
A previous project Track_Train_ESP inherit two previous projects:
1. LDR_Serial_Sensor_Class that reads the status of 4 LDR sensors placed in the track, and
2.Signals3_Serial_Class that controlled 4 sets of RED-AMBER-GREEN signals.
This project will update Track_Train_ESP into a class Track_Train_ESP_Class.
As shown another project will be to develop a class Track_Train_ESP_WiFi where the train location is sent via WiFi
---------------------------------------------------------------------------
This discussion will assumes that readers have undertaken the Track_Train_ESP project.
1. In the Arduino IDE open the file Track_Train_ESP and save_as Track_Train_ESP_Class. (The IDE will add the extension *.ino)
2. In the Arduino IDE use the inverted triangle and New_Tab option to create two new files. Give them the names Track_Train_ESP_Class.cpp and Track_Train_ESP_Class. (same name different extensions)
3. Add the following to the *.h file:
Notes:
1. The header file is enclosed in a wrapper #ifndef ......#endif so that in larger projects the header file is only included once.
2. The Arduino header includes many useful definitions. It is included automatically in standard programs but not specifically in library routines.
3. The class Track_Train_ESP_Class { } is declared.
4. Note the class terminates with a semi-colon.**
5. In the class there are two areas public and private.
6. The public area includes the class constructor and a begin method is also included in anticipation.
** Without the semi-colon the code will compile but I have found there may be errors at later stages.
The original code included two libraries. That is:
These must now be included in the class definition:
The original code created two objects:
These objects specified how the circuit was wired.
The new constructor must include arguments that handle the circuit wiring. That is:
The main program or client code must now create one new object with 5 arguments**. That is:
The new object is given the name train_obj.
The new class inherits two classes. The implementation code (*.cpp) must direct the arguments to the appropriate class. This is illustrated in the next section.
** To use the class the *.ino code will need to include the header file
The implementation code must handle the constructor.
As shown the first three arguments become the arguments of the LDR_Serial_Sensor_Class and the last two the arguments for the Signals3_Serial_Class.
The other step is to assign private variables to the arguments. By convention private variable start with an underscore. These are declared in the header file.
In the client code the begin will be a method as apart of the instance train_obj. That is
The begin( ) method must call the begin( ) methods of the inherited classes. That is in the implementation code**:
Notes. 1. Since the "begin" label is not unique the scope resolution operator :: must be used to resolve which begin is required.
2. The Signals3_Serial_Class has the option of describing the circuit using three arguments. ie ext: external gate used for AMBER, pol: polarity of signals, sets: number of sets of signals. This option is included in the new class. The default arguments are 1,1,4.
** The begin( ) also includes initialising a train state and the serial port both required later in the project.
An noted in the Introduction the train will progress through a number of states. These were defined using an enum where_is_train. The implementation final code will read the sensors and determine the location of the the train. This definition will be moved to the header file**:
It is anticipated that the final product will be a WiFi system that displays the location of the train. It will therefor need to know the state. The client code will be of the form:
The method monitor_train( ) to be written will be a public method that will basically encapsulate all the methods in the original code (ie train(int s0, int s1, int s2, int s3) and dump(where_is_train loc)) and return the current state.
** The final code will use the option of the train travelling in either direction.
The method monitor_train( ) is added to the class:
As noted monitor_train( ) will encapsulate the dump and train methods of the original code. These are defined as private methods for the new class**. Futher these methods will use the current state that is also declared as private. The only way the client can know the current state is via the public method monitor_train( ).
** dump( ) has been renamed dump_loc( )
The code for the monitor_train( ) will be basically the code from the original program**. That is
** The original code included a watchdog timer that if the train did not change state in a specified time (20 seconds used) the program reset.I have chosen to reset the time to 200 seconds. This will imply iof someone causes a shadow over the coming sensor the design will take 200 seconds to reset. However it will also imply that a train can wait at the station for almost 200 seconds before the system resets.
--------------------------------------
As given the train signals turn RED when the train is over a sensor. When the train leaves they go through a delayed RED-AMBER_GREEN sequence where the signal is RED for a further 5 seconds and then AMBER for 5 before turning GREEN. This is probably fine for a model train. However with the layout shown there is station where a train could be stationary for an extended period. In that time the coming and arriving signals could have moved through the sequence and were now GREEN indicating it is safe for a following train to proceed.
In a full scale railway while the train was in the station the arriving signal would remain RED and the coming signal AMBER. The method dump_loc in the final code is modified as shown to achieve something approaching this requirement.
--------------------------------
The performance was observed by monitoring the 3 input and 2 output signals. In addition the pin D8 was set and the start of each program loop and reset at the end**. That is
The results were observed as follows:
An explanation is as follows;
1.The top channel gives the serial data, the LDR status being clocked into the ESP8266
2. The second channel is the input clock - 8 pulses per sample
3. The third channel is the Shift/Load signal - high for moving serial data and low to read the LDRs into the shift register^^
4. The fourth channel is the serial out to drive the signals (LEDs)
5. The fifth channel is the serial out clock!!
6. The sixth channel is high when the program is in the loop.
To reduce flickering on the output LEDs the output activity has been programmed to occur every 100mS. When there is output activity the complete cycle is 173uSec as shown by cursors. For all other cycles when there is no output activity the cycle takes 143uS . See cycle after second cursor.
In summary for 100/0.143 ~ 700 cycles the program is just reading the LDRs but doing nothing with the results. Since the code is non-blocking this time could be used for other activities.
** The setup function will need to include pinMode(D8,OUTPUT);
^^ The nature of the C++ class is to read the data in and then read one bit. Thus to read 4 LDRs four cycles are required.
!! With the design - 4 sets of signals but AMBER generated externally by RED-GREEN there are 8 clock pulses
This code will be used as the basic for a WiFi system. One objective was to make the client code (the *.ino) as simple as possible so as not to clutter the WiFi code.
The starting cod for the WiFi project will be**:
/Track_Train_ESP_Class.ino#include <Track_Train_ESP_Class.h>** The given code assumes that Track_Train_ESP_Class has been made into a library.
Make a zip file of the *.cpp and *.h files in the Track_Train_ESP_Class folder in your preference directory. In IDE see File-->Preferences.
In the IDE select Sketch -->Include Library --> Add *.zip library and select Track_Train_ESP_Class.