After you get a single-task version working, we can start to expand the problem to something that needs more tasks.
@richard-damon
I have implemented the traffic light control logic in a single task using vTaskDelay using ESP32. This approach differs slightly from the table you provided,
One key point to mention is that I couldnāt add the flashing logic for the RED lights, so I have set them to ON in my code. Adding flashing logic would require additional complexity,
Please let me know if you have any specific questions or if there are further improvements youād like to make to the code.
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#define NS_VEHICLE_RED 23
#define NS_VEHICLE_GREEN 22
#define NS_VEHICLE_YELLOW 1
#define NS_PED_RED 3
#define NS_PED_GREEN 21
#define EW_VEHICLE_RED 4
#define EW_VEHICLE_GREEN 2
#define EW_VEHICLE_YELLOW 15
#define EW_PED_RED 17
#define EW_PED_GREEN 16
typedef enum {
STATE_INIT,
STATE_1,
STATE_2,
STATE_3,
STATE_4,
STATE_5,
STATE_6,
STATE_7,
STATE_8,
STATE_9,
STATE_10,
STATE_11,
STATE_12,
} TrafficState;
TrafficState currentState = STATE_INIT;
void setup() {
pinMode(NS_VEHICLE_RED, OUTPUT);
pinMode(NS_VEHICLE_GREEN, OUTPUT);
pinMode(NS_VEHICLE_YELLOW, OUTPUT);
pinMode(EW_VEHICLE_RED, OUTPUT);
pinMode(EW_VEHICLE_GREEN, OUTPUT);
pinMode(EW_VEHICLE_YELLOW, OUTPUT);
pinMode(NS_PED_RED, OUTPUT);
pinMode(NS_PED_GREEN, OUTPUT);
pinMode(EW_PED_RED, OUTPUT);
pinMode(EW_PED_GREEN, OUTPUT);
xTaskCreatePinnedToCore(trafficTask, "TrafficTask", 10000, NULL, 1, NULL, 0);
}
void loop() {
// Nothing to do here
}
void trafficTask(void *parameter) {
while (1) {
switch (currentState) {
case STATE_INIT:
// Initialize all lights to red for a brief period
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(EW_PED_RED, HIGH);
digitalWrite(EW_PED_GREEN, LOW);
vTaskDelay(pdMS_TO_TICKS(10000)); // 10 seconds
case STATE_1:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, HIGH);
digitalWrite(NS_PED_RED, LOW);
digitalWrite(NS_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_2;
break;
case STATE_2:
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(NS_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(7000)); // 7 seconds
currentState = STATE_3;
break;
case STATE_3:
// State 3: NS vehicle light yellow
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, HIGH);
vTaskDelay(pdMS_TO_TICKS(3000)); // 3 seconds
currentState = STATE_4;
break;
case STATE_4:
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, HIGH);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(EW_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_5;
break;
case STATE_5:
digitalWrite(EW_PED_GREEN, LOW);
digitalWrite(EW_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(7000)); // 7 seconds
currentState = STATE_6;
break;
case STATE_6:
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, HIGH);
vTaskDelay(pdMS_TO_TICKS(3000)); // 3 seconds
currentState = STATE_7;
break;
case STATE_7:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, HIGH);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(NS_PED_RED, LOW);
digitalWrite(NS_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_8;
break;
case STATE_8:
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(NS_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(7000)); // 7 seconds
currentState = STATE_9;
break;
case STATE_9:
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, HIGH);
digitalWrite(NS_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(3000)); // 3 seconds
currentState = STATE_10;
break;
case STATE_10:
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, HIGH);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(EW_PED_RED, LOW);
digitalWrite(EW_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_11;
break;
case STATE_11:
digitalWrite(EW_PED_GREEN, LOW);
digitalWrite(EW_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(7000)); // 7 seconds
currentState = STATE_12;
break;
case STATE_12:
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, HIGH);
vTaskDelay(pdMS_TO_TICKS(3000)); // 3 seconds
currentState = STATE_1;
break;
}
}
}
First, you should give meaningful names to your states.
Then, see if you can figure out how to make a set of āflashingā states. You may need an auxiliary variable to handle it. (Let me know if you need more of a hint).
Lastly, it is best if ALL your states end in a break, preceeded with an assignment to the next state to go to (STATE_INIT is just falling through to STATE_1)
Looks like you are on a good track, thumbs up!
Just a minor style issue: I would recommend to give your FSM states descriptive names as in your init state instead of STATE_1 to STATE_X, that improves the readability considerably.
Edit: It would also force you to think about what a particular FSM state means, for example whether a state indicates an empty or flushing crossing, flowing traffic N-S or E-W, respectively and so on. It helps a lot in understanding your system as a whole to have to name the scenario which is represented by a particular fsm state.
I agree that using meaningful state names will improve the codeās readability. Iām in the process of updating the code with these changes.
Regarding the flashing logic, I would appreciate more hints on how to implement it effectively. Specifically, how can I integrate it into the state transitions to handle the flashing behavior?
A hint on flashing, you can have two states for flashing that you switch between at the desired rate, and then use a variable to count how many times you have flashed āonā.
For something slightly more localized, you create one āentryā state that you go to from the green state, which sets the counter variable to its starting value, and then have two more states that you toggle between. If you donāt do this, then you want to set the variable in the state transitioning to the flashing state, or initialize it in the init state, and reset it as you get ready to leave the last flashing state. While one task is handling everything, you will have two flashing states for each of the states currently marked as Flashing Red.
Eventually, if we separate the pedestrian controller task from the vehicle control task, we can eliminate the duplicate flashing logic.
Eventually, we may end up with 4 tasks for the lights, 2 for the vehicle (which can use the same function with a different control block attached) and 2 for the pedestrian (which can also use there some function, different from the vehicle), and possible a ācontrolā task that schedules them.
Do you think these are meaningful and descriptive state names ?
typedef enum {
STATE_INIT,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED,
} TrafficState;
Thatās better, but the names are a bit long to be convenient, but also, all your states are NS_RED ⦠(Probably a copy and paste and forgot to edit) and you seem to have two full cycles listed (I did that in the chart to demonstarte the cyclic nature).
One thing to note is that the āRedā is redundant, because if one direction isnāt red, then the other must be red.
fwiw, there is also the option to nest fsms for the flashing states. Ie during the flash, the āouterā fsm remains in one state, but within that state, a sub (nested) fsm is traversed which does the counting as Richard suggested. Once that sub fsm enters its final state, the outer fsm proceeds to the next state. You can of course recycle that nested fsm for your various flashing states.
Time to code that out and test it then⦠Note that if you do not have the peripherals or hardware to see real LEDs illuminating, you can always use debug outputs to display the virtual colors of your traffic lights.
I apologize for any confusion earlier. I forgot to edit each state name as I intended, but my goal was to provide meaningful and descriptive names for each state.
I have correctly followed the sequence and pattern in the code to represent the traffic states.
The code I posted is working correctly for me. Iāve tested the code by connecting LEDs on a breadboard. In total, I connected 10 LEDs: 4 red, 4 green, and 2 yellow. Everything is functioning as expected
( Not implemented flashing logic)
Before, I were keeping the red LED continuously high
case STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED__RED:
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(NS_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(7000)); // 7 seconds
currentState = STATE_NS_YELLO_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED;
break;
Now I am thinking approch like The RED LED blinks on and off, repeating the pattern seven times, for a total duration of 7 seconds:
case STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED__RED:
digitalWrite(NS_PED_GREEN, LOW);
for (int i = 0; i < 7; i++) { // 7 iterations for 7 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_YELLO_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED;
break;
Does the updated behavior align with the intended purpose and objectives of the traffic light system ?
Yes that would work. IT doesnāt use āstatesā to do the flashing, but is sort of the āsub-machineā method mentioned elsewhere.
I am facing issue when both cycle complete and start new state EW_PED_RED
doesnāt turn ON . Iāve verified my hardware connections, and they seem fine. Iām wondering if thereās something in the logic or the code flow that Iām missing. Can you help me why EW_PED_RED doesnāt turn on at the start of new cycle
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#define NS_VEHICLE_RED 23
#define NS_VEHICLE_GREEN 22
#define NS_VEHICLE_YELLOW 1
#define NS_PED_RED 3
#define NS_PED_GREEN 21
#define EW_VEHICLE_RED 4
#define EW_VEHICLE_GREEN 2
#define EW_VEHICLE_YELLOW 15
#define EW_PED_RED 17
#define EW_PED_GREEN 16
typedef enum {
STATE_INIT,
STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED,
STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED,
STATE_NS_YELLO_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED_1,
STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1,
STATE_NS_YELLOW_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN_1,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED_1,
STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED_1,
} TrafficState;
TrafficState currentState = STATE_INIT;
void setup() {
pinMode(NS_VEHICLE_RED, OUTPUT);
pinMode(NS_VEHICLE_GREEN, OUTPUT);
pinMode(NS_VEHICLE_YELLOW, OUTPUT);
pinMode(EW_VEHICLE_RED, OUTPUT);
pinMode(EW_VEHICLE_GREEN, OUTPUT);
pinMode(EW_VEHICLE_YELLOW, OUTPUT);
pinMode(NS_PED_RED, OUTPUT);
pinMode(NS_PED_GREEN, OUTPUT);
pinMode(EW_PED_RED, OUTPUT);
pinMode(EW_PED_GREEN, OUTPUT);
xTaskCreatePinnedToCore(trafficTask, "TrafficTask", 10000, NULL, 1, NULL, 0);
}
void loop() {
// Nothing to do here
}
void trafficTask(void *parameter) {
while (1) {
switch (currentState) {
case STATE_INIT:
// Initialize all lights to red for a brief period
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(EW_PED_RED, HIGH);
digitalWrite(EW_PED_GREEN, LOW);
vTaskDelay(pdMS_TO_TICKS(10000)); // 10 seconds
currentState = STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED;
break;
case STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, HIGH);
digitalWrite(NS_PED_RED, LOW);
digitalWrite(NS_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED;
break;
case STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED:
digitalWrite(NS_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 14 iterations for 7 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_YELLO_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED;
break;
case STATE_NS_YELLO_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED:
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, HIGH);
for (int i = 0; i < 6; i++) { // 6 iterations for 3 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN;
break;
case STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN:
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, HIGH);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(EW_PED_RED, LOW);
digitalWrite(EW_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED;
break;
case STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED:
digitalWrite(EW_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 17 iterations for 7 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED;
break;
case STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED:
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, HIGH);
for (int i = 0; i < 6; i++) { // 6 iterations for 3 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED_1;
break;
case STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED_1:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, HIGH);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(NS_PED_RED, LOW);
digitalWrite(NS_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1;
break;
case STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1:
digitalWrite(NS_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 14 iterations for 7 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_YELLOW_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1;
break;
case STATE_NS_YELLOW_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1:
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, HIGH);
digitalWrite(NS_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(3000)); // 3 seconds
currentState = STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN_1;
break;
case STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN_1:
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, HIGH);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(EW_PED_RED, LOW);
digitalWrite(EW_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED_1;
break;
case STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED_1:
digitalWrite(EW_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 14 iterations for 7 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED_1;
break;
case STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED_1:
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, HIGH);
for (int i = 0; i < 6; i++) { // 6 iterations for 3 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
currentState = STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED;
break;
}
}
}
State STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED isnāt setting any of the EW bits. You might want to as a habit set ALL the GPIO bits like init does in most states. Perhaps the āYellowā states might not need to clear RED (as Red ā Yellow is an illegal transition) but doing all, and in the same order for visual clarity, would be helpful.
I did that but Iām encountering a issue in situation where the East-West pedestrian red light (EW_PED_RED) doesnāt turn on as expected at new cycle start. I see North-South vehicle green and North-South pedestrian green are active and East-West vehicle red but East-West pedestrian red light is off
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#define NS_VEHICLE_RED 23
#define NS_VEHICLE_GREEN 22
#define NS_VEHICLE_YELLOW 1
#define NS_PED_RED 3
#define NS_PED_GREEN 21
#define EW_VEHICLE_RED 4
#define EW_VEHICLE_GREEN 2
#define EW_VEHICLE_YELLOW 15
#define EW_PED_RED 17
#define EW_PED_GREEN 16
typedef enum {
STATE_INIT,
STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED,
STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED,
STATE_NS_YELLO_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED,
STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED_1,
STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1,
STATE_NS_YELLOW_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN_1,
STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED_1,
STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED_1,
} TrafficState;
TrafficState currentState = STATE_INIT;
void setup() {
pinMode(NS_VEHICLE_RED, OUTPUT);
pinMode(NS_VEHICLE_GREEN, OUTPUT);
pinMode(NS_VEHICLE_YELLOW, OUTPUT);
pinMode(EW_VEHICLE_RED, OUTPUT);
pinMode(EW_VEHICLE_GREEN, OUTPUT);
pinMode(EW_VEHICLE_YELLOW, OUTPUT);
pinMode(NS_PED_RED, OUTPUT);
pinMode(NS_PED_GREEN, OUTPUT);
pinMode(EW_PED_RED, OUTPUT);
pinMode(EW_PED_GREEN, OUTPUT);
xTaskCreatePinnedToCore(trafficTask, "TrafficTask", 10000, NULL, 1, NULL, 0);
}
void loop() {
// Nothing to do here
}
void trafficTask(void *parameter) {
while (1) {
switch (currentState) {
case STATE_INIT:
// Initialize all lights to red for a brief period
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(EW_PED_RED, HIGH);
digitalWrite(EW_PED_GREEN, LOW);
vTaskDelay(pdMS_TO_TICKS(10000)); // 10 seconds
currentState = STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED;
break;
case STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, HIGH);
digitalWrite(EW_VEHICLE_RED, HIGH); // Set EW vehicle light to red
digitalWrite(EW_PED_RED, HIGH); // Set EW pedestrian light to red
digitalWrite(NS_PED_RED, LOW);
digitalWrite(NS_PED_GREEN, HIGH);
digitalWrite(EW_PED_GREEN, LOW);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED;
break;
case STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED:
digitalWrite(NS_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 14 iterations for 7 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_YELLO_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED;
break;
case STATE_NS_YELLO_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED:
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, HIGH);
for (int i = 0; i < 6; i++) { // 6 iterations for 3 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN;
break;
case STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN:
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, HIGH);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(EW_PED_RED, LOW);
digitalWrite(EW_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED;
break;
case STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED:
digitalWrite(EW_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 17 iterations for 7 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED;
break;
case STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED:
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, HIGH);
for (int i = 0; i < 6; i++) { // 6 iterations for 3 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED_1;
break;
case STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED_1:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, HIGH);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(NS_PED_RED, LOW);
digitalWrite(NS_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1;
break;
case STATE_NS_GREEN_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1:
digitalWrite(NS_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 14 iterations for 7 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_YELLOW_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1;
break;
case STATE_NS_YELLOW_EW_RED_NS_PED_FLASHING_RED_EW_PED_RED_1:
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, HIGH);
digitalWrite(NS_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(3000)); // 3 seconds
currentState = STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN_1;
break;
case STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_GREEN_1:
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, HIGH);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(EW_PED_RED, LOW);
digitalWrite(EW_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED_1;
break;
case STATE_NS_RED_EW_GREEN_NS_PED_RED_EW_PED_FLASHING_RED_1:
digitalWrite(EW_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 14 iterations for 7 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED_1;
break;
case STATE_NS_RED_EW_YELLOW_NS_PED_RED_EW_PED_FLASHING_RED_1:
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, HIGH);
for (int i = 0; i < 6; i++) { // 6 iterations for 3 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = STATE_NS_GREEN_EW_RED_NS_PED_GREEN_EW_PED_RED;
break;
}
}
}
Again, looking at your code, you are not consistent at setting all the lights an every state, so having cases where a light doesnāt change isnāt surprising, as you may have omitted the command to actually set that light in that state. When unexpected things start happening, ācleaning upā untidy and inconsistent code is often a good first step.
Also, you donāt need the _1 states, as they are just (or should be) repeats of the first copy, so are just making more work. They were in the chart to make the phase delay more clear.
I have modified the code all in a single task and tested the behaviour of each LED on the breadboard. itās working as expected.
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#define NS_VEHICLE_RED 23
#define NS_VEHICLE_GREEN 22
#define NS_VEHICLE_YELLOW 1
#define NS_PED_RED 3
#define NS_PED_GREEN 21
#define EW_VEHICLE_RED 4
#define EW_VEHICLE_GREEN 2
#define EW_VEHICLE_YELLOW 15
#define EW_PED_RED 17
#define EW_PED_GREEN 16
typedef enum {
INIT_STATE,
NS_GREEN_STATE,
NS_PED_FLASHING_RED_STATE,
NS_YELLOW_STATE,
EW_GREEN_STATE,
EW_PED_FLASHING_RED_STATE,
EW_YELLOW_STATE
} TrafficState;
TrafficState currentState = INIT_STATE;
void setup() {
pinMode(NS_VEHICLE_RED, OUTPUT);
pinMode(NS_VEHICLE_GREEN, OUTPUT);
pinMode(NS_VEHICLE_YELLOW, OUTPUT);
pinMode(EW_VEHICLE_RED, OUTPUT);
pinMode(EW_VEHICLE_GREEN, OUTPUT);
pinMode(EW_VEHICLE_YELLOW, OUTPUT);
pinMode(NS_PED_RED, OUTPUT);
pinMode(NS_PED_GREEN, OUTPUT);
pinMode(EW_PED_RED, OUTPUT);
pinMode(EW_PED_GREEN, OUTPUT);
xTaskCreatePinnedToCore(trafficTask, "TrafficTask", 10000, NULL, 1, NULL, 0);
}
void loop() {
// Nothing to do here
}
void trafficTask(void *parameter) {
while (1) {
switch (currentState) {
case INIT_STATE:
// Initialize all lights to red for a brief period
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_PED_RED, HIGH);
digitalWrite(EW_PED_GREEN, LOW);
vTaskDelay(pdMS_TO_TICKS(10000)); // 10 seconds
currentState = NS_GREEN_STATE;
break;
case NS_GREEN_STATE:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, HIGH);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_RED, LOW);
digitalWrite(NS_PED_GREEN, HIGH);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_PED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = NS_PED_FLASHING_RED_STATE;
break;
case NS_PED_FLASHING_RED_STATE:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, HIGH);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_PED_RED, HIGH);
digitalWrite(NS_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 14 iterations for 7 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = NS_YELLOW_STATE;
break;
case NS_YELLOW_STATE:
digitalWrite(NS_VEHICLE_RED, LOW);
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, HIGH);
for (int i = 0; i < 6; i++) { // 6 iterations for 3 seconds
digitalWrite(NS_PED_RED, HIGH); // Turn on NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(NS_PED_RED, LOW); // Turn off NS_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_RED, HIGH);
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_PED_RED, LOW);
currentState = EW_GREEN_STATE;
break;
case EW_GREEN_STATE:
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, HIGH);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_PED_RED, LOW);
digitalWrite(EW_PED_GREEN, HIGH);
vTaskDelay(pdMS_TO_TICKS(20000)); // 20 seconds
currentState = EW_PED_FLASHING_RED_STATE;
break;
case EW_PED_FLASHING_RED_STATE:
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, HIGH);
digitalWrite(EW_VEHICLE_YELLOW, LOW);
digitalWrite(EW_PED_GREEN, LOW);
for (int i = 0; i < 14; i++) { // 17 iterations for 7 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = EW_YELLOW_STATE ;
break;
case EW_YELLOW_STATE:
digitalWrite(NS_VEHICLE_RED, HIGH);
digitalWrite(NS_VEHICLE_GREEN, LOW);
digitalWrite(NS_VEHICLE_YELLOW, LOW);
digitalWrite(NS_PED_RED, HIGH);
digitalWrite(NS_PED_GREEN, LOW);
digitalWrite(EW_VEHICLE_RED, LOW);
digitalWrite(EW_VEHICLE_GREEN, LOW);
digitalWrite(EW_VEHICLE_YELLOW, HIGH);
digitalWrite(EW_PED_GREEN, LOW);
for (int i = 0; i < 6; i++) { // 6 iterations for 3 seconds
digitalWrite(EW_PED_RED, HIGH); // Turn on EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
digitalWrite(EW_PED_RED, LOW); // Turn off EW_PED_RED
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
}
currentState = NS_GREEN_STATE;
break;
}
}
}
Since the single-task version is working well, Now, can we move forward and start expanding the problem to something that requires more tasks.
Ok, now lets split the program into 3 tasks.
One task controls the NS lights, another which controls the EW lights, and a 3rd master control.
Have each of the light controllers take messages from the master control to tell it to turn green or cycle towards red (7 seconds of flashing red PED, then 3 seconds of flashing red PED plus yellow, and then turns solid red) at which point it signals master control that it is done.
Have the master control send NS green, wait 20 seconds, then NS to red, wait for the completion, then send EW green, wait 20 seconds, then ES to red, wait for the completion, and then repeat.