#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#include "math.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_continuous.h"
#include "esp_log.h"
#include <string.h>
// basic board configurations
#define LED_PIN GPIO_NUM_2 // Using GPIO2 for the LED
#define LDR_PIN GPIO_NUM_32 // light sensor
#define LDR_ADC_CHANNEL ADC_CHANNEL_4
#define SENSOR_THRESHOLD 500
#define BUTTON_GPIO GPIO_NUM_4 // push button
#define LOG_BUFFER_SIZE 50
const static char *TAG = "APP-3>";
#define CONFIG_LOG_DEFAULT_LEVEL_VERBOSE (1)
#define CONFIG_LOG_DEFAULT_LEVEL (5)
#define CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE (1)
#define CONFIG_LOG_MAXIMUM_LEVEL (5)
#define CONFIG_LOG_COLORS (1)
#define CONFIG_LOG_TIMESTAMP_SOURCE_RTOS (1)
// Shared buffer and index
static uint16_t sensor_log[LOG_BUFFER_SIZE];
static int log_index = 0;
// Synchronization
static SemaphoreHandle_t xLogMutex; // Hand off access to the buffer!
static SemaphoreHandle_t xButtonSem; // Hand off on button press!
// ISR: Triggered on button press
void IRAM_ATTR button_isr_handler(void *arg) {
ESP_LOGV(TAG,"button pressed - setting semaphore to be taken by logger \n");
BaseType_t xHigherPrioTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(xButtonSem, &xHigherPrioTaskWoken);
portYIELD_FROM_ISR(xHigherPrioTaskWoken);
}
adc_oneshot_unit_handle_t adc1_handle;
//LED toggles every 1.4 seconds modified from application 2
void led_task(void *pvParameters) {
bool led_status = false;
TickType_t currentTime = pdTICKS_TO_MS( xTaskGetTickCount() );
while (1) {
currentTime = pdTICKS_TO_MS( xTaskGetTickCount() );
led_status = !led_status;
gpio_set_level(LED_PIN, led_status);
ESP_LOGV(TAG,"LED Cycle %s @ %lu\n", led_status ? "ON" : "OFF", currentTime);
vTaskDelay(pdMS_TO_TICKS(1400)); // Delay for 1400 ms or 1.4 seconds
}
vTaskDelete(NULL);
}
// Task to print a message every 7000 ms (7 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)
ESP_LOGV(TAG,"Satellite Communication TRANSMITTING @ time %lu [period = %lu]!\n",currentTime, currentTime-previousTime);
vTaskDelay(pdMS_TO_TICKS(7000)); // Delay for 1000 ms
}
vTaskDelete(NULL); // We'll never get here; tasks run forever
}
//TODO11: Create new task for sensor reading every 300ms
void sensor_task(void *pvParameters) {
ESP_LOGV(TAG,"sensor entered \n");
// Configure ADC channel
adc_oneshot_chan_cfg_t config = {
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_12,
};
adc_oneshot_config_channel(adc1_handle, LDR_ADC_CHANNEL, &config);
// Raw reading variable
int raw;
const TickType_t periodTicks = pdMS_TO_TICKS(300); // e.g. 500 ms period
TickType_t lastWakeTime = xTaskGetTickCount(); // initialize last wake time
while (1) {
// Read current sensor value
adc_oneshot_read(adc1_handle, LDR_ADC_CHANNEL, &raw);
// Lock mutex to write value to buffer || What happens if you lock it for too short or long of a time? :)
if (xSemaphoreTake(xLogMutex, pdMS_TO_TICKS(10))) {
ESP_LOGV(TAG,"sensor taking log data semaphore for write\n");
sensor_log[log_index] = raw;
// this circular buffer will have issues at startup and when cleared; no clearing logic here
log_index = (log_index + 1) % LOG_BUFFER_SIZE;
xSemaphoreGive(xLogMutex); //give up the semaphore!
ESP_LOGV(TAG,"sensor releasing log data semaphore\n");
}
vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(300));
}
}
// Task: Wait for button, "compress" and dump log
void logger_task(void *arg) {
while (1) {
// Block until ISR signals button press
if (xSemaphoreTake(xButtonSem, portMAX_DELAY) == pdTRUE) {
ESP_LOGV(TAG,"logger entered based on button semaphore; will release on exit\n");
uint16_t copy[LOG_BUFFER_SIZE];
int count = 0;
// Lock mutex to read from buffer
if (xSemaphoreTake(xLogMutex, pdMS_TO_TICKS(100))) {
ESP_LOGV(TAG,"logger took log data semaphore for copy\n");
memcpy(copy, sensor_log, sizeof(copy));
count = log_index;
xSemaphoreGive(xLogMutex);
ESP_LOGV(TAG,"logger releasing log data semaphore\n");
}
// Simulate compression (calculate stats)
uint16_t min = 4095, max = 0;
uint32_t sum = 0;
for (int i = 0; i < LOG_BUFFER_SIZE; i++) {
if (copy[i] < min) min = copy[i];
if (copy[i] > max) max = copy[i];
sum += copy[i];
}
uint16_t avg = sum / LOG_BUFFER_SIZE;
ESP_LOGI(TAG, "[LOG DUMP] previous solar readings %d: min=%d, max=%d, avg=%d", LOG_BUFFER_SIZE, min, max, avg);
}
}
}
void app_main() {
gpio_reset_pin(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
gpio_reset_pin(LDR_PIN);
gpio_set_direction(LDR_PIN, GPIO_MODE_INPUT);
// Create ADC unit
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT_1,
};
adc_oneshot_new_unit(&init_config, &adc1_handle);
// Configure ADC channel
adc_oneshot_chan_cfg_t config = {
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_12,
};
adc_oneshot_config_channel(adc1_handle, LDR_ADC_CHANNEL, &config);
esp_log_level_set(TAG, ESP_LOG_INFO);
// Create synchronization primitives
xLogMutex = xSemaphoreCreateMutex();
xButtonSem = xSemaphoreCreateBinary();
assert(xLogMutex && xButtonSem);
gpio_install_isr_service(0);
// configuring button with pulldown resistor
ESP_LOGI(TAG,"configuring button\n");
gpio_reset_pin(BUTTON_GPIO);
gpio_set_direction(BUTTON_GPIO, GPIO_MODE_INPUT);
gpio_pullup_en(BUTTON_GPIO);
gpio_set_intr_type(BUTTON_GPIO, GPIO_INTR_NEGEDGE);
gpio_isr_handler_add(BUTTON_GPIO, button_isr_handler, NULL);
// Create tasks
xTaskCreatePinnedToCore(logger_task, "logger_task", 4096, NULL, 3, NULL, 1);
xTaskCreatePinnedToCore(led_task, "LED", 2048, NULL, 1, NULL,1);
xTaskCreatePinnedToCore(print_status_task, "STATUS", 2048, NULL, 1, NULL,1);
xTaskCreatePinnedToCore(sensor_task, "SensorTask", 4096, NULL,2,NULL,1);
ESP_LOGI(TAG, "System ready. Press the button to dump the log.");
}