OXRecorder::OXBatteryManager

ADC battery / power-rail sensing on GPIO3 with a drop-curve percentage estimate

Overview

OXBatteryManager reads an ADC input, reconstructs the original pre-divider voltage, and maps it to a percentage through a configurable drop curve. The default wiring matches the current board: GPIO3 at the midpoint of two 1k resistors, with the top resistor on the raw single-cell LiPo VCC and the bottom resistor on GND.

Declared in include/OXBatteryManager.h; portable core in src/managers/OXBatteryManager.cpp; ESP32-S3 ADC backend in src/gpio/EspAdcInput.cpp.

Important wiring note

The divider must sense the raw LiPo cell before any boost converter or regulator. A regulated 5 V rail stays nearly constant until shutdown and cannot represent remaining battery charge. With the 1k/1k divider, a full 4.20 V cell appears as about 2.10 V on GPIO3.

BatteryConfig

FieldTypeDefaultMeaning
adcPinint3ADC pin connected to the divider midpoint.
sampleCountuint8_t8How many raw + millivolt reads to average per sample.
dividerTopOhmsuint32_t1000Top resistor from the sensed rail to GPIO3.
dividerBottomOhmsuint32_t1000Bottom resistor from GPIO3 to GND.
curveconst BatteryCurvePoint*nullptrVoltage-to-percent mapping. If omitted, the manager uses its built-in 1-cell LiPo curve (4.20 V = 100%, 3.30 V = 0%).
curveCountsize_t0Number of points in curve.

BatteryReading

FieldTypeMeaning
rawuint16_tAverage raw ADC reading.
adcMilliVoltsuint16_tAverage midpoint voltage seen directly on GPIO3.
sourceMilliVoltsuint16_tReconstructed pre-divider voltage.
percentuint8_tInterpolated estimate from the active curve.
validboolFalse when no ADC backend is available.

API reference

bool begin(const BatteryConfig &cfg = BatteryConfig{})

Configures the ADC pin and stores the divider/curve settings. Returns false if no ADC backend is available.

BatteryReading sample()

Takes a fresh averaged sample now and caches it in lastReading().

uint16_t readAdcMilliVolts()

uint16_t readSourceMilliVolts()

uint8_t readPercent()

Convenience wrappers that call sample() and return one field from the resulting reading.

static uint16_t sourceMilliVoltsFromAdc(uint16_t adcMilliVolts, uint32_t dividerTopOhms, uint32_t dividerBottomOhms)

Reconstructs the pre-divider voltage using Vsource = Vadc × (Rtop + Rbottom) / Rbottom.

static uint8_t percentForMilliVolts(uint16_t milliVolts, const BatteryCurvePoint *curve, size_t curveCount)

Maps voltage to percent with linear interpolation between curve points. Curves must be ordered highest voltage first.

static const BatteryCurvePoint *defaultBoost5vCurve(size_t *count = nullptr)

static const BatteryCurvePoint *defaultLiPoCurve(size_t *count = nullptr)

Return the optional 5 V rail curve or the default raw single-cell LiPo curve.

Examples

1. Current board wiring: raw LiPo VCC / GND on a 1k/1k divider

#include "OXBatteryManager.h"
#include "OXLog.h"

using namespace OXRecorder;

OXBatteryManager battery;

void setup() {
  OXLog::begin(115200);
  battery.begin();
  BatteryReading r = battery.sample();
  OXLog::info("battery estimate %u%% (%u mV source, %u mV ADC)",
              r.percent, r.sourceMilliVolts, r.adcMilliVolts);
}

2. Explicitly select the raw LiPo curve

BatteryConfig cfg;
size_t curveCount = 0;
cfg.curve = OXBatteryManager::defaultLiPoCurve(&curveCount);
cfg.curveCount = curveCount;
battery.begin(cfg);

3. Custom resistor values

BatteryConfig cfg;
cfg.dividerTopOhms = 150000;
cfg.dividerBottomOhms = 100000;
battery.begin(cfg);

Notes