mirror of
https://gitlab.dit.htwk-leipzig.de/phillip.kuehne/dezibot.git
synced 2025-05-21 20:11:46 +02:00
Added wrapper for PowerScheduler
This commit is contained in:
parent
915ad85526
commit
fbe205035e
@ -9,6 +9,7 @@ Dezibot::Dezibot() : multiColorLight() {};
|
|||||||
|
|
||||||
void Dezibot::begin(void)
|
void Dezibot::begin(void)
|
||||||
{
|
{
|
||||||
|
Power.begin();
|
||||||
Wire.begin(SDA_PIN, SCL_PIN);
|
Wire.begin(SDA_PIN, SCL_PIN);
|
||||||
infraredLight.begin();
|
infraredLight.begin();
|
||||||
lightDetection.begin();
|
lightDetection.begin();
|
||||||
@ -17,10 +18,4 @@ void Dezibot::begin(void)
|
|||||||
colorDetection.begin();
|
colorDetection.begin();
|
||||||
multiColorLight.begin();
|
multiColorLight.begin();
|
||||||
display.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";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -53,7 +53,7 @@ class Motor{
|
|||||||
uint8_t pin;
|
uint8_t pin;
|
||||||
ledc_timer_t timer;
|
ledc_timer_t timer;
|
||||||
ledc_channel_t channel;
|
ledc_channel_t channel;
|
||||||
Power powerManager;
|
Power* powerManager;
|
||||||
uint16_t duty;
|
uint16_t duty;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ Motor::Motor(uint8_t pin, ledc_timer_t timer, ledc_channel_t channel){
|
|||||||
this->channel = channel;
|
this->channel = channel;
|
||||||
this->timer = timer;
|
this->timer = timer;
|
||||||
this->duty = 0;
|
this->duty = 0;
|
||||||
this->powerManager = *Power::getPowerManager();
|
this->powerManager =
|
||||||
};
|
};
|
||||||
|
|
||||||
void Motor::begin(void){
|
void Motor::begin(void){
|
||||||
@ -25,10 +25,10 @@ void Motor::begin(void){
|
|||||||
|
|
||||||
void Motor::setSpeed(uint16_t duty){
|
void Motor::setSpeed(uint16_t duty){
|
||||||
if(duty>0) {
|
if(duty>0) {
|
||||||
powerManager.waitForPowerAllowance(CONSUMPTION_MOTOR, portMAX_DELAY);
|
powerManager->waitForPowerAllowance(CONSUMPTION_MOTOR, portMAX_DELAY);
|
||||||
Serial.println("Motor got power");
|
Serial.println("Motor got power");
|
||||||
} else {
|
} else {
|
||||||
powerManager.releasePower(CONSUMPTION_MOTOR);
|
powerManager->releasePower(CONSUMPTION_MOTOR);
|
||||||
Serial.println("Motor released power");
|
Serial.println("Motor released power");
|
||||||
}
|
}
|
||||||
int difference = duty-this->getSpeed();
|
int difference = duty-this->getSpeed();
|
||||||
|
@ -10,114 +10,41 @@
|
|||||||
#include "Power.h"
|
#include "Power.h"
|
||||||
static portMUX_TYPE mux;
|
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()
|
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
|
powerScheduler = PowerScheduler::getPowerScheduler();
|
||||||
taskENTER_CRITICAL(&mux);
|
if (!(powerScheduler->tryAccquirePowerAllowance(CONSUMPTION_ESP_BASE)))
|
||||||
if (powerInstance == nullptr)
|
|
||||||
{
|
{
|
||||||
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)
|
bool Power::tryAccquirePowerAllowance(uint16_t neededPower)
|
||||||
{
|
{
|
||||||
if (this->freePowerBudget >= neededPower)
|
return powerScheduler->tryAccquirePowerAllowance(neededPower);
|
||||||
{
|
}
|
||||||
this->freePowerBudget -= neededPower;
|
|
||||||
return true;
|
bool Power::waitForPowerAllowance(uint16_t neededPower, TickType_t TicksToWait)
|
||||||
}
|
{
|
||||||
else
|
return powerScheduler->waitForPowerAllowance(neededPower, TicksToWait);
|
||||||
{
|
}
|
||||||
return false;
|
|
||||||
}
|
void Power::beginPermanentDeepSleep(void)
|
||||||
|
{
|
||||||
|
return powerScheduler->beginPermanentDeepSleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Power::releasePower(uint16_t power)
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,40 +4,27 @@
|
|||||||
* @brief This component provides utilities for keeping track of power usage
|
* @brief This component provides utilities for keeping track of power usage
|
||||||
* consumption.
|
* consumption.
|
||||||
* @version 0.1
|
* @version 0.1
|
||||||
* @date 2024-11-23
|
* @date 2024-11-23
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <queue>
|
#include "PowerScheduler.h"
|
||||||
#include <Arduino.h>
|
|
||||||
#include "Consumptions.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
|
|
||||||
#ifndef Power_h
|
#ifndef Power_h
|
||||||
#define Power_h
|
#define Power_h
|
||||||
|
|
||||||
#define TOTAL_POWER_MILLIWATTS POWER_BUDGET
|
#define TOTAL_POWER_MILLIWATTS POWER_BUDGET
|
||||||
|
|
||||||
enum TaskResumptionReason {
|
enum TaskResumptionReason
|
||||||
|
{
|
||||||
POWER_AVAILABLE,
|
POWER_AVAILABLE,
|
||||||
TIMEOUT
|
TIMEOUT
|
||||||
};
|
};
|
||||||
|
|
||||||
class Power {
|
class Power
|
||||||
protected:
|
{
|
||||||
static const uint16_t totalPowerBudget = TOTAL_POWER_MILLIWATTS;
|
|
||||||
uint16_t freePowerBudget;
|
|
||||||
std::queue<TaskHandle_t> waitingTasks;
|
|
||||||
void checkWaitingTasks(void);
|
|
||||||
bool takePowerIfAvailable(uint16_t neededPower);
|
|
||||||
static Power* powerInstance;
|
|
||||||
Power();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void begin();
|
static void begin();
|
||||||
/// @brief Initialize the singleton instance of the power manager
|
Power();
|
||||||
/// @return reference to the power manager
|
|
||||||
static Power *getPowerManager();
|
|
||||||
uint16_t getFreePowerBudget(void);
|
uint16_t getFreePowerBudget(void);
|
||||||
/// @brief Request an allowance of a certain number of milliwatts from the power scheduler
|
/// @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)
|
/// @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 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
|
/// @param TicksToWait the amount of time to wait for the power to become available
|
||||||
/// @return whether the power could be successfully allocatedy
|
/// @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.
|
/// @brief Put the ESP32 into deep sleep mode, without a method to wake up again. Basically this is a shutdown.
|
||||||
void beginPermanentDeepSleep(void);
|
void beginPermanentDeepSleep(void);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static PowerScheduler *powerScheduler;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //Power
|
#endif // Power
|
109
src/power/PowerScheduler.cpp
Normal file
109
src/power/PowerScheduler.cpp
Normal file
@ -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;
|
||||||
|
}
|
71
src/power/PowerScheduler.h
Normal file
71
src/power/PowerScheduler.h
Normal file
@ -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 <queue>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#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<TaskHandle_t> 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
|
Loading…
x
Reference in New Issue
Block a user