/** * @file potentiometer_manager.c * @brief Potentiometer Manager Implementation - ESP-IDF v4.4 Compatible * * Features: * - Dual ADC reading (Volume + Brightness) * - Moving average filter (smoothing) * - Automatic monitoring task * - Callback on changes * - Direct ST7789 brightness control * * NOTE: Simplified for ESP-IDF v4.4 - removed esp_adc_cal (not available in v4.4) */ #include #include "esp_log.h" #include "driver/adc.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; /** * @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 ESP_LOGI(TAG, "ADC configured for potentiometers"); return ESP_OK; } /** * @brief Read ADC with averaging (simplified - no calibration) */ static uint32_t poti_read_adc1_averaged(void) { uint32_t sum = 0; for (int i = 0; i < ADC_SAMPLES; i++) { int raw = adc1_get_raw(ADC1_CHANNEL_2); if (raw >= 0) { sum += raw; } vTaskDelay(pdMS_TO_TICKS(1)); } return sum / ADC_SAMPLES; } static uint32_t poti_read_adc2_averaged(void) { uint32_t sum = 0; int valid_samples = 0; for (int i = 0; i < ADC_SAMPLES; i++) { int raw; // 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 && raw >= 0) { sum += raw; valid_samples++; } else { ESP_LOGD(TAG, "ADC2 read failed (WiFi active?)"); } vTaskDelay(pdMS_TO_TICKS(1)); } if (valid_samples == 0) { return 0; // No valid readings } return sum / valid_samples; } /** * @brief Convert raw ADC value to percentage (0-100%) * For 12-bit ADC: 0-4095 range */ static uint8_t raw_to_percent(uint32_t raw_value, uint32_t max_value) { if (raw_value >= max_value) return 100; return (raw_value * 100) / max_value; } /** * @brief Potentiometer monitoring task */ static void poti_monitor_task(void *arg) { ESP_LOGI(TAG, "Potentiometer monitoring task started"); while (poti_task_running) { // Read potentiometers (raw ADC values) uint32_t volume_raw = poti_read_adc1_averaged(); uint32_t brightness_raw = poti_read_adc2_averaged(); // Convert to percentage (12-bit ADC: 0-4095) uint8_t new_volume = raw_to_percent(volume_raw, 4095); uint8_t new_brightness = raw_to_percent(brightness_raw, 4095); // 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_err_t ret = poti_adc_init(); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize ADC"); return ret; } // 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_raw = poti_read_adc1_averaged(); uint32_t brightness_raw = poti_read_adc2_averaged(); current_volume = raw_to_percent(volume_raw, 4095); current_brightness = raw_to_percent(brightness_raw, 4095); 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; }