🤖 Moves esp32 project to own project folder
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
#pragma once
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
|
||||
// Stack size for the taskmgr's idle threads
|
||||
#define IDLE_STACK_SIZE 2048
|
||||
#define DEFAULT_STACK_SIZE 2048+512
|
||||
|
||||
class IdleTask
|
||||
{
|
||||
private:
|
||||
|
||||
float _idleRatio = 0;
|
||||
unsigned long _lastMeasurement;
|
||||
|
||||
const int kMillisPerLoop = 1;
|
||||
const int kMillisPerCalc = 1000;
|
||||
|
||||
unsigned long counter = 0;
|
||||
|
||||
public:
|
||||
|
||||
void ProcessIdleTime()
|
||||
{
|
||||
_lastMeasurement = millis();
|
||||
counter = 0;
|
||||
|
||||
// We need to whack the watchdog so we delay in smalle bites until we've used up all the time
|
||||
|
||||
while (true)
|
||||
{
|
||||
int delta = millis() - _lastMeasurement;
|
||||
if (delta >= kMillisPerCalc)
|
||||
{
|
||||
//Serial.printf("Core %u Spent %lu in delay during a window of %d for a ratio of %f\n",
|
||||
// xPortGetCoreID(), counter, delta, (float)counter/delta);
|
||||
_idleRatio = ((float) counter / delta);
|
||||
_lastMeasurement = millis();
|
||||
counter = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
esp_task_wdt_reset();
|
||||
delayMicroseconds(kMillisPerLoop*1000);
|
||||
counter += kMillisPerLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If idle time is spent elsewhere, it can be credited to this task. Shouldn't add up to more time than actual though!
|
||||
|
||||
void CountBonusIdleMillis(uint millis)
|
||||
{
|
||||
counter += millis;
|
||||
}
|
||||
|
||||
IdleTask() : _lastMeasurement(millis())
|
||||
{
|
||||
}
|
||||
|
||||
// GetCPUUsage
|
||||
//
|
||||
// Returns 100 less the amount of idle time that we were able to squander.
|
||||
|
||||
float GetCPUUsage() const
|
||||
{
|
||||
// If the measurement failed to even get a chance to run, this core is maxed and there was no idle time
|
||||
|
||||
if (millis() - _lastMeasurement > kMillisPerCalc)
|
||||
return 100.0f;
|
||||
|
||||
// Otherwise, whatever cycles we were able to burn in the idle loop counts as "would have been idle" time
|
||||
return 100.0f-100*_idleRatio;
|
||||
}
|
||||
|
||||
// Stub entry point for calling into it without a THIS pointer
|
||||
|
||||
static void IdleTaskEntry(void * that)
|
||||
{
|
||||
IdleTask * pTask = (IdleTask *)that;
|
||||
pTask->ProcessIdleTime();
|
||||
}
|
||||
};
|
||||
|
||||
// TaskManager
|
||||
//
|
||||
// TaskManager runs two tasks at just over idle priority that do nothing but try to burn CPU, and they
|
||||
// keep track of how much they can burn. It's assumed that everything else runs at a higher priority
|
||||
// and thus they "starve" the idle tasks when doing work.
|
||||
|
||||
class TaskManager
|
||||
{
|
||||
TaskHandle_t _hIdle0 = nullptr;
|
||||
TaskHandle_t _hIdle1 = nullptr;
|
||||
|
||||
IdleTask _taskIdle0;
|
||||
IdleTask _taskIdle1;
|
||||
|
||||
public:
|
||||
|
||||
float GetCPUUsagePercent(int iCore = -1) const
|
||||
{
|
||||
if (iCore < 0)
|
||||
return (_taskIdle0.GetCPUUsage() + _taskIdle1.GetCPUUsage()) / 2;
|
||||
else if (iCore == 0)
|
||||
return _taskIdle0.GetCPUUsage();
|
||||
else if (iCore == 1)
|
||||
return _taskIdle1.GetCPUUsage();
|
||||
else
|
||||
throw new std::runtime_error("Invalid core passed to GetCPUUsagePercentCPU");
|
||||
}
|
||||
|
||||
TaskManager() {}
|
||||
|
||||
void begin()
|
||||
{
|
||||
Serial.printf("Replacing Idle Tasks with TaskManager...\n");
|
||||
// The idle tasks get created with a priority just ABOVE idle so that they steal idle time but nothing else. They then
|
||||
// measure how much time is "wasted" at that lower priority and deem it to have been free CPU
|
||||
|
||||
xTaskCreatePinnedToCore(_taskIdle0.IdleTaskEntry, "Idle0", IDLE_STACK_SIZE, &_taskIdle0, tskIDLE_PRIORITY + 1, &_hIdle0, 0);
|
||||
xTaskCreatePinnedToCore(_taskIdle1.IdleTaskEntry, "Idle1", IDLE_STACK_SIZE, &_taskIdle1, tskIDLE_PRIORITY + 1, &_hIdle1, 1);
|
||||
|
||||
// We need to turn off the watchdogs because our idle measurement tasks burn all of the idle time just
|
||||
// to see how much there is (it's how they measure free CPU). Thus, we starve the system's normal idle tasks
|
||||
// and have to feed the watchdog on our own.
|
||||
|
||||
esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0));
|
||||
esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(1));
|
||||
esp_task_wdt_add(_hIdle0);
|
||||
esp_task_wdt_add(_hIdle1);
|
||||
}
|
||||
};
|
||||
|
||||
void IRAM_ATTR NetworkHandlingLoopEntry(void *);
|
||||
void IRAM_ATTR JSONWriterTaskEntry(void *);
|
||||
void IRAM_ATTR MovementHandlingLoopEntry(void *);
|
||||
|
||||
#define DELETE_TASK(handle) if (handle != nullptr) vTaskDelete(handle)
|
||||
|
||||
class ESPTaskManager : public TaskManager
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
TaskHandle_t _taskNetwork = nullptr;
|
||||
TaskHandle_t _taskMovement = nullptr;
|
||||
TaskHandle_t _taskJSONWriter = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
~ESPTaskManager()
|
||||
{
|
||||
DELETE_TASK(_taskNetwork);
|
||||
DELETE_TASK(_taskMovement);
|
||||
DELETE_TASK(_taskJSONWriter);
|
||||
}
|
||||
|
||||
void StartThreads(){
|
||||
StartNetworkThread();
|
||||
StartMovementThread();
|
||||
StartJSONWriterThread();
|
||||
}
|
||||
|
||||
void StartNetworkThread()
|
||||
{
|
||||
#if USE_WIFI
|
||||
log_i( ">> Launching Network Thread. Mem: %u, LargestBlk: %u, PSRAM Free: %u/%u, ", ESP.getFreeHeap(),ESP.getMaxAllocHeap(), ESP.getFreePsram(), ESP.getPsramSize());
|
||||
xTaskCreatePinnedToCore(NetworkHandlingLoopEntry, "NetworkHandlingLoop", STACK_SIZE, nullptr, NET_PRIORITY, &_taskNetwork, NET_CORE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void StartMovementThread()
|
||||
{
|
||||
log_i(">> Launching Movement Thread");
|
||||
xTaskCreatePinnedToCore(MovementHandlingLoopEntry, "MovementHandlingLoop", STACK_SIZE, nullptr, MOVEMENT_PRIORITY, &_taskMovement, MOVEMENT_CORE);
|
||||
}
|
||||
|
||||
void StartJSONWriterThread()
|
||||
{
|
||||
log_i(">> Launching JSON Writer Thread");
|
||||
xTaskCreatePinnedToCore(JSONWriterTaskEntry, "JSON Writer Loop", STACK_SIZE, nullptr, JSONWRITER_PRIORITY, &_taskJSONWriter, JSONWRITER_CORE);
|
||||
}
|
||||
|
||||
void NotifyJSONWriterThread()
|
||||
{
|
||||
if (_taskJSONWriter == nullptr)
|
||||
return;
|
||||
|
||||
log_w(">> Notifying JSON Writer Thread");
|
||||
// Wake up the writer invoker task if it's sleeping, or request another write cycle if it isn't
|
||||
xTaskNotifyGive(_taskJSONWriter);
|
||||
}
|
||||
|
||||
void NotifyNetworkThread()
|
||||
{
|
||||
if (_taskNetwork == nullptr)
|
||||
return;
|
||||
|
||||
//debugW(">> Notifying Network Thread");
|
||||
// Wake up the network task if it's sleeping, or request another read cycle if it isn't
|
||||
xTaskNotifyGive(_taskNetwork);
|
||||
}
|
||||
|
||||
void NotifyMovementThread()
|
||||
{
|
||||
if (_taskMovement == nullptr)
|
||||
return;
|
||||
|
||||
// Wake up the movement task if it's sleeping, or request another read cycle if it isn't
|
||||
xTaskNotifyGive(_taskMovement);
|
||||
}
|
||||
};
|
||||
|
||||
extern ESPTaskManager g_TaskManager;
|
||||
Reference in New Issue
Block a user