Aquisição de dados rede rs485 com protocolo Modbus RTU via Arduino PM5300

Bom dia

Acredito que ao entrar nesse blog perguntei sobre como realizar a aquisição de dados pela rede rs485 com protocolo modbus e Arduino, sendo o Arduino o Master.

Como consegui resolver meu problema, gostaria de compartilhar com todos do blog.

Antes de usar ou tentar ler o código sugiro que você busque como funciona a rede rs485, e o protocolo Modbus RTU, o foco deste compartilhamento é somente mostrar os materiais utilizados e como foi implementado.

Sugiro algumas leituras em relação à isso:

https://www.embarcados.com.br/protocolo-modbus/

http://mundoprojetado.com.br/modbus-rtu-o-que-e-e-como-funciona/

http://www.simplymodbus.ca/FAQ.htm

http://mundoprojetado.com.br/rs-485/

https://www.citisystems.com.br/rs485

https://www.embarcados.com.br/redes-de-comunicacao-em-rs-485/

Vamos ao tutorial, os materiais utilizados para essa aquisição de dados foram os seguintes:

  • Arduino Mega
  • MAX485
  • PM5300

Seguem as fotos dos materiais utilizados

A partir disso foram feitas as seguintes ligações

Gostaria de informá-los que o equipamento da imagem acima é diferente, porém as ligações são as mesmas, a escolha do Arduino Mega , foi a possibilidade de utilizar múltiplas portas seriais e diversos padrões de comunicação, as ligações com o MAX485 configuram uma ligação Half-duplex onde existe o tempo de aquisição e de resposta usando o barramento comum, vamos agora para parte do código do Arduino.

pm5300_T.ino

REG_PM5300.h

A explicação dos códigos:

Vou começar pela explicação do REG_PM5300.h, nesse arquivo devem estar todos os registros de leitura que você desejar ler, os registros do Schneider PM5300 podem ser encontrados no site abaixo:

https://www.se.com/sg/en/faqs/FA234017/

#define ID_meter 1
#define Total_of_Reg 15

// tensões
#define Reg_VAB 3019 //
#define Reg_VBC 3021
#define Reg_VCA 3023 //
#define Reg_VAVG 3025 // Tensão Media das Fases
// correntes
#define Reg_IA 2999 // Corrente da linha A
#define Reg_IB 3001 // Corrente da linha B
#define Reg_IC 3003 // Corrente da linha C
#define Reg_IAVG 3009 // Corrente Media

// potências totais
#define Reg_WT 3059 // Potencia Ativa
#define Reg_QT 3067 // Potencia Reativa
#define Reg_ST 3075 // Potencia Aparente

#define Reg_FPT 3083 // Fator de Potência
// THD
#define Reg_THDVAB 21321
#define Reg_THDVBC 21323
#define Reg_THDVCA 21325

uint16_t Reg_addr[15] = {
Reg_VAB,
Reg_VBC,
Reg_VCA,
Reg_VAVG,
Reg_IA,
Reg_IB,
Reg_IC,
Reg_IAVG,
Reg_WT,
Reg_QT,
Reg_ST,
Reg_FPT,
Reg_THDVAB,
Reg_THDVBC,
Reg_THDVCA,
};

float DATA_METER [Total_of_Reg] ;

  

A biblioteca contempla o endereço do Escravo, o  registros a serem lidos e o n° de registros, é muito importante se atentar que geralmente os registros modbus tem esse offset de (N° do Registro -1), outra coisa é o tipo de dado a ser lido, existe a possibilidade dos dados não serem do tipo float e será necessário alterar o tipo do vetor de armazenamento, um exemplo nesse mesmo equipamento são os registros de energia, do tipo INT_64;

Vamos agora para parte do código:

#include "REG_PM5300.h"
#include <ModbusMaster.h>

As duas primeiras linhas fazem a inclusão da biblioteca ModbusMaster do Github, essa biblioteca é uma das mais completas bibliotecas Modbus para Arduino, e contempla 3 exemplos, o exemplo RS485_HalfDuplex é essencial para entender o que está acontecendo no código, o link da biblioteca modbusmaster :

https://github.com/4-20ma/ModbusMaster. 

ModbusMaster node; 

#define MAX485_DE 22
#define MAX485_RE_NEG 21

void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}

As linhas acimas apresentam a criação do objeto Node, mestre da nossa comunicação, no nosso caso o Arduino,

os pinos para o MAX485 foram escolhidos conforme um tutorial do eiglabs, mas podem ser escolhidos outros pinos sem problema nenhum, as funções de preTransmission e postTransmission serão realizadas pelo MAX485, por isso se você estiver usando outro módulo e importante que você configure as mesmas funções.

float HexTofloat(uint32_t x)
{
return (*(float*)&x);
}

uint32_t FloatTohex(float x)
{
return (*(uint32_t*)&x);
}
int64_t UinttoInt(uint64_t x)
{
return (*(int64_t*)&x);
}

double HextoDouble(uint32_t x) 

{
return (*(double*)&x);
}

As linhas acima apresentam as funções de cast do sistema, importantíssimas para leitura correta dos dados.

//------------------------------------------------

float Read_Meter_float(char addr , uint16_t REG)
{
float i = 0;
uint8_t result,j;

uint16_t data[2];
uint32_t value = 0;
node.begin(ID_meter,Serial1);
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);

if(REG!=12 )
{
result = node.readHoldingRegisters(REG,2); ///< Modbus function 0x03 Read Holding Registers
delay(500);
if (result == node.ku8MBSuccess)
{
for (j = 0; j < 2; j++)
{
data[j] = (node.getResponseBuffer(j));
}
//Serial.print(data[1],HEX);
//Serial.println(data[0],HEX);

value = data[0];
value = value 16;
value = value + data[1];

i = HexTofloat(value);

return i;
}
}
if(REG == 12){
double i = 0;
uint8_t result,j;
uint16_t data[2];
uint32_t value;
result = node.readHoldingRegisters(REG,2); ///< Modbus function 0x03 Read Holding Registers
delay(500);
if (result == node.ku8MBSuccess)
{
for (j = 0; j < 2; j++)
{
data[j] = (node.getResponseBuffer(j));
}

value = data[0];
value = value 16;
value = value + data[1];
i = HextoDouble(value);
return i;
}
}
else
{
Serial.print("Connect modbus fail. REG >>> "); Serial.println(REG); // Debug
delay(1000);
return 0;
}
}

As linhas acima realizam a função de leitura dos registros conforme o descrito no exemplo III do Github, existem duas linhas comentadas mostrando os dados em Hexa, essas linhas são importantes pelo seguinte fato, pode ser que seu dado 1 represente os inteiros e o dado 2 represente os decimais, ou isso pode estar invertidos, por isso é importante que você tenha como validar o dado que você está lendo como uma composição, um site que converter os dados em hexa para float, int e uint é o :

https://www.scadacore.com/tools/programming-calculators/online-hex-...

aqui você terá as opções de visualização em Big endiam, little endiam, mid-big, mid-little, e com o dado que aparecer tanto no medidor quanto na leitura será o dado correto.

void GET_METER()
{ // Update read all data
delay(1000);
for (char i = 0; i < Total_of_Reg ; i++)
{
DATA_METER [i] = Read_Meter_float(ID_meter, Reg_addr[i]);
}
}

As linhas acima apenas realizam as leituras e as armazenam nos vetores de DATA_METER;

//**************************************************************************************************************
void setup()
{
Serial.begin(9600);
Serial1.begin(9600,SERIAL_8N2);

Serial.println(F("Teste"));
pinMode(MAX485_RE_NEG, OUTPUT);
pinMode(MAX485_DE, OUTPUT);
// Init in receive mode
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}

As linhas acima configuram a comunicação do Arduino e PC, com velocidade de 9600, Serial 8N1. Configuram a Serial de comunicação com o medidor com velocidade de  9600, Serial_8N2 oito bits de dados sem paridade e 2 bits de parada, se você não entende o que isso significa peço que procure sobre comunicação pois é muito importante para este projeto;

void loop()
{
GET_METER();
Serial.println("Lendo as tensões do dispositivo");
Serial.print("Vab = "); Serial.print(DATA_METER[0],3);Serial.println(" V");
Serial.print("Vbc = "); Serial.print(DATA_METER[1],3);Serial.println(" V");
Serial.print("Vca = "); Serial.print(DATA_METER[2],3);Serial.println(" V");
Serial.print("Vavg = "); Serial.print(DATA_METER[3],3);Serial.println(" V");
Serial.print("");
Serial.println("Lendo as correntes do dispositivo");
Serial.print("Ia = "); Serial.print(DATA_METER[4],3);Serial.println(" A");
Serial.print("Ib = "); Serial.print(DATA_METER[5],3);Serial.println(" A");
Serial.print("Ic = "); Serial.print(DATA_METER[6],3);Serial.println(" A");
Serial.print("Iavg = "); Serial.print(DATA_METER[7],3);Serial.println(" A");
Serial.print("");
Serial.println("Lendo as potências do dispositivo");
Serial.print("Wt = "); Serial.print(DATA_METER[8],3);Serial.println(" KW");
Serial.print("Qt = "); Serial.print(DATA_METER[9],3);Serial.println(" KVAr");
Serial.print("St = "); Serial.print(DATA_METER[10],3);Serial.println(" KVA");
Serial.print("FPt "); Serial.print(DATA_METER[11],6);Serial.println(" ");
Serial.print("");
Serial.println("Lendo dos THDs por Fase");
Serial.print("THDAB = "); Serial.print(DATA_METER[12]); Serial.println(" %");
Serial.print("THDBC = "); Serial.print(DATA_METER[13]); Serial.println(" %");
Serial.print("THDCA = "); Serial.print(DATA_METER[14]); Serial.println(" %");
Serial.print("");

delay(10000);
}

As linhas acima somente mostram na serial do Arduino os dados obtidos, é necessário por as legendas de unidade de cada variável para melhorar a interpretação dos mesmos, essas unidades se encontram na própria lista de registros, o medidor pm5300 estava ligado a entrada da subestação 3 fases e sem neutro, por isso só é possível ler as tensões de linha, outra coisa no manual do pm5300 está detalhado a forma de alterar os parâmetros de comunicação, como BAUD RATE, paridade , não é possível alterar stop bits nesse equipamento.

Aqui o print de alguns resultados:

Para ler a energia é necessário realizar algumas alterações caso alguém tenha interesse:

pm5300_FINAL.ino

REG_PM5300.h

Exibições: 5837

Comentar

Você precisa ser um membro de Laboratorio de Garagem (arduino, eletrônica, robotica, hacking) para adicionar comentários!

Entrar em Laboratorio de Garagem (arduino, eletrônica, robotica, hacking)

Comentário de Weber Girardi em 14 novembro 2022 às 18:21
Olá a todos , após inúmeras tentativas hoje finalmente foi possível finalizar o projeto com a leitura dos dados de energia acumulada, com os parâmetros com dados de int64 que vem em 4 registros ( 3203, 3204, 3205 e 3206) dos Schneider PM5100 e 5300 em breve vou postar o projeto final e a explicação de como obter os dados!!!
Comentário de Weber Girardi em 12 novembro 2022 às 17:15

Ola Yago , parabéns pelo ótimo tutorial, realmente muito bem detalhado, estou implementando um projeto onde vou usar a aquisição de dados de um Schneider PM5110, e o seu código funciona muito bem, apenas quanto aos dados da Energia Acumulada que estou com muita dificuldade em conseguir converter para ficar igual ao display do medidor pois os dados são INT64 , voce conseguiu algum sucesso em ler estas informações ?? consegue me ajudar como resolver ?? Obrigado 

Comentário de Eliezer de Jesus Dantas em 27 maio 2020 às 18:41
Comentário de Wyara Maria Carlos Souza Pontes em 11 março 2020 às 13:38

Olá, obrigada por mostrar a explicação dos resultados, me ajudou a compreender muita coisa!

Estou tentando fazer algo semelhante com um datalogger utilizando um conversor max3232(rs232/TTL) e o Arduino UNO como mestre. o Hardware é o seguinte:


datalogger(RS232)----DB9(FEMEA)----DB9(FEMEA)---MAX3232----ARDUINO(TX/RX)

O protocolo é o Modbus RTU. Realizei alguns testes com as bibliotecas ModbusMaster que achei mas sem sucesso pois na serial sempre retornava um erro de código 226 e alguns caracteres estranhos. Já adiante que todos os dispositivos estão com baudrate 9600...o problema é que mesmo que eu não conecte no datalogger, ou seja, deixando só o Arduino conectado ao computador pela USB, as mensagens estranhas ainda aparecem e além disso os caracteres tem um certo padrão...enfim, não sei o que significa se alguém puder me ajudar, agradeço muito!

Comentário de José Gustavo Abreu Murta em 10 dezembro 2019 às 16:44

Yago, Parabéns por ter resolvido o seu problema. E por ter postado toda a sua solução! 

E parabéns para todos que lhe ajudaram indiretamente, através dos inúmeros tópicos sobre o assunto. 

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço