#include "battery.h" #include "system.h" #include "gpio.h" #include "esp_log.h" #include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali.h" #include "esp_adc/adc_cali_scheme.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" static const char *TAG = "battery"; static adc_oneshot_unit_handle_t adc1_handle = NULL; static adc_cali_handle_t adc1_cali_handle = NULL; static bool adc_calibration_enabled = false; // ADC Calibration initialization static bool adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle) { adc_cali_handle_t handle = NULL; esp_err_t ret = ESP_FAIL; bool calibrated = false; #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED if (!calibrated) { ESP_LOGI(TAG, "Calibration scheme: Curve Fitting"); adc_cali_curve_fitting_config_t cali_config = { .unit_id = unit, .chan = channel, .atten = atten, .bitwidth = BATTERY_ADC_WIDTH, }; ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle); if (ret == ESP_OK) { calibrated = true; } } #endif #if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED if (!calibrated) { ESP_LOGI(TAG, "Calibration scheme: Line Fitting"); adc_cali_line_fitting_config_t cali_config = { .unit_id = unit, .atten = atten, .bitwidth = BATTERY_ADC_WIDTH, }; ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle); if (ret == ESP_OK) { calibrated = true; } } #endif *out_handle = handle; if (ret == ESP_OK) { ESP_LOGI(TAG, "ADC calibration successful"); } else { ESP_LOGW(TAG, "ADC calibration failed: %s", esp_err_to_name(ret)); } return calibrated; } esp_err_t battery_init(void) { esp_err_t ret; // Configure ADC1 oneshot mode adc_oneshot_unit_init_cfg_t init_config = { .unit_id = ADC_UNIT_1, }; ret = adc_oneshot_new_unit(&init_config, &adc1_handle); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize ADC unit: %s", esp_err_to_name(ret)); return ret; } // Configure ADC channel adc_oneshot_chan_cfg_t config = { .bitwidth = BATTERY_ADC_WIDTH, .atten = BATTERY_ADC_ATTEN, }; ret = adc_oneshot_config_channel(adc1_handle, BATTERY_ADC_CHANNEL, &config); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to configure ADC channel: %s", esp_err_to_name(ret)); return ret; } // Initialize calibration adc_calibration_enabled = adc_calibration_init(ADC_UNIT_1, BATTERY_ADC_CHANNEL, BATTERY_ADC_ATTEN, &adc1_cali_handle); ESP_LOGI(TAG, "Battery monitoring initialized on GPIO34 (ADC1_CH6)"); return ESP_OK; } int battery_read_raw(void) { int adc_raw = 0; esp_err_t ret = adc_oneshot_read(adc1_handle, BATTERY_ADC_CHANNEL, &adc_raw); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to read ADC: %s", esp_err_to_name(ret)); return -1; } return adc_raw; } int battery_read_voltage_mv(void) { int voltage_mv = 0; int adc_sum = 0; int valid_samples = 0; // Take multiple samples and average for (int i = 0; i < BATTERY_SAMPLES; i++) { int adc_raw = battery_read_raw(); if (adc_raw >= 0) { adc_sum += adc_raw; valid_samples++; } vTaskDelay(pdMS_TO_TICKS(1)); // Small delay between samples } if (valid_samples == 0) { ESP_LOGE(TAG, "No valid ADC samples"); return -1; } int adc_avg = adc_sum / valid_samples; // Convert to voltage using calibration if available if (adc_calibration_enabled) { esp_err_t ret = adc_cali_raw_to_voltage(adc1_cali_handle, adc_avg, &voltage_mv); if (ret != ESP_OK) { ESP_LOGW(TAG, "Calibration conversion failed, using raw calculation"); adc_calibration_enabled = false; // Disable for future reads } } // Fallback to manual calculation if calibration not available if (!adc_calibration_enabled) { // Simple linear conversion for 12-bit ADC with 12dB attenuation // Approximate range: 0-3300mV for 0-4095 raw values voltage_mv = (adc_avg * 3300) / 4095; } // Apply voltage divider ratio to get actual battery voltage voltage_mv = (int)(voltage_mv * BATTERY_VOLTAGE_DIVIDER_RATIO); return voltage_mv; } int battery_get_percentage(void) { int voltage_mv = battery_read_voltage_mv(); if (voltage_mv < 0) { return -1; } // Clamp to min/max range if (voltage_mv >= BATTERY_VOLTAGE_MAX) { return 100; } if (voltage_mv <= BATTERY_VOLTAGE_MIN) { return 0; } // Linear interpolation between min and max int percentage = ((voltage_mv - BATTERY_VOLTAGE_MIN) * 100) / (BATTERY_VOLTAGE_MAX - BATTERY_VOLTAGE_MIN); return percentage; } // Battery monitoring task static void battery_monitoring_task(void *pvParameters) { ESP_LOGI(TAG, "Battery monitoring task started"); while (1) { int voltage_mv = battery_read_voltage_mv(); int percentage = battery_get_percentage(); if (voltage_mv >= 0 && percentage >= 0) { // Update system state with battery info system_setBatteryVoltage(voltage_mv); system_setBatteryPercentage(percentage); ESP_LOGI(TAG, "Battery: %d mV (%d%%)", voltage_mv, percentage); } else { ESP_LOGW(TAG, "Failed to read battery voltage"); } // Read battery every 30 seconds vTaskDelay(pdMS_TO_TICKS(30000)); } } void battery_start_monitoring_task(void) { xTaskCreate(battery_monitoring_task, "battery_task", 3072, NULL, 5, NULL); ESP_LOGI(TAG, "Battery monitoring task created"); }