Decision Making #2

I’ve followed your suggestion to create three tasks for controlling the traffic lights. I’ve encountered a challenge when it comes to implementing the flashing logic for pedestrian signals in the code.

#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

// Define pin numbers for traffic lights
#define NS_VEHICLE_RED     23
#define NS_VEHICLE_GREEN   22
#define NS_VEHICLE_YELLOW  21
#define NS_PED_RED         19
#define NS_PED_GREEN       18
#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

// Define states for traffic lights
typedef enum {
    RED,
    GREEN,
    YELLOW,
    nsPedestrianRED,
    nsGREEN,
    ewPedestrianRED,
    ewGREEN
} LightState;

LightState nsVehicleState = RED;
LightState ewVehicleState = RED;
LightState nsPedestrianState = RED;
LightState ewPedestrianState = RED;

// Task handles
TaskHandle_t nsLightTaskHandle;
TaskHandle_t ewLightTaskHandle;

// Function to control NS lights
void nsLightTask(void *parameter) {
    while (1) {
        switch (nsVehicleState) {
            case RED:
                digitalWrite(NS_VEHICLE_RED, HIGH);
                digitalWrite(NS_VEHICLE_GREEN, LOW);
                digitalWrite(NS_VEHICLE_YELLOW, LOW);
                break;
            case GREEN:
                digitalWrite(NS_VEHICLE_RED, LOW);
                digitalWrite(NS_VEHICLE_GREEN, HIGH);
                digitalWrite(NS_VEHICLE_YELLOW, LOW);
                break;
            case YELLOW:
                digitalWrite(NS_VEHICLE_RED, LOW);
                digitalWrite(NS_VEHICLE_GREEN, LOW);
                digitalWrite(NS_VEHICLE_YELLOW, HIGH);
                break;
            case nsPedestrianRED:
                digitalWrite(NS_PED_RED, HIGH);
                digitalWrite(NS_PED_GREEN, LOW);
                break;
            case nsGREEN:
                digitalWrite(NS_PED_RED, LOW);
                digitalWrite(NS_PED_GREEN, HIGH);
                break;
        }

        //vTaskDelay(pdMS_TO_TICKS(1000));  // Adjust the delay as needed
    }
}

// Function to control EW lights
void ewLightTask(void *parameter) {
    while (1) {
        switch (ewVehicleState) {
            case RED:
                digitalWrite(EW_VEHICLE_RED, HIGH);
                digitalWrite(EW_VEHICLE_GREEN, LOW);
                digitalWrite(EW_VEHICLE_YELLOW, LOW);
                break;
            case GREEN:
                digitalWrite(EW_VEHICLE_RED, LOW);
                digitalWrite(EW_VEHICLE_GREEN, HIGH);
                digitalWrite(EW_VEHICLE_YELLOW, LOW);
                break;
            case YELLOW:
                digitalWrite(EW_VEHICLE_RED, LOW);
                digitalWrite(EW_VEHICLE_GREEN, LOW);
                digitalWrite(EW_VEHICLE_YELLOW, HIGH);
                break;
            case ewPedestrianRED:
                digitalWrite(EW_PED_RED, HIGH);
                digitalWrite(EW_PED_GREEN, LOW);
                break;
            case ewGREEN:
                digitalWrite(EW_PED_RED, LOW);
                digitalWrite(EW_PED_GREEN, HIGH);
                break;
        }

//        vTaskDelay(pdMS_TO_TICKS(1000));  // Adjust the delay as needed
    }
}

// Function for the master control task
void masterControlTask(void *parameter) {
    while (1) {
        // Send commands to control NS and EW lights
        nsVehicleState = GREEN;
        ewVehicleState = RED;
        nsPedestrianState = GREEN;
        ewPedestrianState = GREEN;
        vTaskDelay(pdMS_TO_TICKS(20000));  // 20 seconds for NS green
        nsVehicleState = RED;
        nsPedestrianState = nsGREEN;
        vTaskDelay(pdMS_TO_TICKS(7000));   // 7 seconds 
        nsVehicleState = RED;
        nsPedestrianState = YELLOW;
        vTaskDelay(pdMS_TO_TICKS(3000));   // 3 seconds 
        nsVehicleState = RED;
        nsPedestrianState = RED;
        ewVehicleState = GREEN;
        vTaskDelay(pdMS_TO_TICKS(20000));  // 20 seconds for EW green
        ewPedestrianState = ewGREEN;
        ewVehicleState = RED;
        vTaskDelay(pdMS_TO_TICKS(7000));   // 7 seconds 
        ewPedestrianState = YELLOW;
        vTaskDelay(pdMS_TO_TICKS(3000));   // 3 seconds
        ewVehicleState = RED;
    }
}

void setup() {
    pinMode(NS_VEHICLE_RED, OUTPUT);
    pinMode(NS_VEHICLE_GREEN, OUTPUT);
    pinMode(NS_VEHICLE_YELLOW, OUTPUT);
    digitalWrite(NS_VEHICLE_YELLOW, LOW);
    pinMode(NS_PED_RED, OUTPUT);
    pinMode(NS_PED_GREEN, OUTPUT);
    pinMode(EW_VEHICLE_RED, OUTPUT);
    pinMode(EW_VEHICLE_GREEN, OUTPUT);
    pinMode(EW_VEHICLE_YELLOW, OUTPUT);
    pinMode(EW_PED_RED, OUTPUT);
    pinMode(EW_PED_GREEN, OUTPUT);

    // Initially set all lights to red
    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);

    // Add a delay for 10 seconds to keep all lights red initially
    delay(10000);

    // Create tasks for NS and EW lights
    xTaskCreatePinnedToCore(nsLightTask, "NSLightTask", 10000, NULL, 1, &nsLightTaskHandle, 0);
    xTaskCreatePinnedToCore(ewLightTask, "EWLightTask", 10000, NULL, 1, &ewLightTaskHandle, 0);

    // Create the master control task
    xTaskCreatePinnedToCore(masterControlTask, "MasterControlTask", 10000, NULL, 1, NULL, 0);
}

void loop() {
    // Nothing to do here
}

The “Control” tasks should NOT be changing the state of the tasks that are handling the lights. It should.just be sending messages to the tasks, and (for turning red) waiting for a response.

The lights tasks should be handling the state transition operations, in response to these messages, and every state in there should probably write to all 5 of the lights under its control. (when we split the lights into Vehicle and Pedestrian tasks, then Vehicle will write to its 3, and Pedestrian to its 2). There are perhaps some things that could be optimized a bit, like when turning on Red Vehicle, we shouldn’t need to turn off Green, as we should have come from Yellow, but the redundant Green Off is cheap, and avoids issues if we add other possible transitions later.

Also remember, ALL tasks (with a very very few exceptions) should have a blocking call in all their paths in the task loop waiting for the next thing to do. This might be a delay, or it might be blocking on some event primative.

For instance, in a more advanced case and a smart light, might go Yellow → Green if an emergency vehicle approaches to a side getting ready to go to Red, and it requests, and is granted, a priority Green. If Green assumed that it could only come from Red, the light would be in a “strange” state at that point.

1 Like

I think message queues provide a way for the master control task to synchronize and coordinate the actions of the NS and EW lights control tasks. For example, when the master control task decides to transition from one traffic state to another, it can send a message via the queue to instruct the lights control tasks to change their state accordingly.

Yes, which is why I was suggesting that you use them.

My program is not following third step

NS Vehicle Green EW Vehicle Red NS Ped Green EW PED Red
NS Vehicle Green EW Vehicle Red NS Ped Green EW PED Red
NS Vehicle Yellow EW Vehicle Red NS Ped Flashing Red EW PED Red

Insted of third step I see NS Vehicle Green while i am turning off it in state NS_YELLOW

#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.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

QueueHandle_t nsMessageQueue;
QueueHandle_t ewMessageQueue;

typedef enum {
    NS_GREEN,
    NS_PED_FLASHING_RED,
    NS_YELLOW,
    EW_GREEN,
    EW_PED_FLASHING_RED,
    EW_YELLOW,
} TrafficState;

void nsLightsTask(void *parameter) {
    TrafficState nsState = NS_GREEN;

    while (1) {
        if (xQueueReceive(nsMessageQueue, &nsState, portMAX_DELAY) == pdTRUE) {
            // Update NS lights based on the received state
            switch (nsState) {
                case NS_GREEN:
                    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);
                    break;
                case NS_PED_FLASHING_RED:
                    digitalWrite(NS_PED_GREEN, LOW);
                    for (int i = 0; i < 14; i++) {
                        digitalWrite(NS_PED_RED, HIGH);
                        vTaskDelay(pdMS_TO_TICKS(500));
                        digitalWrite(NS_PED_RED, LOW);
                        vTaskDelay(pdMS_TO_TICKS(500));
                    }
                    break;
                case NS_YELLOW:
                    digitalWrite(NS_VEHICLE_GREEN, LOW);
                    digitalWrite(NS_PED_GREEN, LOW);
                    digitalWrite(NS_VEHICLE_YELLOW, HIGH);
                    for (int i = 0; i < 6; i++) {
                        digitalWrite(NS_PED_RED, HIGH);
                        vTaskDelay(pdMS_TO_TICKS(500));
                        digitalWrite(NS_PED_RED, LOW);
                        vTaskDelay(pdMS_TO_TICKS(500));
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

void ewLightsTask(void *parameter) {
    TrafficState ewState = EW_GREEN;

    while (1) {
        if (xQueueReceive(ewMessageQueue, &ewState, portMAX_DELAY) == pdTRUE) {
            // Update EW lights based on the received state
            switch (ewState) {
                case EW_GREEN:
                    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);
                    break;
                case EW_PED_FLASHING_RED:
                    for (int i = 0; i < 7; i++) {
                        digitalWrite(EW_PED_RED, HIGH);
                        vTaskDelay(pdMS_TO_TICKS(500));
                        digitalWrite(EW_PED_RED, LOW);
                        vTaskDelay(pdMS_TO_TICKS(500));
                    }
                    break;
                case EW_YELLOW:
                    digitalWrite(EW_VEHICLE_GREEN, LOW);
                    digitalWrite(EW_VEHICLE_YELLOW, HIGH);
                    for (int i = 0; i < 3; i++) {
                        digitalWrite(EW_PED_RED, HIGH);
                        vTaskDelay(pdMS_TO_TICKS(500));
                        digitalWrite(EW_PED_RED, LOW);
                        vTaskDelay(pdMS_TO_TICKS(500));
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

void trafficTask(void *parameter) {
    TrafficState currentState = NS_GREEN;

    while (1) {
        // Update the traffic light state
        xQueueSend(nsMessageQueue, &currentState, portMAX_DELAY);
        xQueueSend(ewMessageQueue, &currentState, portMAX_DELAY);

        vTaskDelay(pdMS_TO_TICKS(20000));

        switch (currentState) {
            case NS_GREEN:
                currentState = NS_PED_FLASHING_RED;
                break;
            case NS_PED_FLASHING_RED:
                currentState = NS_YELLOW;
                break;
            case NS_YELLOW:
                currentState = EW_GREEN;
                break;
            case EW_GREEN:
                currentState = EW_PED_FLASHING_RED;
                break;
            case EW_PED_FLASHING_RED:
                currentState = EW_YELLOW;
                break;
            case EW_YELLOW:
                currentState = NS_GREEN;
                break;
            default:
                break;
        }
    }
}

void setup() {
    pinMode(NS_VEHICLE_RED, OUTPUT);
    pinMode(NS_VEHICLE_GREEN, OUTPUT);
    pinMode(NS_VEHICLE_YELLOW, OUTPUT);
    pinMode(NS_PED_RED, OUTPUT);
    pinMode(NS_PED_GREEN, OUTPUT);
    pinMode(EW_VEHICLE_RED, OUTPUT);
    pinMode(EW_VEHICLE_GREEN, OUTPUT);
    pinMode(EW_VEHICLE_YELLOW, OUTPUT);
    pinMode(EW_PED_RED, OUTPUT);
    pinMode(EW_PED_GREEN, OUTPUT);

    // Initially set the traffic lights
    digitalWrite(NS_VEHICLE_RED, HIGH);
    digitalWrite(NS_PED_RED, HIGH);
    digitalWrite(EW_VEHICLE_RED, HIGH);
    digitalWrite(EW_PED_RED, HIGH);

    nsMessageQueue = xQueueCreate(1, sizeof(TrafficState));
    ewMessageQueue = xQueueCreate(1, sizeof(TrafficState));

    // Delay for 10 seconds
    vTaskDelay(pdMS_TO_TICKS(10000));

    // Now, start the traffic light control tasks
    xTaskCreate(trafficTask, "TrafficTask", 10000, NULL, 1, NULL);
    xTaskCreate(nsLightsTask, "NSLightsTask", 10000, NULL, 1, NULL);
    xTaskCreate(ewLightsTask, "EWLightsTask", 10000, NULL, 1, NULL);
}

void loop() {
    // Nothing to do here
}

Since you have two DIFFERENT state machines (for the NS and EW lights) you should have two state variables, one for the NS states, and one for the EW states. You could have one enum defining the values for that one direction: Green, Ped_Flashing, Yellow, and Red, but the North/South lights and the East/West lights will be in different states.

Also, you are not having the xxLightsTask tell the controller when they have made it to Red. trafficTask isn’t supposed to handle ALL the state transitions for the lights (at that point, it might as well just change the lights too), but just handle the MAJOR state of turn from red to green (which can be quick) or turn from green to red, which needs to take time for the warning states in between. Note, trafficTask might have its own set of states that it is using to decide what to command the lights to do.