// ---------------------- BIBLIOTECAS BÁSICAS ----------------------
// Biblioteca padrão de entrada e saída (funções como printf)
#include <stdio.h>
// Biblioteca para usar o tipo booleano (true/false)
#include <stdbool.h>
// ---------------------- BIBLIOTECAS FreeRTOS ----------------------
// Biblioteca principal do FreeRTOS (sistema de tarefas em tempo real)
#include <freertos/FreeRTOS.h>
// Biblioteca para criar e gerenciar tarefas (tasks)
#include <freertos/task.h>
// Biblioteca para usar semáforos e mecanismos de sincronização
#include "freertos/semphr.h"
// ---------------------- BIBLIOTECAS DO ESP32 ----------------------
// Funções gerais do sistema ESP32
#include "esp_system.h"
// Biblioteca de tratamento de erros do ESP-IDF
#include <esp_err.h>
// Biblioteca para controle dos pinos GPIO
#include "driver/gpio.h"
// ---------------------- BIBLIOTECA DO SENSOR ULTRASSÔNICO ----------------------
// Cabeçalho do sensor ultrassônico (declarações de funções e estruturas)
#include "ultrasonic.h"
// Inclusão do arquivo de implementação (código fonte do sensor) — normalmente não se faz isso diretamente
#include "ultrasonic.c" // Apenas usado aqui por simplicidade no Wokwi, em projetos reais é ruim de prática
// ---------------------- DEFINIÇÃO DOS PINOS DO SENSOR ----------------------
// Pino digital usado para ler o tempo de resposta (Echo)
#define ECHO_GPIO 14
// Pino digital usado para enviar o pulso (Trigger)
#define TRIGGER_GPIO 12
// Define a distância máxima a ser medida pelo sensor (em centímetros)
#define MAX_DISTANCE_CM 400
// Define os pinos GPIO onde os LEDs estão conectados
#define LED_VERMELHO GPIO_NUM_23
#define LED_AMARELO GPIO_NUM_22
#define LED_VERDE GPIO_NUM_21
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<LED_VERMELHO) | (1ULL<<LED_AMARELO) | (1ULL<<LED_VERDE))
enum estados_leds {VERMELHO,AMARELO,VERDE};
static TaskHandle_t xtask_handle_leds = NULL; //CRIA UMA TASK SEM NADA DENTRO AINDA
enum estados_leds state = VERMELHO;
// ---------------------- TAREFA PARA LER O SENSOR ULTRASSÔNICO ----------------------
void ultrasonic_test(void *pvParameters) {
float distance; // Variável para armazenar a distância medida
// Estrutura de configuração do sensor
ultrasonic_sensor_t sensor = {
.trigger_pin = TRIGGER_GPIO, // Define o pino de trigger
.echo_pin = ECHO_GPIO // Define o pino de echo
};
// Inicializa o sensor com os pinos definidos
ultrasonic_init(&sensor);
int ALERTA = 0;
// Loop infinito da task
while (true) {
// Realiza uma medição com o sensor ultrassônico
esp_err_t res = ultrasonic_measure(&sensor, MAX_DISTANCE_CM, &distance);
// Se a medição for bem-sucedida
if (res == ESP_OK) {
printf("Distance: %0.04f m\n", distance); // Se funcionar imprima distância que o sensor captou
if (distance <= 1 && ALERTA == 0) { //Caso algum obejto aparece a menos de um metro do sensor
gpio_set_level(LED_VERMELHO, 1);
gpio_set_level(LED_AMARELO, 0);
gpio_set_level(LED_VERDE, 0);
vTaskSuspend(xtask_handle_leds);
state = LED_VERMELHO;
ALERTA = 1;
}
else if (distance > 1 && ALERTA == 1) {
vTaskResume(xtask_handle_leds);
ALERTA = 0;
}
}
else {
printf("Error %d: ", res); //Caso dê algum erro
switch (res) {
case ESP_ERR_ULTRASONIC_PING: //erro de ping
printf("Cannot ping (device is in invalid state)\n");
break;
case ESP_ERR_ULTRASONIC_PING_TIMEOUT: //tempo de espera de resposta do sensor ultrapassou o limite previsto
printf("Ping timeout (no device found)\n");
break;
case ESP_ERR_ULTRASONIC_ECHO_TIMEOUT: //tempo de espera de resposta do pino echo do sensor ultrapassou o limite previsto
printf("Echo timeout (i.e. distance too big)\n");
break;
default:
printf("%s\n", esp_err_to_name(res)); //erro não especificado
}
// Aguarda 200 milissegundos antes da próxima leitura
vTaskDelay(pdMS_TO_TICKS(200));
}
}
// Configura os pinos dos LEDs como OUTPUT
//gpio_reset_pin(LED_VERMELHO); // Reseta o pino para garantir estado conhecido
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<LED_VERMELHO) | (1ULL<<LED_AMARELO) | (1ULL<<LED_VERDE))
enum estados_leds {VERMELHO, AMARELO, VERDE}; //CRIA VARIAVEIS COM NOMES RED,YELLOW,GREEN E ENUMERA ELAS
static TaskHandle_t xtask_handle_leds = NULL; //CRIA UMA TASK SEM NADA DENTRO AINDA
enum estados_leds state = VERMELHO;
void init_leds() {
//Configura os três LEDs
gpio_config_t led_conf = {};
led_conf.intr_type = GPIO_INTR_DISABLE; //SEM INTERRUPÇÃO
led_conf.mode = GPIO_MODE_OUTPUT; //PORTA GPIO IRÁ EMITIR SINAIS E NÃO RECEBER
led_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
led_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; //PULLDOWN PRA LED DESNECESSARIO ENTAO FICA DESLIGADO
led_conf.pull_up_en = GPIO_PULLUP_DISABLE; //PULLUP PRA LED DESNECESSARIO ENTAO FICA DESLIGADO
gpio_config(&led_conf);
// Garante que todos os LEDs comecem desligados
gpio_set_level(LED_VERMELHO, 0); // 0 = LOW
gpio_set_level(LED_AMARELO, 0); // 0 = LOW
gpio_set_level(LED_VERDE, 0); // 0 = LOW
}
// LEDS ------------------------------------------------------
int update_leds(enum estados_leds * state) {
if (*state == VERMELHO) { //LIGA LUZ VERMELHA
printf("Amarelo desligou, "); //teste visual
gpio_set_level(LED_VERMELHO, 0);
gpio_set_level(LED_AMARELO, 0);
gpio_set_level(LED_VERDE, 1);
*state = VERDE;
return 10;
}
else if (*state == VERDE) { //LIGA LUZ VERDE
printf("Vermelho desligou, "); //teste visual
gpio_set_level(LED_VERMELHO, 0);
gpio_set_level(LED_AMARELO, 1);
gpio_set_level(LED_VERDE, 0);
*state = AMARELO;
return 1;
}
else { //LIGA LUZ AMARELA
printf("Verde desligou, "); //teste visual
gpio_set_level(LED_VERMELHO, 1);
gpio_set_level(LED_AMARELO, 0);
gpio_set_level(LED_VERDE, 0);
*state = VERMELHO;
return 5;
}
}
// LEDS ------------------------------------------------------
void control_leds(void *pvParameters) {
while (true) {
printf("Estado atual: %d\n", state);
int time = update_leds(&state);
vTaskDelay(pdMS_TO_TICKS(1000 * time));
}
}
// LEDS ------------------------------------------------------
// ---------------------- FUNÇÃO PRINCIPAL ----------------------
// app_main é o ponto de entrada do programa no ESP-IDF
void app_main() {
// A função xTaskCreate() é usada para criar uma task (tarefa) no FreeRTOS.
// Uma task é uma função que será executada de forma "concorrente" com outras,
// permitindo que o microcontrolador faça várias coisas ao mesmo tempo.
// Parâmetros da função xTaskCreate:
//
// 1. ultrasonic_test
// - Esta é a função que define o código da task.
// - No nosso caso, ela ficará lendo continuamente o sensor ultrassônico
// e imprimindo a distância detectada no terminal.
//
// 2. "ultrasonic_test"
// - Nome da task. Usado apenas para fins de debug, logs, ou leitura via ferramentas.
// - Pode ser qualquer string que ajude a identificar essa task.
//
// 3. configMINIMAL_STACK_SIZE * 3
// - Define o tamanho da pilha de memória que a task vai usar.
// - `configMINIMAL_STACK_SIZE` é um valor padrão mínimo definido pela ESP-IDF
// que garante que a task rode operações básicas.
// - Multiplicamos por 3 para garantir mais espaço, já que estamos usando printf()
// e bibliotecas como `ultrasonic_measure`, que consomem mais stack.
//
// 4. NULL
// - Aqui seria possível passar algum parâmetro para a task (por exemplo: um struct
// com configurações ou ponteiro para dados).
// - Como essa task não precisa de parâmetros externos, passamos NULL.
//
// 5. 5
// - Define a prioridade da task.
// - Quanto maior o valor, mais prioridade essa task tem para o escalonador do FreeRTOS.
// - Isso significa que ela pode "pausar" outras tasks de prioridade menor para rodar.
// - No nosso caso, 5 é um valor razoável para uma task de leitura periódica.
// -0: geralmente usada por tasks de background ou que não são urgentes.
// -1 ~ 4: tarefas regulares.
// -5 ~ 10: tarefas que precisam de resposta rápida.
// -20+: use com cuidado — só se for uma task que nunca pode esperar, tempo real hard, ex:
// Controle de superfície de voo (alerões, leme, etc.)
// Aquisição de sensores de altitude e velocidade
// Comunicação com sistemas de aterrissagem
//
// 6. NULL
// - Este é o "handle" da task.
// - Um handle é como uma “referência” para a task criada.
// - Se quiséssemos pausar, deletar ou reiniciar essa task mais tarde,
// deveríamos armazenar o handle em uma variável.
// - Como não precisamos controlar essa task externamente, deixamos como NULL.
init_leds();//Inicializa as GPIOS dos LED's
//Chama a função do sensor //quantidade de memória alocada para a pilha
xTaskCreate(ultrasonic_test, "ultrasonic_test", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL); //NULL'S: ponteiros que podem ser usados para passar parâmetros à tarefa
//Cria task com esse nome //Prioridade 5
xTaskCreate(control_leds, "Semáforo", 2048, NULL, 5, &xtask_handle_leds);
} //Mesma prioridade task simultâneas