Arquitetura¶
O firmware é organizado em duas camadas e um ponto único de estado. A ideia central é o desacoplamento: os drivers de hardware não sabem nada sobre incubadora, e as tarefas não conversam diretamente entre si — tudo passa por um estado compartilhado. Assim, cada parte pode ser entendida, testada e trocada isoladamente.
Camadas¶
| Camada | Pasta | Responsabilidade |
|---|---|---|
| Componentes | incubadora/components/ |
Drivers de hardware reutilizáveis (bme280, dht11, ssd1306_display, rgb_led, buzzer, rele) — só falam com o dispositivo, sem regra de negócio |
| Aplicação | incubadora/main/ |
As tarefas do FreeRTOS, o estado compartilhado e a configuração (app_config.h) — a lógica da incubadora |
A fronteira entre as duas é clara: um componente nunca decide quando ligar o aquecedor; ele só sabe como ligá-lo. Quem decide é uma tarefa da aplicação. Por isso o mesmo driver de relé serviria para qualquer outro projeto.
Fluxo de dados¶
flowchart LR
subgraph HW[Sensores]
BMP[BMP280]
DHT[DHT11]
end
subgraph Prod[Tarefas produtoras]
TS[task_sensor]
TD[task_dht11]
TC[task_control]
end
SS[(shared_state)]
subgraph Cons[Tarefas consumidoras]
TL[task_led_status]
TB[task_buzzer]
TDP[task_display]
TH[HTTP server]
end
subgraph Out[Saídas]
RELE[Relé / aquecedor]
LED[LED RGB]
BUZ[Buzzer]
OLED[OLED]
WEB[Página web]
end
BMP --> TS --> SS
DHT --> TD --> SS
SS --> TC --> RELE
TC --> SS
SS --> TL --> LED
SS --> TB --> BUZ
SS --> TDP --> OLED
SS --> TH --> WEB
O caminho é sempre o mesmo: os sensores alimentam as tarefas produtoras, que escrevem no
shared_state; as tarefas consumidoras leem desse estado e comandam as saídas. A
task_control é ao mesmo tempo consumidora (lê a temperatura) e produtora (publica o estado
do aquecedor de volta), por isso aparece com uma seta nos dois sentidos.
Estado compartilhado (shared_state)¶
O shared_state é a única fonte de verdade do sistema. Ele guarda a leitura atual —
temperatura, pressão, umidade e o estado do aquecedor — em uma estrutura protegida por um
mutex (um "cadeado" que garante que só uma tarefa mexa no dado por vez). Sem esse cuidado,
duas tarefas escrevendo ao mesmo tempo poderiam deixar o valor corrompido.
Como dois sensores publicam no mesmo estado, ele usa escritores segmentados — cada produtor atualiza só a sua parte, sem apagar o dado dos outros:
shared_state_set_env(...)→ temperatura e pressão (do BMP280);shared_state_set_humidity(...)→ umidade (do DHT11);shared_state_set_heater(...)→ estado do aquecedor (datask_control).
As tarefas consumidoras chamam shared_state_get(...) para pegar uma cópia consistente do
estado, e compute_status(...) para traduzir a temperatura em situação (ideal, alerta,
alarme...). Com isso decidem a cor do LED, o buzzer, o texto do OLED e o JSON da web.
Detalhe técnico — mutex e leitura consistente
O mutex é um SemaphoreHandle_t criado com xSemaphoreCreateMutex(). Toda leitura e
escrita acontece entre xSemaphoreTake e xSemaphoreGive. O shared_state_get copia a
estrutura inteira de uma vez para uma variável local da tarefa, de modo que ela trabalhe
com um retrato coerente — sem risco de ler temperatura de um instante e umidade de
outro. Os escritores segmentados evitam o problema clássico de um produtor "zerar" o campo
de outro só porque reescreveu a estrutura toda.
Inicialização segura¶
A ordem em que o sistema sobe importa por causa dos atuadores. No boot, o app_main() segue
esta sequência:
- inicializa o estado compartilhado (mutex);
- coloca os atuadores em estado seguro — aquecedor desligado e buzzer mudo;
- inicializa os sensores e o display;
- sobe o Wi-Fi e o servidor HTTP;
- por fim, cria as tarefas.
Assim o aquecedor nunca liga sozinho antes de a task_control estar no ar para
supervisioná-lo.
Por que essa organização¶
- Desacoplamento — trocar um sensor, um pino ou um limite não afeta as outras tarefas;
muda-se apenas o driver ou o
app_config.h. - Concorrência segura — o mutex elimina as condições de corrida entre tarefas que rodam em paralelo (inclusive nos dois núcleos do ESP32).
- Reuso — cada componente é um driver isolado, que pode ser aproveitado em outros projetos sem arrastar a lógica da incubadora junto.
- Clareza — lendo só o
main/, entende-se o comportamento do sistema; lendo só um componente, entende-se um periférico. As responsabilidades não se misturam.