|
|
|
@ -26,6 +26,8 @@ static const TickType_t kBatteryCheckPeriod = pdMS_TO_TICKS(60 * 1000); |
|
|
|
|
*/ |
|
|
|
|
static const uint32_t kFullChargeMilliVolts = 4200; |
|
|
|
|
|
|
|
|
|
static const uint32_t kCriticalChargeMilliVolts = 3500; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Battery voltage, in millivolts, at which *we* will consider the battery to |
|
|
|
|
* be completely discharged. This is intentionally higher than the charger IC |
|
|
|
@ -65,12 +67,35 @@ auto Battery::Update() -> void { |
|
|
|
|
// Ideally the way you're 'supposed' to measure battery charge percent is to
|
|
|
|
|
// keep continuous track of the amps going in and out of it at any point. I'm
|
|
|
|
|
// skeptical of this approach, and we're not set up with the hardware needed
|
|
|
|
|
// to do it anyway. Instead, we use a curve-fitting formula by StackOverflow
|
|
|
|
|
// user 'Roho' to estimate the remaining capacity based on the battery's
|
|
|
|
|
// voltage. This seems to work pretty good!
|
|
|
|
|
double v = mV / 1000.0; |
|
|
|
|
uint_fast8_t percent = static_cast<uint_fast8_t>(std::clamp<double>( |
|
|
|
|
123 - (123 / std::pow(1 + std::pow(v / 3.7, 80.0), 0.165)), 0.0, 100.0)); |
|
|
|
|
// to do it anyway. Instead, we use a piecewise linear formula derived from
|
|
|
|
|
// voltage measurements of our actual cells.
|
|
|
|
|
uint_fast8_t percent; |
|
|
|
|
if (mV > kCriticalChargeMilliVolts) { |
|
|
|
|
// Above the 'critical' point, the relationship between battery voltage and
|
|
|
|
|
// charge percentage is close enough to linear.
|
|
|
|
|
percent = ((mV - kCriticalChargeMilliVolts) * 100 / |
|
|
|
|
(kFullChargeMilliVolts - kCriticalChargeMilliVolts)) + |
|
|
|
|
5; |
|
|
|
|
} else { |
|
|
|
|
// Below the 'critical' point, battery voltage drops very very quickly.
|
|
|
|
|
// Give this part of the curve the lowest 5% to work with.
|
|
|
|
|
percent = (mV - kEmptyChargeMilliVolts) * 5 / |
|
|
|
|
(kCriticalChargeMilliVolts - kEmptyChargeMilliVolts); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// A full charge is always 100%.
|
|
|
|
|
if (charge_state == ChargeStatus::kFullCharge) { |
|
|
|
|
percent = 100; |
|
|
|
|
} |
|
|
|
|
// Critical charge is always <= 5%
|
|
|
|
|
if (charge_state == ChargeStatus::kBatteryCritical) { |
|
|
|
|
percent = std::min<uint_fast8_t>(percent, 5); |
|
|
|
|
} |
|
|
|
|
// When very close to full, the BMS transitions to a constant-voltage charge
|
|
|
|
|
// algorithm. Hold off on reporting 100% charge until this stage is finished.
|
|
|
|
|
if (percent >= 95 && charge_state != ChargeStatus::kFullCharge) { |
|
|
|
|
percent = std::min<uint_fast8_t>(percent, 95); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool is_charging; |
|
|
|
|
if (!charge_state) { |
|
|
|
|