#include "BatterySensor.h" void BatterySensor::begin() { pinMode(PIN_BATTERY_ADC, INPUT); pinMode(PIN_LBO, INPUT); pinMode(PIN_CHG, INPUT_PULLUP); // CHG is LOW when charging analogReadResolution(12); // 12-bit ADC (0-4095) Serial.println("[Battery] Initialized"); Serial.println("[Battery] ADC on GPIO 2, LBO on GPIO 5, CHG on GPIO 4"); // Initial reading _voltage = readBatteryVoltage(); _percent = voltageToPercent(_voltage); _isLow = (_voltage < VOLTAGE_LOW); Serial.print("[Battery] Initial voltage: "); Serial.print(_voltage); Serial.print("V, "); Serial.print(_percent); Serial.println("%"); } void BatterySensor::loop() { unsigned long now = millis(); if (now - _lastCheckMs < CHECK_INTERVAL_MS) return; _lastCheckMs = now; float prevVoltage = _voltage; int prevPercent = _percent; bool wasLow = _isLow; bool wasCharging = _isCharging; // Read battery voltage via ADC _voltage = readBatteryVoltage(); _percent = voltageToPercent(_voltage); // Check CHG pin (LOW when charging) bool chgLow = !digitalRead(PIN_CHG); _isCharging = chgLow; // Check LBO pin as backup confirmation bool lboHigh = digitalRead(PIN_LBO); bool lboIndicatesLow = !lboHigh; // Battery is low if either voltage is low OR LBO pin indicates low (and not charging) _isLow = ((_voltage < VOLTAGE_LOW) || lboIndicatesLow) && !_isCharging; // Log significant changes if (abs(_percent - prevPercent) >= 5 || _isLow != wasLow || _isCharging != wasCharging) { Serial.print("[Battery] Voltage: "); Serial.print(_voltage, 2); Serial.print("V ("); Serial.print(_percent); Serial.print("%) "); if (_isCharging) { Serial.print("CHARGING "); } Serial.print("LBO: "); Serial.println(lboHigh ? "OK" : "LOW"); if (_isLow && !wasLow && !_isCharging) { Serial.println("[Battery] ⚠️ LOW BATTERY WARNING - Please recharge!"); } if (_isCharging && !wasCharging) { Serial.println("[Battery] 🔌 Charging started"); } else if (!_isCharging && wasCharging) { Serial.println("[Battery] ✓ Charging complete or disconnected"); } } } float BatterySensor::readBatteryVoltage() { // Take multiple samples and average them for stability long sum = 0; for (int i = 0; i < SAMPLES; i++) { sum += analogRead(PIN_BATTERY_ADC); delay(5); } int avgRaw = sum / SAMPLES; // Convert ADC value to voltage at the pin // ESP32-C3 ADC: 12-bit (0-4095) maps to 0-3.3V float adcVoltage = (avgRaw / 4095.0) * 3.3; // Multiply by divider ratio to get actual battery voltage float batteryVoltage = adcVoltage * DIVIDER_RATIO; return batteryVoltage; } int BatterySensor::voltageToPercent(float voltage) { // Clamp voltage to valid range if (voltage >= VOLTAGE_MAX) return 100; if (voltage <= VOLTAGE_MIN) return 0; // Linear mapping from voltage to percentage // This is a simplified model; LiPo discharge curves are non-linear // For better accuracy, you could use a lookup table float percent = ((voltage - VOLTAGE_MIN) / (VOLTAGE_MAX - VOLTAGE_MIN)) * 100.0; return constrain((int)percent, 0, 100); } bool BatterySensor::shouldBlink() const { // Blink every 500ms when battery is low (< 20%) return (_percent < 20) && ((millis() / 500) % 2 == 0); } int BatterySensor::iconFillWidth(int maxWidth) const { // Calculate fill width for battery icon // Returns 0 to maxWidth based on percentage return (_percent * maxWidth) / 100; }