This project demonstrates task synchronization and mutual exclusion in FreeRTOS using a binary semaphore on an Arduino UNO.
Two concurrent tasks coordinate access to a shared resource—a red LED—while a pushbutton controls when the LED turns ON or OFF.
Project OverviewIn real-time systems, multiple tasks may need access to the same hardware resource. Without protection, this can lead to unpredictable behavior.
This project uses a binary semaphore to ensure that only one task can control the LED at a time. A 1-second delay inside each task simulates a critical section, making semaphore behavior easy to observe.
FreeRTOS semaphore support is enabled by including:
#include <Arduino_FreeRTOS.h>
#include <semphr.h>Hardware SetupThe project is simulated using Wokwi Simulator and includes:
- Arduino UNO running FreeRTOS
- Red LED on Pin 8
- Pushbutton on Pin 2
- Internal pull-up enabled (INPUT_PULLUP)
⚠️ The button is active LOW (pressed = LOW).
Task Roles and SynchronizationThe semaphore guarantees exclusive access to the LED.
LED ON Task (Semaphore Consumer)
This task waits for the semaphore, turns the LED ON, and simulates holding the shared resource for 1 second.
void TaskTurnOnLed(void *pvParameters) {
while (1) {
if (xSemaphoreTake(xLedSemaphore, portMAX_DELAY) == pdTRUE) {
Serial.println("Task ON: Semaphore taken");
digitalWrite(LED_RED, HIGH);
vTaskDelay(pdMS_TO_TICKS(1000)); // Simulated critical section
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}LED OFF Task (Semaphore Producer)
This task monitors the button. When pressed, it turns the LED OFF. When released, it gives the semaphore so the ON task can run again.
void TaskTurnOffLed(void *pvParameters) {
while (1) {
int buttonState = digitalRead(BUTTON_USER);
if (buttonState == LOW) {
digitalWrite(LED_RED, LOW);
Serial.println("Task OFF: LED turned OFF");
}
else {
xSemaphoreGive(xLedSemaphore);
Serial.println("Task OFF: Semaphore released");
}
vTaskDelay(pdMS_TO_TICKS(50)); // Debounce
}
}Semaphore Creation and Task Setup
The binary semaphore is created and initialized in setup(). Both tasks run at the same priority.
void setup() {
Serial.begin(9600);
pinMode(LED_RED, OUTPUT);
pinMode(BUTTON_USER, INPUT_PULLUP);
xLedSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(xLedSemaphore); // Initial availability
xTaskCreate(TaskTurnOnLed, "LED_ON", 128, NULL, 1, NULL);
xTaskCreate(TaskTurnOffLed, "LED_OFF", 128, NULL, 1, NULL);
}The loop()function is unused because FreeRTOS manages scheduling:
void loop() {}Expected Behavior- Binary semaphores provide mutual exclusion
- Tasks can safely share hardware resources
- Semaphores synchronize task execution order
- Artificial delays help visualize critical sections
- Understand how xSemaphoreTake() and xSemaphoreGive() work
- Learn to protect shared resources in FreeRTOS
- Design safer multitasking embedded applications
This project offers a clear, hands-on introduction to binary semaphores in FreeRTOS, a core concept used in professional real-time embedded systems.
That's all!If you have any questions or suggestions, don’t hesitate to leave a comment below.


_ztBMuBhMHo.jpg?auto=compress%2Cformat&w=48&h=48&fit=fill&bg=ffffff)

Comments