我正在使用ESP8266和ModbusMaster.h库与启用RS485的功率计通信。沟通很好,但回应却使我感到困惑,我无法获得正确的价值观。我的功率计显示为1.49 kWh,但Modbus的响应为16318。这是我的代码:
#include <ArduinoOTA.h>
#include <BlynkSimpleEsp8266.h>
#include <SimpleTimer.h>
#include <ModbusMaster.h>
#include <ESP8266WiFi.h>
/*
Debug. Change to 0 when you are finished debugging.
*/
const int debug = 1;
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
int timerTask1, timerTask2, timerTask3;
float battBhargeCurrent, bvoltage, ctemp, btemp, bremaining, lpower, lcurrent, pvvoltage, pvcurrent, pvpower;
float stats_today_pv_volt_min, stats_today_pv_volt_max;
uint8_t result;
// this is to check if we can write since rs485 is half duplex
bool rs485DataReceived = true;
float data[100];
ModbusMaster node;
SimpleTimer timer;
// tracer requires no handshaking
void preTransmission() {}
void postTransmission() {}
// a list of the regisities to query in order
typedef void (*RegistryList[])();
RegistryList Registries = {
AddressRegistry_0001 // samo potrosnju
};
// keep log of where we are
uint8_t currentRegistryNumber = 0;
// function to switch to next registry
void nextRegistryNumber() {
currentRegistryNumber = (currentRegistryNumber + 1) % ARRAY_SIZE( Registries);
}
void setup()
{
// Serial.begin(115200);
Serial.begin(9600, SERIAL_8E1); //, SERIAL_8E1
// Modbus slave ID 1
node.begin(1, Serial);
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
// WiFi.mode(WIFI_STA);
while (Blynk.connect() == false) {}
ArduinoOTA.setHostname(OTA_HOSTNAME);
ArduinoOTA.begin();
timerTask1 = timer.setInterval(9000, updateBlynk);
timerTask2 = timer.setInterval(9000, doRegistryNumber);
timerTask3 = timer.setInterval(9000, nextRegistryNumber);
}
// --------------------------------------------------------------------------------
void doRegistryNumber() {
Registries[currentRegistryNumber]();
}
void AddressRegistry_0001() {
uint8_t j;
uint16_t dataval[2];
result = node.readHoldingRegisters(0x00, 2);
if (result == node.ku8MBSuccess)
{
for (j = 0; j < 2; j++) // set to 0,1 for two
datablocks
{
dataval[j] = node.getResponseBuffer(j);
}
terminal.println("---------- Show power---------");
terminal.println("kWh: ");
terminal.println(dataval[0]);
terminal.println("crc: ");
terminal.println(dataval[1]);
terminal.println("-----------------------");
terminal.flush();
node.clearResponseBuffer();
node.clearTransmitBuffer();
} else {
rs485DataReceived = false;
}
}
void loop()
{
Blynk.run();
// ArduinoOTA.handle();
timer.run();
}
我尝试过类似的操作,但是使用Raspberry Pi和USB-RS485,它可以工作。 下面是NodeJS代码示例。它看起来类似于Arduino代码。
// create an empty modbus client
var ModbusRTU = require("modbus-serial");
var client = new ModbusRTU();
// open connection to a serial port
client.connectRTUBuffered("/dev/ttyUSB0", { baudRate: 9600, parity: 'even' }, read);
function write() {
client.setID(1);
// write the values 0, 0xffff to registers starting at address 5
// on device number 1.
client.writeRegisters(5, [0 , 0xffff])
.then(read);
}
function read() {
// read the 2 registers starting at address 5
// on device number 1.
console.log("Ocitavanje registra 0000: ");
client.readHoldingRegisters(0000, 12)
.then(function(d) {
var floatA = d.buffer.readFloatBE(0);
// var floatB = d.buffer.readFloatBE(4);
// var floatC = d.buffer.readFloatBE(8);
// console.log("Receive:", floatA, floatB, floatC); })
console.log("Potrosnja u kWh: ", floatA); })
.catch(function(e) {
console.log(e.message); })
.then(close);
}
function close() {
client.close();
}
此代码在控制台中显示1.493748298302。
如何在Arduino中实现此var floatA = d.buffer.readFloatBE(0);
?看起来readFloatBE(0)
可以解决问题,但仅在NodeJS / javascript中可用。
如果有人可以向我指出更好的方向,那我将是完整的。
更新:
我找到了ShortBus Modbus Scanner软件并测试了读数。 库读取结果为无符号整数,但需要交换浮点数和字序。如下图所示。
请问有人可以设置正确的转换。
答案 0 :(得分:0)
对,所以实际上问题出在var floatA = d.buffer.readFloatBE(0);
的部分上Modbus返回一个字节数组,客户端必须解释这些字节,理想情况下由您正在使用的库完成,但如果不可用在Arduino上,您可以通过以下注意事项手动尝试字节解码功能:
Modbus寄存器的长度为16位,因此长度1 = 16位,长度 2 = 32位,因此文档中标注为float32的数据类型表示 “ 2个用于该值的寄存器,解释为浮点数”。
因此,在client.readHoldingRegisters(0000, 12)
上,您要求读取地址为00且大小为12的寄存器...所以这没有任何意义,您只需要2个寄存器。
在您的示例节点代码上,首先您要编写
2个寄存器可访问client.writeRegisters(5, [0 , 0xffff])
中的地址5
寄存器5 = 0,寄存器6 = 0xFFFF,为什么?那你去看书
从地址0(在read()中)开始,该地址是每千千瓦时的总地址
您的文档。
因此,您应该获得一个字节数组,并且需要 将它们解码为浮点型。 Modbus是单词和字节的Big Endian,因此 您需要在解码功能中使用它们。我不知道 Arduino提供了什么,但希望您能弄清楚 有了这些额外的信息。
我想,如果仅发送缓冲区以进行打印,则将得到该值的整数解释,因此出现了问题
答案 1 :(得分:0)
您正确使用了此功能吗? 我正面临着同样的问题,并试图找出如何从modbus地址8192(这是2个数据块)中获取一个浮点数2个小数位。
void loop()
{
uint8_t j, result;
uint16_t dataval[2];
result = node.readHoldingRegisters(8192, 2); // slave: read (6) 16-bit registers starting at register .. to RX buffer , this address is in Decimal, so convert hex to decimal to use correct address
if (result == node.ku8MBSuccess) // do something with data if read is successful
{
for (j = 0; j < 2; j++) // set to 0,1 for two datablocks
{
dataval[j] = node.getResponseBuffer(j);
}
//********************************
String myValuea = String(dataval[0], HEX); //Convert it into Hexadecimal
String myValueb = String(dataval[1], HEX); //Convert it into Hexadecimal