Decision Making #2

After you get a single-task version working, we can start to expand the problem to something that needs more tasks.

1 Like

@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)

1 Like

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.

1 Like

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.

1 Like

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.

1 Like

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.

1 Like

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.

1 Like

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;
    }
  }
}

@richard-damon

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.

1 Like