// vTaskDelayUntil reference: https://github.com/greiman/FreeRTOS-Arduino/blob/master/libraries/FreeRTOS_ARM/examples/FreeRTOSBook/Example005/Example005.ino
// vTaskDelete reference: https://microcontrollerslab.com/freertos-arduino-how-to-delete-tasks-with-vtaskdelete-api/
// vTaskDelay reference: https://www.freertos.org/a00127.html
// Semaphore API reference: https://www.freertos.org/a00113.html
#define SERIAL_BAUD_RATE 4000000
// Delay in ms for each task
#define MS_TASK_DELAY 200
// Define the offset of ticks between each task start
#define TICK_OFFSET_PER_TASK 2
// The pins to the buttons
#define GREEN_LED_PIN 2
#define RED_LED_PIN 3
// The pins to the leds
#define RED_BUTTON_PIN 11
#define GREEN_BUTTON_PIN 12
#define BLACK_BUTTON_PIN 13
// The parameters to the green led toggle rate
#define MIN_DELAY (unsigned long)100
#define MAX_DELAY (unsigned long)2000
#define INITIAL_DELAY (unsigned long)1000
#define DELAY_STEP 100
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
#include "Button.h"
// Track the state of the leds
int greenLedState = LOW;
int redLedState = LOW;
unsigned int tickOffset = 0;
// Used to track time for green led blink
unsigned long greenLedDelay = INITIAL_DELAY;
// Button objects
Button blackButton(BLACK_BUTTON_PIN);
Button redButton(RED_BUTTON_PIN);
Button greenButton(GREEN_BUTTON_PIN);
// Used to handle non-permanent task
TaskHandle_t greenLedBlinkHandle = NULL;
// Semaphore handler
SemaphoreHandle_t xSemaphore = NULL;
// Read the state of the black button
// On press change the state of the red led
void readBlackButton(void *pvParameters){
(void) pvParameters;
TickType_t xLastWakeTime;
// Delay for appropriate sequential start
xSemaphoreTake(xSemaphore, portMAX_DELAY);
vTaskDelay(tickOffset);
tickOffset += TICK_OFFSET_PER_TASK;
xSemaphoreGive(xSemaphore);
xLastWakeTime = xTaskGetTickCount();
for(;;){
// Update the state of the button
blackButton.scanButtonState();
// Check if the button has been pressed once
if(blackButton.getButtonPressed()){
// Toggle the state of the red led
xSemaphoreTake(xSemaphore, portMAX_DELAY);
if(redLedState == LOW){
redLedState = HIGH;
if(greenLedBlinkHandle != NULL){
vTaskDelete(greenLedBlinkHandle);
greenLedBlinkHandle = NULL;
}
}
else{
redLedState = LOW;
xTaskCreate(greenLedBlink, "Green Led Blink", 128, NULL, 3, &greenLedBlinkHandle);
}
digitalWrite(RED_LED_PIN, redLedState);
xSemaphoreGive(xSemaphore);
}
vTaskDelayUntil(&xLastWakeTime, MS_TASK_DELAY / portTICK_PERIOD_MS);
}
}
void readDelayChanges(void *pvParameters){
(void) pvParameters;
TickType_t xLastWakeTime;
xSemaphoreTake(xSemaphore, portMAX_DELAY);
vTaskDelay(tickOffset);
tickOffset += TICK_OFFSET_PER_TASK;
xSemaphoreGive(xSemaphore);
xLastWakeTime = xTaskGetTickCount();
for(;;){
greenButton.scanButtonState();
redButton.scanButtonState();
// Decrease the delay until the minimum limit
if(redButton.getButtonPressed()){
xSemaphoreTake(xSemaphore, portMAX_DELAY);
greenLedDelay -= DELAY_STEP;
if(greenLedDelay < MIN_DELAY){
greenLedDelay = MIN_DELAY;
}
printf("Delay: %ld\n\r", greenLedDelay);
}
// Increase the delay until the maximum limit
if(greenButton.getButtonPressed()){
greenLedDelay += DELAY_STEP;
if(greenLedDelay > MAX_DELAY){
greenLedDelay = MAX_DELAY;
}
printf("Delay: %ld\n\r", greenLedDelay);
}
xSemaphoreGive(xSemaphore);
vTaskDelayUntil(&xLastWakeTime, MS_TASK_DELAY / portTICK_PERIOD_MS);
}
}
void greenLedBlink(void *pvParameters){
(void) pvParameters;
TickType_t xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
for(;;){
xSemaphoreTake(xSemaphore, portMAX_DELAY);
if(redLedState == LOW){
if(greenLedState == LOW){
greenLedState = HIGH;
}else{
greenLedState = LOW;
}
digitalWrite(GREEN_LED_PIN, greenLedState);
vTaskDelayUntil(&xLastWakeTime, greenLedDelay / portTICK_PERIOD_MS);
}
xSemaphoreGive(xSemaphore);
}
}
// When the green and the red button are pressed at the same time
// Print the state of the system
// In Wokwi Ctr+Click will set the button state to DOWN
void getAllStates(void *pvParameters){
(void) pvParameters;
TickType_t xLastWakeTime;
xSemaphoreTake(xSemaphore, portMAX_DELAY);
vTaskDelay(tickOffset);
tickOffset += TICK_OFFSET_PER_TASK;
xSemaphoreGive(xSemaphore);
xLastWakeTime = xTaskGetTickCount();
for(;;){
xSemaphoreTake(xSemaphore, portMAX_DELAY);
if(redButton.getButtonDown() && greenButton.getButtonDown()){
if(redLedState == LOW){
printf("The Red Led is currently off\n\r");
printf("The Green Led is curently in the blinking state\n\r");
}else{
printf("The Red Led is currently on\n\r");
printf("The Green Led is currenly not in the blinking state\n\r");
}
if(greenLedState == LOW){
printf("The Green Led is currently off\n\r");
}else{
printf("The Red Led is currently on\n\r");
}
printf("Current blink delay: %ld\n\r", greenLedDelay);
}
xSemaphoreGive(xSemaphore);
vTaskDelayUntil(&xLastWakeTime, MS_TASK_DELAY / portTICK_PERIOD_MS);
}
}
void setup() {
blackButton.setup();
redButton.setup();
greenButton.setup();
Serial.begin(SERIAL_BAUD_RATE);
redirectSerialOutput();
xSemaphore = xSemaphoreCreateMutex();
// Set the red led to inital state
xSemaphoreTake(xSemaphore, portMAX_DELAY);
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(RED_LED_PIN, OUTPUT);
redLedState = HIGH;
digitalWrite(RED_LED_PIN, redLedState);
xSemaphoreGive(xSemaphore);
// Create the button read tasks with highest priority (3)
xTaskCreate(readBlackButton, "Read the state of the black button", 128, NULL, 3, NULL);
xTaskCreate(readDelayChanges, "Read if delay has been changed", 128, NULL, 3, NULL);
xTaskCreate(getAllStates, "Print all the states", 128, NULL, 3, NULL);
// Start task execution
vTaskStartScheduler();
}
void loop() {}