#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h> // For binary semaphore to signal high priority task
// --- Pin Definitions ---
const int redLedPin = 7; // Red LED
const int greenLedPin = 6; // Green LED
const int yellowLedPin = 5; // Yellow LED
const int buttonPin = 8; // Push Button
// --- Task Priorities ---
// Higher number means higher priority
#define PRIORITY_RED_LED_TASK 3
#define PRIORITY_GREEN_LED_TASK 2
#define PRIORITY_YELLOW_LED_TASK 1 // Lowest priority
// --- Synchronization for Button Interrupt ---
// Binary semaphore to signal the Red LED task from the ISR
SemaphoreHandle_t xRedLedSignalSemaphore;
// --- Interrupt Service Routine (ISR) for Button ---
// This ISR will simply give a semaphore, signaling the Red LED task.
// This is the correct way to communicate from an ISR to a task.
void IRAM_ATTR buttonISR() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// Give the semaphore, waking up the Red LED task if it's waiting
xSemaphoreGiveFromISR(xRedLedSignalSemaphore, &xHigherPriorityTaskWoken);
// If giving the semaphore caused a higher priority task to become ready,
// request a context switch.
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
// --- Task 1: Red LED Task (High Priority, Event-Driven) ---
// This task waits for a signal (from the button ISR) and then performs a burst blink.
void redLedTask(void *parameter) {
pinMode(redLedPin, OUTPUT);
digitalWrite(redLedPin, LOW); // Start off
for (;;) {
// Wait indefinitely for the semaphore from the ISR
if (xSemaphoreTake(xRedLedSignalSemaphore, portMAX_DELAY) == pdTRUE) {
Serial.println(">>> Red LED Task: Signaled by button! Performing critical operation...");
// Simulate a critical, burst operation (blinking quickly)
for (int i = 0; i < 5; i++) {
digitalWrite(redLedPin, HIGH);
vTaskDelay(pdMS_TO_TICKS(50)); // Very short delay
digitalWrite(redLedPin, LOW);
vTaskDelay(pdMS_TO_TICKS(50));
}
Serial.println("<<< Red LED Task: Critical operation complete. Going to sleep.");
}
// After the burst, the task will go back to waiting for the semaphore,
// allowing lower priority tasks to run.
}
}
// --- Task 2: Green LED Task (Medium Priority, Periodic) ---
void greenLedTask(void *parameter) {
pinMode(greenLedPin, OUTPUT);
digitalWrite(greenLedPin, LOW); // Start off
const long blinkInterval = 500; // 0.5 seconds
for (;;) {
digitalWrite(greenLedPin, HIGH);
Serial.println(" Green LED Task: ON");
vTaskDelay(pdMS_TO_TICKS(blinkInterval));
digitalWrite(greenLedPin, LOW);
Serial.println(" Green LED Task: OFF");
vTaskDelay(pdMS_TO_TICKS(blinkInterval));
}
}
// --- Task 3: Yellow LED Task (Low Priority, Periodic) ---
void yellowLedTask(void *parameter) {
pinMode(yellowLedPin, OUTPUT);
digitalWrite(yellowLedPin, LOW); // Start off
const long blinkInterval = 1000; // 1 second
for (;;) {
digitalWrite(yellowLedPin, HIGH);
Serial.println(" Yellow LED Task: ON");
vTaskDelay(pdMS_TO_TICKS(blinkInterval));
digitalWrite(yellowLedPin, LOW);
Serial.println(" Yellow LED Task: OFF");
vTaskDelay(pdMS_TO_TICKS(blinkInterval));
}
}
void setup() {
Serial.begin(115200);
Serial.println("--- FreeRTOS Task Scheduling Demo ---");
// --- Initialize Pins ---
pinMode(redLedPin, OUTPUT);
pinMode(greenLedPin, OUTPUT);
pinMode(yellowLedPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP); // Use internal pull-up for the button
// --- Create Binary Semaphore for ISR to Task Communication ---
xRedLedSignalSemaphore = xSemaphoreCreateBinary();
if (xRedLedSignalSemaphore == NULL) {
Serial.println("Error: Failed to create Red LED signal semaphore!");
while (true); // Halt on error
}
// --- Attach Interrupt ---
attachInterrupt(digitalPinToInterrupt(buttonPin), buttonISR, FALLING);
// --- Create FreeRTOS Tasks ---
// xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask)
xTaskCreate(
redLedTask,
"Red_LED_Task",
2048, // Stack size
NULL, // No parameters
PRIORITY_RED_LED_TASK, // High priority
NULL // No task handle needed
);
xTaskCreate(
greenLedTask,
"Green_LED_Task",
2048, // Stack size
NULL,
PRIORITY_GREEN_LED_TASK, // Medium priority
NULL
);
xTaskCreate(
yellowLedTask,
"Yellow_LED_Task",
2048, // Stack size
NULL,
PRIORITY_YELLOW_LED_TASK, // Low priority
NULL
);
Serial.println("Tasks created. Scheduler starting...");
// The scheduler automatically starts after setup() completes if tasks are created.
}
void loop() {
// In FreeRTOS applications, loop() is often empty as tasks handle main logic.
// We add a tiny delay to yield control back to the scheduler.
// This prevents the loop from consuming 100% CPU if no other tasks are ready.
vTaskDelay(pdMS_TO_TICKS(10));
}
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h> // Include the FreeRTOS queue header
// --- Pin Definitions ---
const int redLedPin = 7; // Red LED
const int greenLedPin = 6; // Green LED
const int yellowLedPin = 5; // Yellow LED
const int buttonPin = 8; // Push Button
// --- Message Queue Handle ---
// This will hold the handle to our message queue
QueueHandle_t xEventQueue;
// --- Message Types ---
// Define simple integer messages for clarity
#define MSG_BUTTON_PRESS 1
#define MSG_PERIODIC_EVENT 2
// --- Interrupt Service Routine (ISR) for Button ---
// This ISR sends a message to the queue when the button is pressed.
void IRAM_ATTR buttonISR() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
int messageToSend = MSG_BUTTON_PRESS;
// Send the message to the queue from ISR context
// xQueueSendFromISR returns pdTRUE if successful, pdFALSE otherwise
if (xQueueSendFromISR(xEventQueue, &messageToSend, &xHigherPriorityTaskWoken) != pdTRUE) {
// This part is generally for debugging in a real system,
// as ISRs should not print to Serial.
// In a real application, you might increment an error counter.
// Serial.println("ISR: Failed to send button message!");
}
// If sending the message woke a higher priority task, request a context switch.
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
// --- Task 1: Red LED Task (Producer Task - Sends Periodic Messages) ---
// This task periodically sends a "periodic event" message to the queue.
void redLedProducerTask(void *parameter) {
pinMode(redLedPin, OUTPUT);
digitalWrite(redLedPin, LOW); // Start off
const long sendInterval = 2500; // Send message every 2.5 seconds
int messageToSend = MSG_PERIODIC_EVENT;
for (;;) {
digitalWrite(redLedPin, HIGH);
Serial.println("Red LED Producer: Sending periodic event message...");
// Send the message to the queue.
// xQueueSend will block if the queue is full (timeout of 0 means no block).
if (xQueueSend(xEventQueue, &messageToSend, 0) != pdTRUE) {
Serial.println("Red LED Producer: Failed to send periodic message (queue full?)");
}
digitalWrite(redLedPin, LOW);
vTaskDelay(pdMS_TO_TICKS(sendInterval));
}
}
// --- Task 2: Green LED Task (Consumer Task - Receives and Processes Messages) ---
// This task waits for messages from the queue and reacts based on the message type.
void greenLedConsumerTask(void *parameter) {
pinMode(greenLedPin, OUTPUT);
digitalWrite(greenLedPin, LOW); // Start off
int receivedMessage; // Variable to store the received message
for (;;) {
// Wait indefinitely for a message to arrive in the queue
// xQueueReceive will block until a message is available
if (xQueueReceive(xEventQueue, &receivedMessage, portMAX_DELAY) == pdTRUE) {
Serial.print("Green LED Consumer: Received message: ");
Serial.println(receivedMessage);
// Process the received message
if (receivedMessage == MSG_BUTTON_PRESS) {
Serial.println("Green LED Consumer: Processing button press!");
// Toggle green LED for button press
digitalWrite(greenLedPin, HIGH);
vTaskDelay(pdMS_TO_TICKS(100)); // Brief ON
digitalWrite(greenLedPin, LOW);
vTaskDelay(pdMS_TO_TICKS(100)); // Brief OFF
} else if (receivedMessage == MSG_PERIODIC_EVENT) {
Serial.println("Green LED Consumer: Processing periodic event!");
// Blink green LED twice for periodic event
for (int i = 0; i < 2; i++) {
digitalWrite(greenLedPin, HIGH);
vTaskDelay(pdMS_TO_TICKS(200));
digitalWrite(greenLedPin, LOW);
vTaskDelay(pdMS_TO_TICKS(200));
}
} else {
Serial.println("Green LED Consumer: Unknown message received!");
}
}
}
}
// --- Task 3: Yellow LED Task (Independent Background Task) ---
// This task blinks the yellow LED independently.
void yellowLedTask(void *parameter) {
pinMode(yellowLedPin, OUTPUT);
digitalWrite(yellowLedPin, LOW); // Start off
const long blinkInterval = 1000; // 1 second
for (;;) {
digitalWrite(yellowLedPin, HIGH);
Serial.println(" Yellow LED Task: ON");
vTaskDelay(pdMS_TO_TICKS(blinkInterval));
digitalWrite(yellowLedPin, LOW);
Serial.println(" Yellow LED Task: OFF");
vTaskDelay(pdMS_TO_TICKS(blinkInterval));
}
}
void setup() {
Serial.begin(115200);
Serial.println("--- FreeRTOS Message Queue Demo ---");
// --- Initialize Pins ---
pinMode(redLedPin, OUTPUT);
pinMode(greenLedPin, OUTPUT);
pinMode(yellowLedPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP); // Use internal pull-up for the button
// --- Create the Message Queue ---
// xQueueCreate(uxQueueLength, uxItemSize)
// uxQueueLength: Number of messages the queue can hold (e.g., 10 messages)
// uxItemSize: Size of each message in bytes (e.g., sizeof(int) for an integer)
xEventQueue = xQueueCreate(10, sizeof(int));
if (xEventQueue == NULL) {
Serial.println("Error: Failed to create message queue!");
while (true); // Halt on error
}
// --- Attach Interrupt ---
attachInterrupt(digitalPinToInterrupt(buttonPin), buttonISR, FALLING);
// --- Create FreeRTOS Tasks ---
// xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask)
xTaskCreate(
redLedProducerTask,
"Red_Producer",
2048, // Stack size
NULL, // No parameters
2, // Medium priority
NULL // No task handle needed
);
xTaskCreate(
greenLedConsumerTask,
"Green_Consumer",
2048, // Stack size
NULL,
3, // High priority (so it can react quickly to messages)
NULL
);
xTaskCreate(
yellowLedTask,
"Yellow_LED_Task",
2048, // Stack size
NULL,
1, // Low priority
NULL
);
Serial.println("Queue and Tasks created. Scheduler starting...");
}
void loop() {
// In FreeRTOS applications, loop() is often empty as tasks handle main logic.
// We add a tiny delay to yield control back to the scheduler.
vTaskDelay(pdMS_TO_TICKS(10));
}