diff --git a/platformio.ini b/platformio.ini index da5f7a8..d1c5dc3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,6 +10,9 @@ lib_deps = adafruit/Adafruit SSD1306 bblanchon/ArduinoJson +; board_build.filesystem = spiffs +; board_build.partitions = default.csv + build_flags = -D PB_VERSION=\"1.0.0\" -D PB_HOSTNAME=\"faceplant\" diff --git a/src/sensors/BatterySensor.cpp b/src/sensors/BatterySensor.cpp index 07628bc..5575663 100644 --- a/src/sensors/BatterySensor.cpp +++ b/src/sensors/BatterySensor.cpp @@ -3,11 +3,12 @@ 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"); + Serial.println("[Battery] ADC on GPIO 2, LBO on GPIO 5, CHG on GPIO 4"); // Initial reading _voltage = readBatteryVoltage(); @@ -29,30 +30,47 @@ void BatterySensor::loop() { 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 - _isLow = (_voltage < VOLTAGE_LOW) || lboIndicatesLow; + // 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) { + 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("%) LBO: "); + Serial.print("%) "); + + if (_isCharging) { + Serial.print("CHARGING "); + } + + Serial.print("LBO: "); Serial.println(lboHigh ? "OK" : "LOW"); - if (_isLow && !wasLow) { + 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"); + } } } diff --git a/src/sensors/BatterySensor.h b/src/sensors/BatterySensor.h index eab8898..ce3ac9c 100644 --- a/src/sensors/BatterySensor.h +++ b/src/sensors/BatterySensor.h @@ -10,6 +10,7 @@ public: int percent() const { return _percent; } float voltage() const { return _voltage; } bool isLow() const { return _isLow; } + bool isCharging() const { return _isCharging; } bool shouldBlink() const; // For display @@ -18,6 +19,7 @@ public: 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 @@ -33,6 +35,7 @@ private: int _percent = 100; float _voltage = 3.7; bool _isLow = false; + bool _isCharging = false; unsigned long _lastCheckMs = 0; float readBatteryVoltage(); diff --git a/src/ui/Display.cpp b/src/ui/Display.cpp index fe5758b..da2c0a7 100644 --- a/src/ui/Display.cpp +++ b/src/ui/Display.cpp @@ -60,11 +60,11 @@ _oled.display(); delay(2100); } -void Display::drawBatteryIcon(int x, int y, int percent, bool blink) { +void Display::drawBatteryIcon(int x, int y, int percent, bool blink, bool charging) { if (!_ok) return; - // Skip if blinking and blink state is off - if (blink && ((millis() / 500) % 2 == 1)) return; + // Skip if blinking and blink state is off (unless charging) + if (!charging && blink && ((millis() / 500) % 2 == 1)) return; // Battery body: 14x7 rectangle _oled.drawRect(x, y, 14, 7, 1); @@ -74,13 +74,33 @@ void Display::drawBatteryIcon(int x, int y, int percent, bool blink) { // Fill level: 12 pixels max width inside battery int fillWidth = (percent * 12) / 100; - if (fillWidth > 0) { - _oled.fillRect(x + 1, y + 1, fillWidth, 5, 1); - } - // Low battery warning: blink outline - if (percent < 20 && blink) { - // Draw thicker outline for emphasis - _oled.drawRect(x - 1, y - 1, 16, 9, 1); + if (charging) { + // Draw animated lightning bolt when charging + bool phase = (millis() / 300) % 2 == 0; + int color = phase ? 1 : 0; // Blink between white and inverted + + // Lightning bolt shape (simple zigzag) + _oled.drawLine(x + 7, y + 1, x + 5, y + 3, color); // Top diagonal + _oled.drawLine(x + 5, y + 3, x + 9, y + 3, color); // Middle horizontal + _oled.drawLine(x + 9, y + 3, x + 7, y + 5, color); // Bottom diagonal + + // Always draw at least some fill when charging + if (fillWidth == 0 && phase) { + _oled.fillRect(x + 1, y + 1, 2, 5, 1); // Small pulse even at 0% + } else if (fillWidth > 0) { + _oled.fillRect(x + 1, y + 1, fillWidth, 5, 1); + } + } else { + // Normal fill level + if (fillWidth > 0) { + _oled.fillRect(x + 1, y + 1, fillWidth, 5, 1); + } + + // Low battery warning: blink outline + if (percent < 20 && blink) { + // Draw thicker outline for emphasis + _oled.drawRect(x - 1, y - 1, 16, 9, 1); + } } } diff --git a/src/ui/Display.h b/src/ui/Display.h index a5876a7..07f8423 100644 --- a/src/ui/Display.h +++ b/src/ui/Display.h @@ -11,7 +11,7 @@ public: void showStatus(const String& line1, const String& line2); void bootAnimation(); - void drawBatteryIcon(int x, int y, int percent, bool blink); + void drawBatteryIcon(int x, int y, int percent, bool blink, bool charging); private: static constexpr int PIN_SDA = 6; diff --git a/src/ui/FaceRenderer.cpp b/src/ui/FaceRenderer.cpp index 27ef97f..4b9b676 100644 --- a/src/ui/FaceRenderer.cpp +++ b/src/ui/FaceRenderer.cpp @@ -157,7 +157,7 @@ void FaceRenderer::renderDead(unsigned long now, const BatterySensor& battery) { } // Draw battery icon in top-right corner - _display->drawBatteryIcon(110, 2, battery.percent(), battery.shouldBlink()); + _display->drawBatteryIcon(110, 2, battery.percent(), battery.shouldBlink(), battery.isCharging()); d.display(); } @@ -199,7 +199,7 @@ void FaceRenderer::renderBatteryLow(unsigned long now, const BatterySensor& batt drawMouthNervous(); // Draw battery icon in top-right corner (blinking) - _display->drawBatteryIcon(110, 2, battery.percent(), true); + _display->drawBatteryIcon(110, 2, battery.percent(), true, battery.isCharging()); d.display(); } @@ -230,7 +230,7 @@ void FaceRenderer::renderNormal(unsigned long now, const BatterySensor& battery) } // Draw battery icon in top-right corner - _display->drawBatteryIcon(110, 2, battery.percent(), battery.shouldBlink()); + _display->drawBatteryIcon(110, 2, battery.percent(), battery.shouldBlink(), battery.isCharging()); d.display(); }