public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ v2] battery: improve the display of the charge level
@ 2025-10-27 15:11 Roman Smirnov
  2025-10-27 15:37 ` Bastien Nocera
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Roman Smirnov @ 2025-10-27 15:11 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Roman Smirnov

The battery charge level may fluctuate due to uncalibrated
sensors. Commit smooths out such fluctuations.

The algorithm for determining uncalibrated sensors consists of
finding the number of changes in charge direction (i.e., "spikes").
If the number of spikes is zero, the device is charging or discharging.
If there is one spike, it may mean that the device has started charging
or has been disconnected from charging. If there are two or more
spikes, this is a clear indication of an uncalibrated sensor.

Check that the battery charge is fluctuating. If the battery charge
is fluctuating, use the average charge value, otherwise use the current
value.

Fixes: https://github.com/bluez/bluez/issues/1612
---
 src/battery.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/src/battery.c b/src/battery.c
index 59e4fc570..f97d9b8f3 100644
--- a/src/battery.c
+++ b/src/battery.c
@@ -33,10 +33,16 @@
 #define BATTERY_PROVIDER_MANAGER_INTERFACE "org.bluez.BatteryProviderManager1"
 
 #define BATTERY_MAX_PERCENTAGE 100
+#define LAST_CHARGES_SIZE 8
+#define MAX_CHARGE_STEP 5
 
 struct btd_battery {
 	char *path; /* D-Bus object path */
 	uint8_t percentage; /* valid between 0 to 100 inclusively */
+	uint8_t *last_charges; /* last charges received */
+	uint8_t lru_charge_id; /* oldest battery charge */
+	float avg_charge; /* average battery charge */
+	bool is_fluctuating; /* true, if the battery sensor fluctuates */
 	char *source; /* Descriptive source of the battery info */
 	char *provider_path; /* The provider root path, if any */
 };
@@ -92,6 +98,11 @@ static struct btd_battery *battery_new(const char *path, const char *source,
 	battery = new0(struct btd_battery, 1);
 	battery->path = g_strdup(path);
 	battery->percentage = UINT8_MAX;
+	battery->last_charges = new0(uint8_t, LAST_CHARGES_SIZE);
+	battery->lru_charge_id = 0;
+	battery->avg_charge = 0;
+	battery->is_fluctuating = false;
+
 	if (source)
 		battery->source = g_strdup(source);
 	if (provider_path)
@@ -105,6 +116,9 @@ static void battery_free(struct btd_battery *battery)
 	if (battery->path)
 		g_free(battery->path);
 
+	if (battery->last_charges)
+		g_free(battery->last_charges);
+
 	if (battery->source)
 		g_free(battery->source);
 
@@ -217,6 +231,39 @@ bool btd_battery_unregister(struct btd_battery *battery)
 	return true;
 }
 
+static void check_fluctuations(struct btd_battery *battery)
+{
+	uint8_t spikes = 0;
+	int8_t step = 0;
+	int8_t direction = 0;
+	int8_t prev_direction;
+
+	for (uint8_t id = 0; id < LAST_CHARGES_SIZE - 1; id++) {
+		prev_direction = direction;
+		step = battery->last_charges[id] - battery->last_charges[id + 1];
+
+		/*
+		 * The battery charge fluctuates too much,
+		 * which may indicate a battery problem, so
+		 * the actual value should be displayed.
+		 */
+		if (step > MAX_CHARGE_STEP) {
+			battery->is_fluctuating = false;
+			return;
+		}
+
+		if (step > 0)
+			direction = 1;
+		else if (step < 0)
+			direction = -1;
+
+		if (direction != prev_direction && !prev_direction)
+			spikes++;
+	}
+
+	battery->is_fluctuating = (spikes > 1) ? true : false;
+}
+
 bool btd_battery_update(struct btd_battery *battery, uint8_t percentage)
 {
 	DBG("path = %s", battery->path);
@@ -231,6 +278,21 @@ bool btd_battery_update(struct btd_battery *battery, uint8_t percentage)
 		return false;
 	}
 
+	if (!battery->avg_charge)
+		battery->avg_charge = percentage;
+
+	/* exponential smoothing */
+	battery->avg_charge = battery->avg_charge * 0.7 + percentage * 0.3;
+	battery->last_charges[battery->lru_charge_id] = percentage;
+
+	if (battery->lru_charge_id == LAST_CHARGES_SIZE - 1)
+		check_fluctuations(battery);
+
+	battery->lru_charge_id = (battery->lru_charge_id + 1) % LAST_CHARGES_SIZE;
+
+	if (battery->is_fluctuating)
+		percentage = battery->avg_charge;
+
 	if (battery->percentage == percentage)
 		return true;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-10-28  9:05 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-27 15:11 [PATCH BlueZ v2] battery: improve the display of the charge level Roman Smirnov
2025-10-27 15:37 ` Bastien Nocera
2025-10-28  8:54   ` Roman Smirnov
2025-10-27 15:59 ` Luiz Augusto von Dentz
2025-10-28  9:05   ` Roman Smirnov
2025-10-27 16:37 ` [BlueZ,v2] " bluez.test.bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox