// Inclui o cabeçalho principal do Arduino, que já prepara o ambiente FreeRTOS no ESP32
#include <Arduino.h>
// --- Protótipos das Tarefas ---
void vTaskBlink(void *pvParameters);
void vTaskWorkload(void *pvParameters);
void vTaskDiagnostics(void *pvParameters);
// Handle para a tarefa que será suspensa/retomada
TaskHandle_t xWorkloadTaskHandle = NULL;
// Função para converter o estado da tarefa (enum) em uma string legível
const char* getTaskStateString(eTaskState state) {
switch (state) {
case eRunning: return "Executando";
case eReady: return "Pronta";
case eBlocked: return "Bloqueada";
case eSuspended: return "Suspensa";
case eDeleted: return "Excluida";
default: return "Desconhecido";
}
}
//*****************************************************************************
// FUNÇÃO SETUP - EXECUTADA UMA VEZ
//*****************************************************************************
void setup() {
// 1. Inicializa a comunicação serial com uma taxa de 115200 bps
Serial.begin(115200);
delay(1000); // Pequeno atraso para o monitor serial estabilizar
Serial.println("--- Iniciando o sistema e criando tarefas FreeRTOS ---");
// 2. Cria as tarefas. Para o ESP32, é uma boa prática fixá-las em um núcleo.
// O núcleo 1 (APP_CPU) é geralmente usado para tarefas de aplicação.
xTaskCreatePinnedToCore(
vTaskBlink, // Função da tarefa
"TaskBlink", // Nome da tarefa
1536, // Tamanho da pilha (stack)
NULL, // Parâmetros da tarefa
1, // Prioridade
NULL, // Handle da tarefa
1); // Núcleo onde a tarefa será executada (0 ou 1)
xTaskCreatePinnedToCore(
vTaskWorkload,
"TaskWorkload",
2048,
NULL,
2,
&xWorkloadTaskHandle, // Passa o endereço do handle para controlá-la
1);
xTaskCreatePinnedToCore(
vTaskDiagnostics,
"TaskDiagnostics", // Nome
4096, // Mais pilha para os buffers de log
NULL, // Parâmetros
3, // Prioridade mais alta para garantir que rode
NULL, // Handle
1); // Núcleo
Serial.println("--- Tarefas criadas com sucesso! ---");
}
//*****************************************************************************
// FUNÇÃO LOOP - PODE FICAR VAZIA
//*****************************************************************************
void loop() {
// O loop principal pode ficar vazio. O escalonador do FreeRTOS está no controle
// e gerenciará a execução das tarefas que criamos.
// A tarefa do loop() ainda roda com prioridade 1 no núcleo 1,
// então um vTaskDelay aqui evita que ela consuma CPU desnecessariamente.
vTaskDelay(pdMS_TO_TICKS(1000));
}
//*****************************************************************************
// IMPLEMENTAÇÃO DAS TAREFAS
//*****************************************************************************
/**
* @brief Tarefa simples que imprime uma mensagem a cada 2 segundos.
*/
void vTaskBlink(void *pvParameters) {
for (;;) {
Serial.println("[Blink] Executando...");
// Fica bloqueada por 2 segundos
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
/**
* @brief Tarefa que simula uma carga de trabalho e depois se suspende.
*/
void vTaskWorkload(void *pvParameters) {
for (;;) {
Serial.println("[Workload] Iniciando trabalho pesado...");
// Simula um trabalho que leva tempo (sem usar delay)
for (volatile int i = 0; i < 3000000; i++);
Serial.println("[Workload] Trabalho concluído. Suspendendo...");
vTaskSuspend(NULL); // Suspende a si mesma
// O código abaixo só será executado quando a tarefa for retomada
Serial.println("[Workload] Fui retomada!");
}
}
/**
* @brief Tarefa de diagnóstico que imprime o estado do sistema a cada 10 segundos.
*/
void vTaskDiagnostics(void *pvParameters) {
// Buffer para a saída de vTaskList()
static char pcTaskListBuffer[512];
// Aguarda um pouco para as outras tarefas iniciarem
vTaskDelay(pdMS_TO_TICKS(1000));
for (;;) {
Serial.println("\n======================================================");
Serial.println(" GERANDO RELATÓRIO DE DIAGNÓSTICO");
Serial.println("======================================================");
// --- 1. Usando vTaskList() ---
Serial.println("\n--- Saída de vTaskList() ---");
vTaskList(pcTaskListBuffer); // Gera a tabela de tarefas formatada
Serial.println("Nome Estado Prio PilhaLivre ID");
Serial.println("--------------------------------------------");
Serial.print(pcTaskListBuffer);
Serial.println("--------------------------------------------");
// --- 2. Usando uxTaskGetSystemState() ---
Serial.println("\n--- Saída de uxTaskGetSystemState() (Processada) ---");
TaskStatus_t *pxTaskStatusArray;
volatile UBaseType_t uxArraySize;
uint32_t ulTotalRunTime;
uxArraySize = uxTaskGetNumberOfTasks();
pxTaskStatusArray = (TaskStatus_t *)pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
if (pxTaskStatusArray != NULL) {
uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime);
Serial.println("Nome | Estado | Prio | PilhaLivre | ID");
Serial.println("------------------------------------------------------");
for (UBaseType_t i = 0; i < uxArraySize; i++) {
char buffer[150];
sprintf(buffer, "%-15s| %-11s| %-5lu| %-11u| %lu",
pxTaskStatusArray[i].pcTaskName,
getTaskStateString(pxTaskStatusArray[i].eCurrentState),
pxTaskStatusArray[i].uxCurrentPriority,
pxTaskStatusArray[i].usStackHighWaterMark,
pxTaskStatusArray[i].xTaskNumber);
Serial.println(buffer);
}
vPortFree(pxTaskStatusArray);
} else {
Serial.println("Falha ao alocar memória para o array de status!");
}
// Após o primeiro relatório, retoma a tarefa 'Workload' para o próximo ciclo
if (xWorkloadTaskHandle != NULL && eTaskGetState(xWorkloadTaskHandle) == eSuspended) {
Serial.println("\n>>> Retomando a TaskWorkload para o próximo ciclo...");
vTaskResume(xWorkloadTaskHandle);
}
// Aguarda 10 segundos antes de gerar o próximo relatório
vTaskDelay(pdMS_TO_TICKS(10000));
}
}