/* --------------------------------------------------------------
Application: 04 - Rev1
Release Type: Inter-task communication and web integration
Class: Real Time Systems - Su 2025
Author: [Daniel Bakos]
Email: [[email protected]]
Company: [University of Central Florida]
Website: wokwi.com
AI Use: Please commented inline where you use(d) AI
Website Link: http://localhost:9080
---------------------------------------------------------------*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <uri/UriBraces.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
// Defining the WiFi channel speeds up the connection:
#define WIFI_CHANNEL 6
WebServer server(80);
#define LED1 26
#define LED2 27
#define BUTTON_PIN GPIO_NUM_18
#define POT_ADC_CHANNEL GPIO_NUM_34 // GPIO34
bool led1State = false;
bool led2State = false;
bool in_alert_state = false;
#define SENSOR_THRESHOLD 3000 //Sensor Threshold when it tiggers an alert
#define MAX_COUNT_SEM 10 //The size of the counting semaphore
volatile int SEMCNT = 0;
// Handles for synchronization primatives - Initialized in setup()
SemaphoreHandle_t sem_button;
SemaphoreHandle_t sem_sensor;
SemaphoreHandle_t print_mutex;
SemaphoreHandle_t state_mutex;
void sendHtml() {
String response = R"(
<!DOCTYPE html><html>
<head>
<title>ESP32 Web Server Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html { font-family: sans-serif; text-align: center; }
body { display: inline-flex; flex-direction: column;
background-image: url(https://www.goodfreephotos.com/albums/astrophotography/starry-milky-way-galaxy.jpg);
background-size: 100% 100%; background-repeat: no-repeat; height: 100vh; background-position-y: center}
h1 { margin-bottom: 1.2em; color: #fff}
h2 { margin: 0; color: #fff}
div { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: auto auto; grid-auto-flow: column; grid-gap: 1em; }
.btn { background-color: #98f5f5; border: 3px solid #98f5f5; color: #333; border-bottom-left-radius: 8px; border-top-right-radius: 8px;
padding: 0.5em 1em; font-size: 2em; text-decoration: none }
.btn.OFF { background-color: #333; color: #fff}
</style>
</head>
<body>
<h1>ESP32 Web Server</h1>
<div>
<h2>LED 1</h2>
<a href="/toggle/1" class="btn LED1_TEXT">LED1_TEXT</a>
<h2>LED 2</h2>
<a href="/toggle/2" class="btn LED2_TEXT">LED2_TEXT</a>
</div>
</body>
</html>
)";
response.replace("LED1_TEXT", led1State ? "ON" : "OFF");
response.replace("LED2_TEXT", led2State ? "ON" : "OFF");
server.send(200, "text/html", response);
}
void initializeWebpage() {
WiFi.begin(WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
Serial.print("Connecting to WiFi ");
Serial.print(WIFI_SSID);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(100);
Serial.print(".");
}
Serial.println(" Connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.on("/", sendHtml);
server.on(UriBraces("/toggle/{}"), []() {
String led = server.pathArg(0);
Serial.print("Toggle LED #");
Serial.println(led);
switch (led.toInt()) {
case 1:
led1State = !led1State;
digitalWrite(LED1, led1State);
break;
case 2:
led2State = !led2State;
digitalWrite(LED2, led2State);
break;
}
sendHtml();
});
server.begin();
Serial.println("HTTP server started");
}
void statusBeaconTask(void* arg)
{
TickType_t lastAwakeTime = xTaskGetTickCount();
bool led_state = 1;
while (1) {
digitalWrite(LED2, led_state);
led_state = !led_state;
vTaskDelayUntil(&lastAwakeTime, pdMS_TO_TICKS(1000));
}
}
void sensorMonitorTask(void* arg)
{
while (1) {
int val = analogRead(POT_ADC_CHANNEL);
TickType_t lastAwakeTime = xTaskGetTickCount();
if (val > SENSOR_THRESHOLD) {
//if(SEMCNT < MAX_COUNT_SEM+1) SEMCNT++; // DO NOT REMOVE THIS LINE
xSemaphoreGive(sem_sensor); // Signal sensor event
}
vTaskDelayUntil(&lastAwakeTime, pdMS_TO_TICKS(17));
}
}
void eventResponseTask(void* arg)
{
while (1) {
if (xSemaphoreTake(sem_sensor, 0)) {
SEMCNT--; // DO NOT MODIFY THIS LINE
xSemaphoreTake(print_mutex, portMAX_DELAY);
Serial.print("Sensor event: Threshold exceeded!");
//Serial.print(SEMCNT);
//Serial.println(" times!");
xSemaphoreGive(print_mutex);
//If the sensor isn't already in an alert state, set it in one
if (!in_alert_state && xSemaphoreTake(state_mutex, pdMS_TO_TICKS(10)))
{
in_alert_state = true;
xSemaphoreGive(state_mutex);
}
digitalWrite(LED1, 1);
vTaskDelay(pdMS_TO_TICKS(100));
digitalWrite(LED1, 0);
}
if (xSemaphoreTake(sem_button, 0)) {
xSemaphoreTake(print_mutex, portMAX_DELAY);
Serial.println("Button event: Button Pressed!");
xSemaphoreGive(print_mutex);
if (xSemaphoreTake(state_mutex, pdMS_TO_TICKS(10)))
{
in_alert_state = !in_alert_state;
Serial.print("State set to ");
Serial.println(in_alert_state ? "ALERT" : "NORMAL");
xSemaphoreGive(state_mutex);
}
digitalWrite(LED1, 1);
vTaskDelay(pdMS_TO_TICKS(300));
digitalWrite(LED1, 0);
}
vTaskDelay(pdMS_TO_TICKS(10)); // Idle delay to yield CPU
}
}
void buttonWatchTask(void* arg) {
static TickType_t lastInterruptTime = 0;
static bool pressed = false;
while (1) {
int state = digitalRead(BUTTON_PIN);
TickType_t now = xTaskGetTickCount();
//Debounce and check for falling edge
if (!pressed && state == 0 && (now - lastInterruptTime) > pdMS_TO_TICKS(50)){
pressed = true;
xSemaphoreGive(sem_button);
if (xSemaphoreTake(print_mutex, pdMS_TO_TICKS(10)))
{
xSemaphoreGive(print_mutex);
}
lastInterruptTime = now;
}
else if (state == 1 && pressed)
{
pressed = false;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void IRAM_ATTR buttonISR() {
static unsigned long lastInterruptTime = 0;
unsigned long now = millis(); // Use millis() in ISR for debounce
if (now - lastInterruptTime > 50) {
lastInterruptTime = now;
BaseType_t higherWoken = pdFALSE;
xSemaphoreGiveFromISR(sem_button, &higherWoken);
portYIELD_FROM_ISR(higherWoken);
}
}
void setup(void) {
Serial.begin(115200);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
//Initialize Primatives
sem_button = xSemaphoreCreateBinary();
sem_sensor = xSemaphoreCreateCounting(MAX_COUNT_SEM, 0);
print_mutex = xSemaphoreCreateMutex();
state_mutex = xSemaphoreCreateMutex();
//Initialize button interrupt
pinMode(BUTTON_PIN, INPUT_PULLUP);
//Initialize Tasks
xTaskCreate(statusBeaconTask, "StatusBeaconTask", 1024, NULL, 1, NULL);
xTaskCreate(sensorMonitorTask, "SensorMonitorTask", 2048, NULL, 2, NULL);
xTaskCreate(eventResponseTask, "EventResponseTask", 2048, NULL, 3, NULL);
xTaskCreate(buttonWatchTask, "ButtonWatchTask", 2048, NULL, 4, NULL);
Serial.println("Successfully made tasks");
//Initialize Webpage
initializeWebpage();
}
void loop(void) {
server.handleClient();
delay(2);
}