Author: Alex Dolzhenkov <admin@alexx.pw>
Add linear approximation and use it for improving battery percentage Add linear approximation class and use it to better model the non-linear discharge curve of the battery. Changed the minimum voltage level to 3.5V and the maximum to 4.18V. For reference the maximum observed voltage is 4.21V during charging.
src/components/battery/BatteryController.cpp | 45 ++++----------------- src/components/battery/BatteryController.h | 1 src/components/utility/LinearApproximation.h | 41 ++++++++++++++++++++
diff --git a/src/components/battery/BatteryController.cpp b/src/components/battery/BatteryController.cpp index 206b26ed4d042591f0cc5761f5df80478176d95c..b61f0ce3df9dc3791ec23dd496223e0aabe82d4c 100644 --- a/src/components/battery/BatteryController.cpp +++ b/src/components/battery/BatteryController.cpp @@ -1,4 +1,5 @@ #include "components/battery/BatteryController.h" +#include "components/utility/LinearApproximation.h" #include "drivers/PinMap.h" #include <hal/nrf_gpio.h> #include <nrfx_saadc.h> @@ -60,6 +61,14 @@ APP_ERROR_CHECK(nrfx_saadc_buffer_convert(&saadc_value, 1)); } void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) { + static const Utility::LinearApproximation<uint16_t, uint8_t, 6> aprox {{{ + {3500, 0}, // Minimum voltage before shutdown (depends on the battery) + {3600, 10}, // Keen point that corresponds to 10% + {3700, 25}, + {3750, 50}, + {3900, 75}, + {4180, 100} // Maximum voltage during charging is 4.21V + }}}; if (p_event->type == NRFX_SAADC_EVT_DONE) { @@ -76,7 +85,7 @@ uint8_t newPercent; if (isFull) { newPercent = 100; } else { - newPercent = std::min(GetBatteryPercentageFromVoltage(voltage), static_cast<uint8_t>(isCharging ? 99 : 100)); + newPercent = std::min(aprox.GetValue(voltage), isCharging ? uint8_t {99} : uint8_t {100}); } if ((isPowerPresent && newPercent > percentRemaining) || (!isPowerPresent && newPercent < percentRemaining) || firstMeasurement) { @@ -93,37 +102,3 @@ void Battery::Register(Pinetime::System::SystemTask* systemTask) { this->systemTask = systemTask; } - -uint8_t Battery::GetBatteryPercentageFromVoltage(uint16_t voltage) { - // The number of line segments used to approximate the battery discharge curve. - static const uint8_t LINE_SEGMENT_COUNT = 7; - - // The voltages (mV) at the endpoints of the line segments. Any two consecutive - // values represent the start and end voltage of a line segment. - static const uint16_t voltageOffsets[LINE_SEGMENT_COUNT + 1] {4157, 4063, 3882, 3747, 3716, 3678, 3583, 3500}; - - // The battery percentages at the endpoints of the line segments. Note that last - // value is omitted: It is not needed because we only need the percentages at - // the start of each line segment. - static const float percentageOffsets[LINE_SEGMENT_COUNT] {100.000, 95.197, 70.429, 48.947, 35.158, 18.971, 5.801}; - - // The pre-calculated slopes (in battery percentage points per millivolt) of the - // line segments. - static const float percentageSlopes[LINE_SEGMENT_COUNT] {0.05109, 0.13684, 0.15913, 0.44481, 0.42595, 0.13863, 0.06989}; - - if (voltage >= voltageOffsets[0]) { - return 100; - } - - if (voltage <= voltageOffsets[7]) { - return 0; - } - - for (uint8_t i = 0; i < LINE_SEGMENT_COUNT; i++) { - if (voltage > voltageOffsets[i + 1]) { - return static_cast<uint8_t>(roundf(percentageOffsets[i] + percentageSlopes[i] * (voltage - voltageOffsets[i]))); - } - } - - return 0; -} diff --git a/src/components/battery/BatteryController.h b/src/components/battery/BatteryController.h index 15559916a18cb4e7e317bf95a5a3ffc38e7c12fb..5a7394c4d48c774168335a1fd523341503115695 100644 --- a/src/components/battery/BatteryController.h +++ b/src/components/battery/BatteryController.h @@ -49,7 +49,6 @@ void SaadcInit(); void SaadcEventHandler(nrfx_saadc_evt_t const* p_event); static void AdcCallbackStatic(nrfx_saadc_evt_t const* event); - static uint8_t GetBatteryPercentageFromVoltage(uint16_t voltage); bool isReading = false; diff --git a/src/components/utility/LinearApproximation.h b/src/components/utility/LinearApproximation.h new file mode 100644 index 0000000000000000000000000000000000000000..f7104ced280d57c47226d8454eedeb70e54c55be --- /dev/null +++ b/src/components/utility/LinearApproximation.h @@ -0,0 +1,41 @@ +#pragma once + +#include <cstddef> +#include <array> + +namespace Pinetime { + namespace Utility { + + // based on: https://github.com/SHristov92/LinearApproximation/blob/main/Linear.h + template <typename Key, typename Value, std::size_t Size> class LinearApproximation { + using Point = struct { + Key key; + Value value; + }; + + public: + LinearApproximation(const std::array<Point, Size>&& sorted_points) : points {sorted_points} { + } + + Value GetValue(Key key) const { + if (key <= points[0].key) { + return points[0].value; + } + + for (std::size_t i = 1; i < Size; i++) { + const auto& p = points[i]; + const auto& p_prev = points[i - 1]; + + if (key < p.key) { + return p_prev.value + (key - p_prev.key) * (p.value - p_prev.value) / (p.key - p_prev.key); + } + } + + return points[Size - 1].value; + } + + private: + std::array<Point, Size> points; + }; + } +}