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: 61

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 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. 

Destaques

Registre-se no
Lab de Garagem
Clicando aqui

Convide um
amigo para fazer
parte

curso gratis de arduino

© 2020   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço