mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
[ESP-IDF 5.1] Fix ADC calibration & crashes on ESP32-C3
This commit is contained in:
@@ -25,6 +25,10 @@ extra_scripts = ${esp32_base_idf5.extra_scripts}
|
||||
build_unflags = ${esp32_base_idf5.build_unflags}
|
||||
-fexceptions
|
||||
board_build.filesystem = littlefs
|
||||
lib_ignore =
|
||||
${core_esp32_IDF5_1__2_0_13.lib_ignore}
|
||||
Adafruit NeoPixel
|
||||
Adafruit NeoMatrix
|
||||
|
||||
|
||||
[env:custom_ESP32c3_4M316k_CDC]
|
||||
|
||||
@@ -3212,6 +3212,29 @@ To create/register a plugin, you have to :
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// RMT driver used in Adafruit_NeoPixel does NOT work on IDF5.1 for RiscV chips
|
||||
#ifdef ESP32C3
|
||||
|
||||
#ifdef USES_P038
|
||||
#undef USES_P038
|
||||
#endif
|
||||
#ifdef USES_P041
|
||||
#undef USES_P041 // NeoClock
|
||||
#endif
|
||||
#ifdef USES_P042
|
||||
#undef USES_P042 // Candle
|
||||
#endif
|
||||
#ifdef USES_P070
|
||||
#undef USES_P070
|
||||
#endif
|
||||
#ifdef USES_P131
|
||||
#undef USES_P131
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Internal temp sensor causes crashes on ESP32-C3
|
||||
#ifdef FEATURE_INTERNAL_TEMPERATURE
|
||||
# undef FEATURE_INTERNAL_TEMPERATURE
|
||||
|
||||
@@ -99,6 +99,8 @@
|
||||
|
||||
#endif
|
||||
|
||||
#include "../Helpers/Hardware_ADC_cali.h"
|
||||
|
||||
#endif // ifdef ESP32
|
||||
|
||||
|
||||
@@ -545,63 +547,58 @@ int espeasy_analogRead(int pin) {
|
||||
#ifdef ESP32
|
||||
|
||||
// ESP32 ADC calibration datatypes.
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#include <esp_adc/adc_cali.h>
|
||||
#include <esp_adc/adc_cali_scheme.h>
|
||||
|
||||
//esp_adc_cal_value_t adc1_calibration_type = ESP_ADC_CAL_VAL_NOT_SUPPORTED;
|
||||
adc_cali_handle_t adc_chars[ADC_ATTENDB_MAX] = {};
|
||||
|
||||
// FIXME TD-er: For now keep a local array of the adc calibration
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
Hardware_ADC_cali_t ESP32_ADC_cali[ADC_ATTEN_MAX];
|
||||
#else
|
||||
esp_adc_cal_value_t adc1_calibration_type = ESP_ADC_CAL_VAL_NOT_SUPPORTED;
|
||||
esp_adc_cal_characteristics_t adc_chars[ADC_ATTEN_MAX];
|
||||
Hardware_ADC_cali_t ESP32_ADC_cali[ADC_ATTENDB_MAX];
|
||||
#endif
|
||||
|
||||
|
||||
void initADC() {
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
# ifndef DEFAULT_VREF
|
||||
# define DEFAULT_VREF 1100
|
||||
# endif // ifndef DEFAULT_VREF
|
||||
const adc_bits_width_t adc_bit_width = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
||||
|
||||
for (size_t atten = 0; atten < ADC_ATTEN_MAX; ++atten) {
|
||||
adc1_calibration_type =
|
||||
esp_adc_cal_characterize(ADC_UNIT_1, static_cast<adc_atten_t>(atten), adc_bit_width, DEFAULT_VREF, &adc_chars[atten]);
|
||||
}
|
||||
#else
|
||||
for (uint32_t i = 0; i < ADC_ATTENDB_MAX; ++i) {
|
||||
if (adc_chars[i] == nullptr) {
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.atten = static_cast<adc_atten_t>(i),
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
|
||||
adc_cali_create_scheme_line_fitting(&cali_config, &adc_chars[i]);
|
||||
for (size_t atten = 0; atten < NR_ELEMENTS(ESP32_ADC_cali); ++atten) {
|
||||
if (!ESP32_ADC_cali[atten].initialized()) {
|
||||
// FIXME TD-er: For now fake some pin which is connected to ADC1
|
||||
#ifdef ESP32_CLASSIC
|
||||
const int pin = 36;
|
||||
#else
|
||||
const int pin = 1;
|
||||
#endif
|
||||
ESP32_ADC_cali[atten].init(pin, static_cast<adc_atten_t>(atten));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
float applyADCFactoryCalibration(float raw_value, adc_atten_t attenuation)
|
||||
{
|
||||
if (attenuation < NR_ELEMENTS(ESP32_ADC_cali))
|
||||
return ESP32_ADC_cali[attenuation].applyFactoryCalibration(raw_value);
|
||||
return raw_value;
|
||||
}
|
||||
|
||||
bool hasADC_factory_calibration() {
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
return esp_adc_cal_check_efuse(adc1_calibration_type) == ESP_OK;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
return ESP32_ADC_cali[0].useFactoryCalibration();
|
||||
}
|
||||
|
||||
const __FlashStringHelper* getADC_factory_calibration_type() {
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
switch (adc1_calibration_type) {
|
||||
case ESP_ADC_CAL_VAL_EFUSE_VREF: return F("V_ref in eFuse");
|
||||
case ESP_ADC_CAL_VAL_EFUSE_TP: return F("Two Point values in eFuse");
|
||||
case ESP_ADC_CAL_VAL_DEFAULT_VREF: return F("Default reference voltage");
|
||||
case ESP_ADC_CAL_VAL_EFUSE_TP_FIT: return F("Two Point values and fitting curve in eFuse");
|
||||
case ESP_ADC_CAL_VAL_NOT_SUPPORTED:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return F("Unknown");
|
||||
const __FlashStringHelper* getADC_factory_calibration_type()
|
||||
{
|
||||
return ESP32_ADC_cali[0].getADC_factory_calibration_type();
|
||||
}
|
||||
|
||||
float getADC_factory_calibrated_min(adc_atten_t attenuation)
|
||||
{
|
||||
if (attenuation < NR_ELEMENTS(ESP32_ADC_cali))
|
||||
return ESP32_ADC_cali[attenuation].getMinOut();
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float getADC_factory_calibrated_max(adc_atten_t attenuation)
|
||||
{
|
||||
if (attenuation < NR_ELEMENTS(ESP32_ADC_cali))
|
||||
return ESP32_ADC_cali[attenuation].getMaxOut();
|
||||
return MAX_ADC_VALUE;
|
||||
}
|
||||
|
||||
int getADC_num_for_gpio(int pin) {
|
||||
|
||||
@@ -63,22 +63,22 @@ int espeasy_analogRead(int pin);
|
||||
|
||||
#ifdef ESP32
|
||||
void initADC();
|
||||
float applyADCFactoryCalibration(
|
||||
float raw_value,
|
||||
adc_atten_t attenuation);
|
||||
|
||||
bool hasADC_factory_calibration();
|
||||
const __FlashStringHelper* getADC_factory_calibration_type();
|
||||
|
||||
float getADC_factory_calibrated_min(adc_atten_t attenuation);
|
||||
float getADC_factory_calibrated_max(adc_atten_t attenuation);
|
||||
|
||||
int getADC_num_for_gpio(int pin);
|
||||
int getADC_num_for_gpio(int pin, int& channel);
|
||||
|
||||
int espeasy_analogRead(int pin,
|
||||
bool readAsTouch = false);
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
// ADC Factory calibration definition
|
||||
extern adc_cali_handle_t adc_chars[ADC_ATTENDB_MAX];
|
||||
#else
|
||||
extern esp_adc_cal_characteristics_t adc_chars[ADC_ATTEN_MAX];
|
||||
#endif
|
||||
#endif // ifdef ESP32
|
||||
|
||||
#if FEATURE_INTERNAL_TEMPERATURE
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "../Helpers/Hardware_ADC.h"
|
||||
|
||||
|
||||
#ifdef ESP32
|
||||
# include "../Helpers/Hardware.h"
|
||||
#endif // ifdef ESP32
|
||||
|
||||
#ifdef ESP8266
|
||||
bool Hardware_ADC_t::init() {}
|
||||
|
||||
|
||||
int Hardware_ADC_t::read() {
|
||||
if (!WiFiEventData.wifiConnectInProgress) {
|
||||
# if FEATURE_ADC_VCC
|
||||
_lastADCvalue = ESP.getVcc();
|
||||
# else // if FEATURE_ADC_VCC
|
||||
_lastADCvalue = analogRead(A0);
|
||||
# endif // if FEATURE_ADC_VCC
|
||||
}
|
||||
return _lastADCvalue;
|
||||
}
|
||||
|
||||
#endif // ifdef ESP8266
|
||||
|
||||
|
||||
#ifdef ESP32
|
||||
Hardware_ADC_t::~Hardware_ADC_t()
|
||||
{
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
adc_oneshot_del_unit(_adc_handle);
|
||||
# else
|
||||
// FIXME TD-er: Should we reset the GPIO config for this pin?
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
}
|
||||
|
||||
bool Hardware_ADC_t::init(int pin, adc_atten_t attenuation)
|
||||
{
|
||||
int ch{};
|
||||
int t = -1;
|
||||
|
||||
if (!getADC_gpio_info(pin, _adc, ch, t)) {
|
||||
return false;
|
||||
}
|
||||
_pin = pin;
|
||||
# if HAS_TOUCH_GPIO
|
||||
_isTouchPin = t >= 0;
|
||||
# endif // if HAS_TOUCH_GPIO
|
||||
_attenuation = attenuation;
|
||||
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
_channel = static_cast<adc_channel_t>(ch);
|
||||
# if HAS_ADC2
|
||||
const adc_unit_t unit = (_adc == 1) ? ADC_UNIT_1 : ADC_UNIT_2;
|
||||
# else // if HAS_ADC2
|
||||
|
||||
if (_adc != 1) {
|
||||
return false;
|
||||
}
|
||||
const adc_unit_t unit = ADC_UNIT_1;
|
||||
# endif // if HAS_ADC2
|
||||
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = unit
|
||||
};
|
||||
|
||||
if (ESP_OK != adc_oneshot_new_unit(&init_config, &_adc_handle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
adc_oneshot_chan_cfg_t config = {
|
||||
.atten = attenuation,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
return ESP_OK == adc_oneshot_config_channel(_adc_handle, _channel, &config);
|
||||
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
if ((_adc == 1) || (_adc == 2)) {
|
||||
analogSetPinAttenuation(_pin, static_cast<adc_attenuation_t>(_attenuation));
|
||||
}
|
||||
|
||||
return true;
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
}
|
||||
|
||||
bool Hardware_ADC_t::adc_calibration_init()
|
||||
{
|
||||
_useFactoryCalibration = _adc_cali_handle.init(_pin, _attenuation);
|
||||
return _useFactoryCalibration;
|
||||
}
|
||||
|
||||
int Hardware_ADC_t::read(bool readAsTouch) {
|
||||
# if HAS_HALL_EFFECT_SENSOR
|
||||
|
||||
if (_adc == 0) {
|
||||
return hallRead();
|
||||
}
|
||||
# endif // if HAS_HALL_EFFECT_SENSOR
|
||||
|
||||
// See:
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#configuration-and-reading-adc
|
||||
// ADC2 is shared with WiFi, so don't read ADC2 when WiFi is on.
|
||||
const bool canread = _adc == 1 || WiFi.getMode() == WIFI_OFF;
|
||||
|
||||
if (canread) {
|
||||
# if HAS_TOUCH_GPIO
|
||||
|
||||
if (readAsTouch && _isTouchPin) {
|
||||
return touchRead(_pin);
|
||||
}
|
||||
# endif // if HAS_TOUCH_GPIO
|
||||
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
int adc_raw{};
|
||||
|
||||
if (ESP_OK == adc_oneshot_read(_adc_handle, _channel, &adc_raw)) {
|
||||
_lastADCvalue = adc_raw;
|
||||
}
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
_lastADCvalue = analogRead(_pin);
|
||||
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
}
|
||||
return _lastADCvalue;
|
||||
}
|
||||
|
||||
float Hardware_ADC_t::applyFactoryCalibration(float rawValue) {
|
||||
return _adc_cali_handle.applyFactoryCalibration(rawValue);
|
||||
}
|
||||
|
||||
/*
|
||||
bool Hardware_ADC_t::hasADC_factory_calibration() {
|
||||
# if ESP_IDF_VERSION_MAJOR < 5
|
||||
return esp_adc_cal_check_efuse(_adc_calibration_type) == ESP_OK;
|
||||
# else // if ESP_IDF_VERSION_MAJOR < 5
|
||||
return false;
|
||||
# endif // if ESP_IDF_VERSION_MAJOR < 5
|
||||
}
|
||||
*/
|
||||
|
||||
#endif // ifdef ESP32
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef HELPERS_HARDWARE_ADC_H
|
||||
#define HELPERS_HARDWARE_ADC_H
|
||||
|
||||
|
||||
#include "../../ESPEasy_common.h"
|
||||
|
||||
#ifdef ESP8266
|
||||
# define HAS_ADC2 0
|
||||
#endif // ifdef ESP8266
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
# include "../Helpers/Hardware_ADC_cali.h"
|
||||
|
||||
#endif // ifdef ESP32
|
||||
|
||||
|
||||
class Hardware_ADC_t {
|
||||
public:
|
||||
|
||||
Hardware_ADC_t() = default;
|
||||
#ifdef ESP8266
|
||||
bool init();
|
||||
|
||||
int read();
|
||||
#endif // ifdef ESP8266
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
~Hardware_ADC_t();
|
||||
|
||||
bool init(int pin,
|
||||
adc_atten_t attenuation = ADC_ATTEN_DB_11);
|
||||
|
||||
// Return whether factory calibration is actually enabled.
|
||||
// Cannot enable factory calibration when no calibration is present.
|
||||
bool adc_calibration_init();
|
||||
|
||||
int read(bool readAsTouch = false);
|
||||
|
||||
float applyFactoryCalibration(float rawValue);
|
||||
|
||||
|
||||
// bool hasADC_factory_calibration();
|
||||
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
static bool adc_calibration_init(
|
||||
int pin,
|
||||
adc_atten_t atten,
|
||||
adc_cali_handle_t *out_handle);
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
static float mapADCtoFloat(float float_value,
|
||||
float adc1,
|
||||
float adc2,
|
||||
float out1,
|
||||
float out2);
|
||||
|
||||
private:
|
||||
|
||||
int _pin = -1;
|
||||
int _adc = -1;
|
||||
|
||||
# if HAS_TOUCH_GPIO
|
||||
bool _isTouchPin = false;
|
||||
# endif // if HAS_TOUCH_GPIO
|
||||
bool _useFactoryCalibration = false;
|
||||
adc_atten_t _attenuation = ADC_ATTEN_DB_11;
|
||||
|
||||
|
||||
// ADC Factory calibration definition
|
||||
Hardware_ADC_cali_t _adc_cali_handle;
|
||||
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
adc_channel_t _channel;
|
||||
adc_oneshot_unit_handle_t _adc_handle;
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
#endif // ifdef ESP32
|
||||
|
||||
int _lastADCvalue{};
|
||||
};
|
||||
|
||||
|
||||
#endif // ifndef HELPERS_HARDWARE_ADC_H
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "../Helpers/Hardware_ADC_cali.h"
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
# include "../Helpers/ESPEasy_math.h"
|
||||
# include "../Helpers/Hardware.h"
|
||||
|
||||
|
||||
Hardware_ADC_cali_t::~Hardware_ADC_cali_t()
|
||||
{
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
if (_useFactoryCalibration) {
|
||||
# if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_delete_scheme_curve_fitting(_adc_cali_handle);
|
||||
|
||||
# elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
adc_cali_delete_scheme_line_fitting(_adc_cali_handle);
|
||||
# endif // if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
}
|
||||
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
}
|
||||
|
||||
bool Hardware_ADC_cali_t::init(int pin,
|
||||
adc_atten_t attenuation)
|
||||
{
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5 && ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
_useHighResInterpolation = false;
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5 && ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
_useHighResInterpolation = attenuation != adc_atten_t::ADC_ATTEN_DB_11;
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5 && ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
if (_adc_cali_handle != nullptr) {
|
||||
# if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_delete_scheme_curve_fitting(_adc_cali_handle);
|
||||
|
||||
# elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
adc_cali_delete_scheme_line_fitting(_adc_cali_handle);
|
||||
|
||||
# endif // if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
}
|
||||
|
||||
_useFactoryCalibration = Hardware_ADC_cali_t::adc_calibration_init(
|
||||
pin,
|
||||
attenuation,
|
||||
&_adc_cali_handle);
|
||||
|
||||
if (_useFactoryCalibration) {
|
||||
int tmp{};
|
||||
adc_cali_raw_to_voltage(_adc_cali_handle, 0, &tmp);
|
||||
_min_out = tmp;
|
||||
adc_cali_raw_to_voltage(_adc_cali_handle, MAX_ADC_VALUE, &tmp);
|
||||
_max_out = tmp;
|
||||
}
|
||||
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
# ifndef DEFAULT_VREF
|
||||
# define DEFAULT_VREF 1100
|
||||
# endif // ifndef DEFAULT_VREF
|
||||
constexpr adc_bits_width_t adc_bit_width = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
||||
_adc_calibration_type =
|
||||
esp_adc_cal_characterize((getADC_num_for_gpio(pin) == 1) ? ADC_UNIT_1 : ADC_UNIT_2,
|
||||
static_cast<adc_atten_t>(attenuation),
|
||||
adc_bit_width,
|
||||
DEFAULT_VREF,
|
||||
&_adc_chars);
|
||||
_useFactoryCalibration = esp_adc_cal_check_efuse(_adc_calibration_type) == ESP_OK;
|
||||
|
||||
if (_useFactoryCalibration) {
|
||||
_min_out = esp_adc_cal_raw_to_voltage(0, &_adc_chars);
|
||||
_max_out = esp_adc_cal_raw_to_voltage(MAX_ADC_VALUE, &_adc_chars);
|
||||
}
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
_initialized = true;
|
||||
|
||||
return _useFactoryCalibration;
|
||||
}
|
||||
|
||||
float Hardware_ADC_cali_t::applyFactoryCalibration(float rawValue) const {
|
||||
if (!_useFactoryCalibration) {
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
if (!_useHighResInterpolation) {
|
||||
const int raw = rawValue;
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
int res{};
|
||||
adc_cali_raw_to_voltage(_adc_cali_handle, raw, &res);
|
||||
return res;
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
return esp_adc_cal_raw_to_voltage(raw, &_adc_chars);
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
}
|
||||
|
||||
// All other attenuations do appear to have a straight calibration curve.
|
||||
// But applying the factory calibration then reduces resolution.
|
||||
// So we interpolate using the calibrated extremes
|
||||
|
||||
return mapADCtoFloat(
|
||||
rawValue,
|
||||
0,
|
||||
MAX_ADC_VALUE,
|
||||
_min_out,
|
||||
_max_out);
|
||||
}
|
||||
|
||||
const __FlashStringHelper * Hardware_ADC_cali_t::getADC_factory_calibration_type() const {
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
if (_useFactoryCalibration) {
|
||||
# if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
return F("Calibration Curve Fitting");
|
||||
# endif // if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
# if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
return F("Calibration Line Fitting");
|
||||
# endif // if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
}
|
||||
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
switch (_adc_calibration_type) {
|
||||
case ESP_ADC_CAL_VAL_EFUSE_VREF: return F("V_ref in eFuse");
|
||||
case ESP_ADC_CAL_VAL_EFUSE_TP: return F("Two Point values in eFuse");
|
||||
case ESP_ADC_CAL_VAL_DEFAULT_VREF: return F("Default reference voltage");
|
||||
case ESP_ADC_CAL_VAL_EFUSE_TP_FIT: return F("Two Point values and fitting curve in eFuse");
|
||||
case ESP_ADC_CAL_VAL_NOT_SUPPORTED:
|
||||
break;
|
||||
}
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
return F("Unknown");
|
||||
}
|
||||
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
bool Hardware_ADC_cali_t::adc_calibration_init(
|
||||
int pin,
|
||||
adc_atten_t atten,
|
||||
adc_cali_handle_t *out_handle)
|
||||
{
|
||||
int ch{};
|
||||
const int adc = getADC_num_for_gpio(pin, ch);
|
||||
const adc_channel_t channel = static_cast<adc_channel_t>(ch);
|
||||
|
||||
# if HAS_ADC2
|
||||
const adc_unit_t unit = (adc == 1) ? ADC_UNIT_1 : ADC_UNIT_2;
|
||||
# else // if HAS_ADC2
|
||||
const adc_unit_t unit = ADC_UNIT_1;
|
||||
# endif // if HAS_ADC2
|
||||
|
||||
adc_cali_handle_t handle = NULL;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
bool calibrated = false;
|
||||
|
||||
# if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
|
||||
if (!calibrated) {
|
||||
// calibration scheme version: Curve Fitting
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.chan = channel,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
# endif // if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
|
||||
# if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
|
||||
if (!calibrated) {
|
||||
// calibration scheme version: Line Fitting
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
# endif // if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
|
||||
*out_handle = handle;
|
||||
|
||||
/*
|
||||
if (ret == ESP_OK) {
|
||||
// Calibration Success
|
||||
} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
|
||||
// eFuse not burnt, skip software calibration
|
||||
} else {
|
||||
// Invalid arg or no memory
|
||||
}
|
||||
*/
|
||||
|
||||
return calibrated;
|
||||
}
|
||||
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
float Hardware_ADC_cali_t::mapADCtoFloat(float float_value,
|
||||
float adc1,
|
||||
float adc2,
|
||||
float out1,
|
||||
float out2)
|
||||
{
|
||||
if (!approximatelyEqual(adc1, adc2))
|
||||
{
|
||||
const float normalized = (float_value - adc1) / (adc2 - adc1);
|
||||
float_value = normalized * (out2 - out1) + out1;
|
||||
}
|
||||
return float_value;
|
||||
}
|
||||
|
||||
#endif // ifdef ESP32
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef HELPERS_HARDWARE_ADC_CALI_H
|
||||
#define HELPERS_HARDWARE_ADC_CALI_H
|
||||
|
||||
#include "../../ESPEasy_common.h"
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
# include "../Helpers/Hardware_defines.h"
|
||||
|
||||
// Needed to get ADC Vref
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
# include <soc/soc_caps.h>
|
||||
# include <esp_adc/adc_oneshot.h>
|
||||
# include <esp_adc/adc_cali.h>
|
||||
# include <esp_adc/adc_cali_scheme.h>
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
# include <esp_adc_cal.h>
|
||||
# include <driver/adc.h>
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
# if (SOC_ADC_PERIPH_NUM >= 2) && !CONFIG_IDF_TARGET_ESP32C3
|
||||
|
||||
/**
|
||||
* On ESP32C3, ADC2 is no longer supported, due to its HW limitation.
|
||||
* Search for errata on espressif website for more details.
|
||||
*/
|
||||
# define HAS_ADC2 1
|
||||
# else // if (SOC_ADC_PERIPH_NUM >= 2) && !CONFIG_IDF_TARGET_ESP32C3
|
||||
# define HAS_ADC2 0
|
||||
# endif // if (SOC_ADC_PERIPH_NUM >= 2) && !CONFIG_IDF_TARGET_ESP32C3
|
||||
|
||||
|
||||
class Hardware_ADC_cali_t {
|
||||
public:
|
||||
|
||||
Hardware_ADC_cali_t() = default;
|
||||
|
||||
~Hardware_ADC_cali_t();
|
||||
|
||||
// Return whether factory calibration is actually enabled.
|
||||
// Cannot enable factory calibration when no calibration is present.
|
||||
bool init(int pin,
|
||||
adc_atten_t attenuation = ADC_ATTEN_DB_11);
|
||||
|
||||
bool initialized() const { return _initialized; }
|
||||
|
||||
// Convert the rawValue to milli Volt when factory calibration is present and initialized.
|
||||
float applyFactoryCalibration(float rawValue) const;
|
||||
|
||||
float getMinOut() const {
|
||||
return _min_out;
|
||||
}
|
||||
|
||||
float getMaxOut() const {
|
||||
return _max_out;
|
||||
}
|
||||
|
||||
bool useFactoryCalibration() const {
|
||||
return _useFactoryCalibration;
|
||||
}
|
||||
|
||||
const __FlashStringHelper* getADC_factory_calibration_type() const;
|
||||
|
||||
private:
|
||||
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
static bool adc_calibration_init(
|
||||
int pin,
|
||||
adc_atten_t atten,
|
||||
adc_cali_handle_t *out_handle);
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
public:
|
||||
|
||||
static float mapADCtoFloat(float float_value,
|
||||
float adc1,
|
||||
float adc2,
|
||||
float out1,
|
||||
float out2);
|
||||
|
||||
private:
|
||||
|
||||
// ADC Factory calibration definition
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
adc_cali_handle_t _adc_cali_handle = nullptr;
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
esp_adc_cal_value_t _adc_calibration_type = ESP_ADC_CAL_VAL_NOT_SUPPORTED;
|
||||
esp_adc_cal_characteristics_t _adc_chars;
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
bool _useFactoryCalibration = false;
|
||||
bool _useHighResInterpolation = false;
|
||||
|
||||
bool _initialized = false;
|
||||
|
||||
float _min_out{};
|
||||
float _max_out = MAX_ADC_VALUE;
|
||||
};
|
||||
|
||||
#endif // ifdef ESP32
|
||||
#endif // ifndef HELPERS_HARDWARE_ADC_CALI_H
|
||||
@@ -4,11 +4,20 @@
|
||||
|
||||
# include "../Globals/RulesCalculate.h"
|
||||
|
||||
#include "../Helpers/Hardware_ADC_cali.h"
|
||||
|
||||
# ifndef DEFAULT_VREF
|
||||
# define DEFAULT_VREF 1100
|
||||
# endif // ifndef DEFAULT_VREF
|
||||
|
||||
#ifndef P002_ADC_ATTEN_MAX
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
#define P002_ADC_ATTEN_MAX ADC_ATTEN_MAX
|
||||
#else
|
||||
#define P002_ADC_ATTEN_MAX ADC_ATTENDB_MAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
void P002_data_struct::init(struct EventStruct *event)
|
||||
{
|
||||
@@ -172,19 +181,14 @@ void P002_data_struct::webformLoad(struct EventStruct *event)
|
||||
# endif // if FEATURE_CHART_JS
|
||||
formatADC_statistics(F("Current ADC to mV"), raw_value);
|
||||
|
||||
for (size_t att = 0; att < NR_ELEMENTS(adc_chars); ++att) {
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
int low, high = 0;
|
||||
adc_cali_raw_to_voltage(adc_chars[att], 0, &low);
|
||||
adc_cali_raw_to_voltage(adc_chars[att], MAX_ADC_VALUE, &high);
|
||||
#else
|
||||
const int low = esp_adc_cal_raw_to_voltage(0, &adc_chars[att]);
|
||||
const int high = esp_adc_cal_raw_to_voltage(MAX_ADC_VALUE, &adc_chars[att]);
|
||||
#endif
|
||||
for (size_t att = 0; att < P002_ADC_ATTEN_MAX; ++att) {
|
||||
const adc_atten_t attenuation = static_cast<adc_atten_t>(att);
|
||||
const int low = getADC_factory_calibrated_min(attenuation);
|
||||
const int high = getADC_factory_calibrated_max(attenuation);
|
||||
const float step = static_cast<float>(high - low) / MAX_ADC_VALUE;
|
||||
|
||||
String rowlabel = F("Attenuation @");
|
||||
rowlabel += AttenuationToString(static_cast<adc_atten_t>(att));
|
||||
rowlabel += AttenuationToString(attenuation);
|
||||
addRowLabel(rowlabel);
|
||||
addHtml(F("Range / Step: "));
|
||||
addHtmlInt(low);
|
||||
@@ -375,14 +379,14 @@ void P002_data_struct::webformLoad_calibrationCurve(struct EventStruct *event)
|
||||
|
||||
size_t current_attenuation = getAttenuation(event);
|
||||
|
||||
if (current_attenuation >= NR_ELEMENTS(adc_chars)) { current_attenuation = ADC_ATTEN_DB_11; }
|
||||
if (current_attenuation >= P002_ADC_ATTEN_MAX) { current_attenuation = ADC_ATTEN_DB_11; }
|
||||
|
||||
for (size_t att = 0; att < NR_ELEMENTS(adc_chars); ++att)
|
||||
for (size_t att = 0; att < P002_ADC_ATTEN_MAX; ++att)
|
||||
{
|
||||
float values[valueCount];
|
||||
|
||||
for (int i = 0; i < valueCount; ++i) {
|
||||
values[i] = applyFactoryCalibration(xAxisValues[i], static_cast<adc_atten_t>(att));
|
||||
values[i] = applyADCFactoryCalibration(xAxisValues[i], static_cast<adc_atten_t>(att));
|
||||
}
|
||||
|
||||
add_ChartJS_dataset(
|
||||
@@ -421,15 +425,10 @@ void P002_data_struct::getInputRange(struct EventStruct *event, int& minInputVal
|
||||
|
||||
if (useFactoryCalibration(event) && !ignoreCalibration) {
|
||||
// reading in mVolt, not ADC
|
||||
const size_t attenuation = getAttenuation(event);
|
||||
const adc_atten_t attenuation = getAttenuation(event);
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
adc_cali_raw_to_voltage(adc_chars[attenuation], 0, &minInputValue);
|
||||
adc_cali_raw_to_voltage(adc_chars[attenuation], MAX_ADC_VALUE, &maxInputValue);
|
||||
#else
|
||||
minInputValue = esp_adc_cal_raw_to_voltage(0, &adc_chars[attenuation]);
|
||||
maxInputValue = esp_adc_cal_raw_to_voltage(MAX_ADC_VALUE, &adc_chars[attenuation]);
|
||||
#endif
|
||||
minInputValue = getADC_factory_calibrated_min(attenuation);
|
||||
maxInputValue = getADC_factory_calibrated_max(attenuation);
|
||||
}
|
||||
# endif // ifdef ESP32
|
||||
}
|
||||
@@ -507,7 +506,7 @@ void P002_data_struct::formatADC_statistics(const __FlashStringHelper *label, in
|
||||
# ifdef ESP32
|
||||
|
||||
if (_useFactoryCalibration) {
|
||||
float_value = applyFactoryCalibration(raw, _attenuation);
|
||||
float_value = applyADCFactoryCalibration(raw, _attenuation);
|
||||
|
||||
html_add_estimate_symbol();
|
||||
addHtmlFloat(float_value, _nrDecimals);
|
||||
@@ -824,7 +823,7 @@ bool P002_data_struct::getValue(float& float_value,
|
||||
# ifdef ESP32
|
||||
|
||||
if (_useFactoryCalibration) {
|
||||
float_value = applyFactoryCalibration(raw_value, _attenuation);
|
||||
float_value = applyADCFactoryCalibration(raw_value, _attenuation);
|
||||
}
|
||||
# endif // ifdef ESP32
|
||||
|
||||
@@ -894,7 +893,7 @@ bool P002_data_struct::getOversamplingValue(float& float_value, int& raw_value)
|
||||
# ifdef ESP32
|
||||
|
||||
if (_useFactoryCalibration) {
|
||||
float_value = applyFactoryCalibration(float_value, _attenuation);
|
||||
float_value = applyADCFactoryCalibration(float_value, _attenuation);
|
||||
}
|
||||
# endif // ifdef ESP32
|
||||
|
||||
@@ -945,7 +944,7 @@ int P002_data_struct::computeADC_to_bin(const int& currentValue) const
|
||||
# ifdef ESP32
|
||||
|
||||
if (_useFactoryCalibration) {
|
||||
calibrated_value = applyFactoryCalibration(calibrated_value, _attenuation);
|
||||
calibrated_value = applyADCFactoryCalibration(calibrated_value, _attenuation);
|
||||
}
|
||||
# endif // ifdef ESP32
|
||||
|
||||
@@ -1040,7 +1039,7 @@ float P002_data_struct::getCurrentValue(struct EventStruct *event, int& raw_valu
|
||||
# ifdef ESP32
|
||||
|
||||
if (useFactoryCalibration(event)) {
|
||||
return applyFactoryCalibration(raw_value, getAttenuation(event));
|
||||
return applyADCFactoryCalibration(raw_value, getAttenuation(event));
|
||||
}
|
||||
# endif // ifdef ESP32
|
||||
|
||||
@@ -1070,49 +1069,6 @@ bool P002_data_struct::useFactoryCalibration(struct EventStruct *event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float P002_data_struct::applyFactoryCalibration(float raw_value, adc_atten_t attenuation)
|
||||
{
|
||||
if (attenuation == adc_atten_t::ADC_ATTEN_DB_11) {
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
int res{};
|
||||
adc_cali_raw_to_voltage(adc_chars[attenuation], raw_value, &res);
|
||||
return res;
|
||||
#else
|
||||
return esp_adc_cal_raw_to_voltage(raw_value, &adc_chars[attenuation]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// All other attenuations do appear to have a straight calibration curve.
|
||||
// But applying the factory calibration then reduces resolution.
|
||||
// So we interpolate using the calibrated extremes
|
||||
|
||||
// Cache the computing of the values.
|
||||
static adc_atten_t last_Attn = static_cast<adc_atten_t>(NR_ELEMENTS(adc_chars));
|
||||
static float last_out1 = 0.0;
|
||||
static float last_out2 = MAX_ADC_VALUE;
|
||||
|
||||
if (last_Attn != attenuation) {
|
||||
last_Attn = attenuation;
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
int tmp{};
|
||||
adc_cali_raw_to_voltage(adc_chars[attenuation], 0, &tmp);
|
||||
last_out1 = tmp;
|
||||
adc_cali_raw_to_voltage(adc_chars[attenuation], MAX_ADC_VALUE, &tmp);
|
||||
last_out2 = tmp;
|
||||
#else
|
||||
last_out1 = esp_adc_cal_raw_to_voltage(0, &adc_chars[attenuation]);
|
||||
last_out2 = esp_adc_cal_raw_to_voltage(MAX_ADC_VALUE, &adc_chars[attenuation]);
|
||||
#endif
|
||||
}
|
||||
|
||||
return mapADCtoFloat(
|
||||
raw_value,
|
||||
0,
|
||||
MAX_ADC_VALUE,
|
||||
last_out1,
|
||||
last_out2);
|
||||
}
|
||||
|
||||
# endif // ifdef ESP32
|
||||
|
||||
# ifndef LIMIT_BUILD_SIZE
|
||||
|
||||
@@ -213,10 +213,6 @@ public:
|
||||
# ifdef ESP32
|
||||
static bool useFactoryCalibration(struct EventStruct *event);
|
||||
|
||||
static float applyFactoryCalibration(float raw_value,
|
||||
adc_atten_t attenuation);
|
||||
|
||||
|
||||
# endif // ifdef ESP32
|
||||
|
||||
private:
|
||||
@@ -276,6 +272,7 @@ private:
|
||||
bool _useFactoryCalibration = false;
|
||||
adc_atten_t _attenuation = ADC_ATTEN_DB_11;
|
||||
# endif // ifdef ESP32
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user