/* --------------------------------------------------------------
Application: 02 - Rev1
Release Type: Baseline Preemption
Class: Real Time Systems - Fa 2025
Ryan Ramdihal
---------------------------------------------------------------*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
// TODO1: ADD IN additional INCLUDES BELOW
#include "driver/adc.h"
#include <math.h>
// TODO1: ADD IN additional INCLUDES ABOVE
#define LED_PIN GPIO_NUM_2 // Using GPIO2 for the LED
// TODO2: ADD IN LDR_PIN to gpio pin 32
#define LDR_PIN GPIO_NUM_32
// TODO3: ADD IN LDR_ADC_CHANNEL -- if you used gpio pin 32 it should map to ADC1_CHANNEL4
#define LDR_ADC_CHANNEL ADC1_CHANNEL_4
// TODO99: Consider Adding AVG_WINDOW and SENSOR_THRESHOLD as global defines
#define AVG_WINDOW 10
#define SENSOR_THRESHOLD 500
#define VSOURCE 3.3
#define R_FIXED 10000
#define GAMMA 0.7
#define R_LUX_REF 250000
//TODO9: Adjust Task to blink an LED at 1 Hz (1000 ms period: 500 ms ON, 500 ms OFF);
void led_task(void *pvParameters) {
bool led_on = false;
TickType_t currentTime;
while (1) {
currentTime = pdTICKS_TO_MS(xTaskGetTickCount());
// TODO9: blink LED using led_on variable
gpio_set_level(LED_PIN, led_on); // Changed to led_on variable
led_on = !led_on; // toggle state for next time
printf("Heartbeat Monitor LED toggled\n");//prints the message which makes an audible sounds about the heart rate
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms delay
}
vTaskDelete(NULL);
}
//TODO10: Task to print a message every 1000 ms (1 seconds)
void print_status_task(void *pvParameters) {
TickType_t currentTime = pdTICKS_TO_MS(xTaskGetTickCount());
TickType_t previousTime = 0;
while (1) {
previousTime = currentTime;
currentTime = pdTICKS_TO_MS(xTaskGetTickCount());
printf("Monitor active for %lu seconds [period = %lu ms]\n",
currentTime/1000, (currentTime - previousTime));
vTaskDelay(pdMS_TO_TICKS(1000)); // 1000 ms delay
}
vTaskDelete(NULL);
}
//TODO11: Create new task for sensor reading every 500ms
void sensor_task(void *pvParameters) {
//TODO110 Configure ADC (12-bit width, 0-3.3V range with 11dB attenuation)
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(LDR_ADC_CHANNEL, ADC_ATTEN_DB_11);
// Variables to compute LUX
int raw;
float Vmeasure = 0;
float Rmeasure = 0;
float lux = 0.;
// Variables for moving average
int luxreadings[AVG_WINDOW] = {0};
int idx = 0;
float sum = 0;
const TickType_t periodTicks = pdMS_TO_TICKS(500);
TickType_t lastWakeTime = xTaskGetTickCount();
while (1) {
raw = adc1_get_raw(LDR_ADC_CHANNEL);
Vmeasure = (raw / 4095.0f) * VSOURCE; // TODO11e
Rmeasure = R_FIXED * Vmeasure / (VSOURCE - Vmeasure); // TODO11f
lux = pow((R_LUX_REF / Rmeasure), (1 / GAMMA)); // TODO11g
// Update moving average
sum -= luxreadings[idx];
luxreadings[idx] = lux;
sum += lux;
idx = (idx + 1) % AVG_WINDOW;
float avg = sum / AVG_WINDOW;
// TODO11h: Check threshold
if (avg >= SENSOR_THRESHOLD) {
printf("**Alert**: avg = %.4f exceeds Sensor Threshold %d\n", avg, SENSOR_THRESHOLD);
} else {
// TODO11i: optional debug output
printf("[Sensor Readings] raw=%d V=%.3f R=%.1f lux=%.4f avg=%.4f\n",
raw, Vmeasure, Rmeasure, lux, avg);
}
// TODO11k: use vTaskDelayUntil
vTaskDelayUntil(&lastWakeTime, periodTicks);
}
vTaskDelete(NULL);
}
void app_main() {
// Initialize LED GPIO
gpio_reset_pin(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
// TODO4: Initialize LDR pin as INPUT
gpio_reset_pin(LDR_PIN);
gpio_set_direction(LDR_PIN, GPIO_MODE_INPUT);
// TODO5: Set ADC1's resolution
adc1_config_width(ADC_WIDTH_BIT_12);
// TODO6: Set ADC channel attenuation
adc1_config_channel_atten(LDR_ADC_CHANNEL, ADC_ATTEN_DB_11);
// TODO7: Pin tasks to core 1
xTaskCreatePinnedToCore(led_task, "LED", 2048, NULL, 1, NULL, 1);
xTaskCreatePinnedToCore(print_status_task, "STATUS", 2048, NULL, 2, NULL, 1);
xTaskCreatePinnedToCore(sensor_task, "SENSOR", 4096, NULL, 3, NULL, 1); // TODO12: sensor task highest priority
// TODO8: Verify everything works
// TODO13: Perform engineering analysis
}