Existem diversos tipos de protocolos de comunicação, mas, quando estamos comunicando com um PLC utilizando um microcontrolador, ou vice versa, é comum de se usar o Modbus RTU. Portanto, vamos aprender a fundo sobre este protocolo para conseguirmos usá-lo em um microcontrolador sem a necessidade de biblioteca.

Gostaria de deixar meus agradecimentos ao laboratório Green PUC Minas, pois foi lá onde pude pesquisar e aprender sobre o protocolo Modbus RTU. Assunto este que dificilmente é bem explicado a ponto de você conseguir reproduzir a comunicação por conta própria.

Informações importantes

Antes de explicar o protocolo Modbus RTU, explicarei brevemente sobre o PLC para quem não souber do que se trata.

Recomendo a leitura dos posts sobre o padrão RS-485, a comunicação serial assíncrona, o I2C, o SPI.

PLC

O Programmable Logic Controller (PLC) ou Controlador Lógico Programável (CLP) é um dispositivo feito para controlar processos (principalmente) industriais. Um exemplo de PLC pode ser visto na imagem abaixo:

Ele possui um conjunto de entradas e saídas digitais e analógicas que permitem: a leitura de sensores, a leitura de botões ou sinais digitais, o acionamento de cargas ou o controle do nível de tensão.

E claro, ele possui outras funções, como comunicar via RS-485 com outro dispositivo. Enfim, ele é como se fosse um microcontrolador mais robusto. Isso, porque os microcontroladores não conseguem acionar cargas de mais de 20mA (em alguns casos), enquanto o PLC suporta cargas de 2A (em alguns casos).

Sendo assim, em aplicações que envolvem sistemas industriais ou sistemas com cargas de maior potência, é muito comum utilizar o PLC.

O que é o Modbus RTU

Definição

Ignorando os termos técnicos, como protocolo comunicação/padrão/interface por ora:

O Modbus RTU não é uma regra que define a estrutura física da comunicação dos dados, mas sim um regra aplicada à forma como os dados são enviados/recebidos.

Isto é, o Modbus RTU define uma forma de interpretação dos dados que são enviados e recebidos por um dispositivo. Portanto, ele é um protocolo de comunicação.

Entretanto, obviamente, o Modbus RTU necessita de ser transmitido em um meio físico. No caso dos PLCs, a regra que define como os dados são enviados fisicamente é normalmente definida pela comunicação/padrão RS-485. Comunicação esta que permite a conexão de diversos dispositivos aos mesmo tempo, sendo que um deles é o mestre (quem coordenada a comunicação) e, o restante, são os escravos.

Por último, vale mencionar que o Modbus é encontrado em três formas diferentes: RTU, ASCII e TCP. Os três não são a mesma coisa, existindo semelhança apenas entre o RTU e o ASCII. Uma vez entendido o RTU, será mais fácil entender o ASCII.

Obs: É recomendável ler o post sobre o padrão RS-485.

Formato

O Modbus RTU, no caso de usar o padrão RS-485, recebe e envia os dados de modo serial assíncrono. Ou seja, os dois dispositivos têm que operar à mesma velocidade e cada mensagem é formada por 8 bits de dados (1 byte), possuindo também bit de parada e, em alguns casos, 1 bit de paridade.

Além disso, cada byte recebido é interpretado como um número inteiro sem sinal. Isto é, os 8 bits de dados formarão números de 0 a 255 (0000000 a 11111111 em binário). E é bem comum de visualizar os dados recebidos/enviados em hexadecimal. Por exemplo, se foi recebido o número 20, é comum exibi-lo como 14 (14 hex. = 20 decimal).

A diferença do Modbus ASCII é que ele interpreta os dados como caracteres da tabela ASCII (cada número, letra ou símbolo é um byte).

Como o Modbus RTU funciona

Formato

De forma genérica, o Modbus RTU define que a mensagem possui 5 partes distintas. E cada uma tem uma função especifica para formar a interpretação final da mensagem. Pare entender o que é cada parte, vou usar a imagem abaixo como referência.

Modbus RTU formato da mensagem

Vamos aos detalhes:

Endereço do escravo

É o endereço do escravo que deve receber a mensagem.

Ele deve variar de 1 a 247, pois os números restantes são reservados para outras aplicações. Por exemplo, o endereço 0 é para comunicar com todos os escravos ao mesmo tempo (broadcast).

Código da função

É o código que define o objetivo da mensagem. Existem 8 principais códigos:

  • 1: Leitura das saídas digitais.
  • 2: Leitura das entradas digitais.
  • 3: Leitura de registradores de saída analógica.
  • 4: Leitura de registradores de entrada analógica.
  • 5: Escrita de uma única saída digital.
  • 6: Escrita de um único registrador de saída analógica.
  • 15: Escrita de mais de uma saída digital.
  • 16: Escrita de mais de um registrador de saída analógica.

Cada código possuirá um formato diferente de mensagem. O formato da mensagem na imagem está genérico. Para saber como é o formato específico de cada mensagem, acesse o site Simply Modbus, que ele descreve como é o formato da requisição para cada código, bem como o formato da resposta.

Endereço do registrador

Define em qual registrador será feita a escrita ou leitura de dados. Se for feita uma leitura ou escrita de vários dados, ele define o registrador inicial.

No PLC, já existem faixas fixas de registradores para cada tipo de entrada/saída. Entretanto, isso não existe exatamente no caso de um microcontrolador. Portanto, para implementar em um microcontrolador, será necessário simular esses registradores dependendo da sua aplicação.

Para você ter uma ideia: Na implementação que fiz, simulei os registradores de saída digital do Arduino usando um vetor de variáveis booleanas.

Dados

O formato depende do código da função. Mas é nessa parte onde os dados específicos da função são enviados.

A quantidade de bytes não é fixa e pode variar também de função para função.

CRC

O Cyclic Redundancy Check (CRC) ou Verificação Cíclica de Redundância é a parte mais complexa de todo o Modbus RTU. Ele é um código gerado a partir da mensagem enviada que serve para verificar erros.

O CRC é gerado pelo dispositivo que o envia junto à mensagem. O outro dispositivo deve gerar novamente o CRC a partir da mesma mensagem recebida e verificar se ele é igual ao CRC recebido. Se não for, o dispositivo que recebeu a mensagem percebe o erro e envia uma mensagem indicando o problema.

Não pretendo explicar a teoria do CRC e nem a lógica matemática envolvida. Quem tiver interesse, leia o material da Wikipedia, pois é possível entender por lá. O importante aqui é saber que já existem funções prontas para calcular o CRC, que é de 16 bits neste caso.

Quantidade de bytes

Na imagem, estão apresentados quantos bytes cada parte possui. Entretanto, pode ser confuso entender isso com base no que eu falei antes sobre os valores variarem entre 0 e 255. Portanto, vou explicar melhor:

As partes que possuem apenas 1 byte de dados são tranquilas de entender, pois seus valores podem variar de 0 a 255, conforme foi informado. Agora, no caso das partes que possuem 2 bytes, como o endereço do registrador, o valor final é formado pelos 2 bytes. Sendo assim, os valores podem variar de 0 a 65535.

A dificuldade está na hora de enviar ou receber os dados, pois o número deve ser desmembrado em 2 bytes diferentes. No código é tranquilo de fazer isso com a ajuda dos operadores bit a bit. Para entender melhor, vejamos um exemplo:

Supondo que o endereço do registrador seja 40.001, que, em binário, é: ‭1001 1100 0100 0001‬. Na hora de enviar o endereço, devemos desmembrar ele em dois bytes. Sendo que o primeiro byte terá os 8 primeiros bits e o segundo byte terá os últimos 8 bits. Portanto:

  • 1º byte: 1001 1100 (156 em decimal e 9C em hexadecimal);
  • 2º byte: 0100 0001 (65 em decimal em 41 em hexadecimal).

Traduzindo tudo pra hexadecimal, o endereço enviado será 9C41. Essa mesma lógica é aplicada ao restante das partes que possuem 2 bytes.

Tempo

Sobre o tempo entre cada mensagem, é comum encontrar falando que ele deve ser no mínimo o tempo de 3,5 caracteres. Mas o que isto significa?

Resumidamente: O tempo de 1 caractere é o tempo que demora para 1 caractere ser enviado/recebido.

Supondo uma comunicação com baudrate de 9600, são enviados 9600 bits a cada segundo (neste caso baudrate e bit/s é a mesma coisa). Portanto, se dividirmos 1 por 9600 descobriremos quanto tempo demora pra enviar 1 único bit. Fazendo a divisão, descobrimos que demora cerca de 104 microssegundos para enviar 1 bit.

Considerando também a seguinte comunicação serial assíncrona: 1 bit de início; 1 bit de parada; nenhum bit de paridade. Ao todo serão 10 bits. Logo, o tempo de cada caractere, para este exemplo, é de 1,04 milissegundos.

Por fim, multiplicando este valor por 3,5, obtemos um tempo de ~4 milissegundos. Desta forma, concluímos que, se não forem recebidos dados por mais de 4 milissegundos, quer dizer que a mensagem foi terminada.

Observações finais

A explicação acima se aplica ao mestre e ao escravo (recebimento e envio dos dados). Por padrão, a resposta do escravo sempre contém o endereço dele e o código da função recebida. E o restante varia de acordo com a função.

Portanto, não deixe de acessar o site Simply Modbus para aprender o formato de cada tipo de código para saber exatamente como implementar cada função corretamente.

E também recomendo a leitura deste documento sobre o Modbus RTU. O documento detalha todas as partes da implementação do Modbus RTU e também inclui um código em C de uma função que calcula o CRC.

Implementando em um Arduino

Na parte 2 deste post, vamos aprender a implementar o Modbus RTU em um Arduino.

Implementando Modbus RTU no Arduino