From df00d77ce1f6e4a216b46a581eb559043c12c123 Mon Sep 17 00:00:00 2001 From: Joshua King Date: Sat, 21 Feb 2026 22:09:13 -0500 Subject: [PATCH] Refactor BatterySensor and Display classes to integrate MAX17048 and SH110X support --- platformio.ini | 3 +- src/sensors/BatterySensor.cpp | 107 ++++++++++------------------------ src/sensors/BatterySensor.h | 18 ++---- src/ui/Display.cpp | 10 ++-- src/ui/Display.h | 6 +- 5 files changed, 45 insertions(+), 99 deletions(-) diff --git a/platformio.ini b/platformio.ini index f614f8d..7ae1d34 100644 --- a/platformio.ini +++ b/platformio.ini @@ -7,9 +7,10 @@ monitor_speed = 115200 lib_deps = adafruit/Adafruit GFX Library - adafruit/Adafruit SSD1306 + adafruit/Adafruit SH110X adafruit/Adafruit VEML7700 Library adafruit/Adafruit MPU6050 + adafruit/Adafruit MAX1704X bblanchon/ArduinoJson ; board_build.filesystem = spiffs diff --git a/src/sensors/BatterySensor.cpp b/src/sensors/BatterySensor.cpp index 5575663..28797d0 100644 --- a/src/sensors/BatterySensor.cpp +++ b/src/sensors/BatterySensor.cpp @@ -1,109 +1,64 @@ #include "BatterySensor.h" +#include void BatterySensor::begin() { - pinMode(PIN_BATTERY_ADC, INPUT); - pinMode(PIN_LBO, INPUT); - pinMode(PIN_CHG, INPUT_PULLUP); // CHG is LOW when charging + _ok = _maxlipo.begin(); + if (!_ok) { + Serial.println("[Battery] MAX17048 not detected"); + return; + } - 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.println("[Battery] MAX17048 initialized on I2C"); + refresh(); Serial.print("[Battery] Initial voltage: "); - Serial.print(_voltage); + Serial.print(_voltage, 2); Serial.print("V, "); Serial.print(_percent); Serial.println("%"); } void BatterySensor::loop() { + if (!_ok) return; + 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); + refresh(); - // 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) { + if (abs(_percent - prevPercent) >= 2 || _isLow != wasLow || _isCharging != wasCharging) { Serial.print("[Battery] Voltage: "); Serial.print(_voltage, 2); Serial.print("V ("); Serial.print(_percent); - Serial.print("%) "); + 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"); - } + if (_isCharging) Serial.print(" CHARGING"); + Serial.println(); } } -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); +void BatterySensor::refresh() { + float v = _maxlipo.cellVoltage(); + float p = _maxlipo.cellPercent(); + float rate = _maxlipo.chargeRate(); + + if (!isnan(v) && isfinite(v) && v > 0.0f) _voltage = v; + + if (!isnan(p) && isfinite(p)) { + int pct = (int)lroundf(p); + if (pct < 0) pct = 0; + if (pct > 100) pct = 100; + _percent = pct; } - 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); + _isLow = (_voltage < VOLTAGE_LOW) || (_percent < 15); + _isCharging = (!isnan(rate) && isfinite(rate) && rate > CHARGE_RATE_MIN_PCT_PER_HR); } bool BatterySensor::shouldBlink() const { @@ -112,7 +67,5 @@ bool BatterySensor::shouldBlink() const { } int BatterySensor::iconFillWidth(int maxWidth) const { - // Calculate fill width for battery icon - // Returns 0 to maxWidth based on percentage return (_percent * maxWidth) / 100; } diff --git a/src/sensors/BatterySensor.h b/src/sensors/BatterySensor.h index ce3ac9c..fe06175 100644 --- a/src/sensors/BatterySensor.h +++ b/src/sensors/BatterySensor.h @@ -1,5 +1,6 @@ #pragma once #include +#include class BatterySensor { public: @@ -17,27 +18,18 @@ public: int iconFillWidth(int maxWidth) const; private: - static constexpr int PIN_BATTERY_ADC = 2; // Voltage divider input - static constexpr int PIN_LBO = 5; // PowerBoost LBO pin - static constexpr int PIN_CHG = 4; // PowerBoost CHG pin (LOW when charging) - static constexpr unsigned long CHECK_INTERVAL_MS = 2000; - static constexpr int SAMPLES = 10; // Number of ADC samples to average - // LiPo voltage thresholds - static constexpr float VOLTAGE_MIN = 3.0; // 0% - static constexpr float VOLTAGE_MAX = 4.2; // 100% static constexpr float VOLTAGE_LOW = 3.2; // Low battery threshold - - // Voltage divider: R1=100k, R2=100k (2:1 ratio) - static constexpr float DIVIDER_RATIO = 2.0; + static constexpr float CHARGE_RATE_MIN_PCT_PER_HR = 0.2f; // heuristic int _percent = 100; float _voltage = 3.7; bool _isLow = false; bool _isCharging = false; unsigned long _lastCheckMs = 0; + bool _ok = false; + Adafruit_MAX17048 _maxlipo; - float readBatteryVoltage(); - int voltageToPercent(float voltage); + void refresh(); }; diff --git a/src/ui/Display.cpp b/src/ui/Display.cpp index def8333..d257894 100644 --- a/src/ui/Display.cpp +++ b/src/ui/Display.cpp @@ -2,7 +2,7 @@ void Display::begin() { Wire.begin(PIN_SDA, PIN_SCL); - _ok = _oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR, true, false); + _ok = _oled.begin(OLED_ADDR, true); if (!_ok) return; _displayEnabled = true; setContrast(_contrast); @@ -25,8 +25,8 @@ void Display::showStatus(const String& line1, const String& line2) { void Display::setContrast(uint8_t contrast) { if (!_ok) return; _contrast = contrast; - _oled.ssd1306_command(SSD1306_SETCONTRAST); - _oled.ssd1306_command(_contrast); + _oled.oled_command(SH110X_SETCONTRAST); + _oled.oled_command(_contrast); } void Display::setDisplayEnabled(bool enabled) { @@ -35,12 +35,12 @@ void Display::setDisplayEnabled(bool enabled) { _displayEnabled = enabled; if (enabled) { - _oled.ssd1306_command(SSD1306_DISPLAYON); + _oled.oled_command(SH110X_DISPLAYON); setContrast(_contrast); } else { _oled.clearDisplay(); _oled.display(); - _oled.ssd1306_command(SSD1306_DISPLAYOFF); + _oled.oled_command(SH110X_DISPLAYOFF); } } diff --git a/src/ui/Display.h b/src/ui/Display.h index ec31eb7..d5596b6 100644 --- a/src/ui/Display.h +++ b/src/ui/Display.h @@ -2,12 +2,12 @@ #include #include #include -#include +#include class Display { public: void begin(); - Adafruit_SSD1306& oled() { return _oled; } + Adafruit_SH1106G& oled() { return _oled; } bool ok() const { return _ok; } void showStatus(const String& line1, const String& line2); @@ -21,7 +21,7 @@ private: static constexpr int PIN_SDA = 6; static constexpr int PIN_SCL = 7; static constexpr uint8_t OLED_ADDR = 0x3C; // try 0x3D if blank - Adafruit_SSD1306 _oled = Adafruit_SSD1306(128, 64, &Wire, -1); + Adafruit_SH1106G _oled = Adafruit_SH1106G(128, 64, &Wire, -1); bool _ok = false; bool _displayEnabled = true; uint8_t _contrast = 0xCF;