// Handles para as tarefas
TaskHandle_t handleTask1 = NULL;
TaskHandle_t handleTask2 = NULL;
TaskHandle_t handleTask3 = NULL;
// Handles para os Mutexes que causarão o deadlock
SemaphoreHandle_t mutexA;
SemaphoreHandle_t mutexB;
// Protótipos das Tarefas
void taskBaixaPrioridade(void *parameter);
void taskMediaPrioridade(void *parameter);
void taskAltaPrioridade(void *parameter);
void taskMonitor(void *parameter);
void setup() {
Serial.begin(115200);
Serial.println("\n--- Exemplo de Debug com FreeRTOS no ESP32 ---");
// Criação dos mutexes
mutexA = xSemaphoreCreateMutex();
mutexB = xSemaphoreCreateMutex();
if (mutexA == NULL || mutexB == NULL) {
Serial.println("Falha ao criar os mutexes. Travando...");
while(1);
}
// Criação das tarefas com diferentes prioridades
// Assinatura: xTaskCreate(função, nome, tam_pilha, param, prioridade, handle)
xTaskCreate(taskBaixaPrioridade, "Task_Baixa_Prio", 2048, NULL, 1, &handleTask1);
xTaskCreate(taskMediaPrioridade, "Task_Media_Prio", 2048, NULL, 2, &handleTask2);
xTaskCreate(taskAltaPrioridade, "Task_Alta_Prio", 2048, NULL, 3, &handleTask3);
// Tarefa de monitoramento para exibir status
xTaskCreate(taskMonitor, "Task_Monitor", 4096, NULL, 4, NULL); // Maior prioridade para garantir a execução
Serial.println("Tarefas e mutexes criados com sucesso.");
}
void loop() {
// O loop do Arduino pode ser deixado vazio, pois o FreeRTOS gerencia as tarefas.
vTaskDelay(portMAX_DELAY); // Coloca a tarefa 'loop' em suspensão para sempre.
}
// --- Definição das Tarefas ---
/**
* @brief Tarefa de baixa prioridade (1).
* Tenta pegar o Mutex A e depois o Mutex B.
*/
void taskBaixaPrioridade(void *parameter) {
Serial.println("Task Baixa Prioridade iniciada.");
while (1) {
Serial.println("[Task 1] Tentando pegar Mutex A...");
if (xSemaphoreTake(mutexA, portMAX_DELAY) == pdTRUE) {
Serial.println("[Task 1] Pegou Mutex A.");
// Um pequeno delay para forçar a troca de contexto e causar o deadlock
vTaskDelay(pdMS_TO_TICKS(100));
Serial.println("[Task 1] Tentando pegar Mutex B...");
if (xSemaphoreTake(mutexB, portMAX_DELAY) == pdTRUE) {
Serial.println("[Task 1] Pegou Mutex B.");
// Seção Crítica
Serial.println("[Task 1] Realizando trabalho...");
vTaskDelay(pdMS_TO_TICKS(500));
// Libera os mutexes
xSemaphoreGive(mutexB);
Serial.println("[Task 1] Liberou Mutex B.");
}
xSemaphoreGive(mutexA);
Serial.println("[Task 1] Liberou Mutex A.");
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
/**
* @brief Tarefa de média prioridade (2).
* Tenta pegar o Mutex B e depois o Mutex A. (Ordem inversa da Task 1 para causar deadlock)
*/
void taskMediaPrioridade(void *parameter) {
Serial.println("Task Média Prioridade iniciada.");
while (1) {
Serial.println("[Task 2] Tentando pegar Mutex B...");
if (xSemaphoreTake(mutexB, portMAX_DELAY) == pdTRUE) {
Serial.println("[Task 2] Pegou Mutex B.");
// Um pequeno delay para forçar a troca de contexto e causar o deadlock
vTaskDelay(pdMS_TO_TICKS(100));
Serial.println("[Task 2] Tentando pegar Mutex A...");
if (xSemaphoreTake(mutexA, portMAX_DELAY) == pdTRUE) {
Serial.println("[Task 2] Pegou Mutex A.");
// Seção Crítica
Serial.println("[Task 2] Realizando trabalho...");
vTaskDelay(pdMS_TO_TICKS(500));
// Libera os mutexes
xSemaphoreGive(mutexA);
Serial.println("[Task 2] Liberou Mutex A.");
}
xSemaphoreGive(mutexB);
Serial.println("[Task 2] Liberou Mutex B.");
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
/**
* @brief Tarefa de alta prioridade (3).
* Apenas realiza uma contagem para mostrar que continua funcionando.
*/
void taskAltaPrioridade(void *parameter) {
Serial.println("Task Alta Prioridade iniciada.");
uint32_t contador = 0;
while (1) {
contador++;
Serial.printf("[Task 3] Executando... Contagem: %u\n", contador);
vTaskDelay(pdMS_TO_TICKS(2000)); // Executa a cada 2 segundos
}
}
/**
* @brief Tarefa de monitoramento.
* Exibe a lista de tarefas e o uso de pilha.
*/
void taskMonitor(void *parameter) {
char buffer[1024]; // Buffer para armazenar a saída de vTaskList
UBaseType_t hwm1, hwm2, hwm3; // High Water Mark
while (1) {
Serial.println("\n----------------- STATUS DO SISTEMA -----------------");
// --- 1. Usando vTaskList() ---
Serial.println("Nome da Tarefa | Estado | Prio | Pilha Livre | ID");
Serial.println("-----------------------------------------------------");
vTaskList(buffer);
Serial.println(buffer);
Serial.println("-----------------------------------------------------");
// --- 2. Usando uxTaskGetStackHighWaterMark() ---
// Retorna o mínimo de pilha que restou (quanto menor o valor, mais pilha foi usada)
hwm1 = uxTaskGetStackHighWaterMark(handleTask1);
hwm2 = uxTaskGetStackHighWaterMark(handleTask2);
hwm3 = uxTaskGetStackHighWaterMark(handleTask3);
Serial.println("Uso de Pilha (High Water Mark - bytes livres):");
Serial.printf(" - Task Baixa Prio: %u bytes\n", hwm1);
Serial.printf(" - Task Media Prio: %u bytes\n", hwm2);
Serial.printf(" - Task Alta Prio: %u bytes\n", hwm3);
Serial.println("-----------------------------------------------------\n");
// Aguarda 5 segundos para o próximo relatório
vTaskDelay(pdMS_TO_TICKS(5000));
}
}