// I ran into library errors and had to add the libraries.txt file
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "freertos/semphr.h"
#include "driver/adc.h"
#include "math.h"
#include "esp_log.h"
//TODO 0a
#define GREEN_LED_PIN GPIO_NUM_2 // Using GPIO2 for the GREEN LED
#define RED_LED_PIN GPIO_NUM_0 // Using GPIO0 for the RED LED
#define SENSOR_ADC_CHANNEL ADC1_CHANNEL_6
#define BUTTON_GPIO GPIO_NUM_18 // using GPIO4 for the button
//TODO 7
// max count reduced to 5 to imitate pumps for blood pressure machine
#define MAX_COUNT_SEM 5
//TODO 1
// changed to 180 to imitate hypertensive crisis
#define SENSOR_THRESHOLD 180
#define LOG_BUFFER_SIZE 50
SemaphoreHandle_t sem_button; // binary sem
SemaphoreHandle_t sem_sensor; // counting
SemaphoreHandle_t print_mutex;
volatile int SEMCNT = 0;
//TODO 0b
// device heartbeat task
void heartbeat_task(void *pvParameters) {
int heartbeatState = 0;
while (1) {
gpio_set_level(GREEN_LED_PIN, heartbeatState);
heartbeatState = !heartbeatState;
vTaskDelay(pdMS_TO_TICKS(1000));
}
vTaskDelete(NULL);
}
//prints vitals to console
void vitals_task(void *pvParameters) {
TickType_t currentTime = pdTICKS_TO_MS( xTaskGetTickCount() );
static bool systole = false;
while (1) {
currentTime = pdTICKS_TO_MS( xTaskGetTickCount() );
systole = !systole;
printf("Heart %s @ Time %lu\n", systole ? "Systole" : "Diastole", currentTime);
vTaskDelay(pdMS_TO_TICKS(7000)); // Delay for 7s
}
vTaskDelete(NULL);
}
// simulates a blood pressure sensor
void pressure_task(void *pvParameters) {
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(SENSOR_ADC_CHANNEL, ADC_ATTEN_DB_12);
int thresholdState = 0; // tracks state to prevent spamming
while (1) {
// Read current sensor value
int val = adc1_get_raw(SENSOR_ADC_CHANNEL);
//TODO 2
if (xSemaphoreTake(print_mutex, pdMS_TO_TICKS(100))){
printf("Sensor's Raw Value: %u\n", val);
xSemaphoreGive(print_mutex);
}
//TODO3
if(!thresholdState){
if (val > SENSOR_THRESHOLD) {
if(SEMCNT < MAX_COUNT_SEM+1) SEMCNT++; // DO NOT REMOVE THIS LINE
xSemaphoreGive(sem_sensor); // Signal sensor event
} thresholdState = 1;
}
thresholdState = 0; // resets state for next rising edge
//BONUS: reduced delay to 10 so task hogs CPU
//vTaskDelay(pdMS_TO_TICKS(10));
vTaskDelay(pdMS_TO_TICKS(100));
}
}
//waits for button press event
void pressure_logger_task(void *pvParameters){
while(1){
int state = gpio_get_level(BUTTON_GPIO);
static TickType_t PrevTime = 0;
TickType_t CurrentTime = xTaskGetTickCount();
//TODO 4a
if((CurrentTime - PrevTime) > pdMS_TO_TICKS(250)){
if(state == 0){
xSemaphoreGive(sem_button);
PrevTime = CurrentTime;
if (xSemaphoreTake(print_mutex, pdMS_TO_TICKS(100))) {
// TODO 4b
printf("Emergency Button Pressed \n");
xSemaphoreGive(print_mutex);
}
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void emergency_handler_task(void *pvParameters) {
while (1) {
if (xSemaphoreTake(sem_sensor, 0)) {
SEMCNT--; // DO NOT MODIFY THIS LINE
xSemaphoreTake(print_mutex, portMAX_DELAY);
printf("Sensor event: Threshold exceeded Times!\n");
xSemaphoreGive(print_mutex);
gpio_set_level(RED_LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(RED_LED_PIN, 0);
}
if (xSemaphoreTake(sem_button, 0)) {
xSemaphoreTake(print_mutex, portMAX_DELAY);
printf("Button event: System alert triggered!\n");
xSemaphoreGive(print_mutex);
gpio_set_level(RED_LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(300));
gpio_set_level(RED_LED_PIN, 0);
}
vTaskDelay(pdMS_TO_TICKS(10)); // Idle delay to yield CPU
}
}
void app_main() {
// Configure output LEDs
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << GREEN_LED_PIN) | (1ULL << RED_LED_PIN),
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&io_conf);
// Configure input button
gpio_config_t btn_conf = {
.pin_bit_mask = (1ULL << BUTTON_GPIO),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE
};
gpio_config(&btn_conf);
// Set ADC1's resolution
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(SENSOR_ADC_CHANNEL, ADC_ATTEN_DB_12);
//TODO0c
//create binary sempahpore & mutex for log buffer
sem_button = xSemaphoreCreateBinary();
print_mutex = xSemaphoreCreateMutex();
xSemaphoreTake(sem_button, 0);
// take so it starts in a blocking state
sem_sensor = xSemaphoreCreateCounting(MAX_COUNT_SEM, 0);
// . pointer to task function,
// . descriptive name, [has a max length; located in the FREERTOS_CONFIG.H]
// . stack depth,
// . parameters [optional] = NULL
// . priority [0 = low],
// . pointer referencing this created task [optional] = NULL
// . core [0,1] to pin task too
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/freertos_additions.html#_CPPv423xTaskCreatePinnedToCore14TaskFunction_tPCKcK8uint32_tPCv11UBaseType_tPC12TaskHandle_tK10BaseType_t
xTaskCreatePinnedToCore(heartbeat_task, "LED", 2048, NULL, 1, NULL, 1);
xTaskCreatePinnedToCore(vitals_task, "STATUS", 2048, NULL, 1, NULL, 1);
xTaskCreatePinnedToCore(pressure_task, "SENSOR", 4096, NULL, 2, NULL, 1);
xTaskCreatePinnedToCore(pressure_logger_task, "LOGGER",4096, NULL, 3, NULL, 1);
xTaskCreatePinnedToCore(emergency_handler_task, "EMERGENCY",2048, NULL, 4, NULL, 1);
//BONUS: increased sensor task priority to 6 to introsuce starvation of other tasks
//xTaskCreatePinnedToCore(pressure_task, "SENSOR", 4096, NULL, 6, NULL, 1);
}