Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/battery voltage #31

Merged
merged 10 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions lib/EFBoard/EFBoard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@

#include "EFBoard.h"


RTC_DATA_ATTR uint32_t bootCount = 0;

volatile int8_t ota_last_progress = -1;
Expand Down Expand Up @@ -74,6 +73,7 @@ void EFBoardClass::setup() {
randomSeed(analogRead(0));

// Check power state
this->updatePowerState();
const EFBoardPowerState pwrstate = this->getPowerState();
if (pwrstate == EFBoardPowerState::BAT_BROWN_OUT_HARD) {
LOGF_ERROR("(EFBoard) HARD BROWN OUT DETECTED (V_BAT = %.2f V). Panic!\r\n", this->getBatteryVoltage());
Expand Down Expand Up @@ -138,8 +138,15 @@ const char *EFBoardClass::getWakeupReason() {
}

const float EFBoardClass::getBatteryVoltage() {
// voltageBattery = adcValue * voltPerADCStep * voltageDividerRatio
return (analogRead(EFBOARD_PIN_VBAT) * (3.3 / 4095.0)) * ((51.1 + 100.0) / 100.0);
// Voltage divider resistors: R11 = 51.1k, R12 = 100k
constexpr float R1 = 51.1f;
constexpr float R2 = 100.0f;
constexpr float ADC_MAX_VALUE = 4095.0f;
constexpr float ADC_REF_VOLTAGE = 3.5f; // Reference voltage for ADC. Should be 3.3V, but the results are wonky. 3.5 works better

constexpr float voltage_divider_factor = (R1 + R2) / R2;
float battery_voltage = (analogRead(EFBOARD_PIN_VBAT) * (ADC_REF_VOLTAGE / ADC_MAX_VALUE)) * voltage_divider_factor;
return battery_voltage;
}

const bool EFBoardClass::isBatteryPowered() {
Expand All @@ -149,7 +156,7 @@ const bool EFBoardClass::isBatteryPowered() {
const uint8_t EFBoardClass::getBatteryCapacityPercent() {
/* Used Varta Longlife AA cells discharge curve as a base.
*
* We consider 1.16 V as empty even though the cells are not empty at that
* We consider 1.12 V as empty even though the cells are not empty at that
* point due to the brown out voltage of the DC/DC for the 3.3V rail.
*
* Data points used to fit cubic function to:
Expand All @@ -160,6 +167,7 @@ const uint8_t EFBoardClass::getBatteryCapacityPercent() {
* - 1.20 V = 23 %
* - 1.16 V = 0 %
*/
// TODO: This is a bit outdated/off
double vBatCell = this->getBatteryVoltage() / 3.0;
double percent = (14.9679 * pow(vBatCell, 3) - 68.9823 * pow(vBatCell, 2) + 106.4289 * vBatCell - 54.0063) * 100.0;
return max(min((int) round(percent), 100), 0);
Expand All @@ -169,7 +177,21 @@ const EFBoardPowerState EFBoardClass::updatePowerState() {
if (!this->isBatteryPowered()) {
this->power_state = EFBoardPowerState::USB;
} else {
const float vbat = this->getBatteryVoltage();
float vbat = this->getBatteryVoltage();

if (vbat <= EFBOARD_BROWN_OUT_SOFT) {
// A low-battery state could be a momentary spike
// We take multiple samples to make sure we are really low on battery
float sum = 0.0f;
constexpr uint8_t samples = 5;
for (uint8_t i = 0; i < samples; i++) {
sum += this->getBatteryVoltage();
delay(1);
}
vbat = sum / samples;
LOGF("BATTERY: Measurement average: %f\r\n", vbat);
}

if (vbat <= EFBOARD_BROWN_OUT_HARD || this->power_state == EFBoardPowerState::BAT_BROWN_OUT_HARD) {
this->power_state = EFBoardPowerState::BAT_BROWN_OUT_HARD;
} else if (vbat <= EFBOARD_BROWN_OUT_SOFT || this->power_state == EFBoardPowerState::BAT_BROWN_OUT_SOFT) {
Expand Down
6 changes: 3 additions & 3 deletions lib/EFBoard/EFBoard.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@
#define EFBOARD_SERIAL_BAUD 115200 //!< Baudrate for the serial device

// The Step-Down converter still manages to hold 3.00V with 3,32V input. The ESP needs 3.0V at least
#define EFBOARD_PIN_VBAT 10 //!< Pin the analog voltage divider for V_BAT is connected to
#define EFBOARD_PIN_VBAT 10 //!< Pin the analog voltage divider for V_BAT is connected to (ADC1_CH9)
#define EFBOARD_NUM_BATTERIES 3 //!< Number of battery cells used for V_BAT
#define EFBOARD_VBAT_MAX (1.60 * EFBOARD_NUM_BATTERIES) //!< Voltage at which battery cells are considered full
#define EFBOARD_VBAT_MIN (1.12 * EFBOARD_NUM_BATTERIES) //!< Voltage at which battery cells are considered empty
#define EFBOARD_VBAT_MIN (1.13 * EFBOARD_NUM_BATTERIES) //!< Voltage at which battery cells are considered empty

#define EFBOARD_BROWN_OUT_SOFT EFBOARD_VBAT_MIN //!< V_BAT threshold after which a soft brown out is triggered
#define EFBOARD_BROWN_OUT_HARD 3.30 //!< V_BAT threshold after which a hard brown out is triggered
#define EFBOARD_BROWN_OUT_HARD (EFBOARD_BROWN_OUT_SOFT - 0.08) //!< V_BAT threshold after which a hard brown out is triggered


/**
Expand Down
67 changes: 39 additions & 28 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#include "util.h"

// Global objects and states
constexpr unsigned int INTERVAL_BATTERY_CHECK = 60000;
constexpr unsigned int INTERVAL_BATTERY_CHECK = 10000;
// Initializing the board with a brightness above 48 can cause stability issues!
constexpr uint8_t ABSOLUTE_MAX_BRIGHTNESS = 45;
FSM fsm(10);
Expand Down Expand Up @@ -90,7 +90,7 @@ void _hardBrownOutHandler() {
);
EFBoard.disableWifi();
// Try getting the LEDs into some known state
EFLed.setBrightnessPercent(20);
EFLed.setBrightnessPercent(30);
EFLed.clear();
EFLed.setDragonNose(CRGB::Red);

Expand All @@ -99,10 +99,10 @@ void _hardBrownOutHandler() {
// Low brightness blink every few seconds
EFLed.enablePower();
EFLed.setDragonNose(CRGB::Red);
esp_sleep_enable_timer_wakeup(80 * 1000); // in ms
esp_sleep_enable_timer_wakeup(200 * 1000); // 200 ms
EFLed.disablePower();
// sleep most of the time.
esp_sleep_enable_timer_wakeup(4 * 1000000);
esp_sleep_enable_timer_wakeup(2 * 1000 * 1000); // 2s
esp_light_sleep_start();
// TODO: Go to deep sleep, but save the reason so when waking up, we can blink and deep sleep again
}
Expand Down Expand Up @@ -134,10 +134,10 @@ void _softBrownOutHandler() {
for (uint8_t n = 0; n < 30; n++) {
EFLed.enablePower();
EFLed.setDragonNose(CRGB::Red);
esp_sleep_enable_timer_wakeup(200 * 1000);
esp_sleep_enable_timer_wakeup(300 * 1000);
esp_light_sleep_start();
EFLed.disablePower();
esp_sleep_enable_timer_wakeup(800 * 1000);
esp_sleep_enable_timer_wakeup(700 * 1000);
esp_light_sleep_start();
}

Expand All @@ -150,6 +150,30 @@ void _softBrownOutHandler() {
}
}

void batteryCheck() {
EFBoardPowerState previousState = pwrstate;
pwrstate = EFBoard.updatePowerState();
if (previousState != pwrstate) {
LOGF_DEBUG("Updated power state: %s\r\n", toString(pwrstate));
}

// Log battery level if battery powered
if (EFBoard.isBatteryPowered()) {
LOGF_INFO(
"Battery voltage: %.2f V (%d %%)\r\n",
EFBoard.getBatteryVoltage(),
EFBoard.getBatteryCapacityPercent()
);
}

// Handle brown out
if (pwrstate == EFBoardPowerState::BAT_BROWN_OUT_HARD) {
_hardBrownOutHandler();
} else if (pwrstate == EFBoardPowerState::BAT_BROWN_OUT_SOFT) {
_softBrownOutHandler();
}
}

/**
* @brief Calculates a wave animation. Used by bootupAnimation()
*/
Expand All @@ -164,7 +188,7 @@ float wave_function(float x, float start, float end, float amplitude) {
/**
* @brief Displays a fancy bootup animation
*/
void bootupAnimation() {
void boopupAnimation() {
CRGB data[EFLED_TOTAL_NUM];
fill_solid(data, EFLED_TOTAL_NUM, CRGB::Black);
EFLed.setAll(data);
Expand All @@ -177,6 +201,10 @@ void bootupAnimation() {

for (uint16_t n = 0; n < 30; n++) {
uint16_t n_scaled = n * 7;
if (n % 10) {
// Low batteries might crash the boopup animation
batteryCheck();
}
for (uint8_t i = 0; i < EFLED_TOTAL_NUM; i++) {
int16_t dx = EFLedClass::getLEDPosition(i).x - pwrX;
int16_t dy = EFLedClass::getLEDPosition(i).y - pwrY;
Expand All @@ -195,6 +223,7 @@ void bootupAnimation() {
EFLed.clear();
delay(400);

batteryCheck();
// dragon awakens ;-)
EFLed.setDragonEye(CRGB(10,0, 0));
delay(60);
Expand Down Expand Up @@ -222,7 +251,7 @@ void setup() {
EFBoard.setup();
EFLed.init(ABSOLUTE_MAX_BRIGHTNESS);
EFLed.setBrightnessPercent(40); // We do not have access to the settings yet, default to 40
bootupAnimation();
boopupAnimation();

// Touchy stuff
EFTouch.init();
Expand All @@ -236,7 +265,7 @@ void setup() {
EFTouch.attachInterruptOnLongpress(EFTouchZone::Nose, isr_noseLongpress);
EFTouch.attachInterruptOnShortpress(EFTouchZone::All, isr_allShortpress);
EFTouch.attachInterruptOnLongpress(EFTouchZone::All, isr_allLongpress);

// Get FSM going
fsm.resume();
}
Expand Down Expand Up @@ -313,25 +342,7 @@ void loop() {

// Task: Battery checks
if (task_battery < millis()) {
pwrstate = EFBoard.updatePowerState();
LOGF_DEBUG("Updated power state: %s\r\n", toString(pwrstate));

// Log battery level if battery powered
if (EFBoard.isBatteryPowered()) {
LOGF_INFO(
"Battery voltage: %.2f V (%d %%)\r\n",
EFBoard.getBatteryVoltage(),
EFBoard.getBatteryCapacityPercent()
);
}

// Handle brown out
if (pwrstate == EFBoardPowerState::BAT_BROWN_OUT_HARD) {
_hardBrownOutHandler();
} else if (pwrstate == EFBoardPowerState::BAT_BROWN_OUT_SOFT) {
_softBrownOutHandler();
}

batteryCheck();
task_battery = millis() + INTERVAL_BATTERY_CHECK;
}
}
Loading