187 lines
6.3 KiB
C++
187 lines
6.3 KiB
C++
/**
|
|
* @file Power.h
|
|
* @author Phillip Kühne
|
|
* @brief This component provides utilities for keeping track of power usage
|
|
* consumption.
|
|
* @version 0.1
|
|
* @date 2024-11-23
|
|
*/
|
|
|
|
#include "Power.h"
|
|
static portMUX_TYPE mux;
|
|
|
|
void Power::begin() {
|
|
// Check if another instance of us already initialized the power scheduler,
|
|
// if not, we will do it.
|
|
if (powerScheduler == nullptr) {
|
|
powerScheduler = &PowerScheduler::getPowerScheduler();
|
|
|
|
if (!(powerScheduler->tryAccquireCurrentAllowance(
|
|
PowerParameters::PowerConsumers::ESP,
|
|
PowerParameters::CurrentConsumptions::CURRENT_ESP_AVG))) {
|
|
Serial.println("Alledgedly not enough power available to reserve the "
|
|
"ESP32s base power consumption. Something is wrong.");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint16_t Power::getFreeCurrentBudget(void) {
|
|
return powerScheduler->getFreeCurrentBudget();
|
|
}
|
|
|
|
bool Power::tryAccquireCurrentAllowance(
|
|
PowerParameters::PowerConsumers consumer, uint16_t neededCurrent,
|
|
uint16_t requestedDurationMs) {
|
|
return powerScheduler->tryAccquireCurrentAllowance(consumer, neededCurrent,
|
|
requestedDurationMs);
|
|
}
|
|
|
|
bool Power::waitForCurrentAllowance(PowerParameters::PowerConsumers consumer,
|
|
uint16_t neededCurrent,
|
|
TickType_t TicksToWait,
|
|
uint16_t requestedDurationMs) {
|
|
return powerScheduler->waitForCurrentAllowance(
|
|
consumer, neededCurrent, TicksToWait, requestedDurationMs);
|
|
}
|
|
|
|
void Power::beginPermanentDeepSleep(void) {
|
|
return powerScheduler->beginPermanentDeepSleep();
|
|
}
|
|
|
|
void Power::releaseCurrent(PowerParameters::PowerConsumers consumer) {
|
|
powerScheduler->releaseCurrent(consumer);
|
|
}
|
|
|
|
float Power::getBatteryVoltage() {
|
|
// Get the battery voltage from the ADC and convert it to a voltage
|
|
// using the voltage divider.
|
|
portENTER_CRITICAL(&mux);
|
|
// Enable voltage divider
|
|
digitalWrite(PowerParameters::PinConfig::BAT_ADC_EN, HIGH);
|
|
// Returns value between 0 and 4095 mapping to between 0 and 3.3V
|
|
uint16_t batteryAdcValue =
|
|
analogRead(PowerParameters::PinConfig::BAT_ADC) *
|
|
(PowerParameters::Battery::BAT_ADC::VOLTAGE_DIVIDER_FACTOR);
|
|
// Disable voltage divider
|
|
digitalWrite(PowerParameters::PinConfig::BAT_ADC_EN, LOW);
|
|
portEXIT_CRITICAL(&mux);
|
|
// Convert ADC value to voltage
|
|
float batteryVoltage =
|
|
(batteryAdcValue * 3.3 / 4095) *
|
|
PowerParameters::Battery::BAT_ADC::VOLTAGE_DIVIDER_FACTOR;
|
|
return batteryVoltage;
|
|
}
|
|
|
|
int Power::getBatteryChargePercent() {
|
|
return percentRemaining;
|
|
}
|
|
|
|
float Power::getBatteryChargeCoulombs() {
|
|
return coloumbsRemaining;
|
|
}
|
|
|
|
|
|
|
|
int Power::getBatteryVoltageChargePercent() {
|
|
// Get the battery voltage and calculate the charge state based on the
|
|
// discharge curve.
|
|
float batteryVoltage = getBatteryVoltage();
|
|
float chargeState = 0;
|
|
// Clamp edge cases
|
|
if (batteryVoltage >=
|
|
PowerParameters::Battery::DISCHARGE_CURVE::VOLTAGES[0]) {
|
|
return PowerParameters::Battery::DISCHARGE_CURVE::CHARGE_STATES[0];
|
|
}
|
|
if (batteryVoltage <=
|
|
PowerParameters::Battery::DISCHARGE_CURVE::VOLTAGES
|
|
[PowerParameters::Battery::DISCHARGE_CURVE::NUM_POINTS - 1]) {
|
|
return PowerParameters::Battery::DISCHARGE_CURVE::CHARGE_STATES
|
|
[PowerParameters::Battery::DISCHARGE_CURVE::NUM_POINTS - 1];
|
|
}
|
|
float p1_x, p1_y, p2_x, p2_y;
|
|
for (int i = 0; i < PowerParameters::Battery::DISCHARGE_CURVE::NUM_POINTS;
|
|
i++) {
|
|
if (batteryVoltage >=
|
|
PowerParameters::Battery::DISCHARGE_CURVE::VOLTAGES[i]) {
|
|
p1_y = PowerParameters::Battery::DISCHARGE_CURVE::CHARGE_STATES[i];
|
|
p1_x = PowerParameters::Battery::DISCHARGE_CURVE::VOLTAGES[i];
|
|
p2_y = PowerParameters::Battery::DISCHARGE_CURVE::CHARGE_STATES[i + 1];
|
|
p2_x = PowerParameters::Battery::DISCHARGE_CURVE::VOLTAGES[i + 1];
|
|
chargeState =
|
|
((p2_y - p1_y) / (p2_x - p1_x)) * (batteryVoltage - p1_x) + p1_y;
|
|
return chargeState;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Power::updatePowerStateHandler() {
|
|
float currentCurrent = powerScheduler->getCurrentCurrent();
|
|
int referenceCurrentMa =
|
|
PowerParameters::Battery::DISCHARGE_CURVE::REFERENCE_CURRENT_A * 1000;
|
|
|
|
// Calculate remaining battery charge in Coulombs based on current and time
|
|
float coloumbsConsumedSinceLastUpdate =
|
|
(currentCurrent / 1000) *
|
|
((pdTICKS_TO_MS(xTaskGetTickCount() - lastPowerStateUpdate)) / 1000.0);
|
|
|
|
// Update coloumbs remaining
|
|
coloumbsRemaining -= coloumbsConsumedSinceLastUpdate;
|
|
|
|
float chargeState;
|
|
|
|
// If current flow is close enough to reference, get battery charge state via
|
|
// voltage curve
|
|
if ((currentCurrent > (referenceCurrentMa * 0.6)) &&
|
|
(currentCurrent < (referenceCurrentMa * 1.4))) {
|
|
// Get battery charge state from voltage curve
|
|
chargeState = getBatteryVoltageChargePercent();
|
|
} else {
|
|
// Calculate battery charge state from Charge consumption
|
|
float oldChargeState = lastSOC[latestSoCIndex];
|
|
float chargeState =
|
|
oldChargeState - ((coloumbsConsumedSinceLastUpdate /
|
|
PowerParameters::Battery::CELL_CHARGE_FULL_COLOUMB) *
|
|
100);
|
|
}
|
|
|
|
addSoCSample(chargeState);
|
|
|
|
// Update percentage remaining based on charge state average
|
|
float sampleSum = 0;
|
|
for (int i = 0; i < PowerParameters::Battery::AVERAGING_SAMPLES; i++) {
|
|
sampleSum += lastSOC[i];
|
|
}
|
|
percentRemaining = sampleSum / PowerParameters::Battery::AVERAGING_SAMPLES;
|
|
|
|
// Update last update time
|
|
lastPowerStateUpdate = xTaskGetTickCount();
|
|
|
|
// Update the available current (changes based on battery state of charge)
|
|
powerScheduler->recalculateCurrentBudgets();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
float Power::getMax3V3Current() {
|
|
float u_bat = getBatteryVoltage();
|
|
float i_bat = PowerParameters::Battery::CELL_CURRENT_1C;
|
|
float eta = PowerParameters::BUCK_BOOST_EFFICIENCY;
|
|
constexpr float u_3v3 = 3.3;
|
|
return (u_bat * i_bat * eta) / u_3v3;
|
|
}
|
|
|
|
void Power::addSoCSample(float soc) {
|
|
latestSoCIndex = (latestSoCIndex + 1) % PowerParameters::Battery::AVERAGING_SAMPLES;
|
|
lastSOC[latestSoCIndex] = soc;
|
|
}
|
|
|
|
PowerScheduler *Power::powerScheduler = nullptr;
|
|
|
|
Power::Power() {
|
|
// Initialize the power scheduler
|
|
powerScheduler = &PowerScheduler::getPowerScheduler();
|
|
// Initialize the mutex
|
|
mux = portMUX_INITIALIZER_UNLOCKED;
|
|
} |