From fbe205035e4075b8baead2c036799301bb0a87d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phillip=20K=C3=BChne?= Date: Sun, 22 Dec 2024 22:07:54 +0100 Subject: [PATCH] Added wrapper for PowerScheduler --- src/Dezibot.cpp | 7 +-- src/motion/Motion.h | 2 +- src/motion/Motor.cpp | 6 +- src/power/Power.cpp | 113 +++++++---------------------------- src/power/Power.h | 38 +++++------- src/power/PowerScheduler.cpp | 109 +++++++++++++++++++++++++++++++++ src/power/PowerScheduler.h | 71 ++++++++++++++++++++++ 7 files changed, 219 insertions(+), 127 deletions(-) create mode 100644 src/power/PowerScheduler.cpp create mode 100644 src/power/PowerScheduler.h diff --git a/src/Dezibot.cpp b/src/Dezibot.cpp index 7008351..6c9e9c4 100644 --- a/src/Dezibot.cpp +++ b/src/Dezibot.cpp @@ -9,6 +9,7 @@ Dezibot::Dezibot() : multiColorLight() {}; void Dezibot::begin(void) { + Power.begin(); Wire.begin(SDA_PIN, SCL_PIN); infraredLight.begin(); lightDetection.begin(); @@ -17,10 +18,4 @@ void Dezibot::begin(void) colorDetection.begin(); multiColorLight.begin(); display.begin(); - Power.begin(); - this->power = Power::getPowerManager(); - if (!this->power.tryAccquirePowerAllowance(CONSUMPTION_ESP_BASE);) - { - throw "Could not allocate power for the base consumption of the ESP32"; - } }; diff --git a/src/motion/Motion.h b/src/motion/Motion.h index 69776d7..daf70c0 100644 --- a/src/motion/Motion.h +++ b/src/motion/Motion.h @@ -53,7 +53,7 @@ class Motor{ uint8_t pin; ledc_timer_t timer; ledc_channel_t channel; - Power powerManager; + Power* powerManager; uint16_t duty; }; diff --git a/src/motion/Motor.cpp b/src/motion/Motor.cpp index da0b7c2..7c629b2 100644 --- a/src/motion/Motor.cpp +++ b/src/motion/Motor.cpp @@ -5,7 +5,7 @@ Motor::Motor(uint8_t pin, ledc_timer_t timer, ledc_channel_t channel){ this->channel = channel; this->timer = timer; this->duty = 0; - this->powerManager = *Power::getPowerManager(); + this->powerManager = }; void Motor::begin(void){ @@ -25,10 +25,10 @@ void Motor::begin(void){ void Motor::setSpeed(uint16_t duty){ if(duty>0) { - powerManager.waitForPowerAllowance(CONSUMPTION_MOTOR, portMAX_DELAY); + powerManager->waitForPowerAllowance(CONSUMPTION_MOTOR, portMAX_DELAY); Serial.println("Motor got power"); } else { - powerManager.releasePower(CONSUMPTION_MOTOR); + powerManager->releasePower(CONSUMPTION_MOTOR); Serial.println("Motor released power"); } int difference = duty-this->getSpeed(); diff --git a/src/power/Power.cpp b/src/power/Power.cpp index 0a47b16..29a44ff 100644 --- a/src/power/Power.cpp +++ b/src/power/Power.cpp @@ -10,114 +10,41 @@ #include "Power.h" static portMUX_TYPE mux; -Power::Power() -{ - // TODO: Create wrappper around all this which handles single-instancing so we look like a normal arduino library from the outside. - this->freePowerBudget = this->totalPowerBudget; -} - void Power::begin() { - if (powerInstance == nullptr) + // Check if another instance of us already initialized the power scheduler, + // if not, we will do it. + if (powerScheduler == nullptr) { - // Double check locking https://www.aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf - taskENTER_CRITICAL(&mux); - if (powerInstance == nullptr) + powerScheduler = PowerScheduler::getPowerScheduler(); + if (!(powerScheduler->tryAccquirePowerAllowance(CONSUMPTION_ESP_BASE))) { - powerInstance = new Power(); + Serial.println("Alledgedly not enough power available to reserve the ESP32s base power consumption. Something is wrong."); + return; } - taskEXIT_CRITICAL(&mux); - } - else - { - // Power.begin() was called twice! - Serial.println("Power.begin() was called twice! No harm done, but this is a bug."); } } -Power *Power::getPowerManager() +uint16_t Power::getFreePowerBudget(void) { - return powerInstance; + return powerScheduler->getFreePowerBudget(); } bool Power::tryAccquirePowerAllowance(uint16_t neededPower) { - if (this->freePowerBudget >= neededPower) - { - this->freePowerBudget -= neededPower; - return true; - } - else - { - return false; - } + return powerScheduler->tryAccquirePowerAllowance(neededPower); +} + +bool Power::waitForPowerAllowance(uint16_t neededPower, TickType_t TicksToWait) +{ + return powerScheduler->waitForPowerAllowance(neededPower, TicksToWait); +} + +void Power::beginPermanentDeepSleep(void) +{ + return powerScheduler->beginPermanentDeepSleep(); } void Power::releasePower(uint16_t power) { - if (this->freePowerBudget + power <= this->totalPowerBudget) - { - this->freePowerBudget += power; - } - 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(); -} - -bool Power::waitForPowerAllowance(uint16_t neededPower, TickType_t ticksToWait) -{ - if (tryAccquirePowerAllowance(neededPower)) - { - return true; - } - 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"; - } - } -} - -void Power::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); - } } diff --git a/src/power/Power.h b/src/power/Power.h index 5451a38..6e885c3 100644 --- a/src/power/Power.h +++ b/src/power/Power.h @@ -4,40 +4,27 @@ * @brief This component provides utilities for keeping track of power usage * consumption. * @version 0.1 - * @date 2024-11-23 + * @date 2024-11-23 */ -#include -#include -#include "Consumptions.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - +#include "PowerScheduler.h" #ifndef Power_h #define Power_h -#define TOTAL_POWER_MILLIWATTS POWER_BUDGET +#define TOTAL_POWER_MILLIWATTS POWER_BUDGET -enum TaskResumptionReason { +enum TaskResumptionReason +{ POWER_AVAILABLE, TIMEOUT }; -class Power { - protected: - static const uint16_t totalPowerBudget = TOTAL_POWER_MILLIWATTS; - uint16_t freePowerBudget; - std::queue waitingTasks; - void checkWaitingTasks(void); - bool takePowerIfAvailable(uint16_t neededPower); - static Power* powerInstance; - Power(); +class Power +{ - public: +public: static void begin(); - /// @brief Initialize the singleton instance of the power manager - /// @return reference to the power manager - static Power *getPowerManager(); + 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) @@ -52,9 +39,12 @@ class Power { /// @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); + 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); + +protected: + static PowerScheduler *powerScheduler; }; -#endif //Power \ No newline at end of file +#endif // Power \ No newline at end of file diff --git a/src/power/PowerScheduler.cpp b/src/power/PowerScheduler.cpp new file mode 100644 index 0000000..b365bc4 --- /dev/null +++ b/src/power/PowerScheduler.cpp @@ -0,0 +1,109 @@ +/** + * @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. + * @version 0.1 + * @date 2024-12-21 + * + * @copyright (c) 2024 + * + */ + +#include "PowerScheduler.h" + +bool PowerScheduler::tryAccquirePowerAllowance(uint16_t neededPower) +{ + if (this->freePowerBudget >= neededPower) + { + this->freePowerBudget -= neededPower; + return true; + } + else + { + return false; + } +} + +void PowerScheduler::releasePower(uint16_t power) +{ + if (this->freePowerBudget + power <= this->totalPowerBudget) + { + this->freePowerBudget += power; + } + 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(); +} + +bool PowerScheduler::waitForPowerAllowance(uint16_t neededPower, TickType_t ticksToWait) +{ + if (tryAccquirePowerAllowance(neededPower)) + { + return true; + } + 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"; + } + } +} + +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); + } +} + +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); + } + return powerSchedulerInstance; +} \ No newline at end of file diff --git a/src/power/PowerScheduler.h b/src/power/PowerScheduler.h new file mode 100644 index 0000000..236430e --- /dev/null +++ b/src/power/PowerScheduler.h @@ -0,0 +1,71 @@ +/** + * @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. + * @version 0.1 + * @date 2024-12-21 + * + * @copyright (c) 2024 + * + */ + +#include +#include +#include "Consumptions.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#ifndef PowerScheduler_h +#define PowerScheduler_h + +#define TOTAL_POWER_MILLIWATTS POWER_BUDGET + +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); + + /// @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); + +protected: + static const uint16_t totalPowerBudget = TOTAL_POWER_MILLIWATTS; + uint16_t freePowerBudget; + std::queue waitingTasks; + void checkWaitingTasks(void); + bool takePowerIfAvailable(uint16_t neededPower); +}; + +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; + +#endif // PowerScheduler_h \ No newline at end of file