The Battery Gauge That Lied for Six Weeks
It read 94%. It read 94% for six weeks. I had built a very confident instrument that was measuring almost nothing at all.
The shed has a small solar setup and a lead-acid battery, and I wanted to know how full it was without walking out there with a multimeter. A voltage divider, an ADC, a little screen. An afternoon's work.
It read 94%. It read 94% the following week. It read 94% in the middle of a fortnight of grey November weather during which the panel produced, generously, nothing.
I want to write this one down properly, because a build that fails loudly teaches you one thing and a build that fails confidently teaches you four.
Wrong reason one: voltage is a terrible proxy for charge
Lead-acid resting voltage maps to state of charge across a range of roughly 12.0V to 12.7V. That is 700 millivolts to describe the entire battery. Worse, that mapping is only valid at rest — no load, no charge, several hours of settling. A battery under any load at all reads lower; a battery being charged reads much higher.
Mine was being trickle-charged whenever there was any light at all. I was not measuring charge. I was measuring whether the sun was up.
Wrong reason two: my reference was the thing being measured
The ESP32's ADC measures against its own supply rail. Its supply rail came, through a regulator, from the battery.
So when the battery sagged, the reference sagged with it, and the ratio between them barely moved. I had built a device that measured the battery in units of itself. This is the kind of mistake that is completely invisible on the bench, where you are powering the board off USB, and completely fatal in the field.
Wrong reason three: 12-bit resolution across 700mV
Even granting the first two, the divider I chose mapped 0–15V onto the ADC's 0–3.3V range. That puts the entire useful 12.0–12.7V band inside about 190 of 4096 counts, before you account for the ESP32's ADC being famously non-linear at the ends of its range. Every count was roughly 0.4% of state of charge, and the noise floor was several counts wide.
The gauge was not precise. It was stuck, and stuck at a plausible number, which is the worst possible failure mode. A reading of 0% or 100% would have had me debugging on day one.
What I should have built
A coulomb counter. You measure current in and out across a shunt and integrate over time — you count what actually goes in and comes back out, rather than inferring it from a voltage that is being pushed around by everything else in the system. An INA226 does this for about four pounds and has its own voltage reference, which fixes reason two for free.
The lesson, which is not about batteries
A sensor that always reads the same plausible number is indistinguishable from a working sensor. I never once questioned it, because 94% is exactly what a healthy battery should say.
Now I do something I should have been doing all along: before a measurement device goes in, I make it read wrong on purpose. Short the input. Disconnect it. Put a known dead cell across it. If the number does not move when reality moves, it is not an instrument, it is a decoration.
More from Didn't Work
The Hallway Light That Took Four Weekends
The brief was one sentence: the hallway light should be on when someone is in the hallway, and off when they are not. Four weekends, two sensor technologies and one genuinely humbling bug later, it is.
What's Actually Inside a £12 Label Printer
Eleven screws, one genuinely nice stepper motor, a thermal head that costs more than the rest of the machine put together, and a small piece of commercial cynicism moulded into the cartridge.
A £3 Part Saved a £700 Dishwasher
The error code said drainage failure. Two engineers would have quoted for a pump. The actual fault was a float switch with a fingernail of limescale on it, and the part costs three pounds.