Custom battery monitoring using calibrated ADC values instead of pangodream library

This commit is contained in:
Serge Baranov
2025-11-21 17:07:47 -08:00
parent e335b8ac74
commit ebb2adfcee
4 changed files with 100 additions and 16 deletions
-1
View File
@@ -16,7 +16,6 @@ board_build.partitions = default_16MB.csv
; Libraries
lib_deps =
zinggjm/GxEPD2@^1.5.9
https://github.com/pangodream/18650CL
; Build flags
build_flags =
+54
View File
@@ -0,0 +1,54 @@
// BatteryMonitor.cpp
#include "BatteryMonitor.h"
#include "esp_adc_cal.h"
BatteryMonitor::BatteryMonitor(uint8_t adcPin, float dividerMultiplier)
: _adcPin(adcPin), _dividerMultiplier(dividerMultiplier)
{
}
uint16_t BatteryMonitor::readPercentage() const
{
return percentageFromMillivolts(readMillivolts());
}
uint16_t BatteryMonitor::readMillivolts() const
{
const uint16_t raw = readRawMillivolts();
const uint32_t mv = millivoltsFromRawAdc(raw);
return static_cast<uint32_t>(mv * _dividerMultiplier);
}
uint16_t BatteryMonitor::readRawMillivolts() const
{
const uint16_t raw = analogRead(_adcPin);
return raw;
}
double BatteryMonitor::readVolts() const
{
return static_cast<double>(readMillivolts()) / 1000.0;
}
uint16_t BatteryMonitor::percentageFromMillivolts(uint16_t millivolts)
{
double volts = millivolts / 1000.0;
// Polynomial derived from LiPo samples
double y = -144.9390 * volts * volts * volts +
1655.8629 * volts * volts -
6158.8520 * volts +
7501.3202;
// Clamp to [0,100] and round
y = max(y, 0.0);
y = min(y, 100.0);
y = round(y);
return static_cast<int>(y);
}
uint16_t BatteryMonitor::millivoltsFromRawAdc(uint16_t adc_raw)
{
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_12, ADC_WIDTH_BIT_12, 1100, &adc_chars);
return esp_adc_cal_raw_to_voltage(adc_raw, &adc_chars);
}
+33
View File
@@ -0,0 +1,33 @@
// BatteryMonitor.h
#pragma once
#include <Arduino.h>
class BatteryMonitor
{
public:
// Optional divider multiplier parameter defaults to 2.0
explicit BatteryMonitor(uint8_t adcPin, float dividerMultiplier = 2.0f);
// Read voltage and return percentage (0-100)
uint16_t readPercentage() const;
// Read the battery voltage in millivolts (accounts for divider)
uint16_t readMillivolts() const;
// Read raw millivolts from ADC (doesn't account for divider)
uint16_t readRawMillivolts() const;
// Read the battery voltage in volts (accounts for divider)
double readVolts() const;
// Percentage (0-100) from a millivolt value
static uint16_t percentageFromMillivolts(uint16_t millivolts);
// Calibrate a raw ADC reading and return millivolts
static uint16_t millivoltsFromRawAdc(uint16_t adc_raw);
private:
uint8_t _adcPin;
float _dividerMultiplier;
};
+13 -15
View File
@@ -4,7 +4,7 @@
#include <GxEPD2_BW.h>
#include <SPI.h>
#include "image.h"
#include "Pangodream_18650_CL.h"
#include "BatteryMonitor.h"
// Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults)
#define EPD_SCLK 8 // SPI Clock
@@ -20,12 +20,9 @@
#define BTN_GPIO3 3 // Power button (digital)
#define UART0_RXD 20 // Used for USB connection detection
#define BAT_GPIO0 0 // Battery voltage
#define READS 10
#define CONV_FACTOR 1.5176
Pangodream_18650_CL BL(BAT_GPIO0, CONV_FACTOR, READS);
static BatteryMonitor g_battery(BAT_GPIO0);
static int rawBat = 0;
@@ -44,7 +41,7 @@ volatile DisplayCommand displayCommand = DISPLAY_NONE;
// GxEPD2 display - Using GxEPD2_426_GDEQ0426T82
// Note: XteinkX4 has 4.26" 800x480 display
GxEPD2_BW<GxEPD2_426_GDEQ0426T82, GxEPD2_426_GDEQ0426T82::HEIGHT> display(
GxEPD2_426_GDEQ0426T82(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));
GxEPD2_426_GDEQ0426T82(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));
// FreeRTOS task for non-blocking display updates
TaskHandle_t displayTaskHandle = NULL;
@@ -78,6 +75,7 @@ volatile Button currentPressedButton = NONE;
const unsigned long POWER_BUTTON_WAKEUP_MS = 1000; // Time required to confirm boot from sleep
const unsigned long POWER_BUTTON_SLEEP_MS = 1000; // Time required to enter sleep mode
// Get button name as string
const char *getButtonName(Button btn)
{
@@ -163,11 +161,11 @@ void drawBatteryInfo()
display.printf("Power: %s", charging ? "Charging" : "Battery");
display.setCursor(40, 200);
display.printf("Raw: %i", rawBat);
display.printf("Raw: %i", g_battery.readRawMillivolts());
display.setCursor(40, 240);
display.printf("Volts: %.2f V", BL.getBatteryVolts());
display.printf("Volts: %.2f V", g_battery.readVolts());
display.setCursor(40, 280);
display.printf("Charge: %i%%", BL.getBatteryChargeLevel());
display.printf("Charge: %i%%", g_battery.readPercentage());
}
// Display update task running on separate core
@@ -410,14 +408,14 @@ void loop()
Serial.println("");
Serial.print("Value from pin: ");
Serial.println(rawBat);
Serial.print("Average value from pin: ");
Serial.println(BL.pinRead());
Serial.print("Value from pin (raw/calibrated): ");
Serial.print(rawBat);
Serial.print(" / ");
Serial.println(BatteryMonitor::millivoltsFromRawAdc(rawBat));
Serial.print("Volts: ");
Serial.println(BL.getBatteryVolts());
Serial.println(g_battery.readVolts());
Serial.print("Charge level: ");
Serial.println(BL.getBatteryChargeLevel());
Serial.println(g_battery.readPercentage());
Serial.println("");
lastLogMs = now;
}