Guilherme Gomes
← Todos os casos

Semáforo inteligente
com prioridade em camadas

Controlar um cruzamento pela internet é fácil. Controlar um cruzamento pela internet de um jeito que continue seguro quando a internet cair é o problema de verdade.

Cliente
Vegalux · projeto de mobilidade urbana
Papel
Arquitetura e implementação ponta a ponta
Camadas
Firmware · gateway · nuvem · painel
Situação
Em produção

O problema

O pedido inicial era simples: permitir que o operador mudasse os tempos de um semáforo remotamente e programasse horários diferentes para o pico da manhã, o pico da tarde e a madrugada. A complicação está no que acontece quando a rede falha, o comando chega pela metade, ou dois pedidos conflitantes chegam ao mesmo tempo. Um semáforo não pode ficar em estado indefinido enquanto espera uma resposta HTTP.

A arquitetura precisava garantir que o cruzamento tivesse sempre um comportamento válido, mesmo totalmente isolado — e que qualquer camada acima pudesse assumir o controle sem ambiguidade sobre quem manda.

Arquitetura

Quatro camadas físicas separadas, cada uma capaz de operar quando a de cima desaparece. O firmware é autônomo; o gateway local vive dentro da rede do cruzamento e é alcançável pela nuvem por uma rede privada Tailscale, sem abrir porta para a internet.

NO CRUZAMENTO Arduino máquina de estados do ciclo semafórico Grupos focais verde / amarelo vermelho Mini PC Linux · gateway Flask serial com o controlador · alcançável só via Tailscale TRANSPORTE Broker MQTT tópicos por cruzamento QoS e last will NUVEM Backend Node.js · Heroku agenda, resolução de prioridade e histórico publica comandos, assina estado Painel do operador React PostgreSQL agenda e auditoria estado ↑ comando ↓ RESOLUÇÃO DE PRIORIDADE — QUEM MANDA QUANDO TODOS FALAM 1 · AGENDA programação por faixa de horário o comportamento padrão do cruzamento 2 · COMANDO intervenção manual do operador sobrepõe a agenda enquanto durar 3 · FÁBRICA ciclo fixo gravado no firmware assume sozinho se tudo acima sumir Regra: a camada mais alta disponível vence. A queda de qualquer camada é degradação prevista, não falha — o cruzamento nunca fica sem um comportamento definido, mesmo isolado da rede.
Linha contínua: estado subindo do cruzamento. Linha tracejada ciano: comando descendo da nuvem. A hierarquia de prioridade é a mesma no backend e no firmware, para que os dois concordem sem precisar negociar.

A falha mais instrutiva do projeto

Durante a integração, comandos enviados pelo gateway eram aceitos, publicados e confirmados — e o semáforo simplesmente ignorava. Nenhum erro, nenhum log vermelho, nada. O firmware esperava campos separados por |; o gateway montava a mensagem com :. O parser lia a string, não encontrava o separador, e descartava em silêncio.

A lição não foi sobre o caractere. Foi sobre o fato de eu ter deixado a definição do protocolo viver em dois lugares, escrita duas vezes, sem contrato compartilhado e sem qualquer validação que recusasse ruidosamente uma mensagem malformada. Um payload inválido precisa gritar. Depois disso, toda fronteira de serialização do projeto ganhou validação explícita e log de rejeição.

Requisitos que moldaram a solução

  • Segurança de rede sem porta exposta: o mini PC não recebe conexão da internet pública. A nuvem o alcança por Tailscale, o que resolve NAT e autenticação de uma vez só.
  • Autonomia local: o Arduino mantém o ciclo mesmo sem gateway, e o gateway mantém a agenda mesmo sem nuvem.
  • Auditoria: todo comando manual fica registrado com autor e horário — em infraestrutura urbana, saber quem mudou o quê deixa de ser conveniência.
  • Idempotência: reenvio de comando não produz efeito duplicado, porque o estado desejado é declarado, não incrementado.

O que este projeto ensinou sobre arquitetura

Sistemas que tocam o mundo físico invertem uma intuição comum do desenvolvimento web: indisponibilidade não é o pior caso. O pior caso é o sistema disponível e errado. Um semáforo offline com ciclo fixo é aceitável; um semáforo online obedecendo a um comando corrompido, não. Isso empurra a arquitetura na direção de fronteiras rígidas, contratos validados e degradação escrita antes da funcionalidade — e é o princípio que passei a levar também para os sistemas puramente digitais que construo.