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 do post sobre comunicação serial assíncrona. Além disto, eis alguns posts complementares ao conteúdo: I2CSPIRS-485.

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 de se utilizar o PLC.

O que é

Definição

Ignorando os termos técnicos, como protocolo comunicação 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 RS-485. Comunicação esta que permite a conexão de diversos positivos aos mesmo tempo, sendo, um deles, o mestre (quem coordenada a comunicação) e, o restante, 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: É extremamente recomendável ler o post sobre o padrão RS-485 para entender como ele funciona e para entender a diferença entre protocolo e comunicação.

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 (0 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 funciona

Formato

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

Vamos aos detalhes de cada parte:

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.

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 ter uma ideia: Na implementação que fiz, simulei os registradores de saída digital do Arduino usando um vetor de variáveis booleanas (post sai semana que vem).

Dados

Assim como foi dito na parte do código da função, o formato varia de função para função. Mas é nesta parte onde os dados específicos de função são enviados.

A quantidade de bytes neste caso 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 por um dispositivo que o envia junto à mensagem. O outro dispositivo que recebe a mensagem 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 está bem explicado. O importante para nós é 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 isto com base no que eu falei antes sobre os valores variarem entre 0 e 255. Portanto, vou explicar melhor esta parte:

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). E traduzindo isso pra hexadecimal, o endereço enviado será: 9C41.

Essa mesma lógica se aplica 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. A cada segundo, são enviados 9600 bits (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. Para sabermos o tempo total gasto,  basta considerar os 8 bits do caractere e os bits de início, parada e paridade.

Considerando uma comunicação serial assíncrona comum: 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