InfiniTimeOrg / InfiniTime

Firmware for Pinetime smartwatch written in C++ and based on FreeRTOS
GNU General Public License v3.0
2.71k stars 926 forks source link

Batery status incorrect #218

Closed danielkucera closed 3 years ago

danielkucera commented 3 years ago

When my watch sleeps over night and then I wake it up, it shows incorrect battery level. I also went to device status screen and it showed 96%. When I turned the screeen off and on, it started showing correct level (values around 84,82,83%)

danielbarry commented 3 years ago

It's likely the right value. When the battery isn't under load the voltage will go up. Once the display has been on for a second or so, the voltage should dip down to its proper value. You can probably force it to update by entering the menu and coming back again.

Setting up a task would likely use too much RAM for the value added. I think this is something that will always resolve itself after just 15 seconds or so of use (either screen timeout or UI change causing a refresh).

danielkucera commented 3 years ago

okay, next morning I will measure it with multimeter before waking up and after but I have doubts.

JF002 commented 3 years ago

Battery voltage is averaged on 5 samples. The measurements are made every 2.5s in sleep mode, and every 1s when the display is ON. It's also done on every event (touch event, for example). As the time when the acquisition is done is not constant, it's sometimes measured when it's fully idle, and sometimes when it's under heavy load , which can add noise on the signal. You should notice that the value stabilizes when opening and closing the system info app, also.

All of this can certainly be improved ;)

danielkucera commented 3 years ago

Okay, another example:

danielbarry commented 3 years ago

I can confirm this behaviour with the battery after charging.

I also see something similar with the heart rate, where the first reading is almost always low.

Which leads me to ask the question: Is there a problem with the averaging technique?

Avamander commented 3 years ago

@danielbarry

similar with the heart rate, where the first reading is almost always low. Which leads me to ask the question: Is there a problem with the averaging technique?

It sounds to me that it's basically just an arithmetic mean in both cases. It tends to react slow to changes, especially if you have an initial value to all of your values in the mean.

There are better algorithms, better filters, but the question is - are they worth the performance cost. I'd vote "no", industry leaders have voted "no", we can wait a bit for the average to stabilize, even if it lags behind the real value a bit.

The true issue is IMHO that the battery percentage is not calibrated towards the total nonlinear voltage and capacity of the battery. This can't be fixed by fine-tuning the average algorithm or signal processing. It's a lot of complexity, at some point it should be implemented, but it's really not trivial.

danielkucera commented 3 years ago

The only problem I see is that very old samples are still considered valid for the computation. What if there were timestamps taken for each sample and only values with recent timestamp would be considered valid?

Avamander commented 3 years ago

Why do you think they're "very old samples"? I kind-of doubt that.

danielkucera commented 3 years ago

The measurements are made every 2.5s in sleep mode, and every 1s when the display is ON. It's also done on every event (touch event, for example).

Are you sure they are? What if there are no tasks to be executed during sleep?

https://github.com/JF002/InfiniTime/blob/master/src/systemtask/SystemTask.cpp#L131

Previously, it was out of this condition for some reason: https://github.com/JF002/InfiniTime/blob/65ecb65b57bd55582c1aa1a5babd4d76df89e621/src/SystemTask/SystemTask.cpp#L187

JF002 commented 3 years ago

Previously, it was out of this condition for some reason: Right, previously, the acquisition of the battery level was done every time the SystemTask was running (max every 2.5s in sleep mode), and now it's only acquired when a message is received by SystemTask. It was probably an attempt to minimize power consumption, but I'm not sure that's a good idea anymore :)

Panky-codes commented 3 years ago

I created the PR #168 which fixed the issue of erratic battery level changes with a simple averaging technique. I did notice that lag where it required some time to go to a correct value. For now, I set the number of averages to 10:

static constexpr uint8_t percentRemainingSamples = 10;

Even with 5 samples averaging, I noticed the fluctuations were still there because of the direct voltage measurement of the battery level which can dramatically affect the readings. And also with charging, as the battery's resistor drop will be added in the voltage measurement (instead of subtracted when we use the watch), so it will look as if the battery is being charged really quickly, which is not really the case.

As @JF002 mentioned, the situation will be better if we sample the battery even when we sleep, then we get rid of the old values, but that, of course, comes at a cost. Another option is to reduce the samples to be 5 instead of 10, then there could be some erratic behavior near the boundaries of 25, 50 75 (the percentage values we use to represent the battery bar).

But the above options mentioned will not improve the situation when the Pinetime is being charged. One option could be to have a different level of voltage offsets that we can subtract from the actual value to give a decently accurate indication of the battery level while we charging. I don't know if we can have anything better with the given hardware constraints.

Avamander commented 3 years ago

But the above options mentioned will not improve the situation when the Pinetime is being charged. One option could be to have a different level of voltage offsets that we can subtract from the actual value to give a decently accurate indication of the battery level while we charging. I don't know if we can have anything better with the given hardware constraints.

There are lower hanging fruit first in my opinion. If we'd empty the samples when the watch is removed from the charger (on full) and calibrate that voltage to 100%, not take empty samples aren't taken into account in the averaging, we could achieve a much more user-friendly battery voltage display - no lag after a charge in discharge status. Even without any discharge curve calculations.

Battery discharge curves are bit more complex topic, there are papers about this if you want to read more. Probably some very great algorithms that could be implemented for lithium batteries in one of those papers (e.g. https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.874.4516&rep=rep1&type=pdf).

But it's also worth mentioning that much bigger (and probably more professional) teams have resorted to four-five bar battery status, most likely for this exact reason.

JF002 commented 3 years ago

Battery indicator was improved in 1.0. Does it still need some work or can we close this issue?

danielkucera commented 3 years ago

It works nicely now, thank you!

moriel5 commented 3 years ago

Today my PineTime showed 5% when waking (I lost it in my room yesterday), however seconds after connecting it to the charger, it jumped up to 15%.

My unit is from one of the original batches, and originally came with the closed-source DaFit firmware.

Firmware version: 1.2.0.