Tipos de dados Modbus explicados — coils, discrete inputs, input registers e holding registers
Tipos de dados Modbus na prática: coils, discrete inputs, input registers e holding registers, endereçamento, function codes e armadilhas reais de campo.
O engenheiro lê pela primeira vez a documentação Modbus de um CLP e encontra quatro tabelas com nomes parecidos, prefixos de cinco dígitos que começam em 0, 1, 3 ou 4, e um function code para cada operação. Em seguida vê que o endereço lógico 40001 vira 0x0000 no frame, que o registro mais óbvio aparece deslocado em uma posição e que metade dos clientes de teste mostra um valor diferente da outra metade. A confusão é herança de um protocolo de 1979 que carrega no nome dos tipos o mapa de memória do CLP Modicon 984. Este post separa as quatro tabelas, mostra os function codes que tocam cada uma e fecha com as armadilhas que aparecem no primeiro dia de campo.
Os 4 tipos de dados Modbus
O modelo de dados original do Modbus tem exatamente quatro tabelas. Cada uma define o tipo (bit ou word de 16 bits), o sentido (leitura, escrita ou ambos), o prefixo do endereço lógico e os function codes que operam sobre ela.
| Tabela | Tipo | Acesso | Prefixo lógico | Function codes |
|---|---|---|---|---|
| Coils | 1 bit | Leitura e escrita | 0xxxx (00001–09999) | 0x01, 0x05, 0x0F |
| Discrete inputs | 1 bit | Somente leitura | 1xxxx (10001–19999) | 0x02 |
| Input registers | 16 bits | Somente leitura | 3xxxx (30001–39999) | 0x04 |
| Holding registers | 16 bits | Leitura e escrita | 4xxxx (40001–49999) | 0x03, 0x06, 0x10 |
Coils e discrete inputs trafegam empacotados, oito por byte. Input e holding registers trafegam em palavras de 16 bits big-endian. Tentar escrever em input register devolve exceção 0x01 (Illegal Function); ler coil com function code 0x02 também devolve 0x01.
Coils (0x) — bit de leitura e escrita
Coils representam saídas digitais individuais. O nome remete à bobina de um relé interno: ligar e desligar contatos, acionar válvulas solenoides, energizar lâmpadas, comandar partidas e paradas. Em equipamentos modernos os coils também aparecem como flags de comando — reset de contador, limpeza de alarme, gatilho de calibração.
Três function codes tocam coils:
- 0x01 Read Coils: lê de 1 a 2000 coils consecutivos; a resposta carrega
ceil(N/8)bytes empacotados. - 0x05 Write Single Coil: escreve um coil. O valor é 0xFF00 para ligar e 0x0000 para desligar; qualquer outro valor é ilegal.
- 0x0F Write Multiple Coils: escreve até 1968 coils numa única transação.
Armadilha frequente: um coil escrito como 0xFF00 retorna como bit 1 na leitura subsequente. O cliente que persistir o valor escrito em vez de reler pode marcar inconsistência onde não há. Sempre comparar contra a representação de leitura.
Discrete inputs (1x) — bit somente leitura
Discrete inputs representam entradas digitais conectadas a sensores binários. O caso clássico é o contato seco: fim de curso, sensor indutivo, pressostato, termostato, botão de emergência, contato auxiliar de disjuntor. Em equipamentos de medição também expõem flags de estado.
Apenas um function code opera:
- 0x02 Read Discrete Inputs: lê de 1 a 2000 entradas consecutivas, com empacotamento de oito bits por byte.
Não existe escrita em discrete input. A tentativa retorna exceção 0x01 ou 0x02 conforme a implementação. A confusão mais comum acontece quando o mesmo equipamento expõe um status (sensor) e um comando (atuador) sobre o mesmo conceito: o fabricante criterioso aloca um discrete input para "alarme ativo" e um coil para "limpar alarme".
Input registers (3x) — word somente leitura
Input registers são palavras de 16 bits somente leitura. O uso original era expor o valor digitalizado de uma entrada analógica (termopar, 4–20 mA, sensor de pressão, encoder). Em equipamentos dedicados, abrigam grandezas medidas: tensão, corrente, temperatura, contadores.
- 0x04 Read Input Registers: lê de 1 a 125 registros consecutivos. A resposta carrega
2×Nbytes em big-endian.
A separação semântica importa: input register é o contrato de "isto é o que o equipamento mede, você não pode mexer". Em arquiteturas Secure by Design, manter grandezas medidas em tabela somente leitura simplifica auditoria. Na prática, muitos fabricantes consolidam tudo em holding registers; o AEM-60DC8 segue essa linha com 147 holding registers documentados.
Holding registers (4x) — word de leitura e escrita
Holding registers são palavras de 16 bits de leitura e escrita. É a tabela mais usada porque cabe tudo: setpoints, limites de alarme, configuração, status, telemetria, contadores, identificação, calibração.
Três function codes dominam:
- 0x03 Read Holding Registers: lê de 1 a 125 registros. Operação mais frequente em supervisórios.
- 0x06 Write Single Register: escreve uma word em um único registro.
- 0x10 Write Multiple Registers: escreve até 123 registros numa transação atômica.
Function codes secundários incluem 0x16 (mask write) e 0x17 (read/write multiple). O contrato de um holding register precisa documentar, além do endereço: unidade, escala, offset, faixa válida, valor sentinela (0x8000 em int16) e persistência.
Endereçamento — origem 0 vs 1, offset Modicon, mapa hex × decimal
O ponto onde mais integrações tropeçam não é o function code, é o endereço. Duas convenções coexistem:
- Endereço lógico (1-based, com prefixo): notação Modicon. Primeiro coil 00001, primeiro discrete input 10001, primeiro input register 30001, primeiro holding register 40001.
- Endereço de protocolo (0-based, sem prefixo): o que vai dentro do frame. Primeiro registro de qualquer tabela é 0x0000. Quem distingue a tabela é o function code.
| Tabela | Endereço lógico | Endereço de protocolo | Function code |
|---|---|---|---|
| Coils | 00001 | 0x0000 | 0x01 |
| Coils | 00100 | 0x0063 | 0x01 |
| Discrete inputs | 10001 | 0x0000 | 0x02 |
| Input registers | 30016 | 0x000F | 0x04 |
| Holding registers | 40001 | 0x0000 | 0x03 |
| Holding registers | 40147 | 0x0092 | 0x03 |
Regra prática: subtrair o prefixo, subtrair mais 1 e converter para hexadecimal. Algumas ferramentas adotam uma terceira convenção com prefixo 4 e endereço 0-based, onde 40000 é o mesmo registro que 40001 na notação clássica. Em ambiguidade, capturar o frame com analisador serial e ler o byte direto.
Como o AEM-60DC8 organiza os 147 holding registers
O AEM-60DC8 (Plataforma Industrial de Supervisão DC, firmware v1.03) expõe 147 holding registers em 17 blocos funcionais. O recorte abaixo é um exemplo simplificado com fins didáticos; o mapa autoritativo é o anexo do manual técnico.
| Bloco | Faixa (hex) | Registros | Sentido | Conteúdo |
|---|---|---|---|---|
| Tensão CH1–CH8 instantânea | 0x0000–0x0007 | 8 | RO efetivo | uint16 com escala ×100 |
| Tensão CH1–CH8 média 1 s | 0x0008–0x000F | 8 | RO efetivo | Janela móvel de 1 s |
| Status por canal | 0x0010–0x0017 | 8 | RO efetivo | Bitfield: presente, em alarme, calibrado |
| Limites superiores/inferiores | 0x0020–0x002F | 16 | RW | Setpoints por canal |
| Configuração de canal | 0x0040–0x004F | 16 | RW | Habilitação, modo, oversampling |
| Configuração de comunicação | 0x0050–0x0057 | 8 | RW | Slave ID, baudrate, paridade, timeout |
| Contadores de evento | 0x0060–0x006F | 16 | RO efetivo | Subida e descida de alarme |
| Telemetria forense | 0x0070–0x007F | 16 | RO efetivo | Uptime, watchdog resets, CRC errors |
| Calibração de fábrica | 0x0080–0x008F | 16 | RW (protegido) | Ganho e offset, exige código |
| Identificação | 0x0090–0x0092 | 3 | RO efetivo | Modelo, serial, firmware v1.03 |
Holding registers com sentido "RO efetivo" aceitam escrita no protocolo, mas o firmware rejeita com exceção 0x04 quando o conteúdo é resultado de medida. Publicar um mapa sem indicar sentido efetivo, unidade, escala e persistência transforma cada integração em negociação.
Erros comuns na leitura
Cinco armadilhas explicam a maioria dos relatos de "Modbus que não funciona" em campo:
Off-by-one: ler holding register 40016 escrevendo 0x0010 em vez de 0x000F. O servidor responde com o valor de 40017. Corrigir: subtrair o prefixo e mais 1 antes de converter.
Byte order: Modbus é big-endian dentro do registro de 16 bits — byte alto primeiro. Implementações little-endian leem com bytes invertidos. Sintoma: 24,00 V vira 0,06 V (0x0960 lido como 0x6009).
Word swapping em 32 bits: float, int32 e contadores ocupam dois registros consecutivos. A ordem das palavras (high word primeiro vs low word primeiro) não é padronizada. As quatro combinações (ABCD, CDAB, BADC, DCBA) aparecem em campo. Sintoma: valores pequenos certos, valores grandes absurdos.
Signed vs unsigned: o protocolo trafega 16 bits crus, sem indicar sinal. -100 chega como 0xFF9C; lido como uint16 vira 65436. Tensões com referência flutuante podem ser negativas e exigem interpretação signed.
Quantidade fora do limite: 0x03 e 0x04 aceitam até 125 registros; 0x10 aceita até 123. Pedidos maiores devolvem exceção 0x03. Ler 147 registros num único request não funciona — quebrar em duas leituras.
Perguntas frequentes
Por que existem quatro tabelas em vez de uma só? A separação reflete a memória do CLP Modicon 984 dos anos 80, onde saídas digitais, entradas digitais, entradas analógicas e memória de configuração ocupavam áreas físicas distintas. A especificação preservou a separação para deixar explícita a semântica RO vs RW e bit vs word.
Posso usar holding register para tudo? Tecnicamente sim, e muitos fabricantes consolidam medidas, setpoints e status em holding registers. A perda é semântica: o cliente precisa saber por convenção quais registros são leitura efetiva.
Como leio um valor float de 32 bits em Modbus? Lendo dois holding registers consecutivos e reconstruindo o float conforme a ordem documentada. A ordem mais comum em equipamentos modernos é CDAB (low word primeiro), mas ABCD aparece em equipamentos europeus.
O AEM-60DC8 expõe coils ou discrete inputs? Não. O AEM-60DC8 consolida todas as 147 variáveis em holding registers, com suporte aos function codes 0x03 e 0x10. Status de canal e flags de alarme são expostos como bitfields.
Como saber se o endereço da documentação é lógico ou de protocolo? Pelo número: cinco dígitos começando em 0, 1, 3 ou 4 é endereço lógico. Hexadecimal ou começando em 0x0000 é endereço de protocolo. Em ambiguidade, capturar o frame e ler o byte direto.