/* --------------------------------------------------------------
Application: 02 - Rev2
Release Type: Baseline Preemption
Class: Real Time Systems - Su 2025
Caden Kohlmeier
[email protected]
DUE: 6/8/25
AI Use: Both ClaudeAI and ChatGPT was used to complete this project. Both for generative purposes and general knowledge checks including syntax for the formulas. Formulas directly derived from provided resources and prompt was closely followed.
Additionally, AI converted the analysis questions to markdown format. Wokwki would not let me add a .MD file so I used a .txt instead. AI use for basic clarifications and error analysis.
---------------------------------------------------------------*/
#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"
// TODO1: ADD IN additional INCLUDES ABOVE
#include "math.h"
#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
#define AVG_WINDOW 10
#define SENSOR_THRESHOLD 200
const float GAMMA = 0.7;
const float RL10 = 50;
// TODO99: Consider Adding AVG_WINDOW and SENSOR_THRESHOLD as global defines
//TODO9: Adjust Task to blink an LED at 1 Hz (1000 ms period: 500 ms ON, 500 ms OFF);
//Consider supressing the output
// Task to blink an LED at 2 Hz (500 ms period: 250 ms ON, 250 ms OFF)
void led_task(void *pvParameters) {
bool led_on = false;
TickType_t last_tick = xTaskGetTickCount();
while (1) {
// TODO: Set LED pin high or low based on led_on flag; right now it's always on... boring; hint in the commented out print statement
gpio_set_level(LED_PIN, led_on); //state
led_on = !led_on; // toggle state for next time
vTaskDelay(pdMS_TO_TICKS(500)); //
//printf("LED tick: %lu\n", xTaskGetTickCount());
}
vTaskDelete(NULL); // We'll never get here; tasks run forever
}
//TODO10: Task to print a message every 1000 ms (1 seconds)
void print_task(void *pvParameters) {
TickType_t currentTime = pdTICKS_TO_MS( xTaskGetTickCount() );
TickType_t previousTime = 0;
while (1) {
previousTime = currentTime;
currentTime = pdTICKS_TO_MS( xTaskGetTickCount() );
// Prints a periodic message based on a thematic area. Output a timestamp (ms) and period (ms)
printf("Monitor running @ time %lu [period = %lu]!\n",currentTime, currentTime-previousTime);
vTaskDelay(pdMS_TO_TICKS(1000)); // Delay for 1000 ms
}
vTaskDelete(NULL); // We'll never get here; tasks run forever
}
//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; //raw adc 0-4096
float Vmeasure = 0.; //calculated voltage from adc raw value
float Rmeasure = 0.; //resistance of ldr
float lux = pow(RL10 * 1e3 * pow(10, GAMMA) / Rmeasure, (1 / GAMMA)); // final lux value
// Variables for moving average
int luxreadings[AVG_WINDOW] = {0};
int idx = 0;
float sum = 0;
//See TODO 99
// Pre-fill the readings array with an initial sample to avoid startup anomaly
for(int i = 0; i < AVG_WINDOW; ++i) {
raw = adc1_get_raw(LDR_ADC_CHANNEL);
Vmeasure = ((float)raw / 4096) * 3.3; //TODO11b correct this with the equation seen earlier
Rmeasure = (Vmeasure * 10000) / 1-(Vmeasure / 3.3);; //TODO11c correct this with the equation seen earlier... correct format just modified with my formula
lux = pow(RL10 * 1e3 * pow(10, GAMMA) / Rmeasure, (1 / GAMMA)); //TODO11d correct this with the equation seen earlier
luxreadings[i] = lux;
sum += luxreadings[i];
}
const TickType_t periodTicks = pdMS_TO_TICKS(1000); // e.g. 500 ms period
TickType_t lastWakeTime = xTaskGetTickCount(); // initialize last wake time
while (1) {
// Read current sensor value
raw = adc1_get_raw(LDR_ADC_CHANNEL);
printf("**raw **: Sensor %d\n", raw);
// Compute LUX
Vmeasure = ((float)raw / 4096) * 3.3; //TODO11e correct this with the equation seen earlier
Rmeasure = (Vmeasure * 10000) / 1-(Vmeasure / 3.3); //TODO11f correct this with the equation seen earlier
lux = pow(RL10 * 1e3 * pow(10, GAMMA) / Rmeasure, (1 / GAMMA)); //TODO11g correct this with the equation seen earlier
// Update moving average buffer
sum -= luxreadings[idx]; // remove oldest value from sum
luxreadings[idx] = lux; // place new reading
sum += lux; // add new value to sum
idx = (idx + 1) % AVG_WINDOW;
int avg = sum / AVG_WINDOW; // compute average
//TODO11h Check threshold and print alert if exceeded or below based on context
if (avg >= SENSOR_THRESHOLD) {
printf("**Alert**: Current avg of %d exceeds threshold avg: %d!\n", avg, SENSOR_THRESHOLD);
} else {
//TODO11i
printf("Heart rate threshold not exceeded @ : %d!\n", avg);
// (you could print the avg value for debugging)
}
//TODO11j: Print out time period [to help with answering Eng/Analysis quetionst (hint check Application Solution #1 )
//printf("Recorded @ Tick %lu\n" , xTaskGetTickCount());
//https://wokwi.com/projects/430683087703949313
//TODO11k Replace vTaskDelay with vTaskDelayUntil with parameters &lastWakeTime and periodTicks
vTaskDelayUntil(&lastWakeTime, periodTicks);
}
}
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 [2 lines mirroring those above]
gpio_reset_pin(LDR_PIN);
gpio_set_direction(LDR_PIN, GPIO_MODE_INPUT);
// TODO5 : Set ADC1's resolution by calling:
adc1_config_width(ADC_WIDTH_BIT_12);
// function adc1_config_width(...)
// with parameter ADC_WIDTH_BIT_12
// TODO6: Set the the input channel to 11 DB Attenuation using
// function adc1_config_channel_atten(...,...)
// with parameters LDR_ADC_CHANNEL and ADC_ATTEN_DB_11
adc1_config_channel_atten(LDR_ADC_CHANNEL, ADC_ATTEN_DB_11);
// Instantiate/ Create tasks:
// . 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
// Learn more here https://www.freertos.org/Documentation/02-Kernel/04-API-references/01-Task-creation/01-xTaskCreate
// TODO7: Pin tasks to core 1
// Convert these xTaskCreate function calls to xTaskCreatePinnedToCore() function calls
// The new function takes one more parameter at the end (0 or 1);
// pin all your tasks to core 1
// This is a special (custom) espressif FreeRTOS function
// . 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
// TODO8: Make sure everything still works as expected before moving on to TODO9 (above).
xTaskCreatePinnedToCore(led_task, "LEDTask", 2048, NULL, 1, NULL, 1);
xTaskCreatePinnedToCore(print_task, "PrintTask", 2048, NULL, 2, NULL, 1);
//TODO12 Add in new Sensor task; make sure it has the correct priority to preempt
//the other two tasks.
xTaskCreatePinnedToCore(sensor_task, "SensorTask", 4096, NULL, 3, NULL, 1);
return;
//TODO13: Make sure the output is working as expected and move on to the engineering
//and analysis part of the application. You may need to make modifications for experiments.
//Make sure you can return back to the working version!
}