mirror of
https://github.com/MrUnknownDE/OpenIris-ESPIDF.git
synced 2026-05-07 06:06:05 +02:00
Move the battery power calculation part to the BatteryMonitor class
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
#include "device_commands.hpp"
|
#include "device_commands.hpp"
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include "LEDManager.hpp"
|
#include "LEDManager.hpp"
|
||||||
#include "MonitoringManager.hpp"
|
#include "MonitoringManager.hpp"
|
||||||
#include "esp_mac.h"
|
#include "esp_mac.h"
|
||||||
@@ -230,53 +228,15 @@ CommandResult getBatteryStatusCommand(std::shared_ptr<DependencyRegistry> regist
|
|||||||
return CommandResult::getErrorResult("MonitoringManager unavailable");
|
return CommandResult::getErrorResult("MonitoringManager unavailable");
|
||||||
}
|
}
|
||||||
|
|
||||||
const float volts = mon->getBatteryVoltageMilliVolts();
|
const auto status = mon->getBatteryStatus();
|
||||||
if (volts <= 0.0f)
|
if (!status.valid)
|
||||||
{
|
{
|
||||||
return CommandResult::getErrorResult("Battery voltage unavailable");
|
return CommandResult::getErrorResult("Battery voltage unavailable");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VoltageSOC
|
|
||||||
{
|
|
||||||
float voltage_mv;
|
|
||||||
float soc;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr std::array<VoltageSOC, 12> lookup = {
|
|
||||||
VoltageSOC{4200.0f, 100.0f}, VoltageSOC{4060.0f, 90.0f}, VoltageSOC{3980.0f, 80.0f}, VoltageSOC{3920.0f, 70.0f},
|
|
||||||
VoltageSOC{3870.0f, 60.0f}, VoltageSOC{3820.0f, 50.0f}, VoltageSOC{3790.0f, 40.0f}, VoltageSOC{3770.0f, 30.0f},
|
|
||||||
VoltageSOC{3740.0f, 20.0f}, VoltageSOC{3680.0f, 10.0f}, VoltageSOC{3450.0f, 5.0f}, VoltageSOC{3300.0f, 0.0f},
|
|
||||||
};
|
|
||||||
|
|
||||||
float percent = 0.0f;
|
|
||||||
if (volts >= lookup.front().voltage_mv)
|
|
||||||
{
|
|
||||||
percent = lookup.front().soc;
|
|
||||||
}
|
|
||||||
else if (volts <= lookup.back().voltage_mv)
|
|
||||||
{
|
|
||||||
percent = lookup.back().soc;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (size_t index = 0; index < lookup.size() - 1; ++index)
|
|
||||||
{
|
|
||||||
const auto high = lookup[index];
|
|
||||||
const auto low = lookup[index + 1];
|
|
||||||
if (volts <= high.voltage_mv && volts >= low.voltage_mv)
|
|
||||||
{
|
|
||||||
const float span = high.voltage_mv - low.voltage_mv;
|
|
||||||
const float ratio = (volts - low.voltage_mv) / (span > 0.0f ? span : 1.0f);
|
|
||||||
percent = low.soc + ratio * (high.soc - low.soc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
percent = std::clamp(percent, 0.0f, 100.0f);
|
|
||||||
|
|
||||||
const auto json = nlohmann::json{
|
const auto json = nlohmann::json{
|
||||||
{"voltage_mv", std::format("{:.2f}", static_cast<double>(volts))},
|
{"voltage_mv", std::format("{:.2f}", static_cast<double>(status.voltage_mv))},
|
||||||
{"percentage", std::format("{:.1f}", static_cast<double>(percent))},
|
{"percentage", std::format("{:.1f}", static_cast<double>(status.percentage))},
|
||||||
};
|
};
|
||||||
return CommandResult::getSuccessResult(json);
|
return CommandResult::getSuccessResult(json);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -71,3 +71,52 @@ int BatteryMonitor::getBatteryMilliVolts() const
|
|||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float BatteryMonitor::voltageToPercentage(int voltage_mv)
|
||||||
|
{
|
||||||
|
const float volts = static_cast<float>(voltage_mv);
|
||||||
|
|
||||||
|
// Handle boundary conditions
|
||||||
|
if (volts >= soc_lookup_.front().voltage_mv)
|
||||||
|
return soc_lookup_.front().soc;
|
||||||
|
|
||||||
|
if (volts <= soc_lookup_.back().voltage_mv)
|
||||||
|
return soc_lookup_.back().soc;
|
||||||
|
|
||||||
|
// Linear interpolation between lookup table points
|
||||||
|
for (size_t i = 0; i < soc_lookup_.size() - 1; ++i)
|
||||||
|
{
|
||||||
|
const auto &high = soc_lookup_[i];
|
||||||
|
const auto &low = soc_lookup_[i + 1];
|
||||||
|
|
||||||
|
if (volts <= high.voltage_mv && volts >= low.voltage_mv)
|
||||||
|
{
|
||||||
|
const float voltage_span = high.voltage_mv - low.voltage_mv;
|
||||||
|
if (voltage_span <= 0.0f)
|
||||||
|
{
|
||||||
|
return low.soc;
|
||||||
|
}
|
||||||
|
const float ratio = (volts - low.voltage_mv) / voltage_span;
|
||||||
|
return low.soc + ratio * (high.soc - low.soc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
BatteryStatus BatteryMonitor::getBatteryStatus() const
|
||||||
|
{
|
||||||
|
BatteryStatus status = {0, 0.0f, false};
|
||||||
|
|
||||||
|
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||||
|
const int mv = getBatteryMilliVolts();
|
||||||
|
if (mv <= 0)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
status.voltage_mv = mv;
|
||||||
|
status.percentage = std::clamp(voltageToPercentage(mv), 0.0f, 100.0f);
|
||||||
|
status.valid = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,15 +14,31 @@
|
|||||||
* +-----------------------+
|
* +-----------------------+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
#include "AdcSampler.hpp"
|
#include "AdcSampler.hpp"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct BatteryStatus
|
||||||
|
* @brief Battery status information
|
||||||
|
*/
|
||||||
|
struct BatteryStatus
|
||||||
|
{
|
||||||
|
int voltage_mv; // Battery voltage in millivolts
|
||||||
|
float percentage; // State of charge percentage (0-100%)
|
||||||
|
bool valid; // Whether the reading is valid
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class BatteryMonitor
|
* @class BatteryMonitor
|
||||||
* @brief Monitors battery voltage through a resistor divider
|
* @brief Monitors battery voltage and calculates state of charge for Li-ion batteries
|
||||||
*
|
*
|
||||||
* Uses AdcSampler (BSP layer) for hardware abstraction.
|
* Uses AdcSampler (BSP layer) for hardware abstraction.
|
||||||
|
* Includes voltage-to-SOC lookup table for typical Li-ion/Li-Po batteries.
|
||||||
|
*
|
||||||
* Configuration is done via Kconfig options:
|
* Configuration is done via Kconfig options:
|
||||||
* - CONFIG_MONITORING_BATTERY_ENABLE
|
* - CONFIG_MONITORING_BATTERY_ENABLE
|
||||||
* - CONFIG_MONITORING_BATTERY_ADC_GPIO
|
* - CONFIG_MONITORING_BATTERY_ADC_GPIO
|
||||||
@@ -39,10 +55,29 @@ public:
|
|||||||
// Initialize battery monitoring hardware
|
// Initialize battery monitoring hardware
|
||||||
bool setup();
|
bool setup();
|
||||||
|
|
||||||
// Read once, update filter, and return battery voltage in mV (after divider compensation), 0 on failure
|
/**
|
||||||
|
* @brief Read battery voltage (with divider compensation)
|
||||||
|
* @return Battery voltage in millivolts, 0 on failure
|
||||||
|
*/
|
||||||
int getBatteryMilliVolts() const;
|
int getBatteryMilliVolts() const;
|
||||||
|
|
||||||
// Whether monitoring is enabled by Kconfig and supported by BSP
|
/**
|
||||||
|
* @brief Calculate battery state of charge from voltage
|
||||||
|
* @param voltage_mv Battery voltage in millivolts
|
||||||
|
* @return State of charge percentage (0-100%)
|
||||||
|
*/
|
||||||
|
static float voltageToPercentage(int voltage_mv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get complete battery status (voltage + percentage)
|
||||||
|
* @return BatteryStatus struct with voltage, percentage, and validity
|
||||||
|
*/
|
||||||
|
BatteryStatus getBatteryStatus() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if battery monitoring is enabled and supported
|
||||||
|
* @return true if enabled and ADC is supported
|
||||||
|
*/
|
||||||
static constexpr bool isEnabled()
|
static constexpr bool isEnabled()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_MONITORING_BATTERY_ENABLE
|
#ifdef CONFIG_MONITORING_BATTERY_ENABLE
|
||||||
@@ -53,6 +88,34 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float scale_{1.0f}; // Voltage divider scaling factor
|
/**
|
||||||
|
* @brief Li-ion/Li-Po voltage to SOC lookup table entry
|
||||||
|
*/
|
||||||
|
struct VoltageSOC
|
||||||
|
{
|
||||||
|
float voltage_mv;
|
||||||
|
float soc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Typical Li-ion single cell discharge curve lookup table
|
||||||
|
* Based on typical 3.7V nominal Li-ion/Li-Po cell characteristics
|
||||||
|
*/
|
||||||
|
static constexpr std::array<VoltageSOC, 12> soc_lookup_ = {{
|
||||||
|
{4200.0f, 100.0f}, // Fully charged
|
||||||
|
{4060.0f, 90.0f},
|
||||||
|
{3980.0f, 80.0f},
|
||||||
|
{3920.0f, 70.0f},
|
||||||
|
{3870.0f, 60.0f},
|
||||||
|
{3820.0f, 50.0f},
|
||||||
|
{3790.0f, 40.0f},
|
||||||
|
{3770.0f, 30.0f},
|
||||||
|
{3740.0f, 20.0f},
|
||||||
|
{3680.0f, 10.0f},
|
||||||
|
{3450.0f, 5.0f}, // Low battery warning
|
||||||
|
{3300.0f, 0.0f}, // Empty / cutoff voltage
|
||||||
|
}};
|
||||||
|
|
||||||
|
float scale_{1.0f}; // Voltage divider scaling factor
|
||||||
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
|
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -127,10 +127,11 @@ void MonitoringManager::run()
|
|||||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||||
if (BatteryMonitor::isEnabled() && now_tick >= next_tick_bat)
|
if (BatteryMonitor::isEnabled() && now_tick >= next_tick_bat)
|
||||||
{
|
{
|
||||||
const int mv = bm_.getBatteryMilliVolts();
|
const auto status = bm_.getBatteryStatus();
|
||||||
if (mv > 0)
|
if (status.valid)
|
||||||
{
|
{
|
||||||
last_battery_mv_.store(mv);
|
std::lock_guard<std::mutex> lock(battery_mutex_);
|
||||||
|
last_battery_status_ = status;
|
||||||
}
|
}
|
||||||
next_tick_bat = now_tick + batt_period;
|
next_tick_bat = now_tick + batt_period;
|
||||||
}
|
}
|
||||||
@@ -161,11 +162,14 @@ float MonitoringManager::getCurrentMilliAmps() const
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float MonitoringManager::getBatteryVoltageMilliVolts() const
|
BatteryStatus MonitoringManager::getBatteryStatus() const
|
||||||
{
|
{
|
||||||
#if CONFIG_MONITORING_BATTERY_ENABLE
|
#if CONFIG_MONITORING_BATTERY_ENABLE
|
||||||
if (BatteryMonitor::isEnabled())
|
if (BatteryMonitor::isEnabled())
|
||||||
return static_cast<float>(last_battery_mv_.load());
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(battery_mutex_);
|
||||||
|
return last_battery_status_;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return 0.0f;
|
return {0, 0.0f, false};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
#include "BatteryMonitor.hpp"
|
#include "BatteryMonitor.hpp"
|
||||||
#include "CurrentMonitor.hpp"
|
#include "CurrentMonitor.hpp"
|
||||||
|
|
||||||
@@ -47,8 +48,8 @@ public:
|
|||||||
|
|
||||||
// Latest filtered current in mA
|
// Latest filtered current in mA
|
||||||
float getCurrentMilliAmps() const;
|
float getCurrentMilliAmps() const;
|
||||||
// Latest battery voltage in mV
|
// Get complete battery status (voltage + percentage + validity)
|
||||||
float getBatteryVoltageMilliVolts() const;
|
BatteryStatus getBatteryStatus() const;
|
||||||
|
|
||||||
// Check if any monitoring feature is enabled
|
// Check if any monitoring feature is enabled
|
||||||
static constexpr bool isEnabled()
|
static constexpr bool isEnabled()
|
||||||
@@ -62,7 +63,8 @@ private:
|
|||||||
|
|
||||||
TaskHandle_t task_{nullptr};
|
TaskHandle_t task_{nullptr};
|
||||||
std::atomic<float> last_current_ma_{0.0f};
|
std::atomic<float> last_current_ma_{0.0f};
|
||||||
std::atomic<int> last_battery_mv_{0};
|
BatteryStatus last_battery_status_{0, 0.0f, false};
|
||||||
|
mutable std::mutex battery_mutex_; // Protect non-atomic BatteryStatus
|
||||||
|
|
||||||
CurrentMonitor cm_;
|
CurrentMonitor cm_;
|
||||||
BatteryMonitor bm_;
|
BatteryMonitor bm_;
|
||||||
|
|||||||
Reference in New Issue
Block a user