101 lines
2.8 KiB
C++
101 lines
2.8 KiB
C++
|
|
#include "BatterySensor.h"
|
||
|
|
|
||
|
|
void BatterySensor::begin() {
|
||
|
|
pinMode(PIN_BATTERY_ADC, INPUT);
|
||
|
|
pinMode(PIN_LBO, INPUT);
|
||
|
|
|
||
|
|
analogReadResolution(12); // 12-bit ADC (0-4095)
|
||
|
|
|
||
|
|
Serial.println("[Battery] Initialized");
|
||
|
|
Serial.println("[Battery] ADC on GPIO 2, LBO on GPIO 5");
|
||
|
|
|
||
|
|
// 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;
|
||
|
|
|
||
|
|
// Read battery voltage via ADC
|
||
|
|
_voltage = readBatteryVoltage();
|
||
|
|
_percent = voltageToPercent(_voltage);
|
||
|
|
|
||
|
|
// 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;
|
||
|
|
|
||
|
|
// Log significant changes
|
||
|
|
if (abs(_percent - prevPercent) >= 5 || _isLow != wasLow) {
|
||
|
|
Serial.print("[Battery] Voltage: ");
|
||
|
|
Serial.print(_voltage, 2);
|
||
|
|
Serial.print("V (");
|
||
|
|
Serial.print(_percent);
|
||
|
|
Serial.print("%) LBO: ");
|
||
|
|
Serial.println(lboHigh ? "OK" : "LOW");
|
||
|
|
|
||
|
|
if (_isLow && !wasLow) {
|
||
|
|
Serial.println("[Battery] ⚠️ LOW BATTERY WARNING - Please recharge!");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|