Skip to content

Commit 97b910a

Browse files
committed
Move the battery power calculation part to the BatteryMonitor class
1 parent 0174747 commit 97b910a

5 files changed

Lines changed: 136 additions & 58 deletions

File tree

components/CommandManager/CommandManager/commands/device_commands.cpp

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
#include "device_commands.hpp"
2-
#include <algorithm>
3-
#include <array>
42
#include "LEDManager.hpp"
53
#include "MonitoringManager.hpp"
64
#include "esp_mac.h"
@@ -230,53 +228,15 @@ CommandResult getBatteryStatusCommand(std::shared_ptr<DependencyRegistry> regist
230228
return CommandResult::getErrorResult("MonitoringManager unavailable");
231229
}
232230

233-
const float volts = mon->getBatteryVoltageMilliVolts();
234-
if (volts <= 0.0f)
231+
const auto status = mon->getBatteryStatus();
232+
if (!status.valid)
235233
{
236234
return CommandResult::getErrorResult("Battery voltage unavailable");
237235
}
238236

239-
struct VoltageSOC
240-
{
241-
float voltage_mv;
242-
float soc;
243-
};
244-
245-
constexpr std::array<VoltageSOC, 12> lookup = {
246-
VoltageSOC{4200.0f, 100.0f}, VoltageSOC{4060.0f, 90.0f}, VoltageSOC{3980.0f, 80.0f}, VoltageSOC{3920.0f, 70.0f},
247-
VoltageSOC{3870.0f, 60.0f}, VoltageSOC{3820.0f, 50.0f}, VoltageSOC{3790.0f, 40.0f}, VoltageSOC{3770.0f, 30.0f},
248-
VoltageSOC{3740.0f, 20.0f}, VoltageSOC{3680.0f, 10.0f}, VoltageSOC{3450.0f, 5.0f}, VoltageSOC{3300.0f, 0.0f},
249-
};
250-
251-
float percent = 0.0f;
252-
if (volts >= lookup.front().voltage_mv)
253-
{
254-
percent = lookup.front().soc;
255-
}
256-
else if (volts <= lookup.back().voltage_mv)
257-
{
258-
percent = lookup.back().soc;
259-
}
260-
else
261-
{
262-
for (size_t index = 0; index < lookup.size() - 1; ++index)
263-
{
264-
const auto high = lookup[index];
265-
const auto low = lookup[index + 1];
266-
if (volts <= high.voltage_mv && volts >= low.voltage_mv)
267-
{
268-
const float span = high.voltage_mv - low.voltage_mv;
269-
const float ratio = (volts - low.voltage_mv) / (span > 0.0f ? span : 1.0f);
270-
percent = low.soc + ratio * (high.soc - low.soc);
271-
break;
272-
}
273-
}
274-
}
275-
percent = std::clamp(percent, 0.0f, 100.0f);
276-
277237
const auto json = nlohmann::json{
278-
{"voltage_mv", std::format("{:.2f}", static_cast<double>(volts))},
279-
{"percentage", std::format("{:.1f}", static_cast<double>(percent))},
238+
{"voltage_mv", std::format("{:.2f}", static_cast<double>(status.voltage_mv))},
239+
{"percentage", std::format("{:.1f}", static_cast<double>(status.percentage))},
280240
};
281241
return CommandResult::getSuccessResult(json);
282242
#else

components/Monitoring/Monitoring/BatteryMonitor.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,52 @@ int BatteryMonitor::getBatteryMilliVolts() const
7171
return 0;
7272
#endif
7373
}
74+
75+
float BatteryMonitor::voltageToPercentage(int voltage_mv)
76+
{
77+
const float volts = static_cast<float>(voltage_mv);
78+
79+
// Handle boundary conditions
80+
if (volts >= soc_lookup_.front().voltage_mv)
81+
return soc_lookup_.front().soc;
82+
83+
if (volts <= soc_lookup_.back().voltage_mv)
84+
return soc_lookup_.back().soc;
85+
86+
// Linear interpolation between lookup table points
87+
for (size_t i = 0; i < soc_lookup_.size() - 1; ++i)
88+
{
89+
const auto &high = soc_lookup_[i];
90+
const auto &low = soc_lookup_[i + 1];
91+
92+
if (volts <= high.voltage_mv && volts >= low.voltage_mv)
93+
{
94+
const float voltage_span = high.voltage_mv - low.voltage_mv;
95+
if (voltage_span <= 0.0f)
96+
{
97+
return low.soc;
98+
}
99+
const float ratio = (volts - low.voltage_mv) / voltage_span;
100+
return low.soc + ratio * (high.soc - low.soc);
101+
}
102+
}
103+
104+
return 0.0f;
105+
}
106+
107+
BatteryStatus BatteryMonitor::getBatteryStatus() const
108+
{
109+
BatteryStatus status = {0, 0.0f, false};
110+
111+
#if CONFIG_MONITORING_BATTERY_ENABLE
112+
const int mv = getBatteryMilliVolts();
113+
if (mv <= 0)
114+
return status;
115+
116+
status.voltage_mv = mv;
117+
status.percentage = std::clamp(voltageToPercentage(mv), 0.0f, 100.0f);
118+
status.valid = true;
119+
#endif
120+
121+
return status;
122+
}

components/Monitoring/Monitoring/BatteryMonitor.hpp

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,31 @@
1414
* +-----------------------+
1515
*/
1616

17+
#include <algorithm>
18+
#include <array>
19+
#include <cmath>
1720
#include "AdcSampler.hpp"
1821
#include "sdkconfig.h"
19-
#include <cmath>
22+
23+
24+
/**
25+
* @struct BatteryStatus
26+
* @brief Battery status information
27+
*/
28+
struct BatteryStatus
29+
{
30+
int voltage_mv; // Battery voltage in millivolts
31+
float percentage; // State of charge percentage (0-100%)
32+
bool valid; // Whether the reading is valid
33+
};
2034

2135
/**
2236
* @class BatteryMonitor
23-
* @brief Monitors battery voltage through a resistor divider
37+
* @brief Monitors battery voltage and calculates state of charge for Li-ion batteries
2438
*
2539
* Uses AdcSampler (BSP layer) for hardware abstraction.
40+
* Includes voltage-to-SOC lookup table for typical Li-ion/Li-Po batteries.
41+
*
2642
* Configuration is done via Kconfig options:
2743
* - CONFIG_MONITORING_BATTERY_ENABLE
2844
* - CONFIG_MONITORING_BATTERY_ADC_GPIO
@@ -39,10 +55,29 @@ class BatteryMonitor
3955
// Initialize battery monitoring hardware
4056
bool setup();
4157

42-
// Read once, update filter, and return battery voltage in mV (after divider compensation), 0 on failure
58+
/**
59+
* @brief Read battery voltage (with divider compensation)
60+
* @return Battery voltage in millivolts, 0 on failure
61+
*/
4362
int getBatteryMilliVolts() const;
4463

45-
// Whether monitoring is enabled by Kconfig and supported by BSP
64+
/**
65+
* @brief Calculate battery state of charge from voltage
66+
* @param voltage_mv Battery voltage in millivolts
67+
* @return State of charge percentage (0-100%)
68+
*/
69+
static float voltageToPercentage(int voltage_mv);
70+
71+
/**
72+
* @brief Get complete battery status (voltage + percentage)
73+
* @return BatteryStatus struct with voltage, percentage, and validity
74+
*/
75+
BatteryStatus getBatteryStatus() const;
76+
77+
/**
78+
* @brief Check if battery monitoring is enabled and supported
79+
* @return true if enabled and ADC is supported
80+
*/
4681
static constexpr bool isEnabled()
4782
{
4883
#ifdef CONFIG_MONITORING_BATTERY_ENABLE
@@ -53,6 +88,34 @@ class BatteryMonitor
5388
}
5489

5590
private:
56-
float scale_{1.0f}; // Voltage divider scaling factor
91+
/**
92+
* @brief Li-ion/Li-Po voltage to SOC lookup table entry
93+
*/
94+
struct VoltageSOC
95+
{
96+
float voltage_mv;
97+
float soc;
98+
};
99+
100+
/**
101+
* @brief Typical Li-ion single cell discharge curve lookup table
102+
* Based on typical 3.7V nominal Li-ion/Li-Po cell characteristics
103+
*/
104+
static constexpr std::array<VoltageSOC, 12> soc_lookup_ = {{
105+
{4200.0f, 100.0f}, // Fully charged
106+
{4060.0f, 90.0f},
107+
{3980.0f, 80.0f},
108+
{3920.0f, 70.0f},
109+
{3870.0f, 60.0f},
110+
{3820.0f, 50.0f},
111+
{3790.0f, 40.0f},
112+
{3770.0f, 30.0f},
113+
{3740.0f, 20.0f},
114+
{3680.0f, 10.0f},
115+
{3450.0f, 5.0f}, // Low battery warning
116+
{3300.0f, 0.0f}, // Empty / cutoff voltage
117+
}};
118+
119+
float scale_{1.0f}; // Voltage divider scaling factor
57120
mutable AdcSampler adc_; // ADC sampler instance (BSP layer)
58121
};

components/Monitoring/Monitoring/MonitoringManager.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,11 @@ void MonitoringManager::run()
127127
#if CONFIG_MONITORING_BATTERY_ENABLE
128128
if (BatteryMonitor::isEnabled() && now_tick >= next_tick_bat)
129129
{
130-
const int mv = bm_.getBatteryMilliVolts();
131-
if (mv > 0)
130+
const auto status = bm_.getBatteryStatus();
131+
if (status.valid)
132132
{
133-
last_battery_mv_.store(mv);
133+
std::lock_guard<std::mutex> lock(battery_mutex_);
134+
last_battery_status_ = status;
134135
}
135136
next_tick_bat = now_tick + batt_period;
136137
}
@@ -161,11 +162,14 @@ float MonitoringManager::getCurrentMilliAmps() const
161162
return 0.0f;
162163
}
163164

164-
float MonitoringManager::getBatteryVoltageMilliVolts() const
165+
BatteryStatus MonitoringManager::getBatteryStatus() const
165166
{
166167
#if CONFIG_MONITORING_BATTERY_ENABLE
167168
if (BatteryMonitor::isEnabled())
168-
return static_cast<float>(last_battery_mv_.load());
169+
{
170+
std::lock_guard<std::mutex> lock(battery_mutex_);
171+
return last_battery_status_;
172+
}
169173
#endif
170-
return 0.0f;
174+
return {0, 0.0f, false};
171175
}

components/Monitoring/Monitoring/MonitoringManager.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <freertos/FreeRTOS.h>
2020
#include <freertos/task.h>
2121
#include <atomic>
22+
#include <mutex>
2223
#include "BatteryMonitor.hpp"
2324
#include "CurrentMonitor.hpp"
2425

@@ -47,8 +48,8 @@ class MonitoringManager
4748

4849
// Latest filtered current in mA
4950
float getCurrentMilliAmps() const;
50-
// Latest battery voltage in mV
51-
float getBatteryVoltageMilliVolts() const;
51+
// Get complete battery status (voltage + percentage + validity)
52+
BatteryStatus getBatteryStatus() const;
5253

5354
// Check if any monitoring feature is enabled
5455
static constexpr bool isEnabled()
@@ -62,7 +63,8 @@ class MonitoringManager
6263

6364
TaskHandle_t task_{nullptr};
6465
std::atomic<float> last_current_ma_{0.0f};
65-
std::atomic<int> last_battery_mv_{0};
66+
BatteryStatus last_battery_status_{0, 0.0f, false};
67+
mutable std::mutex battery_mutex_; // Protect non-atomic BatteryStatus
6668

6769
CurrentMonitor cm_;
6870
BatteryMonitor bm_;

0 commit comments

Comments
 (0)