diff --git a/example/advanced/Power_Measurements/WLAN/WLAN.ino b/example/advanced/Power_Measurements/WLAN/WLAN.ino index ece11b9..26b10a4 100644 --- a/example/advanced/Power_Measurements/WLAN/WLAN.ino +++ b/example/advanced/Power_Measurements/WLAN/WLAN.ino @@ -3,12 +3,14 @@ Dezibot dezibot = Dezibot(); +const uint16_t cycleTime = 5e3; //5000 ms = 5 s + void setup() { + #ifdef DEBUG Serial.begin(112500); while (!Serial) { ; /* Wait for USB-CDC Serial init to complete. */ } - #ifdef DEBUG dezibot.display.begin(); dezibot.display.println("Debug enabled."); Serial.println("Debug enabled."); @@ -31,6 +33,10 @@ void handle_receive(String &message) { void loop() { /* Continuously send to consume power on TX */ - dezibot.communication.sendMessage("Power Test Message"); + for(int i=0; i +Dezibot::Dezibot() : multiColorLight() {}; -Dezibot::Dezibot():multiColorLight(){}; - -void Dezibot::begin(void) { - Wire.begin(SDA_PIN,SCL_PIN); +void Dezibot::begin(void) +{ + Wire.begin(SDA_PIN, SCL_PIN); infraredLight.begin(); - lightDetection.begin(); + lightDetection.begin(); motion.begin(); lightDetection.begin(); 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/Dezibot.h b/src/Dezibot.h index 8dcacf5..4d4274c 100644 --- a/src/Dezibot.h +++ b/src/Dezibot.h @@ -19,6 +19,7 @@ #include "infraredLight/InfraredLight.h" #include "communication/Communication.h" #include "display/Display.h" +#include "power/Power.h" class Dezibot { @@ -33,6 +34,7 @@ public: InfraredLight infraredLight; Communication communication; Display display; + Power power; void begin(void); }; diff --git a/src/motion/Motion.h b/src/motion/Motion.h index 2a7ed8a..69776d7 100644 --- a/src/motion/Motion.h +++ b/src/motion/Motion.h @@ -17,6 +17,7 @@ #include #include "driver/ledc.h" #include "motionDetection/MotionDetection.h" +#include "power/Power.h" #define LEDC_MODE LEDC_LOW_SPEED_MODE #define TIMER LEDC_TIMER_2 #define CHANNEL_LEFT LEDC_CHANNEL_3 @@ -52,7 +53,7 @@ class Motor{ uint8_t pin; ledc_timer_t timer; ledc_channel_t channel; - + Power powerManager; uint16_t duty; }; diff --git a/src/motion/Motor.cpp b/src/motion/Motor.cpp index 0c26a58..da0b7c2 100644 --- a/src/motion/Motor.cpp +++ b/src/motion/Motor.cpp @@ -5,6 +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(); }; void Motor::begin(void){ @@ -23,7 +24,13 @@ void Motor::begin(void){ }; void Motor::setSpeed(uint16_t duty){ - + if(duty>0) { + powerManager.waitForPowerAllowance(CONSUMPTION_MOTOR, portMAX_DELAY); + Serial.println("Motor got power"); + } else { + powerManager.releasePower(CONSUMPTION_MOTOR); + Serial.println("Motor released power"); + } int difference = duty-this->getSpeed(); if (difference > 0){ for(int i = 0;ifree_power_budget = this->total_power_budget; + this->freePowerBudget = this->totalPowerBudget; +} + +void Power::begin() +{ + if (powerInstance == nullptr) + { + // Double check locking https://www.aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf + taskENTER_CRITICAL(&mux); + if (powerInstance == nullptr) + { + powerInstance = new Power(); + } + 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() +{ + return powerInstance; } bool Power::tryAccquirePowerAllowance(uint16_t neededPower) { - if (this->free_power_budget >= neededPower) + if (this->freePowerBudget >= neededPower) { - this->free_power_budget -= neededPower; + this->freePowerBudget -= neededPower; return true; } else @@ -29,12 +54,69 @@ bool Power::tryAccquirePowerAllowance(uint16_t neededPower) void Power::releasePower(uint16_t power) { - if (this->free_power_budget + power <= this->total_power_budget) + if (this->freePowerBudget + power <= this->totalPowerBudget) { - this->free_power_budget += power; + this->freePowerBudget += power; } else // TODO: Maybe we should actually throw an error here, since obviouslsy someone used us wrong. { - this->free_power_budget = this->total_power_budget; + 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 b4b64f9..5451a38 100644 --- a/src/power/Power.h +++ b/src/power/Power.h @@ -7,29 +7,54 @@ * @date 2024-11-23 */ +#include #include +#include "Consumptions.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #ifndef Power_h #define Power_h -#define TOTAL_POWER_MILLIWATTS 700 +#define TOTAL_POWER_MILLIWATTS POWER_BUDGET + +enum TaskResumptionReason { + POWER_AVAILABLE, + TIMEOUT +}; class Power { protected: - static const uint16_t total_power_budget = TOTAL_POWER_MILLIWATTS; - uint16_t free_power_budget; + 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(); public: - Power(); + static void begin(); + /// @brief Initialize the singleton instance of the power manager + /// @return reference to the power manager + static Power *getPowerManager(); 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 power the amount of power to return (in mW) + /// @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); }; #endif //Power \ No newline at end of file