iiOTESP32 Brasil
Modo de contingência: páginas públicas e downloads seguem disponíveis; login, comunidade e dados do servidor podem ficar indisponíveis.
Voltar para catálogo de tutoriais
Ilustração: placa ESP32 ligada a sensores de uma planta industrial e a um servidor, que alimenta telas de supervisão e de indicadores de produção
Imagem ilustrativa gerada por IA
AvançadoSCADALeitura: 15 min

ESP32 enviando dados para SCADA e MES

O SCADA não quer o seu JSON: ele quer tags. Modelagem de tópicos, qualidade do dado, heartbeat e por que o timestamp do gateway não serve.

Tecnologias:#ESP32#SCADA#MQTT#TLS#Sparkplug B#Modbus TCP

Requisitos de hardware:

  • Placa ESP32 ou ESP32-S3
  • Um broker MQTT alcançável pela rede da planta
  • Um SCADA ou plataforma que consuma MQTT

O erro de partida: achar que basta publicar

Fazer o ESP32 publicar num tópico MQTT é a parte de dez minutos. Quase todo tutorial para aí, e é por isso que tanta integração morre na primeira reunião com quem opera o SCADA.

O SCADA não quer o seu JSON. Ele trabalha com tags: um ponto nomeado, com tipo definido, unidade, faixa, qualidade e estampa de tempo. Quem opera espera abrir a tela e ver PAINEL_03.MOTOR_A.TEMPERATURA com valor, unidade e um indicador de que aquele número é confiável agora.

Entregar um JSON com nomes de campo inventados por quem escreveu o firmware transfere para o outro lado o trabalho de adivinhar o que é cada coisa. E é aí que a integração trava.

Este artigo trata do que vem depois do publish funcionar.

Modelagem de tópicos: o contrato entre os dois lados

A hierarquia do tópico é a primeira decisão, e a mais difícil de mudar depois que estiver em produção.

A convenção que funciona vai do geral ao específico, com um nível fixo por dimensão:

code.cpp
cpp
1planta/area/linha/equipamento/grandeza
2usina1/moagem/linha2/motor03/temperatura
3usina1/moagem/linha2/motor03/corrente
4usina1/utilidades/compressor01/pressao

Três regras que evitam retrabalho.

Nunca ponha valor no nome do tópico. Um tópico por grandeza, e o valor na carga. Colocar motor03/temperatura/78 gera um tópico novo a cada leitura e destrói qualquer assinatura por curinga.

Use identificadores estáveis, não descrições. O motor 03 continua sendo o 03 quando trocarem o modelo. motor-weg-w22-2024 vira mentira na primeira manutenção.

Sem acento, sem espaço, sem maiúscula misturada. Broker aceita, e vira dor de cabeça em toda ferramenta que consome depois.

O carimbo de tempo do gateway não serve

Este é o ponto que separa uma integração que passa em auditoria de uma que não passa.

O SCADA carimba o dado quando ele chega. Se a rede engasgou por dois minutos, todas as leituras daquele intervalo chegam juntas e recebem o mesmo horário — e o histórico passa a mostrar um degrau que nunca existiu no processo.

Pior: se você tem buffer offline, seis horas de dados guardados chegam com o horário da reconexão. O gráfico fica inútil justamente no período que interessava investigar.

A carga precisa carregar o instante da leitura:

code.cpp
cpp
1{
2 "t": 1755990000,
3 "v": 78.4,
4 "q": 192,
5 "u": "C"
6}

Campos curtos porque a carga se repete milhares de vezes por dia, e cada byte multiplica. O t é o instante da leitura em segundos desde 1970, o v é o valor, o q é a qualidade e o u a unidade. Para variáveis rápidas, publique t em milissegundos e combine essa unidade com quem configura o SCADA.

E aqui volta o problema do artigo de buffer: o ESP32 não tem relógio ao ligar. Sem NTP sincronizado, ele não pode carimbar. A saída honesta é publicar a qualidade como incerta enquanto o relógio não estiver sincronizado — nunca inventar um horário.

Qualidade do dado: o campo que ninguém preenche

Todo protocolo industrial sério tem qualidade, e é o campo que mais é ignorado por quem vem do lado web.

A razão é simples: zero é um valor válido. Se o sensor queimou e o firmware publica 0, o SCADA mostra zero graus e o operador acredita. Com qualidade, ele mostra zero marcado como ruim, e a diferença é entre um alarme falso e uma decisão errada.

A convenção herdada do OPC usa três faixas:

code.cpp
cpp
1192 Boa leitura normal, dentro do esperado
2 64 Incerta valor antigo, relógio sem NTP, sensor fora de faixa
3 0 Ruim falha de leitura, sensor sem resposta, barramento mudo

Publicar sempre 192 é o mesmo que não ter o campo. O valor de qualidade está em ele mudar.

Heartbeat: distinguir parado de morto

Um valor que não muda há dez minutos pode significar duas coisas opostas: o processo está estável, ou o gateway morreu.

Sem heartbeat, o SCADA não tem como saber. E a resposta errada custa caro nos dois sentidos — alarme falso quando está tudo bem, ou silêncio quando a planta parou.

A solução são duas camadas.

O Last Will, do artigo de MQTT, cobre a morte súbita: o broker publica offline por você quando a conexão cai sem despedida. Para funcionar, o Last Will precisa ser retido e publicado no mesmo tópico de status descrito abaixo: assim ele sobrescreve o último "online" que ficou guardado no broker.

E um tópico de status periódico, publicado a cada trinta segundos com retenção, cobre o resto:

code.cpp
cpp
1usina1/moagem/linha2/gw01/status
2{"t":1755990000,"online":true,"uptime":86400,"rssi":-62,"fw":"1.4.2","reset":"POWERON"}

O reset vem do artigo de watchdog, e vale ouro no lado do SCADA: quando um gateway começa a reportar TASK_WDT repetidamente, a manutenção descobre antes de o equipamento parar.

Configure no SCADA um alarme por falta de atualização: se o t do status ficar mais velho que dois minutos, o gateway sumiu. Alarme por "tópico ausente" não serve aqui, porque com retenção o broker devolve o último status mesmo com o gateway morto.

Retenção: o que o SCADA vê ao reiniciar

Mensagem MQTT comum é entregue e esquecida. Se o SCADA reiniciar, ele fica sem valor nenhum até a próxima publicação — e num sensor de temperatura ambiente que publica a cada cinco minutos, isso são cinco minutos de tela vazia.

A flag de retenção faz o broker guardar a última mensagem de cada tópico e entregá-la a quem se inscrever. O SCADA sobe e já tem o último valor conhecido.

A regra prática: retenha estado, não retenha evento.

Retenha o valor de processo, porque o último valor conhecido tem significado, e o status do gateway, que é estado por definição.

Não retenha alarme momentâneo, porque reentregar alarme velho confunde o operador, nem comando — um comando reentregue reexecuta a ação.

Esse último caso é perigoso de verdade. Comando retido pode reacionar um equipamento quando o SCADA reconectar.

TLS na planta: o que muda

A comunicação com o SCADA quase sempre exige TLS, e no ESP32 isso tem três consequências práticas.

Custa RAM. O handshake consome dezenas de KB. Num firmware apertado, o sintoma é reinício por falta de memória, e não erro de rede — as pessoas perdem horas procurando problema de conectividade.

Exige relógio. Validar certificado depende de saber a data: um certificado válido parece expirado se a placa acha que é 1970. Sincronize NTP antes de tentar o TLS.

Exige o certificado raiz. Ele vai embutido no firmware, e vence. Um dia a conexão para de funcionar sozinha, sem ninguém ter mexido em nada. Anote a data de validade junto da documentação do equipamento — é o tipo de coisa que ninguém lembra três anos depois.

Sparkplug B: quando vale a pena

Se o seu SCADA suporta, vale conhecer. O Sparkplug B padroniza justamente o que este artigo vem descrevendo: hierarquia de tópicos, tipos de dados, qualidade, nascimento e morte de dispositivos.

A vantagem real é o birth certificate: ao conectar, o dispositivo publica a lista completa de suas tags com tipo e unidade. O SCADA descobre sozinho o que aquele gateway oferece, sem ninguém cadastrar tag na mão.

O custo é a carga binária, que exige biblioteca dos dois lados e é bem mais pesada no ESP32 que um JSON curto.

A recomendação honesta: se o seu SCADA já fala Sparkplug e você tem dezenas de gateways, compensa. Para poucos dispositivos com JSON bem modelado, não compensa a complexidade.

Tutorial: do publish à tag na tela

Passo 1: combinar a modelagem antes de codificar

Sente-se com quem opera o SCADA antes de escrever a primeira linha. Defina a hierarquia de tópicos, os nomes das grandezas e as unidades.

Mudar isso depois significa reconfigurar o SCADA e reprogramar todos os gateways. É o passo mais barato de fazer e o mais caro de pular.

Passo 2: sincronizar o relógio antes de tudo

Configure NTP e espere a sincronização antes de publicar ou de abrir TLS. Enquanto não sincronizar, publique com qualidade incerta e sem o campo t: nunca invente um horário.

Passo 3: publicar uma grandeza só

Comece com um tópico. Confirme no SCADA que a tag aparece com valor, unidade e horário corretos.

Uma grandeza funcionando ponta a ponta vale mais que vinte publicando errado.

Passo 4: acrescentar qualidade e testar a degradação

Desconecte o sensor de propósito e confirme que o valor chega com qualidade ruim, e que o SCADA mostra isso em vez de exibir zero como se fosse leitura.

Se o SCADA não distingue, o problema está na configuração da tag do lado dele.

Passo 5: heartbeat e alarme por ausência

Publique o status periódico e configure no SCADA o alarme por falta de atualização do tópico. Depois desligue o gateway e cronometre até o alarme disparar.

Esse é o teste que prova que uma falha silenciosa será percebida.

Passo 6: ligar TLS

Só depois que tudo funciona em texto claro. Embuta o certificado raiz, confirme que o relógio está sincronizado e observe o heap livre depois da conexão.

Se a placa reiniciar aqui, é memória, não rede.

Passo 7: testar a reconexão com dado acumulado

Derrube o broker por alguns minutos com o gateway lendo. Ao voltar, confirme que os dados chegam com o horário da leitura, e não o da reconexão.

É o teste que valida o carimbo de tempo, e o que quase ninguém faz.

Erros comuns e como resolver

O SCADA não reconhece o dado. Modelagem. O JSON precisa ser combinado antes; nomes inventados no firmware não viram tag sozinhos.

Todos os valores com o mesmo horário. O carimbo está sendo posto na chegada. Publique o instante da leitura na carga.

O operador viu zero e acreditou. Falta o campo de qualidade, ou o SCADA não está configurado para exibi-lo.

Alarme falso quando o valor não muda. Falta heartbeat. Configure alarme por falta de atualização do tópico de status, não por valor parado.

O SCADA reinicia e fica sem dado. Falta retenção nos tópicos de estado.

Um comando foi reexecutado sozinho. Comando publicado com retenção. Nunca retenha comando.

A conexão TLS parou de funcionar sem ninguém mexer. Certificado raiz vencido, ou relógio sem sincronizar.

Reinicia ao ligar TLS. RAM do handshake. Use placa com PSRAM ou reduza o firmware.

Ver mais tutoriais técnicosIIoT ESP32 Brasil — Documentação Prática