#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#define ADC_UNIT ADC_UNIT_1
#define ADC_CHANNEL_1 ADC_CHANNEL_4 // GPIO32
#define ADC_CHANNEL_2 ADC_CHANNEL_7 // GPIO35
#define ADC_ATTENUATION ADC_ATTEN_DB_11
// Для линейной модели
#define ADC_MAX_RAW 4095
#define ADC_MAX_MILLIVOLTS 3300
// Функции калибровки
static bool adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle);
static void adc_calibration_deinit(adc_cali_handle_t handle);
// Кусочно-линейная модель из методички (возвращает мВ)
static int piecewise_voltage(int raw) {
float voltage_v;
if (raw <= 3000) {
voltage_v = 0.0008f * raw + 0.1372f;
} else {
voltage_v = 0.0005f * raw + 1.0874f;
}
return (int)(voltage_v * 1000.0f); // в милливольтах
}
void app_main(void)
{
int raw1, raw2;
int cal1 = 0, cal2 = 0;
int lin1, lin2;
int pw1, pw2;
// Инициализация АЦП
adc_oneshot_unit_handle_t adc_handle;
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_handle));
// Настройка каналов
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = ADC_ATTENUATION,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_1, &config));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_2, &config));
// Калибровка (единая для всех каналов с одинаковыми unit/atten)
adc_cali_handle_t cali_handle = NULL;
bool do_cal = adc_calibration_init(ADC_UNIT, ADC_ATTENUATION, &cali_handle);
while (1) {
// Чтение
ESP_ERROR_CHECK(adc_oneshot_read(adc_handle, ADC_CHANNEL_1, &raw1));
ESP_ERROR_CHECK(adc_oneshot_read(adc_handle, ADC_CHANNEL_2, &raw2));
// Линейная модель
lin1 = (raw1 * ADC_MAX_MILLIVOLTS) / ADC_MAX_RAW;
lin2 = (raw2 * ADC_MAX_MILLIVOLTS) / ADC_MAX_RAW;
// Кусочно-линейная модель
pw1 = piecewise_voltage(raw1);
pw2 = piecewise_voltage(raw2);
// Калиброванное (если есть)
if (do_cal) {
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(cali_handle, raw1, &cal1));
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(cali_handle, raw2, &cal2));
}
// === Вывод для канала 1 ===
printf("\n--- Channel %d (GPIO32) ---\n", ADC_CHANNEL_1);
printf("Raw: %d\n", raw1);
printf("Linear model: %4d mV\n", lin1);
printf("Piecewise model: %4d mV\n", pw1);
if (do_cal) printf("Calibrated (API): %4d mV\n", cal1);
// === Вывод для канала 2 ===
printf("\n--- Channel %d (GPIO35) ---\n", ADC_CHANNEL_2);
printf("Raw: %d\n", raw2);
printf("Linear model: %4d mV\n", lin2);
printf("Piecewise model: %4d mV\n", pw2);
if (do_cal) printf("Calibrated (API): %4d mV\n", cal2);
vTaskDelay(pdMS_TO_TICKS(2000));
}
// Очистка (теоретически недостижима)
ESP_ERROR_CHECK(adc_oneshot_del_unit(adc_handle));
if (do_cal) adc_calibration_deinit(cali_handle);
}
/*---------------------------------------------------------------
Калибровка АЦП
---------------------------------------------------------------*/
static bool adc_calibration_init(adc_unit_t unit, 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) {
adc_cali_curve_fitting_config_t cfg = {
.unit_id = unit,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ret = adc_cali_create_scheme_curve_fitting(&cfg, &handle);
if (ret == ESP_OK) calibrated = true;
}
#endif
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
if (!calibrated) {
adc_cali_line_fitting_config_t cfg = {
.unit_id = unit,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ret = adc_cali_create_scheme_line_fitting(&cfg, &handle);
if (ret == ESP_OK) calibrated = true;
}
#endif
*out_handle = handle;
return calibrated;
}
static void adc_calibration_deinit(adc_cali_handle_t handle)
{
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_delete_scheme_curve_fitting(handle);
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
adc_cali_delete_scheme_line_fitting(handle);
#endif
}