Battery percentage tracking _wildly_ wrong

I have the ~9Ah 3P battery pack fitted to my tachyon and have noticed that the percentage reported by /sys/class/power_supply/battery/capacity has so far never been anything remotely sane.

Its a lipo, 4.2V fully charged and the cutoff on tachyon from my own experiments so far seems to be about 3.3V.
Well, mines currently sat at 3.4V nearly empty, having discharged this rapidly overnight and not doing much during that time. Yet its reporting 94% capacity?

particle@tachyon:~$ cat /sys/class/power_supply/battery/voltage_now
3415756
particle@tachyon:~$ cat /sys/class/power_supply/battery/voltage_ocv
3488511
particle@tachyon:~$ cat /sys/class/power_supply/battery/capacity
94

I’ve had similar where it did run down totally flat, I charged it, and all the way up at 4V, it was still reporting that it was empty.

What is actually generating this capacity info? Because its definitely wildly off. I know with fuel gauge ICs they sometimes need a few cycles to recalibrate but mine has had 3 or 4 now, is reporting 0 cycles and has only gotten further away from being correct.

Here we go, its maintained that the battery is definitely nearly full, right up until it took a dive and shut down:

Any comment from anyone at particle on this?
Am I the only one seeing this?
Faulty battery?

I’ll test mine and post back with the results for comparison.

Hey @SixSixSevenSeven - sorry for the slow reply; I was flying back to the US.

Short version: what you’re seeing looks like the fuel-gauge estimating SoC from an OCV table that doesn’t match your pack/use-case, rather than true coulomb counting. That’s why you can sit at ~3.4 V and still read ~94%, and then fall off a cliff near cutoff.

A bit of background:

  • The Qualcomm PMIC/FG stack uses a battery profile (voltage-vs-SoC, temp effects, internal resistance, FCC/Qmax) to compute capacity. If the profile or learning state isn’t right, it’ll lean on OCV mapping and can be wildly off under load or right after charge.

  • Your pack is 3P (3-cell, ~4.2 V full). The FG needs the correct profile for that chemistry and impedance; otherwise OCVā‰ˆSoC curves don’t line up. We performed the battery calibration and mapping and installed it into the internal firmware - if this didn't work, the device crashes, so I'll take this bit is working at least!

  • We validated against a simulator and have seen sane discharge on our benches, but we’ve also reproduced cases where the OCV-only estimate looks ā€œstuck fullā€ until the knee.

Could you share a quick snapshot so we can compare apples-to-apples?

# 1) Full dump of the power_supply view
cat /sys/class/power_supply/battery/uevent

# 2) Key live values
cat /sys/class/power_supply/battery/{status,capacity,capacity_raw,voltage_now,voltage_ocv,current_now,charge_counter,health,temp}

# 3) If present, FG sysfs extras
grep -R . /sys/class/power_supply/battery/ | head -n 200

And which image you’re on (headless vs desktop), plus whether anything is powered from the 5 V boost (RPi header) or USB2. Today the SoC algorithm doesn’t account for those external loads explicitly, which can skew how ā€œwork doneā€ maps to remaining %.

What you can try right now (to nudge learning and get a usable readout):

  1. One calibration cycle (minimal hassle):
  • Charge to full on DC until status=Full, then leave it on charge ~30–60 min (relaxation).
  • Unplug and let it discharge at a steady light/medium load down to auto-shutdown (~3.3 V), avoiding large current spikes and avoiding extra 5 V peripheral load.
  • Let it rest 20–30 min, then recharge to full.This helps the FG update OCV/IR and FCC.
  1. Sanity-check SoC by voltage (temporary UI):As a stop-gap, treat voltage as a coarse proxy while we sort the profile. Very roughly for a 3S Li-ion at room temp and modest load:
  • 4.20 V ā‰ˆ 100%
  • 3.95 V ā‰ˆ ~75%
  • 3.80 V ā‰ˆ ~50%
  • 3.60 V ā‰ˆ ~25%
  • 3.45 V ā‰ˆ ~10%
  • 3.30 V ā‰ˆ ~0–3% (knee/cutoff)(Load and temperature shift these noticeably; this is only to avoid nasty surprises.)
  1. Logging for one run:
while true; do
  date '+%F %T'
  awk '{printf "Vnow=%.3fV\n",$1/1e6}' /sys/class/power_supply/battery/voltage_now
  awk '{printf "Vocv=%.3fV\n",$1/1e6}' /sys/class/power_supply/battery/voltage_ocv
  cat /sys/class/power_supply/battery/{capacity,current_now,temp} 2>/dev/null
  sleep 60
done | tee battery_log.txt
  1. If you can share that log from ~100% down past 3.5 V, it’ll show whether the FG ever transitions from OCV-to-coulomb-counting and how IR droop maps to load.

What I suspect in your case:

  • The OCV table being used is too ā€œoptimisticā€ for your pack/IR, so voltage_ocv~3.49 V → ~94% even while voltage_now~3.41 V under modest background load.

  • Cycle count reporting being 0 suggests the learning state/FCC updates aren’t sticking yet (or that sysfs node isn’t wired on this build).

We can ship a corrected profile once we confirm the deltas (and, if needed, tweak DT/FG params so it favors coulomb counting more aggressively). In the meantime, the voltage-based sanity check above will keep you from getting caught out near the knee.

Appreciate you flagging it and thanks to @radu7 for offering a comparison run!

Thanks

Nick.

1 Like

I left my Tachyon running for 27.5 hours on the 3S LiPo (from 100% starting charge) and it’s just now at 75% capacity remaining.

Here’s the dump of the power_supply view from mine in case it helps anyone:

POWER_SUPPLY_NAME=battery
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_STATUS=Discharging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_CHARGE_TYPE=N/A
POWER_SUPPLY_CAPACITY=75
POWER_SUPPLY_VOLTAGE_OCV=3472944
POWER_SUPPLY_VOLTAGE_NOW=3410306
POWER_SUPPLY_VOLTAGE_MAX=4200000
POWER_SUPPLY_CURRENT_NOW=-589912
POWER_SUPPLY_CHARGE_CONTROL_LIMIT=0
POWER_SUPPLY_CHARGE_CONTROL_LIMIT_MAX=4
POWER_SUPPLY_TEMP=260
POWER_SUPPLY_TECHNOLOGY=Li-ion
POWER_SUPPLY_CHARGE_COUNTER=6526285
POWER_SUPPLY_CYCLE_COUNT=0
POWER_SUPPLY_CHARGE_FULL_DESIGN=8739000
POWER_SUPPLY_CHARGE_FULL=8739000
POWER_SUPPLY_MODEL_NAME=7616544_QUECTEL_SG560D_9450MAH_PM250B_AVERAGED_MASTERSLAVE_JAN6TH2025
POWER_SUPPLY_TIME_TO_FULL_AVG=-1
POWER_SUPPLY_TIME_TO_FULL_NOW=-1
POWER_SUPPLY_TIME_TO_EMPTY_AVG=330740
POWER_SUPPLY_POWER_NOW=19130695
POWER_SUPPLY_POWER_AVG=15208177

I collected all of the other data that Nick suggested, but since it is a lot of output I’ll leave that out. If anyone wants to use mine as a comparison though I’ll be glad to provide it.

Well, I went to reboot my Tachyon and discovered I no longer had display output. Restarting gdm from the console didn’t bring it back either so I just shut it down from the console and powered it back on (display came back), but I noticed that now it was reporting 12% battery remaining instead of the 75% remaining that it showed prior to rebooting it. Just an observation… here is the dump of the power_supply view immediately after the shutdown/power-on:

POWER_SUPPLY_NAME=battery
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_STATUS=Discharging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_CHARGE_TYPE=N/A
POWER_SUPPLY_CAPACITY=12
POWER_SUPPLY_VOLTAGE_OCV=3493472
POWER_SUPPLY_VOLTAGE_NOW=3435414
POWER_SUPPLY_VOLTAGE_MAX=4200000
POWER_SUPPLY_CURRENT_NOW=-442511
POWER_SUPPLY_CHARGE_CONTROL_LIMIT=0
POWER_SUPPLY_CHARGE_CONTROL_LIMIT_MAX=4
POWER_SUPPLY_TEMP=270
POWER_SUPPLY_TECHNOLOGY=Li-ion
POWER_SUPPLY_CHARGE_COUNTER=1018093
POWER_SUPPLY_CYCLE_COUNT=0
POWER_SUPPLY_CHARGE_FULL_DESIGN=8739000
POWER_SUPPLY_CHARGE_FULL=8739000
POWER_SUPPLY_MODEL_NAME=7616544_QUECTEL_SG560D_9450MAH_PM250B_AVERAGED_MASTERSLAVE_JAN6TH2025
POWER_SUPPLY_TIME_TO_FULL_AVG=-1
POWER_SUPPLY_TIME_TO_FULL_NOW=-1
POWER_SUPPLY_TIME_TO_EMPTY_AVG=9520
POWER_SUPPLY_POWER_NOW=19254224
POWER_SUPPLY_POWER_AVG=15110042

I’ve unfortunately had a busy few days. But it has at this point been through 4 or 5 dropping off the knee events, and was still tracking as 0 cycles. I have just checked on it after 1 more event though and it has finally incremented to 1.

Just while it is currently plugged in and on charge:

cat /sys/class/power_supply/battery/uevent
POWER_SUPPLY_NAME=battery
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_STATUS=Full
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_CHARGE_TYPE=N/A
POWER_SUPPLY_CAPACITY=100
POWER_SUPPLY_VOLTAGE_OCV=4165580
POWER_SUPPLY_VOLTAGE_NOW=4164303
POWER_SUPPLY_VOLTAGE_MAX=4200000
POWER_SUPPLY_CURRENT_NOW=0
POWER_SUPPLY_CHARGE_CONTROL_LIMIT=0
POWER_SUPPLY_CHARGE_CONTROL_LIMIT_MAX=4
POWER_SUPPLY_TEMP=300
POWER_SUPPLY_TECHNOLOGY=Li-ion
POWER_SUPPLY_CHARGE_COUNTER=8780000
POWER_SUPPLY_CYCLE_COUNT=1
POWER_SUPPLY_CHARGE_FULL_DESIGN=8739000
POWER_SUPPLY_CHARGE_FULL=8780000
POWER_SUPPLY_MODEL_NAME=7616544_QUECTEL_SG560D_9450MAH_PM250B_AVERAGED_MASTERSLAVE_JAN6TH2025
POWER_SUPPLY_TIME_TO_FULL_AVG=0
POWER_SUPPLY_TIME_TO_FULL_NOW=0
POWER_SUPPLY_TIME_TO_EMPTY_AVG=-1
POWER_SUPPLY_POWER_NOW=44757298
POWER_SUPPLY_POWER_AVG=35289470
cat /sys/class/power_supply/battery/{status,capacity,capacity_raw,voltage_now,voltage_ocv,current_now,charge_counter,health,temp}
Full
100
cat: /sys/class/power_supply/battery/capacity_raw: No such file or directory
4164498
4165567
0
8780000
Good
300

grep output was a bit lorge so is here: grep -R . /sys/class/power_supply/battery/ | head -n 200grep: /sys/class/power - Pastebin.com

Its running the 3P battery. Its had multiple cycles. Its the desktop build. Nothing powered from the header or USB.

Script is being kicked into action

I’ll make full file available when we get there, but right now:

2025-08-17 11:00:05
Vnow=3.704V
Vocv=3.720V
96
-109559
220
2025-08-17 11:01:05
Vnow=3.705V
Vocv=3.720V
96
-100404
220
2025-08-17 11:02:05
Vnow=3.696V
Vocv=3.720V
96
-252383
220
2025-08-17 11:03:05
Vnow=3.703V
Vocv=3.719V
96
-113526
220
2025-08-17 11:04:05
Vnow=3.700V
Vocv=3.719V
96
-148622
220
2025-08-17 11:05:05
Vnow=3.702V
Vocv=3.719V
96
-120851
220

3.72V open circuit, tracked as 96%.

cat /sys/class/power_supply/battery/uevent
POWER_SUPPLY_NAME=battery
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_STATUS=Discharging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_CHARGE_TYPE=N/A
POWER_SUPPLY_CAPACITY=96
POWER_SUPPLY_VOLTAGE_OCV=3718268
POWER_SUPPLY_VOLTAGE_NOW=3700500
POWER_SUPPLY_VOLTAGE_MAX=4200000
POWER_SUPPLY_CURRENT_NOW=-120851
POWER_SUPPLY_CHARGE_CONTROL_LIMIT=0
POWER_SUPPLY_CHARGE_CONTROL_LIMIT_MAX=4
POWER_SUPPLY_TEMP=220
POWER_SUPPLY_TECHNOLOGY=Li-ion
POWER_SUPPLY_CHARGE_COUNTER=8439336
POWER_SUPPLY_CYCLE_COUNT=1
POWER_SUPPLY_CHARGE_FULL_DESIGN=8739000
POWER_SUPPLY_CHARGE_FULL=8780000
POWER_SUPPLY_MODEL_NAME=7616544_QUECTEL_SG560D_9450MAH_PM250B_AVERAGED_MASTERSLAVE_JAN6TH2025
POWER_SUPPLY_TIME_TO_FULL_AVG=-1
POWER_SUPPLY_TIME_TO_FULL_NOW=-1
POWER_SUPPLY_TIME_TO_EMPTY_AVG=2671889
POWER_SUPPLY_POWER_NOW=24417626
POWER_SUPPLY_POWER_AVG=20173124
cat /sys/class/power_supply/battery/{status,capacity,capacity_raw,voltage_now,voltage_ocv,current_now,charge_counter,health,temp}
Discharging
96
cat: /sys/class/power_supply/battery/capacity_raw: No such file or directory
3699721
3717993
-123597
8438458
Good
220

So it finally ran dead this afternoon. Whatever calibration is being used I do maintain isnt even remotely correct. The first time it reported below 90% capacity remaining, it had already gotten down to 3.411V open circuit, 3.383 under load and it once again have an extraordinarily sharp knee.

The file is so big that it causes the editor on this forum to crash, so I have put it on a google drive here: https://drive.google.com/file/d/1-FETdPOwf26C06SIABiGs5Eps3J18URq/view?usp=drive_link

Oh and the cyclecount has gone backwards to 0. It was on 1 when I started the test

1 Like

And here we are again:

root@tachyon-5ed4d56e:~# cat /sys/class/power_supply/battery/uevent
POWER_SUPPLY_NAME=battery
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_STATUS=Discharging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_CHARGE_TYPE=N/A
POWER_SUPPLY_CAPACITY=93
POWER_SUPPLY_VOLTAGE_OCV=3534400
POWER_SUPPLY_VOLTAGE_NOW=3494970
POWER_SUPPLY_VOLTAGE_MAX=4200000
POWER_SUPPLY_CURRENT_NOW=-184633
POWER_SUPPLY_CHARGE_CONTROL_LIMIT=0
POWER_SUPPLY_CHARGE_CONTROL_LIMIT_MAX=4
POWER_SUPPLY_TEMP=270
POWER_SUPPLY_TECHNOLOGY=Li-ion
POWER_SUPPLY_CHARGE_COUNTER=8312467
POWER_SUPPLY_CYCLE_COUNT=1
POWER_SUPPLY_CHARGE_FULL_DESIGN=8739000
POWER_SUPPLY_CHARGE_FULL=8918000
POWER_SUPPLY_MODEL_NAME=7616544_QUECTEL_SG560D_9450MAH_PM250B_AVERAGED_MASTERSLAVE_JAN6TH2025
POWER_SUPPLY_TIME_TO_FULL_AVG=-1
POWER_SUPPLY_TIME_TO_FULL_NOW=-1
POWER_SUPPLY_TIME_TO_EMPTY_AVG=2372208
POWER_SUPPLY_POWER_NOW=13266861
POWER_SUPPLY_POWER_AVG=11302082

Back to thinking its had 1 cycle, but right on the cusp of going over another knee

@mrlambchop any input?

This is basically 100% reproducible that the tracking is not correct on this device

The lack of response from particle on this issue and others is extremely disappointing.

As it is today, I am stuck on an obsolete OS that cannot support the software I need to run, with battery tracking that doesnt work, and a cellular connection that weeks of interaction with particle support has failed to restore.

1 Like

Hi everyone.

Nick and the engineering team are still actively working on a solution for this. Apologies for the inconvenience, and thank you for your patience.

-Kent

1 Like