Hi,
I am on project creating an 3-way Emergency Traffic Light that utilize ESP-NOW and HTTP POST functionality.
So the topology of my project is like this :
There are 3 feeds (traffic light) and 1 controller.
The general logic is like this :
Emergency will send an message containing targeted priority lane to controller, then the controller will relay that messages to all other feeds. But the cycle must not break. So, for example, the target is NORTH, then SOUTH and EAST will have to haste their green duration. The maximum haste duration is 6 seconds, and the interruption duration (green duration for prioritized lane) is 30 seconds.
Now, I originally wanted to make each feed independent of each other, including the controller. So If the controller or other feed dies / malfunction, the feed will still be able to run accordingly. That is what I had in mind when designing the flow of my program (over 250+ line of calculation). But please bear with me, I'll simplify my question, reason, and goal, I'll also include the log of my program so you can see why my calculation should work but didn't.
And because of what I had mind about making each feed dependent, I tried to create every possible scenario (as in calculation, so the timing of each feed is "synced" even though independent of each other). I do that by using HostFeed variable that contain waitTime and nextInLine duration, so I can predict at what time this feed will turn RED or GREEN and how long will they last. And the formula is the same for every feed, the difference only lays in HostFeed.nextInLine and HostFeed.waitTime value. And I've done numerous manual simulation of this formula, like for example, what happen if the interruption is North at 44 second, or South and 99 second, and many more. And I am confident that my formula should work. But it didn't.
For starter, here is the scenario for interruption target NORTH at 44second
So every feed receive the same message at 44 second.
- C will immediately turn RED because C has already passed 6 seconds hasteGreen duration
- A will immediately turn GREEN because it calculate that `cM - pM >= hasteGreen (0) + loading
- B will has its RED turn ON, until
cM - pM >= hasteGreen (0) + loading (30)is true, so RED until 74 (please check switchTraffic for condition preparation).
Now, that's what should happen, but when B receive the message, it immediately turn GREEN, even though the condition is not satisfied. Here is the log that explain the problem on B (east) feed
EAST STATUS : -> GREEN AT 25000
EAST STATUS : -> RED AT 37000
Message from controller: north
Received on :
44936
44936-44936>=0+30000
EAST HASTE STATUS : -> GREEN AT 44936
Switch status : match = false, semiHaste = false, greenLight = 1, hasteGreen = 6000, pM = 44936, Loading = 0
There is 2 problem from this log :
- 44936 - 44936 >= 0 + 30000 is NOT TRUE, so EAST feed should still has its RED turn ON until the cM reach 74936 (+30s of its initial value).
- Notice how "Switch status" is printed AFTER cM - pM calculation? I believe the Switch status should be printed BEFORE them, because Switch status reside in switchTraffic() that run directly after
OnDataRecv.
So, TL;DR,
The formula should be correct, but why does my ESP32 don't do as the program says?
Specifically in this part of code :
else { // match == false
if (greenLight == false) {
if (semiHaste == false) {
if ((cM - pM) >= (hasteGreen + loading)) {
// at the time of interruption, it should translates to 44 - 44 >= 0 + 30
////////
Serial.print(cM);
Serial.print("-");
Serial.print(pM);
Serial.print(">=");
Serial.print(hasteGreen);
Serial.print("+");
Serial.print(loading);
Serial.println("");
///////
pM = cM;
Serial.print("EAST HASTE STATUS : -> GREEN AT ");
Serial.println(pM);
Serial.println("");
hasteGreen = 6000;
if (loading > 0) {
loading = 0;
useSemiHaste = false;
haste = false;
}
greenLight = true;
} else {
red();
}
Any insight and feedback is appreciated, and even if my way of creating this program is unnecessarily complex, I am still curious as to why this happen.
I'll attach the code below,
That is all, thank you
/*********
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-now-one-to-many-esp32-esp8266/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
#include <esp_now.h>
#include <WiFi.h>
//Structure example to receive data
//Must match t
struct Direction {
String direction;
int time;
String previous;
String next;
int nextInLine;
int waitTime;
};
// direction time previous next nextInLine waitTime
Direction hostFeed = { "east", 12000, "north", "south", 25000, 60000 };
// coba add direction 2 untuk siklus interupsi
// Direction east = {"east", 11000, "north", "south", 9000, 21000};
// Direction south = {"south", 12000, "east", "north", 11000, 20000};
// TRAFFIC LAMP SET UP for MAPPI32
#define RED 18 // The ESP32 pin GPIO25 connected to R pin of traffic light module
#define YELLOW 19 // The ESP32 pin GPIO26 connected to Y pin of traffic light module
#define GREEN 23 // The ESP32 pin GPIO27 connected to G pin of traffic light module
// VARIABLE SET UP
bool haste = false;
String from;
String instruction;
bool match;
bool running = false;
bool hold = false;
bool semiHaste = false; // semiHaste true if it is located between target and initiator
bool useSemiHaste = false;
unsigned long pM = 0;
unsigned long cM;
unsigned long interruptTime;
unsigned long iTime;
unsigned long loading = 0;
unsigned long pMv2 = 0;
unsigned long hasteGreen = 6000;
unsigned long diff; // used to find difference between cM / interruptTime and pM
bool greenLight = false;
bool nextInLine = true; // EAST
int intDuration = 0;
void switchTraffic(String command) {
// N23
// E6
// S27
// E18
// S19
// N21
// S8
// N23
// S15
haste = true;
interruptTime = cM;
if (command == hostFeed.direction) {
match = true;
pMv2 = pM + (hostFeed.waitTime - hostFeed.nextInLine); // used when interrupted is directly next to the host
diff = interruptTime - pM;
Serial.print("Switch status : match = true, pMv2 = ");
Serial.print(pMv2);
Serial.print(", pM = ");
Serial.print(pM);
Serial.print(", diff = ");
Serial.println(diff);
Serial.println("");
} else { // match = false
if (command == hostFeed.next) { // semiHaste true
semiHaste = true;
match = false;
if (greenLight == false) {
pM = pM + (hostFeed.waitTime - hostFeed.nextInLine);
}
Serial.print("Switch status : match = false, semiHaste = true, greenLight = ");
Serial.print(greenLight);
Serial.print(", pM = ");
Serial.println(pM);
} else { // semiHaste false
semiHaste = false;
match = false;
if (greenLight == false) {
if (interruptTime - pM >= hasteGreen) { // check if this is false, then go for
hasteGreen = 0;
pM = cM;
}
loading = 30000;
}
Serial.print("Switch status : match = false, semiHaste = false, greenLight = ");
Serial.print(greenLight);
Serial.print(", hasteGreen = ");
Serial.print(hasteGreen);
Serial.print(", pM = ");
Serial.print(pM);
Serial.print(", Loading = ");
Serial.print(loading);
Serial.println("");
}
}
}
void red() {
digitalWrite(RED, HIGH);
digitalWrite(GREEN, LOW);
digitalWrite(YELLOW, LOW);
}
void green() {
digitalWrite(RED, LOW);
digitalWrite(YELLOW, LOW);
digitalWrite(GREEN, HIGH);
}
void handleTraffic() {
// Example template for testing
// N19s
// N19s NORTH
//// n.a... cM - pM > 4 bla bla at Xs trigger to greenLight ON, correct
//// n.a condition : bla bla bla
cM = millis();
if (haste == true) {
if (match == true) {
// pMv2 = pM + (hostFeed.waitTime - hostFeed.nextInLine)
// diff = interruptTime - pM
if (greenLight == false) {
if (diff <= hasteGreen) { // MODE A
if (cM - pM >= 12000) { // if interrupt is not adjacent to the host
pM = cM;
Serial.print("EAST HASTE STATUS : MODE A -> GREEN AT ");
Serial.println(pM);
Serial.println("");
greenLight = true;
} else {
red();
}
} else if (diff > hasteGreen && diff <= 12000) { // MODE B
if (cM - pM >= hasteGreen + diff) {
pM = cM;
Serial.print("EAST HASTE STATUS : MODE B -> GREEN AT ");
Serial.println(pM);
Serial.println("");
greenLight = true;
} else {
red();
}
} else { // MODE C
if (interruptTime > pM + (hostFeed.waitTime - hostFeed.nextInLine)) {
if (cM - pMv2 >= hasteGreen) {
pM = cM;
Serial.print("EAST HASTE STATUS : MODE C -> GREEN AT ");
Serial.println(pM);
greenLight = true;
} else {
red();
}
} else { // MODE D
if (cM >= interruptTime + hasteGreen) {
pM = cM;
Serial.print("EAST HASTE STATUS : MODE D -> GREEN AT ");
Serial.println(pM);
Serial.println("");
greenLight = true;
} else {
red();
}
}
}
} else { // greenLight == true
if (cM - pM >= 30000) {
pM = cM;
Serial.print("EAST HASTE STATUS : -> RED AT ");
Serial.println(pM);
Serial.println("");
greenLight = false;
haste = false;
} else {
green();
}
}
} else { // match == false
if (greenLight == false) {
if (semiHaste == false) {
if ((cM - pM) >= (hasteGreen + loading)) {
////////
Serial.print(cM);
Serial.print("-");
Serial.print(pM);
Serial.print(">=");
Serial.print(hasteGreen);
Serial.print("+");
Serial.print(loading);
Serial.println("");
///////
pM = cM;
Serial.print("EAST HASTE STATUS : -> GREEN AT ");
Serial.println(pM);
Serial.println("");
hasteGreen = 6000;
if (loading > 0) {
loading = 0;
useSemiHaste = false;
haste = false;
}
greenLight = true;
} else {
red();
}
} else { // semiHaste == true
if (useSemiHaste == false) {
if (cM - pM >= hasteGreen + loading) {
pM = cM;
Serial.print("EAST HASTE STATUS (semiHaste) : -> GREEN AT ");
Serial.println(pM);
Serial.println("");
if (loading > 1) {
loading = 0;
haste = false;
}
greenLight = true;
} else {
red();
}
} else { // useSemiHaste == true
if (cM - pM >= loading + hostFeed.nextInLine) {
pM = cM;
if (loading > 0) {
loading = 0;
useSemiHaste = false;
haste = false;
}
greenLight = true;
} else {
red();
}
}
}
} else { // greenLight == true
if (cM - pM >= hasteGreen) {
Serial.print("EAST HASTE STATUS : -> RED AT ");
Serial.println(pM);
Serial.println("");
pM = cM;
greenLight = false;
loading = 30000;
useSemiHaste = true;
} else {
green();
}
}
}
////////////////////////////////////////////// NORMAL /////////////////////////////////////////////////
} else {
if (greenLight == true) {
if (cM - pM >= hostFeed.time) {
pM = cM;
Serial.print("EAST STATUS : -> RED AT ");
Serial.println(pM);
Serial.println("");
// if (hold == true) {
// pM = hostFeed.time + pM;
// } else {
// pM = cM;
// }
// Serial.print("Switch to RED, p : ");
// Serial.println(pM);
hold = false;
greenLight = false;
} else {
green();
}
} else { // greenLight = false
if (nextInLine == true) {
if (cM - pM >= hostFeed.nextInLine) {
pM = cM;
Serial.print("EAST STATUS : -> GREEN AT ");
Serial.println(pM);
Serial.println("");
greenLight = true;
nextInLine = false;
} else {
red();
}
} else {
if (cM - pM >= hostFeed.waitTime) {
pM = cM;
Serial.print("EAST STATUS : -> GREEN AT ");
Serial.println(pM);
Serial.println("");
// Serial.print("Current report : p -> ");
// Serial.println(pM);
// if (hold == true) {
// pM = hostFeed.waitTime + pM;
// } else {
// pM = cM;
// }
// Serial.print("Switch to GREEN, p : ");
// Serial.println(pM);
hold = false;
greenLight = true;
} else {
red();
}
}
}
}
}
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) {
memcpy(&from, incomingData, sizeof(from));
String direction = String((char *)incomingData);
Serial.print("Message from controller: ");
Serial.println(from);
iTime = millis();
Serial.println("Received on : ");
Serial.print(iTime);
Serial.println("");
// if (from == "start") {
// running = true;
// pM = millis();
// } else if (from == "stop") {
// running = false;
// } else {
switchTraffic(from);
// }
}
void setup() {
pinMode(RED, OUTPUT);
pinMode(YELLOW, OUTPUT);
pinMode(GREEN, OUTPUT);
//Initialize Serial Monitor
Serial.begin(115200);
//Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
//Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_register_recv_cb(OnDataRecv);
Serial.println("East Listening..");
}
void loop() {
// if (running == true) {
handleTraffic();
// } else {
// digitalWrite(YELLOW, HIGH);
// }
}

