diff --git a/src/power/Power.cpp b/src/power/Power.cpp index 29a44ff..b67f9ff 100644 --- a/src/power/Power.cpp +++ b/src/power/Power.cpp @@ -10,41 +10,106 @@ #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->tryAccquirePowerAllowance(CONSUMPTION_ESP_BASE))) - { - Serial.println("Alledgedly not enough power available to reserve the ESP32s base power consumption. Something is wrong."); - return; - } +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::getFreePowerBudget(void) -{ - return powerScheduler->getFreePowerBudget(); +uint16_t Power::getFreeCurrentBudget(void) { + return powerScheduler->getFreeCurrentBudget(); } -bool Power::tryAccquirePowerAllowance(uint16_t neededPower) -{ - return powerScheduler->tryAccquirePowerAllowance(neededPower); +bool Power::tryAccquireCurrentAllowance( + PowerParameters::PowerConsumers consumer, uint16_t neededCurrent, + uint16_t requestedDurationMs) { + return powerScheduler->tryAccquireCurrentAllowance(consumer, neededCurrent, + requestedDurationMs); } -bool Power::waitForPowerAllowance(uint16_t neededPower, TickType_t TicksToWait) -{ - return powerScheduler->waitForPowerAllowance(neededPower, TicksToWait); +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::beginPermanentDeepSleep(void) { + return powerScheduler->beginPermanentDeepSleep(); } -void Power::releasePower(uint16_t power) -{ +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() { + // 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; + } + } +} + +PowerScheduler* Power::powerScheduler = nullptr; + +Power::Power() { + // Initialize the power scheduler + powerScheduler = &PowerScheduler::getPowerScheduler(); + // Initialize the mutex + mux = portMUX_INITIALIZER_UNLOCKED; +} \ No newline at end of file diff --git a/src/power/Power.h b/src/power/Power.h index 6e885c3..a748965 100644 --- a/src/power/Power.h +++ b/src/power/Power.h @@ -13,38 +13,77 @@ #define TOTAL_POWER_MILLIWATTS POWER_BUDGET -enum TaskResumptionReason -{ - POWER_AVAILABLE, - TIMEOUT -}; +enum TaskResumptionReason { POWER_AVAILABLE, TIMEOUT }; -class Power -{ +class Power { public: - static void begin(); - Power(); - uint16_t getFreePowerBudget(void); - /// @brief Request an allowance of a certain number of milliwatts from the power scheduler - /// @param neededPower the amount of power we want to be accounted for (in mW) - /// @return whether the power could be successfully allocated - bool tryAccquirePowerAllowance(uint16_t neededPower); - /// @brief "Return" a certain amount of power when it is no longer needed - /// @param neededPower the amount of power to return (in mW) - /// @return whether the power - void releasePower(uint16_t power); + static void begin(void); + Power(); + uint16_t getFreeCurrentBudget(void); + /// @brief Request an allowance of a certain number of milliwatts from the + /// power scheduler + /// @param neededPower the amount of power we want to be accounted for (in mW) + /// @return whether the power could be successfully allocated + static bool + tryAccquireCurrentAllowance(PowerParameters::PowerConsumers consumer, + uint16_t neededCurrent, + uint16_t requestedDurationMs); + /// @brief "Return" the power consumed by an active consumer + /// @param neededPower the amount of power to return (in mW) + /// @return whether the power + static void releaseCurrent(PowerParameters::PowerConsumers consumer); - /// @brief Wait for a certain amount of power to be available - /// @param neededPower 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 - bool waitForPowerAllowance(uint16_t neededPower, TickType_t TicksToWait); - /// @brief Put the ESP32 into deep sleep mode, without a method to wake up again. Basically this is a shutdown. - void beginPermanentDeepSleep(void); + /// @brief Wait for a certain amount of power to be available + /// @param neededPower 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, + TickType_t TicksToWait, + 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 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 int getBatteryChargePercent(); + + /// @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 getAvailableCurrent(); protected: - static PowerScheduler *powerScheduler; + /// @brief PowerScheduler instance to manage power consumption + static PowerScheduler *powerScheduler; + + /// @brief update Power State + static void updatePowerState(); + + /* + * Power State + */ + + /// @brief remaining Charge in coulombs + static int coloumbsRemaining; + + /// @brief remaining Charge in percent + static int percentRemaining; + + friend class PowerScheduler; }; +extern Power power; + #endif // Power \ No newline at end of file diff --git a/src/power/PowerParameters.h b/src/power/PowerParameters.h index a3f6066..98a7a68 100644 --- a/src/power/PowerParameters.h +++ b/src/power/PowerParameters.h @@ -99,7 +99,7 @@ namespace PowerParameters { PT_DL_FRONT, PT_DL_BOTTOM, LED_UV, - DISPLAY, + DISPLAY_OLED, MOTOR_LEFT, MOTOR_RIGHT, IMU diff --git a/src/power/PowerScheduler.cpp b/src/power/PowerScheduler.cpp index b365bc4..4bfec42 100644 --- a/src/power/PowerScheduler.cpp +++ b/src/power/PowerScheduler.cpp @@ -1,7 +1,8 @@ /** * @file PowerScheduler.cpp * @author Phillip Kühne - * @brief The actual power scheduler class, which keeps track of the power budget and allocates power to different components. + * @brief The actual power scheduler class, which keeps track of the power + * budget and allocates power to different components. * @version 0.1 * @date 2024-12-21 * @@ -11,99 +12,186 @@ #include "PowerScheduler.h" -bool PowerScheduler::tryAccquirePowerAllowance(uint16_t neededPower) -{ - if (this->freePowerBudget >= neededPower) - { - this->freePowerBudget -= neededPower; - return true; - } - else - { - return false; - } +bool PowerScheduler::tryAccquireCurrentAllowance( + PowerParameters::PowerConsumers consumer, uint16_t neededCurrent, + uint16_t requestedDurationMs) { + portENTER_CRITICAL(&mux); + if (this->freeLimitCurrentBudget > 0 && + this->freeMaximumCurrentBudget >= neededCurrent) { + this->currentAllowances.push_back( + {.consumer = consumer, + .maxSlackTimeMs = 0, + .requestedDurationMs = requestedDurationMs, + .taskHandle = xTaskGetCurrentTaskHandle(), + .neededCurrent = neededCurrent, + .requestedAt = xTaskGetTickCount(), + .granted = false}); + this->recalculateCurrentBudgets(); + portEXIT_CRITICAL(&mux); + return true; + } else { + return false; + } } -void PowerScheduler::releasePower(uint16_t power) -{ - if (this->freePowerBudget + power <= this->totalPowerBudget) - { - this->freePowerBudget += power; +void PowerScheduler::releaseCurrent(PowerParameters::PowerConsumers consumer) { + portENTER_CRITICAL(&mux); + for (auto it = currentAllowances.begin(); it != currentAllowances.end(); + ++it) { + if (it->consumer == consumer && it->granted) { + currentAllowances.erase(it); + break; } - else // TODO: Maybe we should actually throw an error here, since obviouslsy someone used us wrong. - { - this->freePowerBudget = this->totalPowerBudget; - } - // Check if there are tasks waiting for power - checkWaitingTasks(); + } + recalculateCurrentBudgets(); + portEXIT_CRITICAL(&mux); + // Check if there are tasks waiting for power + checkWaitingTasks(); } -bool PowerScheduler::waitForPowerAllowance(uint16_t neededPower, TickType_t ticksToWait) -{ - if (tryAccquirePowerAllowance(neededPower)) - { - return true; +bool PowerScheduler::waitForCurrentAllowance( + PowerParameters::PowerConsumers consumer, uint16_t neededCurrent, + uint16_t maxSlackTimeMs, uint16_t requestedDurationMs) { + if (tryAccquireCurrentAllowance(consumer, neededCurrent, + requestedDurationMs)) { + return true; + } else { + + // Suspend the task while waiting for power to be available + TaskHandle_t currentTask = xTaskGetCurrentTaskHandle(); + TickType_t initialTickCount = xTaskGetTickCount(); + PowerScheduler::CurrentAllowance newAllowance = { + .consumer = consumer, + .maxSlackTimeMs = maxSlackTimeMs, + .requestedDurationMs = requestedDurationMs, + .taskHandle = currentTask, + .neededCurrent = neededCurrent, + .requestedAt = initialTickCount, + .granted = false}; + + this->currentAllowances.push_back(newAllowance); + uint32_t notificationValue; + BaseType_t notificationStatus = xTaskNotifyWait( + 0, 0, ¬ificationValue, pdMS_TO_TICKS(maxSlackTimeMs)); + // Code below will be executed after the task is woken up + while (notificationStatus == pdPASS) { + if (notificationValue == + PowerScheduler::PowerWakeupReasons::POWER_AVAILABLE) { + // We were woken up because new power is available, check if it is + // enough + if (this->freeLimitCurrentBudget > 0 && + this->freeMaximumCurrentBudget >= neededCurrent) { + return true; + } else { + // Still not enough power available for us. Wait the remaining ticks. + xTaskNotifyWait(0, 0, ¬ificationValue, + pdMS_TO_TICKS(maxSlackTimeMs) - + (xTaskGetTickCount() - initialTickCount)); + } + } } - else - { - // Suspend the task while waiting for power to be available - TaskHandle_t currentTask = xTaskGetCurrentTaskHandle(); - TickType_t initialTickCount = xTaskGetTickCount(); - waitingTasks.push(currentTask); - uint32_t notificationValue; - BaseType_t notificationStatus = xTaskNotifyWait(0, 0, ¬ificationValue, ticksToWait); - // Code below will be executed after the task is woken up - while (notificationStatus == pdPASS) - { - if (notificationValue == POWER_AVAILABLE) - { - // We were woken up because new power is available, check if it is enough - if (tryAccquirePowerAllowance(neededPower)) - { - return true; - } - else - { - // Still not enough power available for us. Wait the remaining ticks. - xTaskNotifyWait(0, 0, ¬ificationValue, ticksToWait - (xTaskGetTickCount() - initialTickCount)); - } - } - } - if (notificationStatus == pdFALSE) - { - // We waited long enough... - return false; - } - else - { - // Should be impossible to reach - throw "Reached impossible state"; + if (notificationStatus == pdFALSE) { + // We waited long enough... + // Remove the task from the list of waiting tasks + for (auto it = currentAllowances.begin(); it != currentAllowances.end(); + ++it) { + if (it->consumer == consumer && + it->requestedAt == initialTickCount) { + currentAllowances.erase(it); + break; } + } + return false; + } else { + // Should be impossible to reach + throw "Reached impossible state"; } + } } -void PowerScheduler::checkWaitingTasks(void) -{ - // Check if there are tasks waiting for power - if (!waitingTasks.empty()) - { - TaskHandle_t task = waitingTasks.front(); - waitingTasks.pop(); - xTaskNotify(task, POWER_AVAILABLE, eSetValueWithOverwrite); +void PowerScheduler::checkWaitingTasks(void) { + // If there are requested allowances, try to grant the one expiring next + if (this->currentAllowances.size() > 0) { + + PowerScheduler::CurrentAllowance* nextAllowance = + getNextExpiringAllowance(); + if (nextAllowance != nullptr) { + xTaskNotify(nextAllowance->taskHandle, + PowerScheduler::PowerWakeupReasons::POWER_AVAILABLE, + eSetValueWithOverwrite); } + } } -PowerScheduler *PowerScheduler::getPowerScheduler() -{ - if (powerSchedulerInstance == nullptr) - { - // Double check locking https://www.aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf - taskENTER_CRITICAL(&mux); - if (powerSchedulerInstance == nullptr) - { - powerSchedulerInstance = new PowerScheduler(); - } - taskEXIT_CRITICAL(&mux); +void PowerScheduler::recalculateCurrentBudgets(void) { + // TODO: replace with actual current modeling + this->freeLimitCurrentBudget = this->limitCurrent; + this->freeMaximumCurrentBudget = this->maximumCurrent * 2; + for (auto &allowance : currentAllowances) { + if (allowance.granted) { + this->freeLimitCurrentBudget -= allowance.neededCurrent; + this->freeMaximumCurrentBudget -= allowance.neededCurrent; } - return powerSchedulerInstance; -} \ No newline at end of file + } +} + +PowerScheduler::CurrentAllowance* +PowerScheduler::getCurrentAllowance(PowerParameters::PowerConsumers consumer) { + for (auto &allowance : currentAllowances) { + if (allowance.consumer == consumer) { + return &allowance; + } + } + return nullptr; +} +PowerScheduler::CurrentAllowance* +PowerScheduler::getCurrentAllowance(TaskHandle_t taskHandle) { + for (auto &allowance : currentAllowances) { + if (allowance.taskHandle == taskHandle) { + return &allowance; + } + } + return nullptr; +} +PowerScheduler::CurrentAllowance* +PowerScheduler::getNextExpiringAllowance(void) { + TickType_t minTicks = UINT32_MAX; + CurrentAllowance *nextAllowance = nullptr; + for (auto &allowance : currentAllowances) { + if (!(allowance.granted)) { + TickType_t ticks = + allowance.requestedAt + pdMS_TO_TICKS(allowance.maxSlackTimeMs); + if (ticks < minTicks) { + minTicks = ticks; + nextAllowance = &allowance; + } + } + } + // Will be nullptr if no allowance was found + return nextAllowance; +} + +PowerScheduler& PowerScheduler::getPowerScheduler(float i_limit_ma, + float i_max_ma) { + if (powerSchedulerInstance == nullptr) { + // Double check locking + // https://www.aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf + taskENTER_CRITICAL(&mux); + if (powerSchedulerInstance == nullptr) { + powerSchedulerInstance = new PowerScheduler(i_limit_ma, i_max_ma); + } + taskEXIT_CRITICAL(&mux); + } + return *powerSchedulerInstance; +} + +PowerScheduler::PowerScheduler(float i_limit_ma, float i_max_ma) { + this->limitCurrent = i_limit_ma; + this->maximumCurrent = i_max_ma; + this->currentAllowances = std::vector(); + mux = portMUX_INITIALIZER_UNLOCKED; +} + +portMUX_TYPE PowerScheduler::mux = portMUX_INITIALIZER_UNLOCKED; + +PowerScheduler::~PowerScheduler() {} \ No newline at end of file diff --git a/src/power/PowerScheduler.h b/src/power/PowerScheduler.h index 236430e..406192a 100644 --- a/src/power/PowerScheduler.h +++ b/src/power/PowerScheduler.h @@ -1,7 +1,8 @@ /** * @file PowerScheduler.hpp * @author Phillip Kühne - * @brief The actual power scheduler class, which keeps track of the power budget and allocates power to different components. + * @brief The actual power scheduler class, which keeps track of the power + * budget and allocates power to different components. * @version 0.1 * @date 2024-12-21 * @@ -9,62 +10,114 @@ * */ -#include -#include -#include "Consumptions.h" +#include "PowerParameters.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include +#include #ifndef PowerScheduler_h #define PowerScheduler_h -#define TOTAL_POWER_MILLIWATTS POWER_BUDGET - -class PowerScheduler -{ +class PowerScheduler { private: - /* data */ -public: - PowerScheduler(); - ~PowerScheduler(); - /// @brief Initialize the singleton instance of the power manager - /// @return reference to the power manager - static PowerScheduler *getPowerScheduler(); - uint16_t getFreePowerBudget(void); - /// @brief Request an allowance of a certain number of milliwatts from the power scheduler - /// @param neededPower the amount of power we want to be accounted for (in mW) - /// @return whether the power could be successfully allocated - bool tryAccquirePowerAllowance(uint16_t neededPower); - /// @brief "Return" a certain amount of power when it is no longer needed - /// @param neededPower the amount of power to return (in mW) - /// @return whether the power - void releasePower(uint16_t power); + static constexpr uint16_t DEFAULT_SLACK_TIME_MS = 100; + PowerScheduler(float i_limit_ma, float i_max_ma); + + public: + ~PowerScheduler(); + /// @brief Initialize the singleton instance of the power manager + /// @return reference to the power manager + static PowerScheduler& getPowerScheduler(float i_limit_ma=0, float i_max_ma=0); + /// @brief Get the current free current budget (to C1 discharge) + /// @return the amount of power that is currently available (in mA) + uint16_t getFreeCurrentBudget(void); + /// @brief Get the current hard maximum free current (to C2 discharge) + /// @return the maximum amount of power that can be allocated (in mA) + uint16_t getFreeHardMaxCurrent(void); - /// @brief Wait for a certain amount of power to be available - /// @param neededPower 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 - bool waitForPowerAllowance(uint16_t neededPower, TickType_t TicksToWait); - /// @brief Put the ESP32 into deep sleep mode, without a method to wake up again. Basically this is a shutdown. - void beginPermanentDeepSleep(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 + 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 + 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 + bool waitForCurrentAllowance(PowerParameters::PowerConsumers consumer, + uint16_t neededCurrent, + uint16_t maxSlackTimeMs = DEFAULT_SLACK_TIME_MS, + uint16_t requestedDurationMs = 0); + /// @brief Put the ESP32 into deep sleep mode, without a method to wake up + /// again. Basically this is a shutdown. + void beginPermanentDeepSleep(void); + + //// @brief Get currently granted power + /// @return the amount of power that is currently allocated (in mA) + uint16_t getCurrentCurrent(void); + + /// @brief Power consumer data structure + struct CurrentAllowance { + PowerParameters::PowerConsumers consumer; + uint16_t maxSlackTimeMs; + uint16_t requestedDurationMs; + TaskHandle_t taskHandle; + uint16_t neededCurrent; + TickType_t requestedAt; + TickType_t startedAt; + bool granted; + }; + + // @brief waiting task wakeup reasons + enum PowerWakeupReasons { + POWER_AVAILABLE = 1, + POWER_EXPIRED = 2, + }; protected: - static const uint16_t totalPowerBudget = TOTAL_POWER_MILLIWATTS; - uint16_t freePowerBudget; - std::queue waitingTasks; - void checkWaitingTasks(void); - bool takePowerIfAvailable(uint16_t neededPower); + // Current above which there will be no new scheduling + uint16_t limitCurrent; + // Absolute maximum current that can be allocated + uint16_t maximumCurrent; + + // Current budget that is currently available to limitCurrent + int16_t freeLimitCurrentBudget; + // Current budget that is currently available to maximumCurrent + int16_t freeMaximumCurrentBudget; + + // @brief Responsible for selecting the next task to be granted power + void checkWaitingTasks(void); + + // @brief Responsible for recalculating the current budgets + void recalculateCurrentBudgets(void); + // @brief Retrieve the current allowance for a given consumer + CurrentAllowance * + getCurrentAllowance(PowerParameters::PowerConsumers consumer); + // @brief Retrieve the current allowance for a given task + CurrentAllowance *getCurrentAllowance(TaskHandle_t taskHandle); + + // @brief Retrieve the allowance that will expire next + CurrentAllowance *getNextExpiringAllowance(void); + + // @brief Mutex to protect the power scheduler from concurrent access + static portMUX_TYPE mux; + + std::vector currentAllowances; }; -PowerScheduler::PowerScheduler(/* args */) -{ - // TODO: Create wrappper around all this which handles single-instancing so we look like a normal arduino library from the outside. - this->freePowerBudget = TOTAL_POWER_MILLIWATTS; -} -PowerScheduler::~PowerScheduler() -{ -} static PowerScheduler *powerSchedulerInstance;