mirror of
https://github.com/MrUnknownDE/OpenIris-ESPIDF.git
synced 2026-04-28 18:43:45 +02:00
Cleaned up code and added ESP32 support for AdcSampler-based BatteryMonitor and current monitoring
This commit is contained in:
@@ -1,32 +1,53 @@
|
||||
# Architecture:
|
||||
# +-----------------------+
|
||||
# | MonitoringManager | ← High-level coordinator
|
||||
# +-----------------------+
|
||||
# | BatteryMonitor | ← Battery logic (platform-independent)
|
||||
# | CurrentMonitor | ← Current logic (platform-independent)
|
||||
# +-----------------------+
|
||||
# | AdcSampler | ← BSP: Unified ADC sampling interface
|
||||
# +-----------------------+
|
||||
# | ESP-IDF ADC HAL | ← Espressif official driver
|
||||
# +-----------------------+
|
||||
|
||||
set(
|
||||
requires
|
||||
Helpers
|
||||
)
|
||||
|
||||
if ("$ENV{IDF_TARGET}" STREQUAL "esp32s3")
|
||||
# Platform-specific dependencies
|
||||
if ("$ENV{IDF_TARGET}" STREQUAL "esp32s3" OR "$ENV{IDF_TARGET}" STREQUAL "esp32")
|
||||
list(APPEND requires
|
||||
driver
|
||||
esp_adc
|
||||
)
|
||||
endif()
|
||||
|
||||
# Common source files (platform-independent business logic)
|
||||
set(
|
||||
source_files
|
||||
""
|
||||
"Monitoring/MonitoringManager.cpp"
|
||||
"Monitoring/BatteryMonitor.cpp"
|
||||
"Monitoring/CurrentMonitor.cpp"
|
||||
)
|
||||
|
||||
# BSP Layer: ADC sampler implementation
|
||||
if ("$ENV{IDF_TARGET}" STREQUAL "esp32s3" OR "$ENV{IDF_TARGET}" STREQUAL "esp32")
|
||||
# Common ADC implementation
|
||||
list(APPEND source_files
|
||||
"Monitoring/AdcSampler.cpp"
|
||||
)
|
||||
|
||||
# Platform-specific GPIO-to-channel mapping
|
||||
if ("$ENV{IDF_TARGET}" STREQUAL "esp32s3")
|
||||
list(APPEND source_files
|
||||
"Monitoring/AdcSampler.cpp"
|
||||
"Monitoring/BatteryMonitor.cpp"
|
||||
"Monitoring/CurrentMonitor_esp32s3.cpp"
|
||||
"Monitoring/MonitoringManager_esp32s3.cpp"
|
||||
)
|
||||
else()
|
||||
list(APPEND source_files
|
||||
"Monitoring/CurrentMonitor_esp32.cpp"
|
||||
"Monitoring/MonitoringManager_esp32.cpp"
|
||||
)
|
||||
list(APPEND source_files
|
||||
"Monitoring/AdcSampler_esp32s3.cpp"
|
||||
)
|
||||
elseif ("$ENV{IDF_TARGET}" STREQUAL "esp32")
|
||||
list(APPEND source_files
|
||||
"Monitoring/AdcSampler_esp32.cpp"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +1,39 @@
|
||||
/**
|
||||
* @file AdcSampler.cpp
|
||||
* @brief BSP Layer - Common ADC sampling implementation
|
||||
*
|
||||
* This file contains platform-independent ADC sampling logic.
|
||||
* Platform-specific GPIO-to-channel mapping is in separate files:
|
||||
* - AdcSampler_esp32.cpp
|
||||
* - AdcSampler_esp32s3.cpp
|
||||
*/
|
||||
|
||||
#include "AdcSampler.hpp"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32)
|
||||
#include <esp_log.h>
|
||||
#include <cmath>
|
||||
|
||||
static const char *TAG_ADC = "[AdcSampler]";
|
||||
static const char *TAG = "[AdcSampler]";
|
||||
|
||||
// Static member initialization
|
||||
adc_oneshot_unit_handle_t AdcSampler::shared_unit_ = nullptr;
|
||||
|
||||
AdcSampler::~AdcSampler()
|
||||
{
|
||||
if (cali_handle_)
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
adc_cali_delete_scheme_curve_fitting(cali_handle_);
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32)
|
||||
adc_cali_delete_scheme_line_fitting(cali_handle_);
|
||||
#endif
|
||||
cali_handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool AdcSampler::init(int gpio, adc_atten_t atten, adc_bitwidth_t bitwidth, size_t window_size)
|
||||
{
|
||||
// Initialize moving average filter
|
||||
if (window_size == 0)
|
||||
{
|
||||
window_size = 1;
|
||||
@@ -31,37 +46,57 @@ bool AdcSampler::init(int gpio, adc_atten_t atten, adc_bitwidth_t bitwidth, size
|
||||
atten_ = atten;
|
||||
bitwidth_ = bitwidth;
|
||||
|
||||
// Map GPIO to ADC channel (platform-specific)
|
||||
if (!map_gpio_to_channel(gpio, unit_, channel_))
|
||||
{
|
||||
ESP_LOGW(TAG_ADC, "GPIO %d may not be ADC-capable on ESP32-S3", gpio);
|
||||
ESP_LOGW(TAG, "GPIO %d is not a valid ADC1 pin on this chip", gpio);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize shared ADC unit
|
||||
if (!ensure_unit())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure the ADC channel
|
||||
if (!configure_channel(gpio, atten, bitwidth))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calibration using curve fitting if available
|
||||
// Try calibration (requires eFuse data)
|
||||
// ESP32-S3 uses curve-fitting, ESP32 uses line-fitting
|
||||
esp_err_t cal_err = ESP_FAIL;
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// ESP32-S3 curve fitting calibration
|
||||
adc_cali_curve_fitting_config_t cal_cfg = {
|
||||
.unit_id = unit_,
|
||||
.chan = channel_,
|
||||
.atten = atten_,
|
||||
.bitwidth = bitwidth_,
|
||||
};
|
||||
if (adc_cali_create_scheme_curve_fitting(&cal_cfg, &cali_handle_) == ESP_OK)
|
||||
cal_err = adc_cali_create_scheme_curve_fitting(&cal_cfg, &cali_handle_);
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32)
|
||||
// ESP32 line-fitting calibration is per-unit, not per-channel
|
||||
adc_cali_line_fitting_config_t cal_cfg = {
|
||||
.unit_id = unit_,
|
||||
.atten = atten_,
|
||||
.bitwidth = bitwidth_,
|
||||
};
|
||||
cal_err = adc_cali_create_scheme_line_fitting(&cal_cfg, &cali_handle_);
|
||||
#endif
|
||||
|
||||
if (cal_err == ESP_OK)
|
||||
{
|
||||
cali_inited_ = true;
|
||||
ESP_LOGI(TAG_ADC, "ADC calibration initialized (curve fitting)");
|
||||
ESP_LOGI(TAG, "ADC calibration initialized");
|
||||
}
|
||||
else
|
||||
{
|
||||
cali_inited_ = false;
|
||||
ESP_LOGW(TAG_ADC, "ADC calibration not available; using raw-to-mV approximation");
|
||||
ESP_LOGW(TAG, "ADC calibration not available; using raw-to-mV approximation");
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -75,8 +110,10 @@ bool AdcSampler::sampleOnce()
|
||||
}
|
||||
|
||||
int raw = 0;
|
||||
if (adc_oneshot_read(shared_unit_, channel_, &raw) != ESP_OK)
|
||||
esp_err_t err = adc_oneshot_read(shared_unit_, channel_, &raw);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "adc_oneshot_read failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -90,11 +127,22 @@ bool AdcSampler::sampleOnce()
|
||||
}
|
||||
else
|
||||
{
|
||||
// Approximation for 11dB attenuation
|
||||
mv = (raw * 2450) / 4095;
|
||||
// Approximate conversion for 12dB attenuation (~0–3600 mV range)
|
||||
// Full-scale raw = (1 << bitwidth_) - 1
|
||||
// For 12-bit: max raw = 4095 → ~3600 mV
|
||||
int full_scale_mv = 3600;
|
||||
int max_raw = (1 << bitwidth_) - 1;
|
||||
if (max_raw > 0)
|
||||
{
|
||||
mv = (raw * full_scale_mv) / max_raw;
|
||||
}
|
||||
else
|
||||
{
|
||||
mv = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Moving average
|
||||
// Update moving average filter
|
||||
sample_sum_ -= samples_[sample_idx_];
|
||||
samples_[sample_idx_] = mv;
|
||||
sample_sum_ += mv;
|
||||
@@ -123,7 +171,7 @@ bool AdcSampler::ensure_unit()
|
||||
esp_err_t err = adc_oneshot_new_unit(&unit_cfg, &shared_unit_);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG_ADC, "adc_oneshot_new_unit failed: %s", esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "adc_oneshot_new_unit failed: %s", esp_err_to_name(err));
|
||||
shared_unit_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
@@ -139,22 +187,11 @@ bool AdcSampler::configure_channel(int gpio, adc_atten_t atten, adc_bitwidth_t b
|
||||
esp_err_t err = adc_oneshot_config_channel(shared_unit_, channel_, &chan_cfg);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG_ADC, "adc_oneshot_config_channel failed (GPIO %d, CH %d): %s", gpio, channel_, esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "adc_oneshot_config_channel failed (GPIO %d, CH %d): %s",
|
||||
gpio, static_cast<int>(channel_), esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AdcSampler::map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &channel)
|
||||
{
|
||||
unit = ADC_UNIT_1;
|
||||
if (gpio >= 1 && gpio <= 10)
|
||||
{
|
||||
channel = static_cast<adc_channel_t>(gpio - 1);
|
||||
return true;
|
||||
}
|
||||
channel = ADC_CHANNEL_0;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
@@ -1,37 +1,96 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file AdcSampler.hpp
|
||||
* @brief BSP Layer - Unified ADC sampling interface (Hardware Abstraction)
|
||||
*
|
||||
* Architecture:
|
||||
* +-----------------------+
|
||||
* | MonitoringManager | ← High-level coordinator
|
||||
* +-----------------------+
|
||||
* | BatteryMonitor | ← Battery logic: voltage, capacity, health
|
||||
* | CurrentMonitor | ← Current logic: power, instantaneous current
|
||||
* +-----------------------+
|
||||
* | AdcSampler | ← BSP: Unified ADC sampling interface (this file)
|
||||
* +-----------------------+
|
||||
* | ESP-IDF ADC HAL | ← Espressif official driver
|
||||
* +-----------------------+
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32)
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "esp_adc/adc_cali.h"
|
||||
#include "esp_adc/adc_cali_scheme.h"
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @class AdcSampler
|
||||
* @brief Hardware abstraction layer for ADC sampling with moving average filter
|
||||
*
|
||||
* This class provides a unified interface for ADC sampling across different
|
||||
* ESP32 variants. Platform-specific GPIO-to-channel mapping is handled internally.
|
||||
*/
|
||||
class AdcSampler
|
||||
{
|
||||
public:
|
||||
AdcSampler() = default;
|
||||
~AdcSampler();
|
||||
|
||||
// Initialize the ADC channel on the shared ADC1 oneshot unit.
|
||||
// window_size: moving-average window (>=1).
|
||||
bool init(int gpio, adc_atten_t atten = ADC_ATTEN_DB_12, adc_bitwidth_t bitwidth = ADC_BITWIDTH_DEFAULT, size_t window_size = 1);
|
||||
// Non-copyable, non-movable (owns hardware resources)
|
||||
AdcSampler(const AdcSampler &) = delete;
|
||||
AdcSampler &operator=(const AdcSampler &) = delete;
|
||||
AdcSampler(AdcSampler &&) = delete;
|
||||
AdcSampler &operator=(AdcSampler &&) = delete;
|
||||
|
||||
// Perform one conversion, update filtered value. Returns false on failure.
|
||||
/**
|
||||
* @brief Initialize the ADC channel on the shared ADC1 oneshot unit
|
||||
* @param gpio GPIO pin number for ADC input
|
||||
* @param atten ADC attenuation setting (default: 12dB for ~0-3.3V range)
|
||||
* @param bitwidth ADC resolution (default: 12-bit)
|
||||
* @param window_size Moving average window size (>=1)
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool init(int gpio,
|
||||
adc_atten_t atten = ADC_ATTEN_DB_12,
|
||||
adc_bitwidth_t bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
size_t window_size = 1);
|
||||
|
||||
/**
|
||||
* @brief Perform one ADC conversion and update filtered value
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool sampleOnce();
|
||||
|
||||
/**
|
||||
* @brief Get the filtered ADC reading in millivolts
|
||||
* @return Filtered voltage in mV
|
||||
*/
|
||||
int getFilteredMilliVolts() const { return filtered_mv_; }
|
||||
|
||||
/**
|
||||
* @brief Check if ADC sampling is supported on current platform
|
||||
* @return true if supported
|
||||
*/
|
||||
static constexpr bool isSupported() { return true; }
|
||||
|
||||
private:
|
||||
// Hardware initialization helpers
|
||||
bool ensure_unit();
|
||||
bool configure_channel(int gpio, adc_atten_t atten, adc_bitwidth_t bitwidth);
|
||||
bool map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &channel);
|
||||
|
||||
// Shared ADC1 oneshot handle and calibration mutex-less state (single-threaded use here).
|
||||
/**
|
||||
* @brief Platform-specific GPIO to ADC channel mapping
|
||||
* @note Implemented separately in AdcSampler_esp32.cpp and AdcSampler_esp32s3.cpp
|
||||
*/
|
||||
static bool map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &channel);
|
||||
|
||||
// Shared ADC1 oneshot handle (single instance for all AdcSampler objects)
|
||||
static adc_oneshot_unit_handle_t shared_unit_;
|
||||
|
||||
// Per-instance state
|
||||
adc_cali_handle_t cali_handle_{nullptr};
|
||||
bool cali_inited_{false};
|
||||
adc_channel_t channel_{ADC_CHANNEL_0};
|
||||
@@ -39,6 +98,7 @@ private:
|
||||
adc_atten_t atten_{ADC_ATTEN_DB_12};
|
||||
adc_bitwidth_t bitwidth_{ADC_BITWIDTH_DEFAULT};
|
||||
|
||||
// Moving average filter state
|
||||
std::vector<int> samples_{};
|
||||
int sample_sum_{0};
|
||||
size_t sample_idx_{0};
|
||||
@@ -47,12 +107,13 @@ private:
|
||||
};
|
||||
|
||||
#else
|
||||
// Stub for non-ESP32-S3 targets to keep interfaces consistent.
|
||||
// Stub for unsupported targets to keep interfaces consistent
|
||||
class AdcSampler
|
||||
{
|
||||
public:
|
||||
bool init(int /*gpio*/, int /*atten*/ = 0, int /*bitwidth*/ = 0, size_t /*window_size*/ = 1) { return false; }
|
||||
bool sampleOnce() { return false; }
|
||||
int getFilteredMilliVolts() const { return 0; }
|
||||
static constexpr bool isSupported() { return false; }
|
||||
};
|
||||
#endif
|
||||
|
||||
59
components/Monitoring/Monitoring/AdcSampler_esp32.cpp
Normal file
59
components/Monitoring/Monitoring/AdcSampler_esp32.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @file AdcSampler_esp32.cpp
|
||||
* @brief BSP Layer - ESP32 specific GPIO to ADC channel mapping
|
||||
*
|
||||
* ESP32 ADC1 GPIO mapping:
|
||||
* - GPIO32 → ADC1_CH4
|
||||
* - GPIO33 → ADC1_CH5
|
||||
* - GPIO34 → ADC1_CH6
|
||||
* - GPIO35 → ADC1_CH7
|
||||
* - GPIO36 → ADC1_CH0
|
||||
* - GPIO37 → ADC1_CH1
|
||||
* - GPIO38 → ADC1_CH2
|
||||
* - GPIO39 → ADC1_CH3
|
||||
*
|
||||
* Note: ADC2 is not used to avoid conflicts with Wi-Fi.
|
||||
*/
|
||||
|
||||
#include "AdcSampler.hpp"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
|
||||
bool AdcSampler::map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &channel)
|
||||
{
|
||||
unit = ADC_UNIT_1; // Only use ADC1 to avoid Wi-Fi conflict
|
||||
|
||||
// ESP32: ADC1 GPIO mapping (GPIO32-39)
|
||||
switch (gpio)
|
||||
{
|
||||
case 36:
|
||||
channel = ADC_CHANNEL_0;
|
||||
return true;
|
||||
case 37:
|
||||
channel = ADC_CHANNEL_1;
|
||||
return true;
|
||||
case 38:
|
||||
channel = ADC_CHANNEL_2;
|
||||
return true;
|
||||
case 39:
|
||||
channel = ADC_CHANNEL_3;
|
||||
return true;
|
||||
case 32:
|
||||
channel = ADC_CHANNEL_4;
|
||||
return true;
|
||||
case 33:
|
||||
channel = ADC_CHANNEL_5;
|
||||
return true;
|
||||
case 34:
|
||||
channel = ADC_CHANNEL_6;
|
||||
return true;
|
||||
case 35:
|
||||
channel = ADC_CHANNEL_7;
|
||||
return true;
|
||||
default:
|
||||
channel = ADC_CHANNEL_0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET_ESP32
|
||||
39
components/Monitoring/Monitoring/AdcSampler_esp32s3.cpp
Normal file
39
components/Monitoring/Monitoring/AdcSampler_esp32s3.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file AdcSampler_esp32s3.cpp
|
||||
* @brief BSP Layer - ESP32-S3 specific GPIO to ADC channel mapping
|
||||
*
|
||||
* ESP32-S3 ADC1 GPIO mapping:
|
||||
* - GPIO1 → ADC1_CH0
|
||||
* - GPIO2 → ADC1_CH1
|
||||
* - GPIO3 → ADC1_CH2
|
||||
* - GPIO4 → ADC1_CH3
|
||||
* - GPIO5 → ADC1_CH4
|
||||
* - GPIO6 → ADC1_CH5
|
||||
* - GPIO7 → ADC1_CH6
|
||||
* - GPIO8 → ADC1_CH7
|
||||
* - GPIO9 → ADC1_CH8
|
||||
* - GPIO10 → ADC1_CH9
|
||||
*
|
||||
* Note: ADC2 is not used to avoid conflicts with Wi-Fi.
|
||||
*/
|
||||
|
||||
#include "AdcSampler.hpp"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
|
||||
bool AdcSampler::map_gpio_to_channel(int gpio, adc_unit_t &unit, adc_channel_t &channel)
|
||||
{
|
||||
unit = ADC_UNIT_1; // Only use ADC1 to avoid Wi-Fi conflict
|
||||
|
||||
// ESP32-S3: ADC1 on GPIO1–10 → CH0–CH9
|
||||
if (gpio >= 1 && gpio <= 10)
|
||||
{
|
||||
channel = static_cast<adc_channel_t>(gpio - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
channel = ADC_CHANNEL_0;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3
|
||||
@@ -1,34 +1,62 @@
|
||||
#include "BatteryMonitor.hpp"
|
||||
/**
|
||||
* @file BatteryMonitor.cpp
|
||||
* @brief Business Logic Layer - Battery monitoring implementation
|
||||
*
|
||||
* Platform-independent battery monitoring logic.
|
||||
* Uses AdcSampler (BSP layer) for hardware abstraction.
|
||||
*/
|
||||
|
||||
#include "BatteryMonitor.hpp"
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG_BAT = "[BatteryMonitor]";
|
||||
static const char *TAG = "[BatteryMonitor]";
|
||||
|
||||
bool BatteryMonitor::setup()
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
if (CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM <= 0)
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
if (!AdcSampler::isSupported())
|
||||
{
|
||||
ESP_LOGE(TAG_BAT, "Invalid divider bottom resistor: %d", CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM);
|
||||
ESP_LOGI(TAG, "Battery monitoring not supported on this target");
|
||||
return false;
|
||||
}
|
||||
scale_ = 1.0f + static_cast<float>(CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM) / static_cast<float>(CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM);
|
||||
|
||||
// Validate divider resistor configuration
|
||||
if (CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM == 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Invalid divider bottom resistor: %d", CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM);
|
||||
return false;
|
||||
}
|
||||
if (CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM <= 0 || CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM < 0)
|
||||
{
|
||||
scale_ = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate voltage divider scaling factor
|
||||
// Vbat = Vadc * (R_TOP + R_BOTTOM) / R_BOTTOM
|
||||
scale_ = 1.0f + static_cast<float>(CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM) / static_cast<float>(CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM);
|
||||
}
|
||||
|
||||
// Initialize ADC sampler (BSP layer)
|
||||
if (!adc_.init(CONFIG_MONITORING_BATTERY_ADC_GPIO, ADC_ATTEN_DB_12, ADC_BITWIDTH_DEFAULT, CONFIG_MONITORING_BATTERY_SAMPLES))
|
||||
{
|
||||
ESP_LOGE(TAG_BAT, "Battery ADC init failed");
|
||||
ESP_LOGE(TAG, "Battery ADC init failed");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(TAG_BAT, "Battery monitor enabled (GPIO=%d, scale=%.3f)", CONFIG_MONITORING_BATTERY_ADC_GPIO, scale_);
|
||||
ESP_LOGI(TAG, "Battery monitor enabled (GPIO=%d, scale=%.3f)", CONFIG_MONITORING_BATTERY_ADC_GPIO, scale_);
|
||||
return true;
|
||||
#else
|
||||
ESP_LOGI(TAG_BAT, "Battery monitoring not supported on this target");
|
||||
ESP_LOGI(TAG, "Battery monitoring disabled by Kconfig");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int BatteryMonitor::getBatteryMilliVolts() const
|
||||
{
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
if (!AdcSampler::isSupported())
|
||||
return 0;
|
||||
|
||||
if (!adc_.sampleOnce())
|
||||
return 0;
|
||||
|
||||
@@ -36,7 +64,8 @@ int BatteryMonitor::getBatteryMilliVolts() const
|
||||
if (mv_at_adc <= 0)
|
||||
return 0;
|
||||
|
||||
const float battery_mv = mv_at_adc * scale_;
|
||||
// Apply voltage divider scaling
|
||||
const float battery_mv = static_cast<float>(mv_at_adc) * scale_;
|
||||
return static_cast<int>(std::lround(battery_mv));
|
||||
#else
|
||||
return 0;
|
||||
|
||||
@@ -1,29 +1,58 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file BatteryMonitor.hpp
|
||||
* @brief Business Logic Layer - Battery monitoring (voltage, capacity, health)
|
||||
*
|
||||
* Architecture:
|
||||
* +-----------------------+
|
||||
* | MonitoringManager | ← High-level coordinator
|
||||
* +-----------------------+
|
||||
* | BatteryMonitor | ← Battery logic (this file)
|
||||
* | CurrentMonitor | ← Current logic
|
||||
* +-----------------------+
|
||||
* | AdcSampler | ← BSP: Unified ADC sampling interface
|
||||
* +-----------------------+
|
||||
*/
|
||||
|
||||
#include "AdcSampler.hpp"
|
||||
#include "sdkconfig.h"
|
||||
#include <cmath>
|
||||
|
||||
/**
|
||||
* @class BatteryMonitor
|
||||
* @brief Monitors battery voltage through a resistor divider
|
||||
*
|
||||
* Uses AdcSampler (BSP layer) for hardware abstraction.
|
||||
* Configuration is done via Kconfig options:
|
||||
* - CONFIG_MONITORING_BATTERY_ENABLE
|
||||
* - CONFIG_MONITORING_BATTERY_ADC_GPIO
|
||||
* - CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM
|
||||
* - CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM
|
||||
* - CONFIG_MONITORING_BATTERY_SAMPLES
|
||||
*/
|
||||
class BatteryMonitor
|
||||
{
|
||||
public:
|
||||
BatteryMonitor() = default;
|
||||
~BatteryMonitor() = default;
|
||||
|
||||
// Initialize battery monitoring hardware
|
||||
bool setup();
|
||||
|
||||
// Read once, update filter, and return battery voltage in mV (after divider compensation).
|
||||
// Read once, update filter, and return battery voltage in mV (after divider compensation), 0 on failure
|
||||
int getBatteryMilliVolts() const;
|
||||
|
||||
// Whether monitoring is enabled by Kconfig
|
||||
// Whether monitoring is enabled by Kconfig and supported by BSP
|
||||
static constexpr bool isEnabled()
|
||||
{
|
||||
#ifdef CONFIG_MONITORING_BATTERY_ENABLE
|
||||
return true;
|
||||
return AdcSampler::isSupported();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
float scale_{1.0f};
|
||||
mutable AdcSampler adc_;
|
||||
float scale_{1.0f}; // Voltage divider scaling factor
|
||||
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
|
||||
};
|
||||
|
||||
@@ -1,25 +1,45 @@
|
||||
#include <esp_log.h>
|
||||
#include <cmath>
|
||||
#include "CurrentMonitor.hpp"
|
||||
/**
|
||||
* @file CurrentMonitor.cpp
|
||||
* @brief Business Logic Layer - Current monitoring implementation
|
||||
*
|
||||
* Platform-independent current monitoring logic.
|
||||
* Uses AdcSampler (BSP layer) for hardware abstraction.
|
||||
*/
|
||||
|
||||
static const char *TAG_CM = "[CurrentMonitor]";
|
||||
#include "CurrentMonitor.hpp"
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG = "[CurrentMonitor]";
|
||||
|
||||
void CurrentMonitor::setup()
|
||||
{
|
||||
#ifdef CONFIG_MONITORING_LED_CURRENT
|
||||
if (!AdcSampler::isSupported())
|
||||
{
|
||||
ESP_LOGI(TAG, "LED current monitoring not supported on this target");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool ok = adc_.init(CONFIG_MONITORING_LED_ADC_GPIO, ADC_ATTEN_DB_12, ADC_BITWIDTH_DEFAULT, CONFIG_MONITORING_LED_SAMPLES);
|
||||
if (!ok)
|
||||
{
|
||||
ESP_LOGE(TAG_CM, "ADC init failed for LED current monitor");
|
||||
ESP_LOGE(TAG, "ADC init failed for LED current monitor");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "LED current monitor enabled (GPIO=%d, Shunt=%dmΩ, Gain=%d)", CONFIG_MONITORING_LED_ADC_GPIO, CONFIG_MONITORING_LED_SHUNT_MILLIOHM,
|
||||
CONFIG_MONITORING_LED_GAIN);
|
||||
#else
|
||||
ESP_LOGI(TAG_CM, "LED current monitoring disabled");
|
||||
ESP_LOGI(TAG, "LED current monitoring disabled by Kconfig");
|
||||
#endif
|
||||
}
|
||||
|
||||
float CurrentMonitor::getCurrentMilliAmps() const
|
||||
{
|
||||
#ifdef CONFIG_MONITORING_LED_CURRENT
|
||||
if (!AdcSampler::isSupported())
|
||||
return 0.0f;
|
||||
|
||||
const int shunt_milliohm = CONFIG_MONITORING_LED_SHUNT_MILLIOHM; // mΩ
|
||||
if (shunt_milliohm <= 0)
|
||||
return 0.0f;
|
||||
@@ -28,6 +48,8 @@ float CurrentMonitor::getCurrentMilliAmps() const
|
||||
return 0.0f;
|
||||
|
||||
int filtered_mv = adc_.getFilteredMilliVolts();
|
||||
|
||||
// Apply gain compensation if using current sense amplifier
|
||||
if (CONFIG_MONITORING_LED_GAIN > 0)
|
||||
filtered_mv = filtered_mv / CONFIG_MONITORING_LED_GAIN; // convert back to shunt voltage
|
||||
|
||||
@@ -1,36 +1,61 @@
|
||||
#ifndef CURRENT_MONITOR_HPP
|
||||
#define CURRENT_MONITOR_HPP
|
||||
#pragma once
|
||||
/**
|
||||
* @file CurrentMonitor.hpp
|
||||
* @brief Business Logic Layer - Current monitoring (power, instantaneous current)
|
||||
*
|
||||
* Architecture:
|
||||
* +-----------------------+
|
||||
* | MonitoringManager | ← High-level coordinator
|
||||
* +-----------------------+
|
||||
* | BatteryMonitor | ← Battery logic
|
||||
* | CurrentMonitor | ← Current logic (this file)
|
||||
* +-----------------------+
|
||||
* | AdcSampler | ← BSP: Unified ADC sampling interface
|
||||
* +-----------------------+
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "sdkconfig.h"
|
||||
#include "AdcSampler.hpp"
|
||||
|
||||
/**
|
||||
* @class CurrentMonitor
|
||||
* @brief Monitors LED current through a shunt resistor
|
||||
*
|
||||
* Uses AdcSampler (BSP layer) for hardware abstraction.
|
||||
* Configuration is done via Kconfig options:
|
||||
* - CONFIG_MONITORING_LED_CURRENT
|
||||
* - CONFIG_MONITORING_LED_ADC_GPIO
|
||||
* - CONFIG_MONITORING_LED_SHUNT_MILLIOHM
|
||||
* - CONFIG_MONITORING_LED_GAIN
|
||||
* - CONFIG_MONITORING_LED_SAMPLES
|
||||
*/
|
||||
class CurrentMonitor
|
||||
{
|
||||
public:
|
||||
CurrentMonitor() = default;
|
||||
~CurrentMonitor() = default;
|
||||
|
||||
// Initialize current monitoring hardware
|
||||
void setup();
|
||||
|
||||
// convenience: combined sampling and compute; returns mA
|
||||
float getCurrentMilliAmps() const;
|
||||
//
|
||||
float getBatteryVoltageMilliVolts() const;
|
||||
|
||||
// Whether monitoring is enabled by Kconfig
|
||||
// Whether monitoring is enabled by Kconfig and supported by BSP
|
||||
static constexpr bool isEnabled()
|
||||
{
|
||||
#ifdef CONFIG_MONITORING_LED_CURRENT
|
||||
return true;
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
return AdcSampler::isSupported();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
mutable AdcSampler adc_;
|
||||
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#include "CurrentMonitor.hpp"
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG_CM = "[CurrentMonitor]";
|
||||
|
||||
void CurrentMonitor::setup()
|
||||
{
|
||||
ESP_LOGI(TAG_CM, "LED current monitoring disabled");
|
||||
}
|
||||
|
||||
float CurrentMonitor::getCurrentMilliAmps() const
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
171
components/Monitoring/Monitoring/MonitoringManager.cpp
Normal file
171
components/Monitoring/Monitoring/MonitoringManager.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* @file MonitoringManager.cpp
|
||||
* @brief High-level Coordinator - Monitoring manager implementation
|
||||
*
|
||||
* Platform-independent monitoring coordination logic.
|
||||
* Manages BatteryMonitor and CurrentMonitor subsystems.
|
||||
*/
|
||||
|
||||
#include "MonitoringManager.hpp"
|
||||
#include <esp_log.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "[MonitoringManager]";
|
||||
|
||||
void MonitoringManager::setup()
|
||||
{
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
if (CurrentMonitor::isEnabled())
|
||||
{
|
||||
cm_.setup();
|
||||
ESP_LOGI(TAG, "LED current monitoring enabled. Interval=%dms, Samples=%d, Gain=%d, R=%dmΩ",
|
||||
CONFIG_MONITORING_LED_INTERVAL_MS,
|
||||
CONFIG_MONITORING_LED_SAMPLES,
|
||||
CONFIG_MONITORING_LED_GAIN,
|
||||
CONFIG_MONITORING_LED_SHUNT_MILLIOHM);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "LED current monitoring not supported on this target");
|
||||
}
|
||||
#else
|
||||
ESP_LOGI(TAG, "LED current monitoring disabled by Kconfig");
|
||||
#endif
|
||||
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
if (BatteryMonitor::isEnabled())
|
||||
{
|
||||
bm_.setup();
|
||||
ESP_LOGI(TAG, "Battery monitoring enabled. Interval=%dms, Samples=%d, R-Top=%dΩ, R-Bottom=%dΩ",
|
||||
CONFIG_MONITORING_BATTERY_INTERVAL_MS,
|
||||
CONFIG_MONITORING_BATTERY_SAMPLES,
|
||||
CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM,
|
||||
CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Battery monitoring not supported on this target");
|
||||
}
|
||||
#else
|
||||
ESP_LOGI(TAG, "Battery monitoring disabled by Kconfig");
|
||||
#endif
|
||||
}
|
||||
|
||||
void MonitoringManager::start()
|
||||
{
|
||||
if (!isEnabled())
|
||||
{
|
||||
ESP_LOGI(TAG, "No monitoring features enabled, task not started");
|
||||
return;
|
||||
}
|
||||
|
||||
if (task_ == nullptr)
|
||||
{
|
||||
xTaskCreate(&MonitoringManager::taskEntry, "MonitoringTask", 2048, this, 1, &task_);
|
||||
ESP_LOGI(TAG, "Monitoring task started");
|
||||
}
|
||||
}
|
||||
|
||||
void MonitoringManager::stop()
|
||||
{
|
||||
if (task_)
|
||||
{
|
||||
TaskHandle_t toDelete = task_;
|
||||
task_ = nullptr;
|
||||
vTaskDelete(toDelete);
|
||||
ESP_LOGI(TAG, "Monitoring task stopped");
|
||||
}
|
||||
}
|
||||
|
||||
void MonitoringManager::taskEntry(void *arg)
|
||||
{
|
||||
static_cast<MonitoringManager *>(arg)->run();
|
||||
}
|
||||
|
||||
void MonitoringManager::run()
|
||||
{
|
||||
if (!isEnabled())
|
||||
{
|
||||
vTaskDelete(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
TickType_t now_tick = xTaskGetTickCount();
|
||||
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
TickType_t next_tick_led = now_tick;
|
||||
const TickType_t led_period = pdMS_TO_TICKS(CONFIG_MONITORING_LED_INTERVAL_MS);
|
||||
#endif
|
||||
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
TickType_t next_tick_bat = now_tick;
|
||||
const TickType_t batt_period = pdMS_TO_TICKS(CONFIG_MONITORING_BATTERY_INTERVAL_MS);
|
||||
#endif
|
||||
|
||||
while (true)
|
||||
{
|
||||
now_tick = xTaskGetTickCount();
|
||||
TickType_t wait_ticks = pdMS_TO_TICKS(50); // Default wait time
|
||||
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
if (CurrentMonitor::isEnabled() && now_tick >= next_tick_led)
|
||||
{
|
||||
float ma = cm_.getCurrentMilliAmps();
|
||||
last_current_ma_.store(ma);
|
||||
next_tick_led = now_tick + led_period;
|
||||
}
|
||||
if (CurrentMonitor::isEnabled())
|
||||
{
|
||||
TickType_t to_led = (next_tick_led > now_tick) ? (next_tick_led - now_tick) : 1;
|
||||
if (to_led < wait_ticks)
|
||||
{
|
||||
wait_ticks = to_led;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
if (BatteryMonitor::isEnabled() && now_tick >= next_tick_bat)
|
||||
{
|
||||
const int mv = bm_.getBatteryMilliVolts();
|
||||
if (mv > 0)
|
||||
{
|
||||
last_battery_mv_.store(mv);
|
||||
}
|
||||
next_tick_bat = now_tick + batt_period;
|
||||
}
|
||||
if (BatteryMonitor::isEnabled())
|
||||
{
|
||||
TickType_t to_batt = (next_tick_bat > now_tick) ? (next_tick_bat - now_tick) : 1;
|
||||
if (to_batt < wait_ticks)
|
||||
{
|
||||
wait_ticks = to_batt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (wait_ticks == 0)
|
||||
{
|
||||
wait_ticks = 1;
|
||||
}
|
||||
vTaskDelay(wait_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
float MonitoringManager::getCurrentMilliAmps() const
|
||||
{
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
if (CurrentMonitor::isEnabled())
|
||||
return last_current_ma_.load();
|
||||
#endif
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float MonitoringManager::getBatteryVoltageMilliVolts() const
|
||||
{
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
if (BatteryMonitor::isEnabled())
|
||||
return static_cast<float>(last_battery_mv_.load());
|
||||
#endif
|
||||
return 0.0f;
|
||||
}
|
||||
@@ -1,15 +1,48 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file MonitoringManager.hpp
|
||||
* @brief High-level Coordinator - Combines Battery and Current monitoring
|
||||
*
|
||||
* Architecture:
|
||||
* +-----------------------+
|
||||
* | MonitoringManager | ← High-level coordinator (this file)
|
||||
* +-----------------------+
|
||||
* | BatteryMonitor | ← Battery logic: voltage, capacity, health
|
||||
* | CurrentMonitor | ← Current logic: power, instantaneous current
|
||||
* +-----------------------+
|
||||
* | AdcSampler | ← BSP: Unified ADC sampling interface
|
||||
* +-----------------------+
|
||||
* | ESP-IDF ADC HAL | ← Espressif official driver
|
||||
* +-----------------------+
|
||||
*/
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <atomic>
|
||||
#include "CurrentMonitor.hpp"
|
||||
#include "BatteryMonitor.hpp"
|
||||
#include "CurrentMonitor.hpp"
|
||||
|
||||
/**
|
||||
* @class MonitoringManager
|
||||
* @brief Coordinates battery and current monitoring subsystems
|
||||
*
|
||||
* This class manages the lifecycle and periodic sampling of both
|
||||
* BatteryMonitor and CurrentMonitor. It runs a background FreeRTOS task
|
||||
* to perform periodic measurements based on Kconfig intervals.
|
||||
*
|
||||
* Thread-safety: Uses atomic variables for cross-thread data access.
|
||||
*/
|
||||
class MonitoringManager
|
||||
{
|
||||
public:
|
||||
MonitoringManager() = default;
|
||||
~MonitoringManager() = default;
|
||||
|
||||
// Initialize monitoring subsystems based on Kconfig settings
|
||||
void setup();
|
||||
// Start the background monitoring task
|
||||
void start();
|
||||
// Stop the background monitoring task
|
||||
void stop();
|
||||
|
||||
// Latest filtered current in mA
|
||||
@@ -17,6 +50,12 @@ public:
|
||||
// Latest battery voltage in mV
|
||||
float getBatteryVoltageMilliVolts() const;
|
||||
|
||||
// Check if any monitoring feature is enabled
|
||||
static constexpr bool isEnabled()
|
||||
{
|
||||
return CurrentMonitor::isEnabled() || BatteryMonitor::isEnabled();
|
||||
}
|
||||
|
||||
private:
|
||||
static void taskEntry(void *arg);
|
||||
void run();
|
||||
@@ -24,6 +63,7 @@ private:
|
||||
TaskHandle_t task_{nullptr};
|
||||
std::atomic<float> last_current_ma_{0.0f};
|
||||
std::atomic<int> last_battery_mv_{0};
|
||||
|
||||
CurrentMonitor cm_;
|
||||
BatteryMonitor bm_;
|
||||
};
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#include "MonitoringManager.hpp"
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char *TAG_MM = "[MonitoringManager]";
|
||||
|
||||
void MonitoringManager::setup()
|
||||
{
|
||||
ESP_LOGI(TAG_MM, "Monitoring disabled by Kconfig");
|
||||
}
|
||||
|
||||
void MonitoringManager::start()
|
||||
{
|
||||
}
|
||||
|
||||
void MonitoringManager::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void MonitoringManager::taskEntry(void *arg)
|
||||
{
|
||||
}
|
||||
|
||||
void MonitoringManager::run()
|
||||
{
|
||||
}
|
||||
|
||||
float MonitoringManager::getCurrentMilliAmps() const {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float MonitoringManager::getBatteryVoltageMilliVolts() const
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
#include "MonitoringManager.hpp"
|
||||
#include <cmath>
|
||||
#include <esp_log.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG_MM = "[MonitoringManager]";
|
||||
void MonitoringManager::setup()
|
||||
{
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
cm_.setup();
|
||||
ESP_LOGI(TAG_MM, "LED current monitoring enabled. Interval=%dms, Samples=%d, Gain=%d, R=%dmΩ",
|
||||
CONFIG_MONITORING_LED_INTERVAL_MS,
|
||||
CONFIG_MONITORING_LED_SAMPLES,
|
||||
CONFIG_MONITORING_LED_GAIN,
|
||||
CONFIG_MONITORING_LED_SHUNT_MILLIOHM);
|
||||
#else
|
||||
ESP_LOGI(TAG_MM, "LED current monitoring disabled by Kconfig");
|
||||
#endif
|
||||
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
bm_.setup();
|
||||
ESP_LOGI(TAG_MM, "Battery monitoring enabled. Interval=%dms, Samples=%d, R-Top=%dΩ, R-Bottom=%dΩ",
|
||||
CONFIG_MONITORING_BATTERY_INTERVAL_MS,
|
||||
CONFIG_MONITORING_BATTERY_SAMPLES,
|
||||
CONFIG_MONITORING_BATTERY_DIVIDER_R_TOP_OHM,
|
||||
CONFIG_MONITORING_BATTERY_DIVIDER_R_BOTTOM_OHM);
|
||||
#else
|
||||
ESP_LOGI(TAG_MM, "Battery monitoring disabled by Kconfig");
|
||||
#endif
|
||||
}
|
||||
|
||||
void MonitoringManager::start()
|
||||
{
|
||||
#if CONFIG_MONITORING_LED_CURRENT || CONFIG_MONITORING_BATTERY_ENABLE
|
||||
if (task_ == nullptr)
|
||||
{
|
||||
xTaskCreate(&MonitoringManager::taskEntry, "MonitoringTask", 2048, this, 1, &task_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MonitoringManager::stop()
|
||||
{
|
||||
if (task_)
|
||||
{
|
||||
TaskHandle_t toDelete = task_;
|
||||
task_ = nullptr;
|
||||
vTaskDelete(toDelete);
|
||||
}
|
||||
}
|
||||
|
||||
void MonitoringManager::taskEntry(void *arg)
|
||||
{
|
||||
static_cast<MonitoringManager *>(arg)->run();
|
||||
}
|
||||
|
||||
void MonitoringManager::run()
|
||||
{
|
||||
#if CONFIG_MONITORING_LED_CURRENT || CONFIG_MONITORING_BATTERY_ENABLE
|
||||
TickType_t now_tick = xTaskGetTickCount();
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
TickType_t next_tick_led = now_tick;
|
||||
const TickType_t led_period = pdMS_TO_TICKS(CONFIG_MONITORING_LED_INTERVAL_MS);
|
||||
#endif
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
TickType_t next_tick_bat = now_tick;
|
||||
const TickType_t batt_period = pdMS_TO_TICKS(CONFIG_MONITORING_BATTERY_INTERVAL_MS);
|
||||
#endif
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
now_tick = xTaskGetTickCount();
|
||||
TickType_t wait_ticks = pdMS_TO_TICKS(50);
|
||||
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
if (now_tick >= next_tick_led)
|
||||
{
|
||||
float ma = cm_.getCurrentMilliAmps();
|
||||
last_current_ma_.store(ma);
|
||||
next_tick_led = now_tick + led_period;
|
||||
}
|
||||
TickType_t to_led = (next_tick_led > now_tick) ? (next_tick_led - now_tick) : 1;
|
||||
if (to_led < wait_ticks)
|
||||
{
|
||||
wait_ticks = to_led;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
if (now_tick >= next_tick_bat)
|
||||
{
|
||||
const int mv = bm_.getBatteryMilliVolts();
|
||||
if (mv > 0)
|
||||
{
|
||||
last_battery_mv_.store(mv);
|
||||
}
|
||||
next_tick_bat = now_tick + batt_period;
|
||||
}
|
||||
TickType_t to_batt = (next_tick_bat > now_tick) ? (next_tick_bat - now_tick) : 1;
|
||||
if (to_batt < wait_ticks)
|
||||
{
|
||||
wait_ticks = to_batt;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (wait_ticks == 0)
|
||||
{
|
||||
wait_ticks = 1;
|
||||
}
|
||||
vTaskDelay(wait_ticks);
|
||||
}
|
||||
#else
|
||||
vTaskDelete(nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
float MonitoringManager::getCurrentMilliAmps() const
|
||||
{
|
||||
#if CONFIG_MONITORING_LED_CURRENT
|
||||
return last_current_ma_.load();
|
||||
#else
|
||||
return 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
float MonitoringManager::getBatteryVoltageMilliVolts() const
|
||||
{
|
||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||
return static_cast<float>(last_battery_mv_.load());
|
||||
#else
|
||||
return 0.0f;
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user