/* --------------------------------------------------------------
Application: 03
Release Type: Baseline Preemption
Class: Real Time Systems - Sp 2026
Author: Amanda Gumbs
Email: [email protected]
Company: [University of Central Florida]
AI Use: Commented inline
---------------------------------------------------------------
*/
#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
//TODO9: Adjust Task to blink an LED at 1 Hz (1000 ms period: 500 ms ON, 500 ms OFF);
//Consider supressing the output
void beacon_task(void *pvParameters) {
bool beacon_status = false;
TickType_t currentTime = pdTICKS_TO_MS( xTaskGetTickCount() );
while (1) {
currentTime = pdTICKS_TO_MS( xTaskGetTickCount() );
gpio_set_level(LED_PIN, beacon_status ? 1:0); //TODO: Set LED pin high or low based on led_status flag;
beacon_status = !beacon_status; //TODO: toggle state for next loop
printf("LED Cycle %s @ %lu\n", beacon_status ? "ON" : "OFF", currentTime);
vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms using MS to Ticks Function vs alternative which is MS / ticks per ms
}
vTaskDelete(NULL); // We'll never get here; tasks run forever
}
//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() );
// Prints a periodic message based on a thematic area. Output a timestamp (ms) and period (ms)
printf("Satellite running for elapsed time @ 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); //could be ADC_ATTEN_DB_11
//TODO11a consider where AVG_WINDOW is defined, it could be here, or global value
//Defined as a global variable at top of code^^^^^^
// Variables to compute LUX
int raw;
float Vmeasure = 0.;
float Rmeasure = 0.;
float lux = 0.;
// Variables for moving average
float 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
/*I was stuck on the equation parts
I started off by writing them out with all the individual values in it.
I could not figure out what AnalogValueRead was, so I plugged the equation section into Claude and asked where the AnalogValueRead was. Claude AI ended up just going through the whole section.
Since it computed all of the equations and went through the instructions, I decided to just double check my work and saw my understanding was lacking in some areas.
It was able to describe the AnalogValueRead, being from the ADC connected to the Photoresistor circuit.
It described the function in code looking like the following: ADC14->MEM[x] (x is memory register for channel)
It also analyzed the Wiki equation which I thought was correct because the voltage divider was being computed based on Circuit equations.
Claude claims it didn’t fully agree because 5 source and Rfixed are fixed values, so they should be substituted.
I had a similar idea, but did not put the values in the correct area.
It also put the Rfixed at 10,000 because it was described in our data sheet that Rfixed = 10k.
I also asked it to explain the math on how we got to ADC14 from ADC10 and it explained that it is just through the binary bit exponents for place values. 2^10 and 2^12, etc.
The AI allowed me to clean up values and check some understanding I was mistaken on.
I also asked Claude to double check my code and to show me where lines of code may have went wrong. I made some of the corrections it suggested and ignored some of the others.
*/
for(int i = 0; i < AVG_WINDOW; ++i) {
raw = adc1_get_raw(LDR_ADC_CHANNEL);
float Vmeasure = (raw/4095.0)*3.3; //TODO11b correct this with the equation seen earlier
float Rmeasure = (10000.0*Vmeasure)/(3.3 - Vmeasure); //TODO11c correct this with the equation seen earlier
float gamma = 0.7;
float RL = 50.0;
float lux = pow((RL/Rmeasure), (1.0 / gamma)); //TODO11d correct this with the equation seen earlier
luxreadings[i] = lux;
sum += luxreadings[i];
}
const TickType_t periodTicks = pdMS_TO_TICKS(500); // e.g. 500 ms period
TickType_t lastWakeTime = xTaskGetTickCount(); // initialize last wake time
// BONUS STARVATION EXPERIMENT - this would replace what is normal below this code
// while (1) {
// raw = adc1_get_raw(LDR_ADC_CHANNEL);
// Vmeasure = (raw / 4095.0) * 3.3;
// Rmeasure = (10000.0 * Vmeasure) / (3.3 - Vmeasure);
// lux = pow((RL / Rmeasure), (1.0 / gamma));
// sum -= luxreadings[idx];
// luxreadings[idx] = lux;
// sum += lux;
// idx = (idx + 1) % AVG_WINDOW;
// float avg = sum / AVG_WINDOW;
// printf("STARVATION TEST - Sensor lux: %.4f\n", avg);
// // No vTaskDelayUntil - task never yields, starves all lower priority tasks
// }
while (1) {
// Read current sensor value
raw = adc1_get_raw(LDR_ADC_CHANNEL);
//printf("**raw **: Sensor %d\n", raw);
// Compute LUX
float Vmeasure = (raw/4095.0)*3.3; //TODO11e correct this with the equation seen earlier
float Rmeasure = (10000.0*Vmeasure)/(3.3 - Vmeasure); //TODO11f correct this with the equation seen earlier
float gamma = 0.7;
float RL = 50.0;
float lux = pow((RL/Rmeasure), (1.0 / gamma)); //TODO11g correct this with the equation seen earlier
//was having issues seeing lux value, so claude recommended this debug print statement to see if our
//math equations for the sensor were working. Math is working but lux is a small value so we need 6 decimal places
// printf("raw=%d, Vmeasure=%.4f, Rmeasure=%.2f, lux=%.4f\n", raw, Vmeasure, Rmeasure, lux);
// 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;
float avg = sum / AVG_WINDOW; // compute average
//TODO11h Check threshold and print alert if exceeded or below based on context
if (avg < SENSOR_THRESHOLD) { //Going based off the space concept
printf("**Alert**: Solar irradiance low! Avg lux: %.6f (threshold: %d) - possible eclipse!\n", avg, SENSOR_THRESHOLD);
} else {
//TODO11i
// (you could print the avg value for debugging)
printf("Sensor reading OK, avg lux: %.6f\n", avg);
}
//TODO11j: Print out time period [to help with answering Eng/Analysis quetionst (hint check Application Solution #1 )
//https://wokwi.com/projects/430683087703949313
TickType_t currentTime = pdTICKS_TO_MS(xTaskGetTickCount());
printf("Sensor reading at %lu ms, avg lux: %.6f\n", currentTime, avg);
//TODO11k Replace vTaskDelay with vTaskDelayUntil with parameters &lastWakeTime and periodTicks
vTaskDelayUntil(&lastWakeTime, periodTicks);
}
vTaskDelete(NULL); // We'll never get here; tasks run forever
}
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:
// function adc1_config_width(...)
// with parameter ADC_WIDTH_BIT_12
adc1_config_width(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
xTaskCreatePinnedToCore(beacon_task, "LED", 2048, NULL, 1, NULL, 1);
xTaskCreatePinnedToCore(print_status_task, "STATUS", 2048, NULL, 2, NULL, 1);
// TODO8: Make sure everything still works as expected before moving on to TODO9 (above).
//TODO12 Add in new Sensor task; make sure it has the correct priority to preempt
//the other two tasks.
xTaskCreatePinnedToCore(sensor_task, "Sensor Task", 4096, NULL, 3, NULL, 1);
//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!
/*When I got a few code errors when I was running the simulation, I put it into claude so I could refer back to it and see what lines had issues. Claude ended up explaining what the errors meant and I was able to go directly to those lines and fix it.
I accidentally declared some functions as variables although I had defined it as a macro at the top of the code.
Once I corrected my function calls, I was able to successfully run the simulation.
*/
}