Embedded Systems Lab Manual
Experiment No. 1 – Interfacing Button (Switch) Controlled LEDs with an Embedded Core
Title
Interfacing a Push Button (Switch) to Control LEDs Using Arduino Uno
Aim
To interface a push button switch with an Arduino Uno and control an LED based on the button's state.
Objective
- To understand digital input and output on the Arduino Uno.
- To learn how to interface a push button with a microcontroller.
- To control an LED using a button press.
Components Required
- Arduino Uno
- Push Button (Tactile Switch)
- LED
- 220 Ω Resistor (for LED)
- 10 kΩ Resistor (for pull-down resistor, if not using internal pull-up)
- Breadboard
- Jumper Wires
- USB Cable
Theory
A push button is a digital input device that can be either in the HIGH or LOW state. When the button is pressed, it changes the input signal received by the Arduino. The Arduino reads this signal using a digital input pin and controls the LED connected to a digital output pin.
In this experiment, when the button is pressed, the Arduino turns the LED ON. When the button is released, the LED turns OFF. The Arduino continuously checks the state of the button using the digitalRead() function and controls the LED using the digitalWrite() function.
Circuit Connections
| Component | Arduino Connection |
|---|---|
| LED Anode (+) | Digital Pin 8 through 220 Ω resistor |
| LED Cathode (–) | GND |
| Push Button | One terminal to Digital Pin 2 |
| Push Button | Other terminal to +5 V |
| 10 kΩ Resistor | Between Digital Pin 2 and GND (Pull-down resistor) |
Observation
| Button State | LED State |
|---|---|
| Released | OFF |
| Pressed | ON |
Result
The push button was successfully interfaced with the Arduino Uno. The LED turned ON when the button was pressed and turned OFF when the button was released, demonstrating successful digital input and output interfacing.
Applications
- Home automation switches
- Industrial control systems
- Security alarm systems
- User input interfaces
- Electronic toys and gaming devices
Precautions
- Use the correct resistor values to protect the LED.
- Ensure all wiring connections are secure.
- Verify the polarity of the LED before connecting.
- Upload the program only after checking the circuit connections.
- Avoid short circuits on the breadboard.
Conclusion
The experiment successfully demonstrated how to interface a push button with an Arduino Uno to control an LED. It illustrates the use of digital input and output operations in Arduino and forms the basis for implementing more advanced embedded system projects involving user interaction.
Program
int led = 13;
int btn = 7;
static int led_state, prev_btn_data;
void setup() {
// put your setup code here, to run once:
pinMode(led, OUTPUT);
pinMode(btn, INPUT_PULLUP);
}
void loop() {
// put your main code here, to run repeatedly:
int btn_data = digitalRead(btn);
if (btn_data == LOW && btn_data != prev_btn_data)
{
led_state = !led_state;
}
digitalWrite(led, led_state);
prev_btn_data = btn_data;
}
Output
Experiment No. 2 – Interfacing a DHT22 Temperature and Humidity Sensor
Aim
To interface a DHT22 temperature and humidity sensor with an Arduino Uno.
Objectives
- To understand the working principle of the DHT22 sensor.
- To interface the DHT22 sensor with Arduino Uno.
- To display temperature and humidity readings on the Serial Monitor.
Apparatus Required
- Arduino Uno
- DHT22 (AM2302) Temperature and Humidity Sensor
- 10 kΩ Resistor (pull-up resistor, if using a bare sensor)
- Breadboard
- Jumper Wires
- USB Cable
- Computer with Arduino IDE
Theory
The DHT22 (AM2302) is a digital sensor capable of measuring both temperature and relative humidity. It provides calibrated digital output, eliminating the need for analog-to-digital conversion. Compared to the DHT11, the DHT22 offers higher accuracy and a wider operating range.
Specifications:
- Temperature Range: –40°C to 80°C
- Temperature Accuracy: ±0.5°C
- Humidity Range: 0–100% RH
- Humidity Accuracy: ±2–5% RH
- Operating Voltage: 3.3 V to 6 V
- Sampling Rate: One reading every 2 seconds
The sensor communicates with the Arduino using a single-wire digital communication protocol.
Circuit Connections
| DHT22 Pin | Arduino Uno |
|---|---|
| VCC | 5V |
| DATA | Digital Pin 7 |
| GND | GND |
| 10 kΩ Resistor | Between VCC and DATA (if required) |
Procedure
- Connect the DHT22 sensor to the Arduino Uno as per the circuit diagram.
- Open the Arduino IDE.
- Install the DHT Sensor Library by Adafruit using the Library Manager.
- Select Arduino Uno as the board and choose the correct COM port.
- Upload the program to the Arduino.
- Open the Serial Monitor and set the baud rate to 9600.
- Observe the temperature and humidity readings.
- Change the surrounding temperature or humidity and observe the updated values.
Observation
| Sr. No. | Temperature (°C) | Humidity (%RH) |
|---|---|---|
Result
The DHT22 sensor was successfully interfaced with the Arduino Uno. The system accurately measured and displayed the ambient temperature (°C) and relative humidity (%RH) on the Serial Monitor.
Program
#include <DHT.h>
#define DHTPIN 7
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
float hum;
float temp;
void setup() {
Serial.begin(9600);
dht.begin();
}
void loop() {
delay(2000);
hum = dht.readHumidity();
temp = dht.readTemperature();
Serial.print("Humidity:");
Serial.print(hum);
Serial.println("%");
Serial.print("Temperature:");
Serial.print(temp);
Serial.println("Celcius");
delay(1000);
}
Output
Humidity:40.00%
Temperature:24.00Celcius
Humidity:40.00%
Temperature:24.00Celcius
Humidity:40.00%
Temperature:24.00Celcius
Humidity:40.00%
Experiment No. 3 – Interfacing a 16×2 I2C LCD with Arduino Uno
Aim
To interface a 16×2 I2C LCD with the Arduino Uno and display text on the LCD.
Objective
- To understand the working of an I2C LCD module.
- To interface the I2C LCD with the Arduino Uno.
- To display text on the LCD using the LiquidCrystal_I2C library.
Components Required
- Arduino Uno
- 16×2 I2C LCD Module
- Jumper wires
Theory
An I2C LCD is a standard 16×2 LCD fitted with an I2C interface module (PCF8574). Unlike a parallel LCD that requires multiple data pins, the I2C LCD uses only two communication lines:
- SDA (Serial Data)
- SCL (Serial Clock)
This reduces the number of Arduino pins required and simplifies wiring. The default I2C address of the LCD is usually 0x27 (sometimes 0x3F).
Circuit Connections
| I2C LCD Pin | Arduino Uno Pin |
|---|---|
| GND | GND |
| VCC | 5V |
| SDA | A4 |
| SCL | A5 |
Procedure
- Open the Wokwi Simulator.
- Create a new Arduino Uno project.
- Add an I2C LCD (16×2) component.
- Connect the LCD to the Arduino:
- VCC → 5V
- GND → GND
- SDA → A4
- SCL → A5
- Copy and paste the Arduino program into the code editor.
- Start the simulation.
- Observe the displayed message on the LCD.
Observation Table
| LCD Line | Displayed Text |
|---|---|
| Line 1 | |
| Line 2 |
Result
The 16×2 I2C LCD was successfully interfaced with the Arduino Uno. The desired text was displayed correctly on the LCD.
Applications
- Temperature and humidity monitoring
- Digital clocks
- Smart home systems
- Industrial automation
- IoT-based monitoring systems
- Embedded system user interfaces
Program
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
int led = 13;
int btn = 7;
static int led_state, prev_btn_data;
void setup() {
lcd.init();
lcd.backlight();
pinMode(led, OUTPUT);
pinMode(btn, INPUT_PULLUP);
}
void loop() {
int btn_data = digitalRead(btn);
if (btn_data == LOW && btn_data != prev_btn_data)
{
led_state = !led_state;
}
digitalWrite(led, led_state);
lcd.setCursor(0, 0);
lcd.print("LED STATE:");
lcd.print(led_state);
prev_btn_data = btn_data;
}
Output
Experiment No. 4 – Interfacing a Relay Module with Arduino Uno
Aim
To interface a relay module with an Arduino Uno and use it to control an LED.
Objectives
- To understand the working of a relay.
- To learn how Arduino controls external devices through a relay.
- To switch an LED ON and OFF using a relay.
Components Required
- Arduino Uno
- 5V Relay Module
- LED
- 220 Ω Resistor
- Breadboard
- Jumper Wires
- USB Cable
Theory
A relay is an electrically operated switch that allows a low-power circuit (Arduino) to control a higher-power circuit. The relay module contains an electromagnetic coil which, when energized by the Arduino, changes the position of the switch contacts. In this experiment, the relay is used to switch an LED ON and OFF. The Arduino sends a digital signal to the relay module, causing the relay to activate and control the LED.
Circuit Connections
Relay Module to Arduino
| Relay Module Pin | Arduino Uno Pin |
|---|---|
| VCC | 5V |
| GND | GND |
| IN | Digital Pin 7 |
LED Connection
- Relay COM → 5V
- Relay NO (Normally Open) → LED Anode (+)
- LED Cathode (–) → 220 Ω Resistor → GND
Procedure
- Connect the relay module to the Arduino Uno as per the circuit.
- Connect the LED through the relay contacts using a 220 Ω resistor.
- Open the Arduino IDE.
- Write or paste the program and select the correct board and COM port.
- Upload the program to the Arduino Uno.
- Observe the relay clicking and the LED turning ON and OFF every second.
Observation
- The relay switches ON and OFF at one-second intervals.
- The LED glows when the relay is activated and turns OFF when the relay is deactivated.
- A clicking sound from the relay indicates successful switching.
Result
The relay module was successfully interfaced with the Arduino Uno, and the LED was controlled through the relay. This demonstrates how Arduino can operate external devices using a relay.
Program
const int relayPin = 7;
void setup() {
pinMode(relayPin, OUTPUT);
}
void loop() {
digitalWrite(relayPin, HIGH);
delay(1000);
digitalWrite(relayPin, LOW);
delay(1000);
}
Output
Experiment No. 5 – Interfacing a DC Motor with Arduino Uno for Speed and Direction Control
1. Aim
To interface a DC motor with an Arduino Uno and control its speed and direction of rotation using a motor driver.
2. Apparatus Required
- Arduino Uno
- DC motor
- L293D motor driver IC/module
- External DC power supply/battery
- Connecting wires
- Breadboard
- USB cable
- Computer with Arduino IDE
3. Theory
A DC motor converts electrical energy into mechanical energy and rotates when a suitable voltage is applied across its terminals.
The direction of rotation of a DC motor can be changed by reversing the polarity of the voltage applied to its terminals. Since an Arduino Uno cannot directly supply the current required by a DC motor, a motor driver such as L293D is used between the Arduino and the motor.
The L293D acts as an interface between the low-power Arduino output and the motor. It allows the motor to rotate in both directions.
Direction Control
The direction of the motor is controlled using the input pins of the motor driver:
| IN1 | IN2 | Motor Direction |
|---|---|---|
| LOW | LOW | Stop |
| HIGH | LOW | Clockwise |
| LOW | HIGH | Anticlockwise |
| HIGH | HIGH | Stop/Brake |
Speed Control
The speed of the motor can be controlled using PWM (Pulse Width Modulation). Arduino generates a PWM signal using the analogWrite() function. The PWM duty cycle determines the average voltage supplied to the motor:
analogWrite(0)→ Motor OFFanalogWrite(128)→ Approximately 50% duty cycleanalogWrite(255)→ Approximately 100% duty cycle
Thus, changing the PWM value changes the speed of the motor.
4. Circuit Connections
For one DC motor using an L293D:
| Arduino Uno | L293D | Function |
|---|---|---|
| Pin 9 (PWM) | EN1 | Speed control |
| Pin 8 | IN1 | Direction control |
| Pin 7 | IN2 | Direction control |
| — | OUT1 | Motor terminal 1 |
| — | OUT2 | Motor terminal 2 |
| 5V | VCC1 | Logic supply |
| External motor supply | VCC2 | Motor supply |
| GND | GND | Common ground |
The two motor terminals are connected to OUT1 and OUT2 of the L293D.
Important: The Arduino ground and the motor-driver ground must be connected together. The motor should preferably be powered from a suitable external supply rather than directly from an Arduino I/O pin.
Procedure
- Connect the L293D motor driver to the Arduino Uno according to the circuit connections.
- Connect the DC motor to the output terminals of the L293D.
- Connect a suitable external power supply to the motor supply input.
- Ensure that the Arduino and motor-driver grounds are common.
- Connect the Arduino Uno to the computer using a USB cable.
- Open the Arduino IDE and enter the program.
- Select the appropriate Arduino Uno board and COM port.
- Upload the program to the Arduino.
- Observe the motor rotating in the clockwise direction.
- Observe the motor stopping and then rotating in the anticlockwise direction.
- Observe the change in motor speed when different PWM values are applied.
8. Observation
| PWM Value | Approx. Duty Cycle | Motor Speed |
|---|---|---|
| 0 | 0% | OFF |
| 64 | 25% | Low |
| 128 | 50% | Medium |
| 192 | 75% | High |
| 255 | 100% | Maximum |
The motor rotates in one direction when IN1 is HIGH and IN2 is LOW. Reversing these signals causes the motor to rotate in the opposite direction.
9. Result
The DC motor was successfully interfaced with the Arduino Uno using an L293D motor driver. The direction of rotation was controlled by digital signals, while the speed was controlled by PWM generated by the Arduino.
Program A – Speed and Direction Control using Potentiometer
const int potPin = A0;
void setup()
{
pinMode(11, OUTPUT);
pinMode(9, OUTPUT);
}
void loop()
{
int potValue = analogRead(potPin);
analogWrite(9, 0);
analogWrite(11, potValue);
delay(4000);
analogWrite(9, 0);
analogWrite(11, 0);
delay(2000);
analogWrite(9, potValue);
analogWrite(11, 0);
delay(4000);
}
Output A
Program B – Direction Control
void setup()
{
pinMode(11, OUTPUT);
pinMode(9, OUTPUT);
}
void loop()
{
digitalWrite(11, HIGH);
digitalWrite(9, LOW);
delay(1000); // Wait for 1000 millisecond(s)
digitalWrite(11, LOW);
digitalWrite(9, LOW);
delay(1000);
digitalWrite(11, LOW);
digitalWrite(9, HIGH);
delay(1000);
}
Output B
Experiment No. 6 – Interfacing a Stepper Motor (to Move by a Particular Angle) with an Embedded Core
Aim
To control a stepper motor using an Arduino and the Stepper library by rotating it one revolution in the clockwise direction and one revolution in the anticlockwise direction, with a delay of 500 milliseconds between the rotations.
Components Required
- Arduino board
- Stepper motor
- Motor driver (if required by the motor)
- Connecting wires
- USB cable
- Arduino IDE
Circuit Connections
| Stepper Motor Pins | Arduino Pins |
|---|---|
| IN1 | Pin 8 |
| IN2 | Pin 9 |
| IN3 | Pin 10 |
| IN4 | Pin 11 |
Note: The connections depend on the stepper motor and driver being used.
Working Principle
The program uses the Arduino Stepper library to control the rotation of a stepper motor. The number of steps per revolution is set to 200, and the motor speed is configured to 60 RPM.
The setup() function initializes serial communication at 9600 baud and sets the motor speed. In the loop() function, the motor rotates one revolution clockwise using myStepper.step(stepsPerRevolution). After a delay of 500 milliseconds, it rotates one revolution in the opposite direction using myStepper.step(-stepsPerRevolution).
This process repeats continuously, allowing the motor to rotate alternately in both directions.
Observation
- The stepper motor is initialized with 200 steps per revolution.
- The motor speed is set to 60 RPM.
- The motor rotates one complete revolution in the clockwise direction.
- A delay of 500 milliseconds occurs between the rotations.
- The motor then rotates one complete revolution in the anticlockwise direction.
- The clockwise and anticlockwise rotation cycle repeats continuously.
Result
The stepper motor was successfully controlled using the Arduino Stepper library. The motor rotated one revolution clockwise and one revolution anticlockwise, with a delay of 500 milliseconds between the two movements.
Conclusion
The experiment demonstrates the control of a stepper motor using an Arduino and the Stepper library. The motor's speed and direction of rotation were controlled by setting the RPM and the number of steps. The experiment successfully demonstrated bidirectional motor rotation and the use of programmed delays to control motor movement.
Program
#include <Stepper.h>
const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution
// for your motor
// initialize the stepper library on pins 8 through 11:
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);
void setup() {
// set the speed at 60 rpm:
myStepper.setSpeed(60);
// initialize the serial port:
Serial.begin(9600);
}
void loop() {
// step one revolution in one direction:
Serial.println("clockwise");
myStepper.step(stepsPerRevolution);
delay(500);
// step one revolution in the other direction:
Serial.println("counterclockwise");
myStepper.step(-stepsPerRevolution);
delay(500);
}
Output
clockwise
counterclockwise
clockwise
counterclockwise
clockwise
Experiment No. 7 – Implementation of Power Management in STM32 Blue Pill
Aim
To implement power management in the STM32 Blue Pill using Sleep mode, with an LED connected to PC13 and a push button connected to PA0.
Components Required
- STM32 Blue Pill (STM32F103C8T6)
- LED
- 220 Ω resistor
- Push button
- Jumper wires
Circuit Connections
| Component | STM32 Pin |
|---|---|
| LED | PC13 |
| Push Button | PA0 |
| LED other terminal | GND |
| Push button other terminal | 3.3 V |
PA0 is configured with an internal pull-down resistor.
Working Principle
When the STM32 is operating normally, the LED connected to PC13 is turned ON. After a short delay, the microcontroller enters Sleep mode using the WFI instruction. In Sleep mode, the CPU stops executing instructions.
When the push button connected to PA0 is pressed, an external interrupt is generated. The interrupt wakes the STM32 from Sleep mode, and the program continues execution. Thus, the push button acts as the wake-up source.
Observation
- Initially, the LED connected to PC13 turns ON.
- After 1 second, the STM32 enters Sleep mode.
- The CPU waits for an interrupt.
- When the PA0 push button is pressed, an interrupt is generated.
- The STM32 wakes up and continues program execution.
- The LED then changes state.
Result
The power-management operation of the STM32 Blue Pill was successfully simulated in Wokwi using PC13 as the LED output and PA0 as the push-button wake-up input.
Conclusion
The experiment demonstrates how Sleep mode and external interrupts can be used to reduce CPU activity and save power in an STM32-based embedded system. The push button connected to PA0 provides an effective wake-up mechanism.
Program
#include <Arduino.h>
#define LED_PIN PC13
#define BTN_PIN PA0
volatile bool wakeup = false;
void wakeUp() {
wakeup = true;
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(BTN_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BTN_PIN), wakeUp, FALLING);
}
void loop() {
digitalWrite(LED_PIN, LOW);
delay(1000);
digitalWrite(LED_PIN, HIGH);
delay(1000);
wakeup = false;
while (!wakeup) {
__WFI();
}
}
Output
Experiment No. 8 – Creating Multiple Tasks Using FreeRTOS
1. Aim
To create multiple tasks using FreeRTOS and understand the multitasking capability of an RTOS by executing different tasks concurrently.
2. Objective
After completing this experiment, you will be able to:
- Understand the concept of tasks in FreeRTOS.
- Create multiple tasks using
xTaskCreate(). - Understand task scheduling.
- Use
vTaskDelay()to control task execution. - Observe how FreeRTOS allows multiple tasks to share the CPU.
- Understand the difference between sequential execution and RTOS-based multitasking.
3. Requirements
For a simple simulation, you can use:
- Arduino Uno / ESP32 / STM32
- Wokwi simulator
- FreeRTOS library
- LEDs
- Resistors
- Serial Monitor
4. Theory
What is an RTOS? RTOS (Real-Time Operating System) is an operating system designed to execute tasks within predictable timing constraints.
Examples: FreeRTOS, Zephyr, ThreadX, VxWorks.
FreeRTOS is widely used in embedded systems because it provides mechanisms for:
- Task management
- Scheduling
- Inter-task communication
- Synchronization
- Timers
- Memory management
What is a Task? A task is an independent unit of execution. For example, an embedded system may have:
- Task 1 → Blink LED
- Task 2 → Read Sensor
- Task 3 → Send data through Serial
FreeRTOS manages these tasks using its scheduler.
5. Multitasking Concept
Suppose we create three tasks:
- Task 1 → LED 1 blinking
- Task 2 → LED 2 blinking
- Task 3 → Display message on Serial Monitor
The CPU switches between these tasks according to the FreeRTOS scheduler. All three tasks execute under the control of the FreeRTOS scheduler.
Observation
It is observed that multiple tasks can be created and managed by FreeRTOS. When one task is delayed or blocked, the scheduler allows other ready tasks to execute. Therefore, different operations appear to run concurrently.
Result
Thus, multiple tasks were successfully created using FreeRTOS. The multitasking capability of FreeRTOS was demonstrated by simultaneously controlling two LEDs and periodically displaying messages through the Serial Monitor.
Code
#include <Arduino.h>
#include <Arduino_FreeRTOS.h>
#define LED1 2
#define LED2 3
void Task1(void *parameter)
{
pinMode(LED1, OUTPUT);
while (1)
{
digitalWrite(LED1, HIGH);
Serial.println("Task 1: LED 1 ON");
vTaskDelay(pdMS_TO_TICKS(500));
digitalWrite(LED1, LOW);
Serial.println("Task 1: LED 1 OFF");
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void Task2(void *parameter)
{
pinMode(LED2, OUTPUT);
while (1)
{
digitalWrite(LED2, HIGH);
Serial.println("Task 2: LED 2 ON");
vTaskDelay(pdMS_TO_TICKS(1000));
digitalWrite(LED2, LOW);
Serial.println("Task 2: LED 2 OFF");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void Task3(void *parameter)
{
while (1)
{
Serial.println("Task 3: FreeRTOS Multitasking running...");
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
void setup()
{
Serial.begin(115200);
xTaskCreate(Task1, "LED1 Task", 128, NULL, 1, NULL);
xTaskCreate(Task2, "LED2 Task", 128, NULL, 1, NULL);
xTaskCreate(Task3, "Serial Task", 256, NULL, 1, NULL);
}
void loop()
{
// FreeRTOS scheduler runs tasks automatically
}
Output
Task 2: LED 2 ON
Task 1: LED 1 ON
Task 1: LED 1 OFF
Task 2: LED 2 OFF
Task 1: LED 1 ON
Task 1: LED 1 OFF
Task 3: FreeRTOS Multitasking running...
Experiment No. 9 – Queue Management in FreeRTOS
Aim
To write a program to illustrate the queue management features of FreeRTOS, including creating a queue, sending data to the queue, receiving data from the queue, and demonstrating communication between tasks.
Objectives
- To understand the concept of queues in FreeRTOS.
- To create and initialize a queue.
- To send data from one task to another using a queue.
- To receive data from a queue.
- To understand task synchronization using queues.
Theory
A queue in FreeRTOS is an inter-task communication mechanism used to transfer data between tasks. A queue follows the FIFO (First-In, First-Out) principle.
A queue can be created using:
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );
Important queue functions include:
xQueueCreate()– Creates a queue.xQueueSend()– Sends an item to the queue.xQueueReceive()– Receives an item from the queue.uxQueueMessagesWaiting()– Returns the number of items currently in the queue.vQueueDelete()– Deletes a queue.
Queues are particularly useful when one task produces data and another task consumes it. They also provide synchronization between tasks.
Working
- A queue capable of storing 5 integer values is created using
xQueueCreate(). - The Producer Task generates integer data and sends it to the queue using
xQueueSend(). - The Consumer Task waits for data using
xQueueReceive(). - When data is available, the consumer receives and displays it.
- If the queue becomes full,
xQueueSend()can fail after the specified timeout. portMAX_DELAYallows the consumer task to wait until an item becomes available.- The FreeRTOS scheduler manages the execution of both tasks.
Result
The program successfully demonstrates FreeRTOS queue management by creating a queue and using it for communication between a producer task and a consumer task. The experiment demonstrates queue creation, data insertion, data retrieval, FIFO operation, and task synchronization.
Conclusion
Thus, the queue management features of FreeRTOS were successfully illustrated. Queues provide a safe and efficient mechanism for exchanging data between tasks and are widely used in real-time embedded applications.
Program
#include <Arduino.h>
#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <semphr.h>
#include <stdlib.h>
// Queue and synchronization handles
QueueHandle_t dataQueue;
SemaphoreHandle_t serialMutex;
SemaphoreHandle_t receivedSemaphore;
// Task declarations
void ProducerTask(void *pvParameters);
void ConsumerTask(void *pvParameters);
void setup()
{
Serial.begin(9600);
// Queue stores 5 integers
dataQueue = xQueueCreate(5, sizeof(int));
// Mutex protects Serial output
serialMutex = xSemaphoreCreateMutex();
// Consumer signals when a number is received
receivedSemaphore = xSemaphoreCreateBinary();
if (dataQueue == NULL ||
serialMutex == NULL ||
receivedSemaphore == NULL)
{
Serial.println("Queue or semaphore creation failed!");
while (1);
}
Serial.println("Queue created successfully.");
Serial.println("Enter numbers separated by spaces or commas:");
xTaskCreate(ProducerTask, "Producer", 128, NULL, 1, NULL);
xTaskCreate(ConsumerTask, "Consumer", 128, NULL, 1, NULL);
}
void loop()
{
// FreeRTOS tasks run independently
}
// Producer Task: Read and send numbers one at a time
void ProducerTask(void *pvParameters)
{
char input[12];
int index = 0;
while (1)
{
while (Serial.available() > 0)
{
char ch = Serial.read();
if (ch == ' ' || ch == ',' ||
ch == '\n' || ch == '\r' || ch == '\t')
{
if (index > 0)
{
input[index] = '\0';
int number = atoi(input);
index = 0;
// Display entered number
xSemaphoreTake(serialMutex, portMAX_DELAY);
Serial.print("Entered: ");
Serial.println(number);
xSemaphoreGive(serialMutex);
// Send number to queue
if (xQueueSend(dataQueue, &number,
portMAX_DELAY) == pdPASS)
{
// Wait until Consumer receives it
xSemaphoreTake(receivedSemaphore, portMAX_DELAY);
}
}
}
else if (ch >= '0' && ch <= '9')
{
if (index < 11)
{
input[index++] = ch;
}
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
// Consumer Task: Receive and acknowledge each number
void ConsumerTask(void *pvParameters)
{
int receivedNumber;
while (1)
{
if (xQueueReceive(dataQueue, &receivedNumber,
portMAX_DELAY) == pdPASS)
{
xSemaphoreTake(serialMutex, portMAX_DELAY);
Serial.print("Received: ");
Serial.println(receivedNumber);
xSemaphoreGive(serialMutex);
// Notify Producer that this number was received
xSemaphoreGive(receivedSemaphore);
}
}
}
Experiment No. 10 – Task Synchronization Using Binary and Counting Semaphores
Aim
To write and execute a FreeRTOS program in Wokwi that demonstrates task synchronization using:
- Binary Semaphore
- Counting Semaphore
The experiment shows how semaphores allow multiple FreeRTOS tasks to coordinate access to shared resources and events.
Software/Hardware Requirements
- Wokwi online simulator
- ESP32 development board
- FreeRTOS (included with the ESP32 Arduino framework)
- Arduino IDE-compatible code
Theory
Binary Semaphore
A binary semaphore can have only two states:
- Available (1)
- Not available (0)
It is commonly used for task synchronization. For example, one task can generate an event and give the semaphore, while another task waits for that event.
In FreeRTOS:
xSemaphoreGive(binarySemaphore);
is used to release/give the semaphore, while:
xSemaphoreTake(binarySemaphore, portMAX_DELAY);
waits until the semaphore becomes available.
Counting Semaphore
A counting semaphore can have a value ranging from 0 to a specified maximum count. It is useful when there are multiple identical resources or multiple events that need to be counted.
For example, if a counting semaphore has a maximum count of 3, three semaphore tokens can be available simultaneously.
xSemaphoreCreateCounting(3, 0);
Here: 3 = maximum count, 0 = initial count.
Circuit in Wokwi
Use an ESP32 with two LEDs:
| Component | ESP32 Pin |
|---|---|
| Binary Semaphore LED | GPIO 2 |
| Counting Semaphore LED | GPIO 4 |
Connect each LED through a 220 Ω resistor to GND.
Observation
- The binary semaphore synchronizes the binary producer and binary consumer tasks.
- The binary consumer executes only after the producer gives the semaphore.
- The counting semaphore maintains a count of available tokens.
- The maximum counting semaphore value in this experiment is 3.
- FreeRTOS tasks execute concurrently under the control of the FreeRTOS scheduler.
- LEDs provide a visual indication that the corresponding consumer task has received a semaphore.
Result
The program was successfully designed to demonstrate task synchronization using Binary and Counting Semaphores in FreeRTOS. The binary semaphore was used to synchronize an event between two tasks, while the counting semaphore was used to maintain multiple semaphore tokens. The experiment can be implemented and simulated using an ESP32 in Wokwi.
Conclusion
This experiment demonstrates that semaphores are important synchronization mechanisms in FreeRTOS. A binary semaphore is suitable when a task needs to signal a single event, whereas a counting semaphore is useful when multiple events or resources must be tracked. Using these mechanisms prevents tasks from operating without the required synchronization and makes multitasking systems more reliable.
Program
#include <Arduino.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
// Semaphore handles
SemaphoreHandle_t binarySemaphore;
SemaphoreHandle_t countingSemaphore;
// LED pins
#define BINARY_LED 2
#define COUNTING_LED 4
// ----------------------------------------------------
// Task 1: Generates events for Binary Semaphore
// ----------------------------------------------------
void binaryProducer(void *parameter)
{
while (1)
{
Serial.println("Binary Producer: Event generated");
// Give binary semaphore
xSemaphoreGive(binarySemaphore);
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
// ----------------------------------------------------
// Task 2: Waits for Binary Semaphore
// ----------------------------------------------------
void binaryConsumer(void *parameter)
{
while (1)
{
// Wait for binary semaphore
if (xSemaphoreTake(binarySemaphore, portMAX_DELAY) == pdTRUE)
{
Serial.println("Binary Consumer: Semaphore received");
digitalWrite(BINARY_LED, HIGH);
vTaskDelay(pdMS_TO_TICKS(500));
digitalWrite(BINARY_LED, LOW);
}
}
}
// ----------------------------------------------------
// Task 3: Generates events for Counting Semaphore
// ----------------------------------------------------
void countingProducer(void *parameter)
{
while (1)
{
Serial.println("Counting Producer: Adding token");
// Give counting semaphore
if (xSemaphoreGive(countingSemaphore) == pdTRUE)
{
Serial.println("Counting Semaphore: Token added");
}
else
{
Serial.println("Counting Semaphore: Maximum count reached");
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// ----------------------------------------------------
// Task 4: Takes tokens from Counting Semaphore
// ----------------------------------------------------
void countingConsumer(void *parameter)
{
while (1)
{
// Wait for a counting semaphore token
if (xSemaphoreTake(countingSemaphore, portMAX_DELAY) == pdTRUE)
{
Serial.println("Counting Consumer: Token received");
digitalWrite(COUNTING_LED, HIGH);
vTaskDelay(pdMS_TO_TICKS(300));
digitalWrite(COUNTING_LED, LOW);
vTaskDelay(pdMS_TO_TICKS(700));
}
}
}
// ----------------------------------------------------
// Setup
// ----------------------------------------------------
void setup()
{
Serial.begin(115200);
pinMode(BINARY_LED, OUTPUT);
pinMode(COUNTING_LED, OUTPUT);
// Create Binary Semaphore
binarySemaphore = xSemaphoreCreateBinary();
// Create Counting Semaphore
// Maximum count = 3
// Initial count = 0
countingSemaphore = xSemaphoreCreateCounting(3, 0);
if (binarySemaphore == NULL || countingSemaphore == NULL)
{
Serial.println("Semaphore creation failed!");
while (1);
}
Serial.println("Semaphores created successfully.");
// Create FreeRTOS tasks
xTaskCreate(binaryProducer, "Binary Producer", 2048, NULL, 1, NULL);
xTaskCreate(binaryConsumer, "Binary Consumer", 2048, NULL, 1, NULL);
xTaskCreate(countingProducer, "Counting Producer", 2048, NULL, 1, NULL);
xTaskCreate(countingConsumer, "Counting Consumer", 2048, NULL, 1, NULL);
}
// ----------------------------------------------------
// Loop
// ----------------------------------------------------
void loop()
{
// FreeRTOS tasks perform the actual work.
vTaskDelay(pdMS_TO_TICKS(1000));
}
