/** * @file potentiometer_manager.c * @brief Potentiometer Manager Implementation - COMPLETE! * * Features: * - Dual ADC reading (Volume + Brightness) * - Moving average filter (smoothing) * - Automatic monitoring task * - Callback on changes * - Direct ST7789 brightness control */ #include #include "esp_log.h" #include "driver/adc.h" #include "esp_adc_cal.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "potentiometer_manager.h" #include "st7789.h" #include "hardware_config.h" static const char *TAG = "POTI"; // ADC Configuration #define ADC_SAMPLES 8 // Samples for averaging #define POTI_THRESHOLD 5 // Change threshold (%) #define POTI_UPDATE_MS 100 // Update interval // Current values static uint8_t current_volume = 50; static uint8_t current_brightness = 80; // Monitoring task static TaskHandle_t poti_task_handle = NULL; static bool poti_task_running = false; static poti_change_callback_t change_callback = NULL; // ADC calibration static esp_adc_cal_characteristics_t adc1_chars; static esp_adc_cal_characteristics_t adc2_chars; /** * @brief Initialize ADC for potentiometers */ static esp_err_t poti_adc_init(void) { // Configure ADC1 (Volume - GPIO 3) adc1_config_width(POTI_ADC_WIDTH); adc1_config_channel_atten(ADC1_CHANNEL_2, POTI_ADC_ATTEN); // GPIO 3 = ADC1_CH2 // Configure ADC2 (Brightness - GPIO 46) adc2_config_channel_atten(ADC2_CHANNEL_5, POTI_ADC_ATTEN); // GPIO 46 = ADC2_CH5 // Characterize ADC esp_adc_cal_characterize(ADC_UNIT_1, POTI_ADC_ATTEN, POTI_ADC_WIDTH, 1100, &adc1_chars); esp_adc_cal_characterize(ADC_UNIT_2, POTI_ADC_ATTEN, POTI_ADC_WIDTH, 1100, &adc2_chars); return ESP_OK; } /** * @brief Read ADC with averaging */ static uint32_t poti_read_adc1_averaged(void) { uint32_t sum = 0; for (int i = 0; i < ADC_SAMPLES; i++) { uint32_t voltage; int raw = adc1_get_raw(ADC1_CHANNEL_2); voltage = esp_adc_cal_raw_to_voltage(raw, &adc1_chars); sum += voltage; vTaskDelay(pdMS_TO_TICKS(1)); } return sum / ADC_SAMPLES; } static uint32_t poti_read_adc2_averaged(void) { uint32_t sum = 0; for (int i = 0; i < ADC_SAMPLES; i++) { int raw; uint32_t voltage; // ADC2 read (can fail if WiFi is active!) esp_err_t ret = adc2_get_raw(ADC2_CHANNEL_5, POTI_ADC_WIDTH, &raw); if (ret == ESP_OK) { voltage = esp_adc_cal_raw_to_voltage(raw, &adc2_chars); sum += voltage; } else { ESP_LOGW(TAG, "ADC2 read failed (WiFi active?)"); return sum / (i > 0 ? i : 1); // Return partial average } vTaskDelay(pdMS_TO_TICKS(1)); } return sum / ADC_SAMPLES; } /** * @brief Convert voltage to percentage (0-100%) */ static uint8_t voltage_to_percent(uint32_t voltage_mv, uint32_t max_mv) { if (voltage_mv >= max_mv) return 100; return (voltage_mv * 100) / max_mv; } /** * @brief Potentiometer monitoring task */ static void poti_monitor_task(void *arg) { ESP_LOGI(TAG, "Potentiometer monitoring task started"); while (poti_task_running) { // Read potentiometers uint32_t volume_mv = poti_read_adc1_averaged(); uint32_t brightness_mv = poti_read_adc2_averaged(); // Convert to percentage uint8_t new_volume = voltage_to_percent(volume_mv, 3300); uint8_t new_brightness = voltage_to_percent(brightness_mv, 3300); // Clamp brightness to minimum (display shouldn't be completely off) if (new_brightness < LCD_BCKL_MIN) { new_brightness = LCD_BCKL_MIN; } // Check if changed significantly bool volume_changed = abs(new_volume - current_volume) >= POTI_THRESHOLD; bool brightness_changed = abs(new_brightness - current_brightness) >= POTI_THRESHOLD; if (volume_changed || brightness_changed) { current_volume = new_volume; current_brightness = new_brightness; ESP_LOGD(TAG, "Volume: %d%%, Brightness: %d%%", current_volume, current_brightness); // Update display brightness immediately st7789_set_backlight(current_brightness); // Call callback if registered if (change_callback != NULL) { change_callback(current_volume, current_brightness); } } vTaskDelay(pdMS_TO_TICKS(POTI_UPDATE_MS)); } ESP_LOGI(TAG, "Potentiometer monitoring task stopped"); vTaskDelete(NULL); } // =========================================== // Public API // =========================================== esp_err_t poti_manager_init(void) { ESP_LOGI(TAG, "Initializing Potentiometer Manager..."); // Initialize ADC ESP_ERROR_CHECK(poti_adc_init()); // Read initial values poti_update(); ESP_LOGI(TAG, "✓ Potentiometer Manager initialized"); ESP_LOGI(TAG, " Volume: %d%%, Brightness: %d%%", current_volume, current_brightness); return ESP_OK; } esp_err_t poti_manager_start(poti_change_callback_t callback) { if (poti_task_running) { ESP_LOGW(TAG, "Potentiometer task already running"); return ESP_OK; } change_callback = callback; poti_task_running = true; BaseType_t ret = xTaskCreate( poti_monitor_task, "poti_monitor", 4096, NULL, 5, // Priority &poti_task_handle ); if (ret != pdPASS) { ESP_LOGE(TAG, "Failed to create potentiometer task!"); poti_task_running = false; return ESP_FAIL; } ESP_LOGI(TAG, "✓ Potentiometer monitoring started"); return ESP_OK; } void poti_manager_stop(void) { if (!poti_task_running) { return; } ESP_LOGI(TAG, "Stopping potentiometer monitoring..."); poti_task_running = false; // Task will delete itself } uint8_t poti_get_volume(void) { return current_volume; } uint8_t poti_get_brightness(void) { return current_brightness; } esp_err_t poti_update(void) { // Manual single update (without task) uint32_t volume_mv = poti_read_adc1_averaged(); uint32_t brightness_mv = poti_read_adc2_averaged(); current_volume = voltage_to_percent(volume_mv, 3300); current_brightness = voltage_to_percent(brightness_mv, 3300); if (current_brightness < LCD_BCKL_MIN) { current_brightness = LCD_BCKL_MIN; } // Update display brightness st7789_set_backlight(current_brightness); ESP_LOGD(TAG, "Manual update: Volume %d%%, Brightness %d%%", current_volume, current_brightness); return ESP_OK; }