January 2023 LCD display added to output/receiver ESP8266
This project takes the positions of model trains collected from multiple WiFi sources and uses ESP_NOW to combine these into a single WiFi Access Point. A smart device can then be used to display the location of model trains.
Note: There are two separate pieces of code:
1. LDR_ESP_NOW that reads the sensors, controls the signals and generates an ESP_NOW signal to transfer the train status to the ESP_NOW receiver, and
2. ESP_NOW_WiFi that receives the train status over ESP_NOW and acts as an Access Point to be read by a Smart Device.
Include File: LDR_ESP_NOW.h
Constructor: LDR_ESP_NOW train;
Public Methods: train.begin(broadcastAddress);**
train_loc = train.monitor_train(); //returns train position
train.send_status(board_number*16+train_loc );++
** Requires receiver address EG uint8_t broadcastAddress[6] = {0xBC, 0xDD, 0xC2, 0xBA, 0xF3, 0x7F};
++ For multiple boards/senders/transmitters requires board number EG uint8_t board_number = 2;
%% For simulation use #define TESTING 1
Include Files: ESP_NOW_WiFi.h and index.h for HTML template
Constructor: ESP_NOW_WiFi trains;
Public Methods: trains.begin(ssid,password );**
train_loc = trains.monitor_train(); //Returns ESP_NOW data
trains.WiFi_message(train_mess); //Text message via ESP_SSE
**Requires ssid and password EG. const char* ssid = "Monitor_Trains"; const char* password = "12345678";
I have a very small model train layout. As a "fun" project I wish to use several sensor circuit to detect the position of trains. The results from several circuits would be combined by a WiFi circuit that could be interrogated by a smart device.
A previous project https://sites.google.com/site/johnkneenprojects/model_trains/wifi/ldr_esp_wifi_sse used LDR sensors to monitor the location of a model train on a short section of track. The results were read by an ESP8266 and transferred via WiFi_SSE (Server Send Events) to a smart device/client. The basic circuit is shown below.
The left hand diagram is a block diagram of the ldr_esp_wifi_sse project while the right hand photo shows the ESP8266 Hardware. The top wires interface with 8 LDR sensors placed in the track and the bottom wires go to 4 sets of RED-AMBER-GREEN signals. For the ldr_esp_wifi_sse project the ESP8266 generates a WiFi_SSE signal to be monitored by a smart phone.
For the Multi_WiFi_to_WiFi_AP project (this project) the ESP8266 will output ESP_NOW signals. There will be no change in the hardware. If there is no hardware, and also to aid testing a simulation or testing mode will be included.
For this project it is proposed to have many WiFi chips monitoring different sections of the layout and passing their results to a central WiFi station that will in turn pass the combined readings to a smart device.
Since the project will build on project https://sites.google.com/site/johnkneenprojects/model_trains/wifi/ldr_esp_wifi_sse it is probably sensible to reload the project ldr_esp_wifi_sse and verify that everything is operational. Without any sensors the smart device should display:
The final project will split the operation into two:
1. The multiple sending ESP8266s will basically read the LDRs and control the signals (code LDR_ESP_NOW), while
2. The receiver ESP8266 or Web Server will send the results using WiFi SSE to the smart device (Code ESP_NOW_WiFi).
To transfer the data from the senders to the receiver the protocol ESP_NOW will be used.
My approach to the project is to save two copies of the code ldr_esp_wifi_sse. The sender code will have the WiFi SSE code replaced with ESP_Now (send) while the receiver code will have the LDR monitoring functions replaced with ESP_Now (receive) code. This is illustrated below.
Using ESP_NOW to send messages to the receiver the talker must know its MAC (media access control) address. Each device has a unique MAC address specified by the manufacturer. To find its MAC address the following code should be run:
For my device the resulting MAC (media access control )address is
The overall project consists of multiple ESP8266s spread around the model railway track. These will (i) monitor the output of LDRs to determine the position of the train, (ii) control the output of signals, and (iii) output the train status. In a previous project https://sites.google.com/site/johnkneenprojects/model_trains/wifi/ldr_esp_wifi_sse the output was sent via ESP_SSE directly to a smart device. In this project a central ESP8266 will collect the outputs of many and send the combined results to a smart device.
Each one of these devices that monitor the train(s) will be know as a "Talker" or Transmitter. Basically it will be identical to the https://sites.google.com/site/johnkneenprojects/model_trains/wifi/ldr_esp_wifi_sse except that instead of sending data to a smart device using ESP_SSE it will send the data to a central ESP8266 using ESP_NOW.
Step 1 will monitor the LDRs and control the signals on the track. Effectively step 1 takes the project ldr_esp_wifi_sse (https://sites.google.com/site/johnkneenprojects/model_trains/wifi/ldr_esp_wifi_sse) and eliminates all the ESP_SSE code. No new code is written for Step 1. Step 2 will enhance Step 1 with ESP_NOW to communicate with the central ESP8266. (The end result is that ESP_SSE code has been replaced with ESP_NOW)
The starting code becomes (given the file name LDR_ESP_NOW):
Step 1a:
#include "LDR_ESP_NOW.h";
The code as given monitors the 4 sensors on the track, controls the RED-AMBER_GREEN signals and determine the state of the passing train (over or between sensors and direction).
The status of the train is given by the variable train_loc. Once the ESP_NOW code (Step2) is added it is this result that ESP_NOW will broadcast.
The following features need to be added to the client code (the *ino):
1. The MAC Address of the receiver. This was determined earlier as 0xBC, 0xDD, 0xC2, 0xBA, 0xF3, 0x7F.
2. A board number to distinguish this board from others. This will be combined with the train status to give results for the complete train layout.**
The *.cpp file will be enhanced to include all the details for the implementation of ESP_NOW and the method send_status( ) that will transmit a combination of the board number and train status.
Note: To aid testing the receiver code an option to simulate the train status is included. This is selected using the #define TESTING statement. For the final product with valid model train data TESTING would be defined as zero
** The board number will need to be defined for each board and the code re-compiled. For a commercial unit external switches might be used to define the board number.
The file LDR_ESP_NOW.cpp is enhanced to include all the details of the ESP_NOW implmentation.
In summary the implementation file includes the following additions to accomodate ESP_NOW.
1. Include the libraries <ESP8266WiFi.h> and <espnow.h>
2. Specify the MAC address for the listening device. In this example an array is allocated (uint8 mac_addr[6];) for the MAC address and the actual address passed as an argument in the begin( ) method. Thus if the listener/destination is changed the MAC address need only be changed in the *.ino file.
3, Include code for a Callback Function or Interrupt Service Routine that is executed each time data is transmitter. In this example the functions prints either success or failure.
4. As part of the begin method the WiFi is set up as a station. ie WiFi.mode(WIFI_STA);
5. The library function esp_now_init() will initialise ESP-NOW and return "0" for a good result.
6. Register the Callback or interrupt service routine esp_now_register_send_cb(OnDataSent);
7. Call the function esp_now_add_peer(mac_addr, ESP_NOW_ROLE_SLAVE, 1, NULL, 0); to pair the sender and receiver.
8. To send data the function will be esp_now_send(mac_addr, (uint8_t*)&train_loc, sizeof(train_loc));. Normally the second parameter will be a data structure but in this example train_loc is actually type uint8_t that is passed from the method send_status( ).
------------------------------------------
The header file has basically only two changes:
1. Parameter mac_addr is added to the begin( ) method.
2. A method send_status( ) is included to call the ESP send function
In the TESTING mode the status 16..31 for board 1, 32..47 for board 2 will be displayed continously on the serial monitor.
For real testing the ESP_NOW_WiFi receiver will need to be implemented (Step 4). Without the receiver there will be one status message only.
----------------------------------------------------------
Steps 1 and 2 implemented the code to read the track sensors and transmit the status using ESP_NOW. That program was known as LDR_ESP_NOW.
The next step is to develop the second program to be known as ESP_NOW_WiFi. This effectively will be the program LDR_ESY_WiFi_SSE code with the code that monitors the sensors etc replaced with ESP_NOW code to receive the status from LDR_ESP_NOW.
For step 3 the interface to the smart device ( that is the server portion will be implemented)
Start up code to interface with a smart device will be:
Step 3a.----------------------
-------------------
---------------------------------
Step 3dThe display on a Smart Device will be:
Monitor Trains.
No trains present.
The next step is to handle the received ESP_NOW messages.
-----------------------------------------------------
Step 3 of ESP_NOW_WiFi has implemented the HTML interface to a smart device.
Step 4 will be to upgrade the code to read messages of the train status from LDR_ESP_NOW and to send a message to the smart device. Currently LDR_ESP_NOW sends an integer to indicate the train status.
The implementation code must:
1.Include the relevant ESP_NOW libraries.<ESP8266WiFi.h> and <espnow.h>
2. Add the Callback function that is executed when data is received: OnDataRecv( )
3. Set the device as both a STA and AP
4. Enable ESP_NOW and set up callback vector.
Possible code will be:
//Implementation file ESP_NOW_WiFi.cpp#include "ESP_NOW_WiFi.h"#include "index.h"----------------------------
The only new method is monitor_train( ); In the implementation file monitor_train( ) returns the train_position that was read as part of the callback method.
//Header file ESP_NOW_WiFi.h#ifndef ESP_NOW_WiFi_H#define ESP_NOW_WiFi_H#include "Arduino.h"//#include <LDR_Ser_Sig3_Ser_Class.h>-------------------------
For starters a second board was programmed as a sender - the only change was to make it "Board 2".
At the receiver there will be two sets of numbers displayed on the monitor: 16 through 31 for board 1; and 32 through 47 for board 2.
The next step is to generate HTML messages for both boards. Possible code will be:
void loop ( ){ static char train_mess[200], mal_mess[40], jg_mess[40], pos_mess[40];static int save_loc;int train_loc = trains.monitor_train();if (train_loc != save_loc) {The messages are very specific to my layout. Using old trains from the Atherton Tableland I have chosen give the two stations the names "Malanda" (board 1) and "Jaggan". (Board2) The trains will then be arriving from / departing to Tolga and Miller Miller in the other.
The code will send out WiFi_SSE message trains.WiFi_message(train_mess); The code will build up this message depending on the ESP_NOW data received. train_loc/16 = 1 for board 1 or Malanda and 2 for board 2 or Jaggan. The actual message given by train_loc & 15 is then appended.
With both board 1 and board 2 in TESTING mode a typical response on a Smart phone will be:
The code will send out WiFi_SSE message trains.WiFi_message(train_mess); The code will build up this message depending on the ESP_NOW data received. train_loc/16 = 1 for board 1 or Malanda and 2 for board 2 or Jaggan. The actual message given by train_loc & 15 is appended either to Mananda or Jaggan.
With both board 1 and Board 2 in TESTING mode a typical response on a Smart phone will be as shown**:
**Note the message does include the HTML command "<P>" for a new paragraph. Without this command the massage will be on one line.
-----------------------------------------------------------------------------------
The design appears satisfactory for two simple sections of track**. The design should be tested and the display modified for additional senders.
My original design consisted of two ESP8266 monitor-WiFi boards (more to be added later) It was found that the ground on each board should be connected. Also on the second board tested a LDR was partly shaded this indicated a train always present. The value of the 4.7K resistor was increased. For a commercial product this would not be satisfactory but for a one off home project the resistors could be individually adjusted.
On my layout board1 monitors the four upper sensors on the outer track while board 2 monitors the four upper sections on the inner track, The remaining tracks include cross over points and shunting yards that will add additional complexity to the program logic.
** The design does assume that the trains are initially outside the area being monitored++. Since the project is basically a WiFi project and it is only a "fun" project this is a reasonable assumption with the locos being placed in the "no train present" section of track.
Consider the possible situation where a loco starts off between the sensors "A" and "D"
When the program starts it is testing for a train at either "A" or "D". If the loco is physically somewhere between A and D and moving towards "D" when the program starts than when the train reaches "D" the code will give the message "Coming from" rather than "Going to". After the train has passed the "D" sensor the message will be "Arriving from" rather than "No train present"
In the algorithm the program is waiting for the train to reach "C". Assuming the train is looping it will pass through "A" and "B" before reaching "C". The software is now looking for the train at "B" which happens on the next loop. It then loops for a train at "A" which happens on the next loop.
At that point the software thinks the train has left the area. Unfortunately, the train has not., it is back where it has started and will never synchronise.
There are two choices:
(i) leave software as is and ensure the train is outside the area when the program starts, or
(ii) draw up a state diagram that includes all possible starting position and implement the code. As many textbooks say this is left as an exercise for the reader. This would need to be done in a commercial product- Good luck
++ A similar situation arises if ever there was a short circuit on the track - Since the circuit boards are powered from the track a short circuit would cause the boards to reset. The possible solutions are:
(i) Ensure there will be no shorts on the track - possibly easier said than done,
(ii) power the circuit boards from a separate power supply, or
(iii) as above draw up a state diagram that includes all possible starting position and implement the code.
____________________________________________________________________________________
While the project was just a "fun" project to display the train locations on a Smartphone it was thought the end product could be enhanced by displaying the results on an LCD**.
A separate project has developed a library LCD_1602. This library uses I2C protocol that implies the following wiring:
To add the LCD to the project:
1. Go to the LCD_1602_Class project and compact the LCD_1602.cpp and LCD_1602.h files into a single *.zip file. It will be given the label LCD_1602.
2. As part of this project in the Arduino IDE use the sequence Sketch-> include Library -> Add *.zip Library and add the library LCD_1602.zip
3. Include the LCD_1602 library in this project (ESP_NOW_WiFi). That is:
#include <LCD_1602.h>
4. Make an object or instance of the LCD_1602 class. ie
LCD_1602 lcd; //without arguments will default to address 0x27
5. In the setup( ) function initialise the LCD and write any messages if desired. ie
lcd.begin( );
lcd.message(0,"--ESP_NOW_WiFi--");
lcd.message(1,"-Monitor Trains-");
6. Write a display routine and call this from the main program loop. For example:
void lcd_message(int loc){
if (loc/16 == 1) lcd.message(0,"Malanda ");
if (loc/16== 2) lcd.message(1,"Jaggan ");
lcd.message(loc&15);
}
void loop ( ){
......
int train_loc = trains.monitor_train();
if (train_loc != save_loc) {
lcd_message(train_loc);
..........
As given the display will be of the form:
"Malanda 3"
where "3" is the current status of the train in the outer loop. Unfortunately the LCD is limited to 16 characters so it is not possible to write the complete message "Train arriving from Tolga."
**With hindsight having a LCD would have been useful during code development.