Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/Power.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ class HasBatteryLevel

virtual bool isVbusIn() { return false; }
virtual bool isCharging() { return false; }

/**
* Called when the charging state changes (external power connected or
* disconnected), so implementations can discard smoothing state that
* tracks the previous power state.
*/
virtual void notifyChargingStateChanged() {}
};
#endif

Expand Down Expand Up @@ -351,6 +358,13 @@ class AnalogBatteryLevel : public HasBatteryLevel
if (scaled > last_read_value)
last_read_value = scaled;
initial_read_done = true;
} else if (reseed_pending) {
// The charging state just changed, which steps the battery voltage
// sharply (charge voltage vs loaded/rested voltage). The previous
// filtered value tracks the old power state, so replace it instead
// of blending the step in over several reads.
last_read_value = scaled;
reseed_pending = false;
} else {
// Already initialized - filter this reading
last_read_value += (scaled - last_read_value) * 0.5; // Virtual LPF
Expand Down Expand Up @@ -523,6 +537,15 @@ class AnalogBatteryLevel : public HasBatteryLevel
return isVbusIn();
}

/// Re-seed the smoothing filter with the next raw reading (and let that
/// reading happen immediately) so the reported voltage tracks the new
/// charging state within one poll instead of converging over several.
virtual void notifyChargingStateChanged() override
{
reseed_pending = true;
last_read_time_ms = 0;
}

private:
/// If we see a battery voltage higher than physics allows - assume charger is
/// pumping in power
Expand All @@ -537,6 +560,9 @@ class AnalogBatteryLevel : public HasBatteryLevel
// This value is over-written by the first ADC reading, it the voltage seems
// reasonable.
bool initial_read_done = false;
// Set when the charging state changes: the next ADC reading replaces the
// filtered value instead of being blended into it.
bool reseed_pending = false;
float last_read_value = (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS);
uint32_t last_read_time_ms = 0;

Expand Down Expand Up @@ -891,6 +917,29 @@ void Power::readPowerStatus()

#endif

// A charging-state transition steps the battery voltage sharply (charge
// voltage vs loaded/rested voltage), but the analog reading is smoothed, so
// the reported voltage/SoC would otherwise keep tracking the old power state
// for several polls of this routine. Tell the sensor to drop its smoothing
// state and take a fresh reading now, so telemetry sent right after a
// plug/unplug reflects the new state within one poll.
static OptionalBool prevIsCharging = OptUnknown;
if (batteryLevel && hasBattery == OptTrue && isChargingNow != OptUnknown && prevIsCharging != OptUnknown &&
isChargingNow != prevIsCharging) {
LOG_INFO("Charging state changed, re-reading battery level");
batteryLevel->notifyChargingStateChanged();
batteryVoltageMv = batteryLevel->getBattVoltage();
int freshPercent = batteryLevel->getBatteryPercent();
if (freshPercent >= 0) {
batteryChargePercent = freshPercent;
} else {
batteryChargePercent = clamp((int)(((batteryVoltageMv - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS)) * 1e2) /
((OCV[0] * NUM_CELLS) - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS))),
0, 100);
}
}
prevIsCharging = isChargingNow;

// Notify any status instances that are observing us
const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent);

Expand Down