# MQTT

> Dispositivos que publicam por um broker MQTT externo, em MQTT simples ou Sparkplug B, com o broker modelado como o nó que todo dispositivo abaixo dele compartilha, e os dados da própria estação publicados num broker.

O driver MQTT lê e escreve as tags de equipamentos que publicam por um **broker MQTT externo**: um gateway, um sensor sem fio, um fluxo do Node-RED, um nó de borda que fala Sparkplug B. A estação é cliente desse broker: assina os tópicos que as tags dela leem e publica as escritas que você faz, e cada broker pode publicar os dados da própria estação, as tags, as variáveis da Logic e os alarmes listados no [cartão Publicar](#the-publish-card) dele, como JSON ou em Sparkplug B. Ela é cliente e não broker, então o equipamento e a estação se conectam os dois a um broker: quando você não tem um, o próprio [broker MQTT](connector-mqtt-broker) da estação pode ser esse broker, e ligá-lo adiciona o broker **Esta estação** a este driver; [Instalar um broker](#installing-a-broker) mostra como montar um à parte.

Todo dispositivo atrás de um broker usa a mesma conexão, por isso a topologia é
**Driver → Broker → Dispositivo → Tag**: o **broker** é dono de onde ele responde e de como
a estação entra nele, digitados uma vez só; cada **dispositivo** abaixo dele é dono apenas
de como publica, **MQTT simples** ou **Sparkplug B**, e das suas tags. Os dois protocolos
convivem lado a lado no mesmo broker. Os valores chegam por assinatura, não por leitura
cíclica, então nenhum dispositivo ou tag MQTT tem intervalo de leitura.

## A página do driver {#the-drivers-page}

A página do driver MQTT lista brokers em vez de dispositivos. A frase abaixo do cabeçalho
diz "Cada broker é uma conexão com um broker MQTT externo. Configure onde ele responde e como a estação entra nele, depois acrescente os dispositivos que publicam por ele."
A tabela **Brokers MQTT** traz **Acrescentar broker** à direita do título e uma linha por
broker: **Nome**, **Endereço** (`mqtt://host:port`, ou `mqtts://host:port` com TLS,
vazio enquanto o broker não tem host), **Dispositivos**, **Estado** (um ponto e o estado
do broker com o que os dispositivos dele estão fazendo, como "Conectado · 2/2 dispositivos conectados"),
depois **Abrir** e, para Configurar Connector, **Habilitar**, **Desabilitar** e
**Excluir**. A tabela vazia diz "Nenhum broker ainda." Um broker que recusou a estação leva o marcador vermelho da árvore depois do nome, e a página nomeia, sob o cabeçalho, os brokers com erro ("1 broker MQTT com erro: Plant").

Na árvore, a linha do driver conta os dois níveis ("1 broker MQTT · 2 dispositivos"), e o
cabeçalho da página mostra o número de brokers entre parênteses. No menu Novo… da coluna, a
entrada MQTT oferece **Novo broker** e depois cada broker com o seu próprio **Novo dispositivo**
e os dispositivos dele, e o menu de contexto da linha do driver oferece **Acrescentar broker**.
A barra do driver não tem Descobrir: uma varredura escuta um único broker, por isso ela fica na
página do broker. Depois de acrescentado, o driver fica sob a pasta Drivers da árvore, com ou
sem broker.

## O broker {#the-broker}

**Acrescentar broker**, o **Novo broker** do menu Novo… e o menu de contexto da linha do driver
criam um broker chamado "Novo broker" (depois "Novo broker 2") e desabilitado, para que a
conexão dele seja preenchida antes de a estação se conectar. O feed diz isso: "Broker 'Novo broker' acrescentado, desabilitado. Informe onde ele responde e depois habilite."
O broker não é aberto: fica destacado na tabela Brokers MQTT da página do driver, para onde a tela vai a partir do Novo… ou do menu de contexto. A página dele, alcançada pelo **Abrir** da linha dele ou pela linha dele na árvore, traz a frase "Um broker MQTT: onde ele responde e como a estação entra nele. Todo dispositivo abaixo dele compartilha esta conexão."
e, no cabeçalho, o estado, o ponto dele e o endereço. Sob o cabeçalho, os dispositivos do broker que têm erro próprio são nomeados ("1 dispositivo com erro: Pump"); enquanto o próprio broker está com erro, os dispositivos apontam para ele: a página de um dispositivo diz "Este dispositivo passa pelo broker MQTT Plant, que está com erro. A página do broker diz por quê."

### O cartão Conexão com o broker {#the-broker-connection-card}

| Campo | Significado | Padrão |
| --- | --- | --- |
| **Nome** | O nome amigável do broker na árvore e em cada dispositivo abaixo dele. Ele nunca entra num endereço. Em branco é recusado: "O nome do broker é obrigatório." | "Novo broker" |
| **Host ou IP** | O nome do host ou o endereço IP do broker. Dá para colar um endereço inteiro: `mqtt://` ou `tcp://` na frente põem o transporte às claras, `mqtts://`, `ssl://` ou `tls://` com TLS, uma porta depois do host vai para **Porta**, e o host é guardado limpo. Outro esquema, um caminho ou um login escrito no endereço é recusado abaixo da caixa, e cada campo fica com o que tinha: "'http://192.168.0.20' não é um endereço de broker. Escreva o nome do host ou o endereço IP do broker, se quiser como mqtt://host:port ou mqtts://host:port." | vazio |
| **Porta** | A porta TCP em que o broker responde, de 1 a 65535. | 1883 |
| **Transporte** | A caixa de seleção **Usar TLS**. Ligada, a conexão é criptografada e o certificado do broker é conferido no repositório de certificados do Windows ou entre os certificados aprovados em [Acesso a equipamentos](#equipment-access): "Criptografado: o Windows precisa confiar no certificado do broker, ou ele precisa estar aprovado em Acesso a equipamentos, na página do driver MQTT." Desligada, a nota avisa "Sem criptografia: os valores e a senha trafegam às claras." Trocá-la leva uma porta deixada em 1883 para 8883, e de volta; qualquer outra porta fica como está. | desligada |
| **Usuário**, **Senha** | A conta com que a estação entra no broker. Um usuário vazio entra de forma anônima. A senha é protegida em repouso como as credenciais de um dispositivo e nunca aparece num endereço, no diário ou na resposta de um agente. | vazio |
| **ID do cliente** | O nome que a sessão da estação tem no broker, de 1 a 128 caracteres, sem caracteres de controle. Um broker mantém uma sessão por ID do cliente e derruba a mais antiga de duas, por isso um ID do cliente pertence a um só broker desta estação: "O ID do cliente 'GanterLab01' já pertence ao broker 'Plant'. Duas sessões com o mesmo ID do cliente derrubam uma à outra do broker: escolha outro." Quando duas estações compartilham um broker, cada uma fica com o seu. | `GanterLab` e 14 dígitos hexadecimais, gerado |
| **Intervalo de keep-alive (s)** | De quanto em quanto tempo a sessão prova que está viva quando nada mais trafega: "O keep-alive precisa estar entre 5 e 3600 segundos." | 60 |

Abaixo dos campos, uma frase diz o que o broker e os dispositivos dele estão fazendo, o que
ainda falta ao broker, e "Todos os dispositivos sob este broker e o que a estação publica nele compartilham esta conexão: a sessão abre quando o primeiro dispositivo habilitado ou a primeira linha publicada precisa dela."
Todo campo continua editável com o broker habilitado. Uma mudança salva em onde ele
responde, no transporte, no login, no ID do cliente ou no keep-alive reconecta a sessão
única que todo dispositivo abaixo do broker compartilha.

### A barra de comandos {#the-command-bar}

| Comando | O que faz | Esmaecido quando (situação) | Não aparece quando (papel) |
| --- | --- | --- | --- |
| **Habilitar** | Deixa os dispositivos abaixo do broker usarem a sessão dele. Um broker sem host é recusado: "Informe o nome do host ou o endereço IP do broker antes de habilitá-lo." | Já está habilitado, a conexão não está completa ("A conexão deste broker ainda não está completa."), uma varredura está rodando, ou a estação recusa mudanças. | Sem Configurar Connector. |
| **Desabilitar** | Fecha a sessão para todo dispositivo abaixo dele; cada dispositivo guarda a própria escolha de Habilitado e volta como estava quando o broker é habilitado de novo. | Já está desabilitado, uma varredura está rodando, ou a estação recusa mudanças. | Sem Configurar Connector. |
| **Excluir o broker** (tinta de perigo, à direita) | Pergunta antes. Num broker vazio: "Este broker não tem dispositivos."; num com dispositivos: "Isso também exclui os 2 dispositivos do broker e todas as tags deles." Depois o driver MQTT fica selecionado e o feed diz "Broker 'Plant' e os 2 dispositivos dele excluídos." | Uma varredura está rodando ("Uma varredura de descoberta está usando este broker. Espere ela terminar."), ou a estação recusa mudanças. | Sem Configurar Connector. |

O menu de contexto da linha de um broker oferece **Acrescentar dispositivo**, **Habilitar**,
**Desabilitar** e **Excluir**.

### Estados do broker {#broker-states}

O broker relata a sessão dele separado dos dispositivos, na linha da árvore, na tabela de
brokers do driver e no próprio cabeçalho:

| Estado | Ponto | Significado |
| --- | --- | --- |
| **Desligado** | cinza | O broker está desabilitado. |
| **Sem sessão** | cinza | Habilitado, mas nenhum dispositivo habilitado o usa e o cartão Publicar dele não publica nada, então nenhuma sessão é aberta. |
| **Conectando…** | âmbar | Um dispositivo está habilitado, ou o broker publica, e a sessão está se conectando, ou se reconectando depois de uma perda. |
| **Conectado** | verde | A sessão está conectada. |
| **Inacessível** | vermelho | Não foi possível alcançar o broker, ou ele recusou a estação. A causa aparece em vermelho abaixo do cabeçalho na língua da estação e, nas palavras do driver, como dica do marcador de erro da linha, nos termos do que corrigir: nada responde naquela porta, o nome não é conhecido nesta rede, o usuário ou a senha foram recusados, o certificado não é confiável ou espera aprovação em [Acesso a equipamentos](#equipment-access), outro cliente tomou a sessão com o mesmo ID do cliente. Um broker que recusa a porta ou um cliente sem usuário é dito com o ajuste dele (veja [Preparar o broker](#prepare-the-broker)). |

A linha da árvore mostra o estado e o número de dispositivos ("Conectado · 2 dispositivos"),
e o resumo abaixo do cartão acrescenta o que os dispositivos estão fazendo:
"Conectado · 2/2 dispositivos conectados", "Conectado · 1 conectados · 1 com problemas",
"Sem sessão · nenhum dispositivo habilitado". Um dispositivo Sparkplug B que anunciou a
própria morte tira do ar só a si mesmo: o broker continua Conectado. A sessão fala MQTT 5 e
recua para MQTT 3.1.1 com um broker que não fala, começa limpa e se reconecta sozinha,
esperando cada vez mais entre as tentativas enquanto o broker continua ausente.

### O cartão Descobrir dispositivos {#the-discover-devices-card}

Desenhado só para Configurar Connector, abaixo da frase "Escuta este broker por um tempo: os nós de borda e dispositivos Sparkplug B que se anunciam, e os tópicos sob um filtro."

| Campo | Significado | Padrão |
| --- | --- | --- |
| **Escutar (s)** | Por quanto tempo a varredura escuta, de 3 a 60 segundos. | 10 |
| **Filtro de tópicos** | Os tópicos simples a listar, como tags de um dispositivo. Os curingas `+` (um nível) e `#` (este nível e tudo abaixo dele, só no fim) são aceitos, por isso "ex.: plant/#". Vazio lista só dispositivos Sparkplug B. | vazio |

**Descobrir** inicia a varredura e vira **Cancelar** enquanto ela roda. A frase abaixo dos
campos diz o que ela vai fazer ("Escuta 192.168.0.20:1883 por 10 s."), e a frase de
progresso conta a janela ("Escutando 192.168.0.20:1883 · 4 de 10 s…"). A varredura precisa
de um host salvo no broker: até lá o cartão diz "Informe o host do broker e deixe a edição ser salva antes da descoberta."
Uma edição pendente no broker é salva primeiro; quando não pode ser, nada começa:
"A descoberta não começou porque a edição do broker não pôde ser salva."

O que uma varredura faz no broker:

- Ela se conecta com um ID do cliente próprio, então nunca derruba do broker a sessão dos
  dispositivos, e pode rodar com o broker habilitado e em uso.
- Ela assina todo tópico Sparkplug B (`spBv1.0/#`) e o filtro de tópicos. Os certificados
  de nascimento do Sparkplug B não ficam retidos, por isso um nó de borda ouvido sem um
  certificado é chamado a se anunciar de novo (o comando `Node Control/Rebirth`, a única
  mensagem que a varredura publica); um nó de borda que está rodando responde dentro da
  janela.
- Cada nó de borda e cada dispositivo atrás dele que se anuncia (NBIRTH, DBIRTH) vira um
  candidato com as métricas dele como tags, tipadas pelo que o nascimento declara. A
  tubulação do próprio nó de borda (`bdSeq` e as métricas `Node Control/…` e
  `Device Control/…`) fica de fora, assim como as métricas que nenhuma tag lê (arrays,
  conjuntos de dados, templates, conjuntos de propriedades, bytes, arquivos), contadas pelo
  motivo. Um nó de borda ouvido mas não nascido dentro da janela é oferecido sem tags.
- Com um filtro de tópicos, os tópicos simples ouvidos sob ele viram um candidato próprio:
  cada tópico é uma tag, ou, num payload JSON, cada valor dentro dele é uma tag pelo
  caminho do valor (até 32 por tópico, quatro níveis de profundidade, os 8 primeiros
  elementos de um array). Um número é oferecido como Double, `true` ou `false` como
  Boolean, qualquer outra coisa como String. A lista para em 500 tópicos. Um tópico cujo
  publicador retém a última mensagem é ouvido na hora; qualquer outro só se publicar
  durante a janela.
- Nada é salvo até você acrescentar um candidato. Cancelar encerra a janela na hora.

O cartão de resultados, **Dispositivos descobertos**, lista **Publica como**
(`group/edge node`, com `/device` atrás de um nó de borda, ou "Tópicos simples"),
**Dispositivo** (o nome com que acrescentá-lo, editável: o ID do dispositivo ou do nó de
borda, ou os níveis do filtro antes do primeiro curinga), **Resultado**
("12 de 12 tags selecionadas") e **Acrescentar dispositivo**, que cria o dispositivo sob
este broker com essas tags. Uma identidade Sparkplug B já configurada no broker mostra
"Já configurado" e oferece **Abrir** no lugar. Uma varredura que não ouviu nada diz
"Nada se anunciou neste broker enquanto a varredura escutava."

Um dispositivo que você já tem acha pela própria página o que ainda não lê: **Descobrir tags**, no cabeçalho do cartão de tags dele, escuta 10 s no broker dele. Um dispositivo Sparkplug B ouve só o próprio nó de borda, a quem se pede o nascimento enquanto nada é pedido a nenhum outro; um dispositivo simples ouve os tópicos abaixo dos níveis que as tags dele compartilham (`plant/oven/#` para `plant/oven/temperature` e `plant/oven/count`), e todos os tópicos do broker quando elas não compartilham nenhum. Só são oferecidos os valores para os quais ele ainda não tem tag (veja [Descobrir as tags de um dispositivo](connector-discovery#discovering-a-devices-tags)).

### O cartão Dispositivos {#the-devices-card}

"Cada dispositivo lê tópicos simples, ou um nó de borda ou dispositivo Sparkplug B. A conexão é herdada do broker e não pode ser mudada aqui."
**Acrescentar dispositivo** cria "Novo dispositivo" sob este broker, habilitado, em MQTT
simples, destacado nesta tabela e rolado até ficar à vista: "Dispositivo 'Novo dispositivo' acrescentado em 'Plant'." A tabela lista
**Dispositivo**, **Publica como**, **Tags** e **Saúde**, com Abrir, Habilitar, Desabilitar
e Excluir em cada linha; vazia, ela diz "Nenhum dispositivo configurado neste broker." Um dispositivo com erro próprio leva o marcador vermelho da árvore depois do nome.

### O cartão Publicar {#the-publish-card}

O último cartão publica os dados da própria estação neste broker, sob a frase "A estação publica cada linha neste broker como JSON no tópico dela sempre que o valor ou a qualidade dele muda, e todas as linhas de novo cada vez que a conexão abre. O broker guarda a última mensagem de cada tópico para quem assinar depois. A estação só assina o tópico de escrita de cada linha em Leitura e escrita."
Quem vê o Connector lê o cartão; quem configura o Connector o muda. O broker Esta estação do
próprio [broker MQTT](connector-mqtt-broker) da estação tem o mesmo cartão.

| Campo | Significado | Padrão |
| --- | --- | --- |
| **Formato da publicação** | Como as linhas saem: **JSON** no tópico de cada linha ([Publicação](#publishing)), ou **Sparkplug B**, a estação como um nó de borda do broker ([Publicação em Sparkplug B](#publishing-in-sparkplug-b)). A dica dele: "Como as linhas saem: JSON no tópico de cada linha, ou Sparkplug B, com esta estação como um nó de borda que o Ignition e outros consumidores Sparkplug B encontram sozinhos." | JSON |
| **Prefixo do tópico** | Só em JSON. O nível sob o qual o tópico de cada linha nova é sugerido. Vazio, o campo mostra o padrão e o usa. "O prefixo do tópico não pode ter + nem #, começar com $ nem ter um caractere que nenhum tópico pode ter." Mudá-lo não altera os tópicos que já estão na lista. | `ganterlab/` e o nome da estação |
| **ID do grupo** | Só em Sparkplug B, no lugar do prefixo. O grupo Sparkplug B do nó de borda da estação neste broker; vazio, é `GanterLab`. Um grupo com `/`, `+` ou `#` é recusado abaixo do campo, e o broker não é salvo até ele ser corrigido. | `GanterLab` |

Em Sparkplug B, a frase abaixo do título diz no lugar dela "A estação é um nó de borda Sparkplug B neste broker: as variáveis e os alarmes do Logic são métricas dele, e cada dispositivo com linhas é um dispositivo Sparkplug B. Ela anuncia todas as métricas quando a conexão abre e sempre que a lista muda, e depois publica cada valor ou qualidade que muda. Só uma linha em Leitura e escrita aceita um valor vindo de um comando.", e abaixo dos campos **ID do nó de borda (o nome da estação)** mostra o nó de borda como o qual ela publica.

A tabela lista **Origem**, **Tópico** e **Acesso**, por tópico; uma linha cuja origem
ainda não foi escolhida diz "Sem origem ainda". Sem nenhuma, ela diz "Nada é publicado neste broker ainda. Adicione uma linha com Nova linha."
Em Sparkplug B, a coluna **Tópico** é **Métrica**: uma tag depois do dispositivo dela (`Oven · Temperature`), uma variável ou um alarme pelo nome no nó de borda (`Logic/Oven/Setpoint`, `Alarms/High`).
Com o broker desabilitado e linhas na lista, um aviso diz "O broker está desabilitado, então nada é publicado até ele ser habilitado."
Com uma linha do broker em Leitura e escrita, um segundo aviso diz "As linhas em Leitura e escrita aceitam os valores publicados no tópico de escrita delas. Quem consegue publicar neste broker consegue mudar essas origens, então o usuário e a senha do broker são a única proteção delas." Em Sparkplug B ele diz "As linhas em Leitura e escrita aceitam valores de comandos Sparkplug B (NCMD, DCMD). Quem consegue publicar neste broker consegue mudar essas origens, então o usuário e a senha do broker são a única proteção delas."

| Comando | O que faz | Esmaecido quando (situação) | Não aparece quando (papel) |
| --- | --- | --- | --- |
| **Nova linha** | Acrescenta uma linha que ainda não publica nada e a seleciona: "Linha acrescentada à lista Publicar do broker. Escolha a origem dela." | Uma execução segura a estação, ou o runtime está parado. | Sem Configurar Connector. |
| **Excluir linha** (tinta de perigo) | Exclui a linha selecionada na hora, com a dica nomeando o que ela publica. A estação para de publicá-la; a mensagem que o broker guardou no tópico dela continua lá. | Nenhuma linha está selecionada ("Selecione uma linha da lista para excluí-la."), uma execução segura a estação, ou o runtime está parado. | Sem Configurar Connector. |

Os campos de uma linha são salvos no momento em que mudam:

| Campo | O que faz |
| --- | --- |
| **Origem** | O que a linha publica: "(nenhum)", depois toda tag habilitada de um dispositivo habilitado ("Tag do Connector · Simulated · Oven · Temperature"), toda variável da Logic que guarda um valor e todo alarme, nomeados pelas pastas. Um comando de dispositivo não publica nada e não é oferecido. A linha guarda a origem pela identidade, então uma renomeação a mantém nela, e o cartão [Usada por](connector-tags#the-used-by-card) da origem lista a linha. |
| **Tópico** | Só em JSON. Onde a linha publica. Escolher uma origem sugere um tópico pelo nome dela: `<prefix>/<device>/<tag>` para uma tag, `<prefix>/logic/<folders>/<variable>` para uma variável, `<prefix>/alarms/<folders>/<alarm>` para um alarme, cada nome um nível de tópico (um espaço, uma barra ou um curinga vira `_`). Edite à vontade: um tópico que ainda é o sugerido acompanha uma origem escolhida depois, um que você escreveu fica, e renomear a origem o mantém. |
| **Acesso** | **Somente leitura**, o padrão de toda linha: a estação publica a linha e não assina nada por ela. **Leitura e escrita**: a estação também aceita os valores publicados no tópico de escrita da linha, o tópico dela seguido de `/set`, e os escreve na origem ([Escrita de volta](#writing-back)). Só é oferecido para uma tag com acesso de escrita num driver que escreve, ou uma variável de memória; nas outras origens fica na lista, esmaecido: "Esta origem não aceita escrita: só uma tag com acesso de escrita ou uma variável de memória aceita." Escolher um alarme volta a linha para somente leitura. Em Sparkplug B, Leitura e escrita aceita, no lugar do tópico de escrita, os valores que um comando Sparkplug B envia para a métrica da linha. |

Abaixo dos campos, uma frase diz o que a linha publica:

| O cartão diz | Quando |
| --- | --- |
| "Esta linha ainda não publica nada: escolha a origem." | Nenhuma origem foi escolhida. |
| "Publica o valor, a qualidade e a hora dele como JSON sempre que o valor ou a qualidade muda." | Uma tag ou uma variável, somente leitura. |
| "Publica o valor, a qualidade e a hora dele como JSON sempre que o valor ou a qualidade muda, e escreve na origem cada valor publicado no tópico de escrita: cada escrita fica no diário com o nome do broker." | Uma tag ou uma variável em Leitura e escrita. |
| "Publica se o alarme está ativo e reconhecido, o estado, a prioridade, a mensagem e a hora dele como JSON sempre que um deles muda." | Um alarme. |
| "A origem desta linha não existe mais, então nada mais é publicado por ela. O broker mantém a última mensagem que guardou." | A tag, a variável ou o alarme foi excluído. |
| "Publica o valor e a qualidade dele em Sparkplug B sempre que o valor ou a qualidade muda." | Sparkplug B: uma tag ou uma variável, somente leitura. |
| "Publica o valor e a qualidade dele em Sparkplug B sempre que o valor ou a qualidade muda, e escreve na origem cada valor que um comando Sparkplug B envia para a métrica dela: cada escrita fica no diário com o nome do broker." | Sparkplug B: uma tag ou uma variável em Leitura e escrita. |
| "Publica se o alarme está ativo e reconhecido, o estado, a prioridade e a mensagem dele em Sparkplug B sempre que um deles muda." | Sparkplug B: um alarme. |
| "A origem desta linha não existe mais, então o nó de borda se anuncia de novo sem ela." | Sparkplug B: a tag, a variável ou o alarme foi excluído. |

Abaixo dessa frase, uma linha em Leitura e escrita mostra o **Tópico de escrita** dela, o tópico da linha seguido de `/set`.
Em Sparkplug B não há tópico de escrita: uma segunda frase diz a métrica como a qual a linha é publicada, do dispositivo Sparkplug B dela para uma tag, do nó de borda da estação para uma variável e, para um alarme, as cinco métricas sob o nome dele (Active, Acked, State, Priority e Message).

Uma mudança que a lista recusa não é salva: o motivo aparece em vermelho acima dos campos da
linha, e a mudança fica na tela para você corrigir.

| O cartão diz | Quando |
| --- | --- |
| "Esta origem já é publicada neste broker: edite a linha que a publica." | Outra linha deste broker publica a origem. |
| "Outra linha deste broker já publica neste tópico: dê a esta linha um tópico próprio." | Duas linhas de um broker publicariam num tópico só. |
| "Nada pode ser publicado num filtro: o tópico não pode ter + ou #." | O tópico tem um curinga. |
| "Um tópico que começa com $ pertence ao broker: escreva outro." | O tópico começa com `$`. |
| "Este tópico não pode ser enviado: tem um caractere que nenhum tópico pode ter, ou é longo demais." | Um caractere de controle, ou mais do que um tópico MQTT carrega. |
| "Uma linha que aceita escrita precisa da tag ou da variável que a recebe." | Leitura e escrita foi escolhido antes de uma origem. |
| "A origem não pode ser escrita: escolha uma tag com acesso de escrita ou uma variável de memória, ou deixe a linha somente leitura." | Leitura e escrita num alarme, numa variável de expressão, numa tag somente leitura ou numa tag cujo driver não escreve. |
| "Este tópico e o tópico de outra linha deste broker só diferem por /set, onde uma linha aceita escrita: dê outro tópico a esta linha." | O tópico de uma linha é o tópico de outra seguido de `/set`. |
| "Esta linha da lista não existe mais. Recarregue e tente de novo." | A linha foi excluída em outro lugar enquanto você a editava. |

O que é publicado, e quando, está em [Publicação](#publishing).

## Dispositivo {#device}

| Campo | Significado | Padrão |
| --- | --- | --- |
| **Broker · fixo** | O nome do broker sobre o endereço dele, somente leitura: um dispositivo é criado sob o seu broker e fica nele. "O broker ainda não tem host" ocupa o lugar do endereço. "Broker ausente" quer dizer que o dispositivo perdeu o broker e não pode ser salvo: "Um broker MQTT é obrigatório." | o broker sob o qual ele é acrescentado |
| **Protocolo** | Como o dispositivo publica. **MQTT simples**: "Cada tag nomeia o tópico que lê e, para um payload JSON, o caminho até o valor." **Sparkplug B**: um nó de borda, ou um dispositivo atrás dele: "Cada tag nomeia uma métrica como o certificado de nascimento do nó de borda a declara; o tipo vem com a métrica." Trocá-lo confere de novo cada tag, já que um endereço passa a ser lido como tópico ou como métrica. | MQTT simples |
| **Tempo máximo sem mensagem (s)** | Só MQTT simples, de 0 a 86400, 0 quer dizer nunca: "As tags deixam de valer como leitura quando nenhuma mensagem chega no tópico delas durante este tempo. 0: nunca, para um tópico publicado só quando o valor muda. Ajuste-o para um dispositivo que publica em ritmo constante, como um sensor que reporta a cada poucos segundos." Fora dessa faixa o dispositivo não é salvo: "O tempo máximo sem mensagem precisa estar entre 0 e 86400 s." Um dispositivo Sparkplug B não tem: o certificado de morte dele já diz quando ele sumiu. | 0 |
| **ID do grupo** | Só Sparkplug B: o grupo sob o qual o nó de borda publica, o nível depois de `spBv1.0` nos tópicos dele. "Um dispositivo Sparkplug B precisa do ID do grupo." | vazio |
| **ID do nó de borda** | Só Sparkplug B: o nó de borda que publica as métricas, como o NBIRTH dele o nomeia. "Um dispositivo Sparkplug B precisa do ID do nó de borda." | vazio |
| **ID do dispositivo** | Só Sparkplug B: o dispositivo atrás do nó de borda, como o DBIRTH dele o nomeia. Vazio, mostrado como "O próprio nó de borda", lê as métricas do próprio nó de borda. | vazio |

O dispositivo não tem host, porta, credenciais nem intervalo de leitura próprios: tudo isso é
do broker. Cada ID Sparkplug B é um único nível de tópico: "Um ID Sparkplug B não pode conter '/', '+' nem '#'."
Um broker nomeia um nó de borda ou dispositivo Sparkplug B uma vez só, então um segundo
dispositivo com os mesmos IDs de grupo, nó de borda e dispositivo nesse broker é recusado:
"O dispositivo Sparkplug B Plant/Line1/Press1 no broker 'Plant' já pertence ao dispositivo 'Press'."
Dispositivos de MQTT simples não nomeiam nada próprio no broker, e dois deles podem ler os
mesmos tópicos.

A Base URI que o cabeçalho mostra é o endereço de runtime:
`mqtt://<broker id>?host=…&port=…&tls=0&keepAlive=60&clientId=…&mode=plain`, com `&silence=…` quando o dispositivo define um tempo máximo sem mensagem, e para um
dispositivo Sparkplug B `mode=sparkplug&group=…&node=…`, com `&device=…` atrás de um nó de
borda. Ela nomeia o broker pela identidade estável, nunca pelo nome amigável, e nunca leva o
usuário nem a senha.

A cópia de um dispositivo MQTT fica no mesmo broker. Uma cópia Sparkplug B pega a primeira
identidade que nenhum dispositivo desse broker tem: o ID mais interno dela, o ID do
dispositivo ou, para as métricas do próprio nó de borda, o ID do nó de borda, é numerado do
jeito que o nome da cópia é ("Press1 (2)").

## Endereçamento da tag {#tag-addressing}

O campo de endereço é **Tópico** num dispositivo de MQTT simples e **Métrica** num
Sparkplug B, e é obrigatório ("Tópico é obrigatório para este driver."). Uma tag simples tem
mais três campos; uma métrica Sparkplug B não tem nenhum, porque leva o próprio tipo e é
escrita pelo comando do próprio protocolo.

| Campo | Significado | Padrão |
| --- | --- | --- |
| **Tópico** | O único tópico que a tag lê, por exemplo `plant/line1/temperature`. "Uma tag lê um único tópico, por isso o tópico não pode ter os curingas + ou #." | obrigatório |
| **Caminho do valor** | Onde o valor fica dentro de um payload JSON: membros separados por pontos e índices de array entre colchetes, por exemplo `sensors[0].value`. Um membro cujo nome tem um ponto ou um colchete é escrito entre aspas dentro de colchetes (`['flow.rate']`), e um `$.` no começo é aceito. Vazio, mostrado como "Payload inteiro", lê o payload inteiro como o valor. Um caminho malformado: "Escreva o caminho do valor como membros separados por pontos e índices de array entre colchetes, por exemplo sensors[0].value." | vazio |
| **Tópico de comando** | Onde uma escrita é publicada. Vazio deixa a tag somente leitura: uma tag cujo Acesso escreve precisa de um ("Uma tag gravável precisa do tópico em que as escritas dela são publicadas."), e ele não tem curinga ("Nada pode ser publicado num filtro: o tópico de comando não pode ter + ou #."). Um equipamento raramente escuta no tópico em que relata, e é por isso que este é um campo à parte. | vazio |
| **Modelo de payload** | O que uma escrita publica: JSON com `{value}` uma vez, fora de aspas, onde vai o valor escrito, por exemplo `{"setpoint": {value}}`. Vazio, mostrado como "O valor como texto", publica o valor como texto. "O modelo de payload tem {value} exatamente uma vez, onde vai o valor escrito." / "Escreva o modelo de payload em JSON, com {value} fora de aspas onde vai um valor JSON." | vazio |
| **Métrica** | Sparkplug B: o nome da métrica como o certificado de nascimento o declara, por exemplo `Inputs/Temperature`. | obrigatório |

Os tipos de dado são Boolean, Int16, Int32, Int64, Float, Double, String e DateTime. A
prévia somente leitura do endereço no fio é o tópico seguido do que se aplica de `path`,
`command` e `payload`
(`plant/line1/temperature?path=sensors[0].value&command=plant/line1/setpoint`), ou o nome da
métrica; um caractere que encerraria uma parte (`%`, `?`, `&`, `=`, `#`, `+`, um espaço) é
escrito como `%XX`.

## Leitura {#reading}

A sessão guarda a última mensagem de cada tópico que assinou, e uma tag lê dela:

| Situação | Tag de MQTT simples | Tag Sparkplug B |
| --- | --- | --- |
| O valor | A última mensagem no tópico: o payload inteiro como texto, ou o valor JSON no caminho do valor, convertido para o tipo da tag. | O valor da métrica na última mensagem de nascimento ou de dados do nó de borda. |
| O carimbo de tempo | Quando a mensagem chegou. Uma mensagem que o publicador reteve dá o primeiro valor assim que a estação assina. | O carimbo de tempo da própria métrica. |
| Nada recebido ainda | A tag espera: sem valor e sem erro, como com qualquer driver antes da primeira resposta. Um tópico cujo publicador não retém fica sem valor até ele publicar de novo. Com um **Tempo máximo sem mensagem (s)**, a espera dura esse tempo desde que a estação começou a escutar, e depois a tag falha a leitura. | O mesmo até o certificado de nascimento chegar; a estação pede ao nó de borda que se anuncie de novo. |
| O publicador se calou, o broker continua acessível | Com um **Tempo máximo sem mensagem (s)**, uma tag cujo tópico está há mais tempo sem mensagem falha a leitura, e a política da tag para [um dispositivo que não respondeu](connector-value-pipeline#the-device-did-not-answer) decide o que ela mostra; a mensagem seguinte traz o valor de volta. O tempo conta a partir da chegada da última mensagem, inclusive uma retida. Com 0 a tag mantém o último valor que recebeu. | Não se aplica: o certificado de morte diz. |
| Uma mensagem que a tag não consegue ler | Uma leitura falha dessa tag, com o motivo: uma mensagem vazia, um payload que não é JSON sob um caminho do valor, nada no caminho, ou um valor que o tipo não comporta. | Uma leitura falha dessa tag: uma métrica que o certificado de nascimento não declara, ou um valor que o tipo dela não comporta. |
| O broker inacessível | Todo dispositivo do broker fica desconectado, e as tags dele falham como as de qualquer dispositivo. | O mesmo. |
| Um certificado de morte (NDEATH, DDEATH) | Não se aplica. | O nó de borda e os dispositivos dele, ou aquele único dispositivo, ficam sem valor até nascerem de novo; todo outro dispositivo do broker segue. |

Uma tag Boolean aceita `true` ou `false`, `1` ou `0`, `on` ou `off`. Uma tag inteira aceita
um número fracionário arredondado e recusa um fora da faixa dela. Uma tag DateTime aceita
texto ISO 8601 ou milissegundos Unix. Uma tag String aceita o texto como está, e um objeto ou
array JSON como o JSON bruto. Uma métrica Sparkplug B de tipo sem sinal é lida no tipo com
sinal imediatamente mais largo (UInt8 como Int16, UInt16 como Int32, UInt32 e UInt64 como
Int64), para que a faixa inteira dela seja lida.

## Escrita {#writing}

Uma escrita passa pelo mesmo funil que a de qualquer outro driver: a caixa de escrita recebe o
valor de engenharia, e os estágios de conversão são revertidos antes de o valor bruto ser
publicado (veja [Connector](connector)).

| Tag | O que é publicado | QoS | O que a escrita relata |
| --- | --- | --- | --- |
| MQTT simples | O valor no **Tópico de comando** da tag, como texto (`21.5`, `true`, o próprio texto, uma data em ISO 8601), ou posto no **Modelo de payload** como valor JSON (um número como número, um booleano como `true` ou `false`, um texto entre aspas e escapado). | 1, sem retenção | Feita quando o broker a confirma; recusada, com o motivo do broker, quando um broker MQTT 5 a rejeita; sem resposta quando nenhuma confirmação chega a tempo. |
| Sparkplug B | Um NCMD (uma métrica do nó de borda) ou um DCMD (uma métrica de um dispositivo atrás dele) levando a métrica, sob o alias que o nascimento deu a ela e com o tipo que o nascimento declarou. | 0, sem retenção, como a especificação Sparkplug B exige dos comandos | Feita quando enviada. O nó de borda responde publicando o valor novo, que é o que a tag lê em seguida. |

Enquanto o broker está inacessível, ou o nó de borda ou o dispositivo anunciou a própria
morte, uma escrita é recusada como desconectada e nada é enviado. Uma escrita Sparkplug B
numa métrica que o certificado de nascimento não declara é recusada, já que o nó de borda não
a aceitaria, e o mesmo vale para um valor que o tipo da métrica não comporta. Uma escrita
nunca fica retida: um setpoint retido seria aplicado de novo por todo assinante que se
conectasse depois.

## Publicação {#publishing}

Esta seção descreve o **Formato da publicação** em **JSON**; o Sparkplug B tem a seção seguinte.

Cada linha do [cartão Publicar](#the-publish-card) de um broker é publicada no tópico dela,
retida, com QoS 1, pela conexão que os dispositivos do broker usam: a estação nunca abre uma
segunda conexão com um broker. Um broker usado só para publicar não precisa de dispositivo. Ele
fica sob o driver MQTT, e o estado dele (Conectando…, Conectado, Inacessível) é o dessa conexão.
Ele publica enquanto está habilitado e a lista tem linhas; não tem chave própria.

| Origem | Mensagem (JSON) |
| --- | --- |
| Tag | `{"value": 21.5, "quality": "good", "timestamp": "2026-10-04T12:30:15.250Z"}`: o valor de engenharia que a tag mostra; `quality` é `good` para uma leitura que chegou, `bad` com `value` nulo para uma que não chegou, e `uncertain` para um valor de substituição, para o último valor mantido e para leituras que pararam de chegar (o último valor publicado fica); o carimbo da leitura, em UTC. |
| Variável da Logic | Os mesmos membros: o valor da variável e `good` enquanto ela está boa, `null` e `bad` caso contrário; o carimbo da última escrita ou do último cálculo. |
| Alarme | `{"active": true, "acked": false, "state": 1, "priority": "High", "message": "Oven above 80 °C", "timestamp": "2026-10-04T12:30:15.250Z"}`: se está ativo e reconhecido, `state` como número (0 normal, 1 ativo e não reconhecido, 2 ativo e reconhecido, 3 normalizado e não reconhecido), a prioridade pelo nome (Critical, High, Medium, Low), a mensagem (vazia quando não há) e o carimbo da última transição, como o [servidor OPC UA](connector-opcua-served-nodes#where-a-client-finds-each-source) o serve. |

Um valor viaja no tipo JSON dele: um número, `true` ou `false`, texto, uma data e hora como
texto ISO 8601; um número que o JSON não carrega (NaN, um infinito) viaja como `null`. Uma tag
de um dispositivo desabilitado publica `null` e `bad`.

| Situação | O que é publicado |
| --- | --- |
| O valor ou a qualidade muda | Aquela linha, verificada cinco vezes por segundo. Uma tag lida de novo com o mesmo valor não é publicada de novo: só o carimbo não é mudança. |
| A conexão abre, ou abre de novo | Todas as linhas, com o estado atual. |
| O broker está fora do ar | Nada, e nada espera numa fila: quando o broker volta, todas as linhas são publicadas com o estado daquele momento. |
| O broker recusa uma mensagem, ou não a confirma | Ela é tentada de novo depois de 30 segundos. O diário registra isso na área Connector, componente `MQTT publication`, uma vez e depois num resumo a cada poucos minutos enquanto durar. |
| Uma origem é excluída | Nada mais; o broker mantém a última mensagem que guardou. |
| Uma linha é excluída | Nada mais; a mensagem retida dela fica no tópico até outra coisa ser publicada ali. |

A estação lê o que publica e não calcula nada para isso: a última leitura de uma tag, o valor
de uma variável, o estado de um alarme. Ela não assina tópico nenhum de uma linha somente leitura, então uma mensagem que outro sistema publica ali nunca chega à estação; uma linha em Leitura e escrita aceita valores no tópico de escrita dela ([Escrita de volta](#writing-back)). Um agente lê as listas com
`mqtt_publish_list` e as muda com `mqtt_publish_upsert` e `mqtt_publish_delete`
([Ferramentas](agent-tools#connector--configure)):

```text
mqtt_publish_upsert(brokerId: "<id from mqtt_broker_list>", source: "Connector.Simulated.Oven.Temperature")
mqtt_publish_upsert(brokerId: "<id from mqtt_broker_list>", source: "Alarms.Oven.HighTemperature", topic: "plant/oven/alarms/high")
mqtt_publish_upsert(id: "<id from mqtt_publish_list>", access: "ReadAndWrite")
mqtt_publish_list()
```

## Publicação em Sparkplug B {#publishing-in-sparkplug-b}

Com **Formato da publicação** em **Sparkplug B**, o broker publica a mesma lista pela especificação
Sparkplug B, com a estação como um nó de borda: um host Sparkplug B, como o MQTT Engine do Ignition,
lista a estação sob o grupo e o nó de borda dela, cada dispositivo abaixo dela e cada métrica com o tipo
dela, sem nenhum mapeamento feito à mão. A conexão é a mesma que os dispositivos do broker usam, como em JSON.

| Nome | O que é |
| --- | --- |
| Grupo | **ID do grupo**, `GanterLab` enquanto vazio. |
| Nó de borda | O nome da estação, com cada `/`, `+` e `#` trocado por `_` (os três caracteres que um ID Sparkplug B não pode ter). |
| Dispositivo | Cada dispositivo do Connector com uma linha de uma das tags dele, nomeado do mesmo jeito. |
| Métrica de uma tag | O nome da tag, no dispositivo dela. |
| Métrica de uma variável | `Logic/<folders>/<variable>`, no nó de borda. |
| Métricas de um alarme | Cinco, no nó de borda, sob `Alarms/<folders>/<alarm>/`: `Active` e `Acked` (Boolean), `State` (um número: 0 normal, 1 ativo e não reconhecido, 2 ativo e reconhecido, 3 normalizado e não reconhecido), `Priority` e `Message` (texto). |

Uma `/` dentro de um nome vira `_`, então as pastas são os únicos níveis. Dois dispositivos, ou duas
métricas, cujos nomes ficam iguais recebem `_2`, `_3` na ordem dos próprios nomes. As linhas mantêm os
tópicos delas, nos quais nada é publicado em Sparkplug B e nos quais a lista volta a publicar quando o
broker volta para JSON.

| Mensagem | Quando |
| --- | --- |
| NDEATH, a morte do nó de borda | Entregue ao broker como última vontade da conexão cada vez que ela abre, para o broker publicá-la se a estação cair. A própria estação também a publica antes de parar de publicar: o broker de volta em JSON, desabilitado ou excluído, a lista esvaziada, o grupo ou o nome da estação mudado, o runtime parado. |
| NBIRTH, o nascimento do nó de borda | Cada vez que a conexão abre, quando um host pede um renascimento (`Node Control/Rebirth`) e quando uma variável ou um alarme entra, sai ou é renomeado: toda métrica do nó de borda com o valor presente, com `bdSeq` e `Node Control/Rebirth`. |
| DBIRTH, o nascimento de um dispositivo | Depois de cada NBIRTH, para todo dispositivo, e para um dispositivo que entra na lista: toda tag do dispositivo com o valor presente. |
| NDATA, DDATA | Cinco vezes por segundo, as métricas cujo valor ou qualidade mudou: uma mensagem para o nó de borda, uma por dispositivo. |
| DDEATH, a morte de um dispositivo | Quando a última linha de um dispositivo sai da lista, e antes de um dispositivo cujas tags mudaram nascer de novo. |

Toda mensagem sai em QoS 0, nunca retida, menos o NDEATH. O número de sequência `seq` começa em 0 a cada
NBIRTH e conta cada mensagem depois dele até 255, e então recomeça; o `bdSeq` conta as conexões, então o
NBIRTH e o NDEATH de uma conexão levam o mesmo número. Nada fica numa fila enquanto o broker está fora do ar.

| Tipo da origem | Tipo Sparkplug B |
| --- | --- |
| `Boolean` | `Boolean` |
| `SByte`, `Byte` | `Int8`, `UInt8` |
| `Int16`, `UInt16` | `Int16`, `UInt16` |
| `Int32`, `UInt32` | `Int32`, `UInt32` |
| `Int64`, `UInt64` | `Int64`, `UInt64` |
| `Float`, `Double` | `Float`, `Double` |
| `String` | `String` |
| `DateTime` | `DateTime` |
| `Guid`, `ByteString` | `UUID`, `Bytes` |

Uma tag é publicada no próprio tipo; uma variável no tipo em que o [servidor OPC UA](connector-opcua-served-nodes#where-a-client-finds-each-source) a serve (um número inteiro como Int64, um número real como Double, um valor de apresentação como texto). Cada métrica leva a qualidade na propriedade `Quality`, a convenção que os hosts Sparkplug B leem: 192 boa, 64 incerta (com o último valor publicado quando a leitura parou de chegar), 0 ruim (a métrica fica então sem valor).

| Comando | O que a estação faz |
| --- | --- |
| NCMD `Node Control/Rebirth` | Publica de novo o NBIRTH e todos os DBIRTH, com os valores presentes. |
| NCMD ou DCMD com a métrica de uma linha em Leitura e escrita | Escreve o valor na origem, como faz a [Escrita de volta](#writing-back): na ordem em que os comandos chegam, registrado no diário com a origem `MQTT <broker name>`. |
| Qualquer outra métrica, um comando sem valor ou um que o broker entrega retido | Nada é escrito; o diário registra isso na área Connector, componente `MQTT publication`. |

A estação escuta os comandos para o nó de borda sempre, para que um host possa pedir um renascimento, e
os comandos para um dispositivo só enquanto uma das linhas dele está em Leitura e escrita. Passar um
broker para Sparkplug B faz a conexão dele abrir de novo uma vez, para o broker guardar a última vontade
do nó de borda; os dispositivos do broker passam por isso como por qualquer reconexão. A estação é um nó
de borda e não uma aplicação host primária: ela não espera a mensagem STATE de um host para nascer.

```text
mqtt_broker_upsert(id: "<id from mqtt_broker_list>", publishFormat: "SparkplugB", sparkplugGroupId: "Plant1")
mqtt_publish_list()
```

## Escrita de volta {#writing-back}

Uma linha de um [cartão Publicar](#the-publish-card) em **Leitura e escrita** aceita valores no
tópico de escrita dela, o tópico da linha seguido de `/set`: um painel na nuvem que lê
`plant/oven/setpoint` manda um novo setpoint em `plant/oven/setpoint/set`. A estação o escreve
como um cliente do [servidor Modbus](connector-modbus-server) ou do
[servidor OPC UA](connector-opcua-served-nodes) dela escreve uma linha marcada leitura e escrita.

| Situação | O que acontece |
| --- | --- |
| Uma linha passa para Leitura e escrita | A estação assina o tópico de escrita dela, com QoS 1, pela conexão que os dispositivos do broker usam, e de novo cada vez que essa conexão abre. Uma linha somente leitura não é assinada, então uma mensagem no tópico de escrita dela nunca chega à estação. |
| Chega uma mensagem no tópico de escrita | `{"value": 65}` ou o valor puro (`65`, `true`, `Receita A`) é escrito na origem, na ordem em que as mensagens chegam: uma tag pelo mesmo caminho da escrita de um operador, uma variável de memória como ela é. Um número JSON, `true` ou `false` e um texto JSON chegam como o que carregam; o que não é JSON é lido como texto. O valor é convertido para o tipo da origem como toda escrita é, e um valor que o tipo não carrega é recusado. |
| A mensagem não carrega valor | Nada é escrito: uma mensagem vazia, `null`, um objeto JSON sem `value`, uma lista. |
| O broker entrega a mensagem que guardou | Nada é escrito. O broker a entrega quando a estação assina, e um setpoint deixado lá há uma hora não é um valor que alguém manda agora. Um valor publicado enquanto a estação escuta é escrito, retido ou não. |
| A origem não aceita mais escrita | Nada é escrito: uma tag que voltou a somente leitura, uma variável excluída. |
| A linha volta para Somente leitura, muda de tópico ou é excluída, ou o broker é desabilitado | A estação para de escutar aquele tópico de escrita em instantes, e uma mensagem que chega nesse meio-tempo não escreve nada. |

O diário registra cada escrita com a origem `MQTT <nome do broker>` na área Connector, como
registra a escrita de um operador. Uma mensagem que não foi escrita fica registrada na área
Connector, componente `MQTT publication`, uma vez e depois num resumo a cada poucos minutos
enquanto se repete.

O usuário e a senha do broker são a única proteção: quem o broker deixa publicar num tópico de
escrita consegue mudar aquela origem, e num broker na nuvem compartilhado com outras pessoas isso
é mais gente do que a planta. Dê à conta do painel no broker só os tópicos de que ele precisa. Num
broker em Sparkplug B, as mesmas linhas aceitam comandos Sparkplug B (NCMD, DCMD) no lugar de um
tópico de escrita ([Publicação em Sparkplug B](#publishing-in-sparkplug-b)).

```text
"C:\Program Files\mosquitto\mosquitto_pub" -h localhost -u dashboard -P <password> -t plant/oven/setpoint/set -m 65
```

## Segurança {#security}

Com **Usar TLS**, o certificado do broker é aceito de um de dois jeitos: a cadeia e o nome do
host dele são validados no repositório de certificados do Windows, como em qualquer conexão
HTTPS deste computador, ou você aprovou exatamente esse certificado em
[Acesso a equipamentos](#equipment-access), na página deste driver. Um broker cujo certificado
vem de uma autoridade certificadora sua passa a ser confiável quando essa autoridade é
instalada no repositório do Windows, e um com certificado autoassinado não precisa de nada
instalado depois que o certificado dele é aprovado. A revogação não é conferida, e nada aceita
um certificado às cegas: um desconhecido é recusado, o broker fica Inacessível, com o motivo, e
os dispositivos dele esperam a aprovação. A senha trafega dentro da conexão TLS; sem TLS ela
trafega às claras, como o cartão avisa.

## Acesso a equipamentos {#equipment-access}

Um broker com um certificado autoassinado, ou emitido por uma autoridade própria, apresenta um
certificado que nenhuma raiz instalada no Windows garante. Essa raiz não precisa ser instalada
neste computador. Com **Usar TLS**, a primeira conexão é recusada, o broker fica Inacessível,
com o motivo, os dispositivos dele mostram **Esperando a aprovação do certificado** na árvore e
nas páginas deles, cujo botão **Revisar o certificado** abre este cartão no certificado, e o
certificado espera em **Acesso a equipamentos**, no fim da página do driver MQTT, o mesmo
cartão que a página do driver OPC UA traz, até você decidir:

| Grupo | O que lista | O que você pode fazer |
|---|---|---|
| Esperando aprovação | Cada certificado recusado, sob o nome que ele declara: a impressão digital SHA-256, o endereço do broker em que foi encontrado, quando tentou pela última vez e quantas vezes, e os detalhes (titular, emissor, nomes de domínio, datas de validade). O que o **Revisar o certificado** de um dispositivo abriu fica marcado como Selecionado para revisão. | **Aprovar** aceita o certificado a partir da próxima tentativa de conexão da sessão. **Bloquear** continua recusando-o sem listá-lo aqui de novo. **Descartar todos os certificados pendentes** esvazia a lista sem aprovar nem bloquear nada. |
| Equipamentos aprovados | Os certificados que você aprovou ou importou, com quem decidiu e quando, e o endereço do broker em que cada um foi encontrado por último. | **Retirar a aprovação** encerra a sessão do broker e devolve o certificado dele à lista. **Importar certificado** aprova um arquivo de certificado público que você já tem (.der, .cer, .crt ou .pem) antes da primeira conexão. |
| Equipamentos bloqueados | Os certificados que você bloqueou. Um certificado bloqueado é recusado mesmo que o Windows confie nele. | **Desbloquear** devolve o certificado à lista, sem aprová-lo. |

Confira a impressão digital com quem administra o broker antes de aprovar o certificado. Uma
aprovação aceita aquele certificado e nenhum outro, e só dentro das datas de validade dele; ela
substitui a raiz que falta no Windows e o nome que o certificado traz, então um broker
alcançado pelo endereço IP conecta mesmo que o certificado dele nomeie outro host, e nada mais
do certificado é afrouxado. Quando o broker responde num endereço cujo certificado você aprovou
com outro, os dispositivos dele param de ler, o novo espera aqui e a estação avisa "Certificado
mudou: `<nome>`" com um botão **Revisar**. Uma descoberta no broker pelo cartão Descobrir
dispositivos esbarra nas mesmas decisões. Um certificado em que o Windows já confia é aceito
como antes e nunca aparece aqui; nada é aprovado automaticamente.

A causa do broker diz o motivo em vez de um erro de TLS sem nome: "O certificado do broker em
`<host>:<porta>` (os primeiros 16 caracteres da impressão digital) espera a sua aprovação em
Acesso a equipamentos, na página do driver MQTT. A leitura começa assim que você aprová-lo." Um
bloqueado diz "…está bloqueado nesta estação. Revise-o em Acesso a equipamentos, na página do
driver MQTT." Um certificado fora das datas de validade, revogado ou com a assinatura quebrada
é recusado com esse motivo e nunca é listado, porque nenhuma aprovação o torna válido.

As decisões ficam na pasta de dados da estação, em `ua\pki\mqtt`, e sobrevivem a um reinício.
Os verbos só são desenhados para um papel que pode configurar o Connector, e só agem na janela
da própria estação: um navegador em outro aparelho lê o cartão sem mudá-lo, e nenhuma
ferramenta de agente aprova um certificado (`connector_browse` indica os dispositivos do broker
com `awaitingCertificate`). A lista guarda até 100 certificados esperando aprovação; quando
está cheia, um novo continua recusado mas não é listado, e o cartão diz isso.

## Preparar o broker {#prepare-the-broker}

Um broker tem de aceitar conexões do computador da estação, e aceitar a entrada da estação. O
Eclipse Mosquitto 2, o broker mais comum, não faz nenhuma das duas coisas de fábrica: sem um
listener no mosquitto.conf ele só escuta no próprio computador, e quando tem um recusa cliente
sem usuário. O MQTT não diz qual broker respondeu, então a estação cita o Mosquitto como o caso
comum, nunca como certeza.

| O broker responde | O que a página do broker diz | O que muda isso |
| --- | --- | --- |
| O computador dele recusa a porta | O broker só aceita conexões do próprio computador | No mosquitto.conf, a linha listener seguida da porta (listener 1883), depois um reinício do broker; a porta liberada no firewall do computador dele |
| Ele recusa um cliente sem usuário | O broker não aceita cliente sem usuário | Usuário e Senha no cartão Conexão com o broker, com uma conta do broker; no Mosquitto 2 essa conta é criada com o mosquitto_passwd e indicada por password_file no mosquitto.conf, ou os clientes anônimos entram com allow_anonymous true |
| Ele recusa o usuário ou a senha | A recusa do próprio broker, apontando Usuário e Senha no cartão Conexão com o broker | A conta, no broker ou no cartão |

Os dois primeiros são ditos na página do broker, no lugar do erro em inglês dele, com estes
passos e a porta; os dispositivos dele continuam apontando para o broker. O aviso some sozinho
quando a sessão conecta. A marca da árvore e o registro mantêm a causa em inglês, que termina
com os mesmos passos do Mosquitto.

## Instalar um broker {#installing-a-broker}

A estação precisa de um broker ao qual se conectar, e o equipamento também. Onde você não tem
nenhum, o Eclipse Mosquitto é um broker pequeno que roda como serviço do Windows:

1. Baixe o instalador para Windows na página de downloads de mosquitto.org e execute-o.
   Ele instala o serviço **Mosquitto Broker** em `C:\Program Files\mosquitto`.
2. De fábrica, o Mosquitto só responde no computador em que roda. Para deixar o
   equipamento da rede entrar, acrescente um listener e um arquivo de senhas ao
   `mosquitto.conf` dessa pasta:

   ```
   listener 1883
   password_file C:\Program Files\mosquitto\passwd
   ```

   Crie o arquivo com a conta que a estação e o equipamento vão usar (o comando pede a
   senha):

   ```
   "C:\Program Files\mosquitto\mosquitto_passwd" -c "C:\Program Files\mosquitto\passwd" station
   ```

3. Reinicie o serviço num prompt de administrador e libere a entrada da porta TCP 1883 no
   Firewall do Windows Defender para a rede do equipamento:

   ```
   net stop mosquitto
   net start mosquitto
   ```

4. Na página do broker, informe `localhost` como **Host ou IP** quando o broker roda no
   próprio computador da estação, ou o endereço desse computador nos outros casos,
   **Porta** 1883 e a conta que você criou, depois **Habilitar**.

Para experimentar uma tag simples antes de o equipamento estar conectado, publique um valor
de teste retido a partir da mesma pasta e aponte o **Tópico** de uma tag para ele:

```
"C:\Program Files\mosquitto\mosquitto_pub" -h localhost -u station -P <password> -t plant/line1/temperature -m 21.5 -r
```

## Limites que vale conhecer {#limits-worth-knowing}

- O driver MQTT é cliente, não broker: o equipamento e a estação se conectam a um broker,
  o próprio [broker MQTT](connector-mqtt-broker) da estação ou outro.
- O cartão Publicar de um broker sai como JSON ou como Sparkplug B, nunca os dois ao mesmo
  tempo. Em Sparkplug B a estação é um nó de borda, não uma aplicação host primária, e não usa
  aliases, templates nem valores históricos.
- A estação não é uma aplicação hospedeira Sparkplug B: ela não publica mensagem STATE. Um
  nó de borda configurado para esperar uma aplicação hospedeira primária antes de publicar
  fica calado.
- O MQTT simples não diz nada de um publicador que se calou: uma tag simples mantém o último
  valor que recebeu enquanto a conexão com o broker se mantém, datado pela chegada, a não ser
  que o dispositivo dela defina um **Tempo máximo sem mensagem (s)**. Um tópico publicado só quando o valor muda
  não distingue um publicador calado de um valor estável, então o dispositivo dele fica em 0. O
  Sparkplug B diz, pelo certificado de morte.
- A sessão começa limpa: o que foi publicado enquanto a estação não estava conectada não é
  reenviado, e é a mensagem retida de um tópico que devolve um valor à tag dele.
- Uma tag lê um tópico, nunca um filtro, e um valor dele; uma métrica Sparkplug B que é um
  array, um conjunto de dados, um template ou um conjunto de propriedades não é lida.
- Só TCP, com ou sem TLS: nada de MQTT sobre WebSockets, nada de certificado de cliente e
  nenhum broker mais antigo que o MQTT 3.1.1.
- Um dispositivo não pode ser movido para outro broker: crie-o sob o outro broker e exclua o
  antigo.
