/** * @file PowerManager.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 "PowerScheduler.h" #include "esp_adc/adc_cali.h" #include "esp_adc/adc_cali_scheme.h" #include "driver/gpio.h" #include "driver/adc.h" #include "esp_adc/adc_oneshot.h" #include "freertos/semphr.h" #ifndef Power_h #define Power_h #define TAG "Power" enum TaskResumptionReason { POWER_AVAILABLE, TIMEOUT }; class PowerManager { private: static SemaphoreHandle_t powerMutex; static constexpr uint16_t MUTEX_TIMEOUT_MS = 1; // RAII for mutex class PowerMutex { private: SemaphoreHandle_t &mutex; bool locked; public: PowerMutex(SemaphoreHandle_t &mutex) : mutex(mutex), locked(false) { locked = (xSemaphoreTake(mutex, pdMS_TO_TICKS(MUTEX_TIMEOUT_MS)) == pdTRUE); if (!locked) { ESP_LOGW(TAG, "Could not take power mutex"); } } ~PowerMutex() { if (locked) { xSemaphoreGive(mutex); } } bool isLocked() { return locked; } }; protected: /// @brief PowerScheduler instance to manage power consumption static PowerScheduler *powerScheduler; /* * Power State */ /// @brief last time of power state update static TickType_t lastPowerStateUpdate; /// @brief remaining Charge in coulombs static float coloumbsRemaining; /// @brief remaining Charge in percent static float percentRemaining; static bool busPowered; static bool chargingState; static float fullVoltageOffset; /// @brief Circular array of last calculated values for current state of /// charge static float lastSOC[PowerParameters::Battery::AVERAGING_SAMPLES]; static int latestSoCIndex; /// @brief Add calculated value to circular array, pushing out oldest value static void addSoCSample(float soc); /// @brief initialize the power state static void initPowerState(void); public: static void begin(void); PowerManager(); ~PowerManager(); /// @brief Get the current free current budget (to C1 discharge) /// @return the amount of power that is currently available (in mA) static float getFreeLimitCurrentBudget(void); /// @brief Get the current hard maximum free current (to C2 discharge) /// @return the maximum amount of power that can be allocated (in mA) static float getFreeMaximumCurrentBudget(void); /// @brief Request an allowance of a certain number of milliamperes from the /// power scheduler without waiting for it (meaning it will not be scheduled /// for future allocation). Only one can be active per consumer. /// @param neededCurrent the amount of current we want to be accounted for /// (in mA) /// @return whether the current could be successfully allocated static bool tryAccquireCurrentAllowance(PowerParameters::PowerConsumers consumer, uint16_t neededcurrent, uint16_t requestedDurationMs = 0); /// @brief "Return" the current currently allocated to a consumer /// @param consumer the active consumer to release the current for static void releaseCurrent(PowerParameters::PowerConsumers consumer); /// @brief Wait for a certain amount of current to be available. This will /// "reseve a spot in the queue". Only one can be active per consumer. /// @param neededCurrent the amount of power we want to be accounted for (in /// mW) /// @param TicksToWait the amount of time to wait for the power to become /// available /// @return whether the power could be successfully allocatedy static bool waitForCurrentAllowance(PowerParameters::PowerConsumers consumer, uint16_t neededCurrent, uint16_t maxSlackTimeMs, uint16_t requestedDurationMs); /// @brief Put the ESP32 into deep sleep mode, without a method to wake up /// again. Basically this is a shutdown. static void beginPermanentDeepSleep(void); /// @brief Get currently granted current /// @return the amount of current that is currently allocated (in mA) static float getCurrentCurrent(void); /// @brief get the current theoretically flowing at the battery /// @return the amount of current that is currently flowing (in mA) static float getBatteryCurrent(void); /// @brief Responsible for recalculating the current budgets /// @note these change based on the current state of charge static void recalculateCurrentBudgets(void); // @brief Get current consumption of a consumer static float getConsumerCurrent(PowerParameters::PowerConsumers consumer); /// @brief Get battery voltage measurement. /// @return Battery Terminal Voltage in Volts static float getBatteryVoltage(); /// @brief Get estimated battery charge state as percentage /// @return Battery charge state in percent static float getBatteryChargePercent(); /// @brief Get estimated battery charge state as percentage based on // voltage directly /// @return Battery charge state in percent static float getBatteryVoltageChargePercent(); /// @brief Get estimated battery charge state as coulombs /// @return Battery charge state in coulombs static float getBatteryChargeCoulombs(); /// @brief get available current (after voltage conversion and efficiency /// losses, referencing 1C discharge) /// @return available current in milliamps static float getMax3V3Current(); /// @brief update Power State /// @note needs to be public for task creation static void updatePowerStateHandler(); /// @brief dump power statistics to serial static void dumpPowerStatistics(); /// @brief dump consumer statistics to serial static void dumpConsumerStatistics(); /// @brief get wether power is supplied via USB /// @return true if power is supplied via USB static bool isUSBPowered(); /// @brief get wether power is supplied via battery /// @return true if power is supplied via battery static bool isBatteryPowered(); /// @brief get wether the battery is currently charging /// @return true if the battery is charging static bool isBatteryCharging(); /// @brief get wether the battery is currently discharging /// @return true if the battery is discharging static bool isBatteryDischarging(); /// @brief get wether the battery is currently fully charged /// @return true if the battery is fully charged static bool isBatteryFullyCharged(); }; extern PowerManager power; #endif // Power