0

I am using ESP8266 and ModbusMaster.h library to communicate with RS485 enabled power meter. Communication works fine but responses are the ones are confusing me and I can not get correct values. My power meter shows 1.49 kWh but response from Modbus is 16318. Here is my code:

    #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();
    }

I have tried similar thing but with Raspberry Pi and USB-RS485 and it works. Sample of NodeJS code is below. It looks similar to Arduino code.

    // 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();
    }

This code displays 1.493748298302 in console.

How can I implement this var floatA = d.buffer.readFloatBE(0); in Arduino? Looks like that readFloatBE(0) does the trick, but available only in NodeJS / javascript.

Here i part of datasheet for my device enter image description here

enter image description here

Here is what I am getting as result from original software that came with device: enter image description here

If someone could point me in better direction I would be thenkfull.

UPDATE:

I found ShortBus Modbus Scanner software and tested readings. Library read result as Unsigned integer, but need Floating Point and Word Order swapped. It is shown on image below.

Can someone tell how to set proper conversion please.

enter image description here

Maka
  • 371
  • 3
  • 14

1 Answers1

0

Right, so indeed the issue is with the part done by var floatA = d.buffer.readFloatBE(0);Modbus returns an array of bytes, and the client has to interpret those bytes, ideally done by the library you're using, but if not available on Arduino, you may try manually with byte decoding functions, with the following considerattions:

Modbus registers are 16 bit in length, so length 1 = 16 bits, length 2 = 32 bits, hence the data type noted on the docs as float32 means "2 registers used for this value, interpret as float".

Therefore, on client.readHoldingRegisters(0000, 12)you're asking to read the register with address 00, and size 12... so this makes no sense, you only need 2 registers.

On your sample Node code, first you're writing 2 registers to address 5 in client.writeRegisters(5, [0 , 0xffff]) register 5 = 0, and register 6 = 0xFFFF, why? Then you go and read from address 0, in read(), which is the address for Total KwH per your docs.

So, you should get an array of bytes, and you need to decode them as a float. Modbus is Big Endian for words and bytes, so you need to use those in the decoding functions. I don't know exactly what is available in Arduino, but hopefully you can figure it out with this extra info.

I suppose that if you just send the buffer to print, you'll get an integer interpretation of the value, hence the problem

Sergio Flores
  • 439
  • 2
  • 8
  • thanks for answer. I am not calling ` function write() ` function in Node, it is there just as a sample code. So what you are saying, I need to write it as ` client.readHoldingRegisters(0000, 2) ` what should return some data (still number which looks like 673725). How to do this "you need to decode them as a float", that decoding part? I have tried with shifting left and right, but could not get any "normal" result. – Maka May 07 '19 at 19:22
  • 1
    readHoldingRegisters is giving you a byte buffer, meaning an array of bytes, and you need to interpret this as a float number, not int. Go to your device's manufacturer manual and look for their modbus spec, somewhere they must say how are they representing decimals in 4 bytes. But as I said, modbus is big endian word, and big endian byte, so you can try extracting each byte and look for combinations of integer part and decimal part. I vote for 2 bytes for integer, and 2 bytes for decimal, but I don't know for sure, I've only done Int16 on modbus with scale factors! – Sergio Flores May 07 '19 at 20:12
  • I will try that. All I have from manual is on images above. Just wondering how does NodeJS app (library) "knows" what to look for and extract correct data by default. Convert it properly etc. – Maka May 07 '19 at 21:11
  • https://stackoverflow.com/questions/3991478/building-a-32-bit-float-out-of-its-4-composite-bytes – Sergio Flores May 08 '19 at 02:16