簡體   English   中英

ESP8266/Arduino modbus RTU Buffer數據轉換

[英]ESP8266 / Arduino modbus RTU Buffer data conversion

我正在使用 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。

我怎樣才能實現這個var floatA = d.buffer.readFloatBE(0); 在阿杜諾? 看起來readFloatBE(0)可以解決問題,但僅在 NodeJS / javascript 中可用。

這是我設備的數據表的一部分在此處輸入圖像描述

在此處輸入圖像描述

這是我從設備附帶的原始軟件中得到的結果: 在此處輸入圖像描述

如果有人能指出我更好的方向,我會非常感激。

更新:

我找到了 ShortBus Modbus Scanner 軟件並測試了讀數。 庫讀取結果為無符號整數,但需要交換浮點數和字序。 如下圖所示。

有人能告訴我如何設置正確的轉換嗎?

在此處輸入圖像描述

是的,所以確實問題在於var floatA = d.buffer.readFloatBE(0); Modbus 返回一個字節數組,客戶端必須解釋這些字節,最好由您正在使用的庫完成,但如果 Arduino 上不可用,您可以嘗試使用字節解碼函數手動嘗試,並考慮以下因素:

Modbus 寄存器的長度為 16 位,因此長度 1 = 16 位,長度 2 = 32 位,因此文檔上注明的數據類型為 float32 表示“2 個寄存器用於此值,解釋為浮點數”。

因此,在client.readHoldingRegisters(0000, 12)上,您要求讀取地址為 00 且大小為 12 的寄存器...所以這沒有意義,您只需要 2 個寄存器。

在您的示例節點代碼中,首先您在client.writeRegisters(5, [0, 0xffff])寄存器 5 = 0 和寄存器 6 = 0xFFFF 中將 2 個寄存器寫入地址 5,為什么? 然后你在 read() 中從地址 0 開始讀取,根據你的文檔,這是 Total KwH 的地址。

所以,你應該得到一個字節數組,你需要將它們解碼為浮點數。 Modbus 是字和字節的 Big Endian,因此您需要在解碼函數中使用它們。 我不確切知道 Arduino 中有什么可用,但希望您可以通過這些額外信息弄清楚。

我想如果你只是發送緩沖區進行打印,你會得到值的整數解釋,因此問題

我正在使用 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。

我怎樣才能實現這個var floatA = d.buffer.readFloatBE(0); 在阿杜諾? 看起來readFloatBE(0)可以解決問題,但僅在 NodeJS / javascript 中可用。

這是我設備數據表的一部分在此處輸入圖片說明

在此處輸入圖片說明

這是我從設備附帶的原始軟件中得到的結果: 在此處輸入圖片說明

如果有人能指出我更好的方向,我會很高興。

更新:

我找到了 ShortBus Modbus Scanner 軟件並測試了讀數。 庫讀取結果為無符號整數,但需要交換浮點和字序。 它如下圖所示。

有人可以告訴如何設置正確的轉換。

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM