espressif / esp-modbus

ESP-Modbus - the officially suppported library for Modbus protocol (serial RS485 + TCP over WiFi or Ethernet).
Apache License 2.0
100 stars 48 forks source link

2 vs 4 byte IEEE754 floats in power meters (IDFGH-7198) #3

Closed brainstorm closed 4 months ago

brainstorm commented 2 years ago

Hello @alisitsyn, I've applied a slight variation of the fix you proposed in the esp32 modbus power metering thread, since __beswap_32 didn't seem to be an available symbol in the 4.4 esp-idf revision I'm using, so I went with __builtin_bswap32 instead which seems to do BE swapping too:

 // mbc_serial_master.c
@@ -401,6 +410,12 @@
         }
         // Set the type of parameter found in the table
         *type = reg_info.param_type;
+       if (((  reg_info.param_type == PARAM_TYPE_FLOAT)
+           || (reg_info.param_type == PARAM_TYPE_U32))
+           && (reg_info.param_size == 4)) {
+           // Fix endianess for FLOAT and U32 parameter here <<<<<<<<<<<<<<<<<<<
+               *(uint32_t*)value_ptr = __builtin_bswap32(*(uint32_t*)value_ptr);
+       }
     } else {
         ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.",
                                                     __FUNCTION__, reg_info.cid);

But I'm not getting the same numbers as the LCD screen is displaying, so before fudging a bit further with the offsets, I just wanted to understand why you think that my meter as 4 byte floats when the PDF manual (shown in the esp32.com forum) states 2 bytes :-?

alisitsyn commented 2 years ago

Hello @brainstorm,

I just wanted to understand why you think that my meter as 4 byte floats when the PDF manual (shown in the esp32.com forum) states 2 bytes

Page 13, table 1 (request to read registers starting from address 0x0017) in your manual shows the example command to read 3 float values. The float value contains 2 Modbus registers = 4 bytes of data. This also clarifies how the float is stored in the data field of the Modbus response (Page13, Table2). This should be checked with your actual device.

But I'm not getting the same numbers as the LCD screen is displaying, so before

__builtin_bswap32() - is used to reverse bytes (it's used for littel/big endian issues (from gcc)). It is a bit different.

Please try to use this macro instead:

// BigEndian 32bit swap 
//   just swap the two 16 bit registers since the two bytes in each
//   register are already swapped (BigEndian) as per modbus standard.
# define __beswap_32(x) \
    (__extension__                                \
     ({ uint32_t __bsx = (x);                         \
        ((((__bsx) >> 16) & 0xffff) | (((__bsx) & 0xffff) << 16)); }))

The alternative macro from Modbus code is flexible and you can change it (the index after src) address to change the order of the bytes for example:

// from : common/include/esp_modbus_common.h
#define _XFER_4_RD(dst, src) { \
    *(uint8_t *)(dst)++ = *(uint8_t*)(src + 2); \
    *(uint8_t *)(dst)++ = *(uint8_t*)(src + 3); \
    *(uint8_t *)(dst)++ = *(uint8_t*)(src + 0); \
    *(uint8_t *)(dst)++ = *(uint8_t*)(src + 1); \
    (src) += 4; \
}

Please for now use the original addresses of start registers from table 6.1.4 on Page 13 (the first column) in the Data Dictionary table. There are 4 combinations of 32-bit float representation in modbus devices: Little-Endian Big-Endian Big-Endian byte swap Little-Endian byte swap If the above-proposed macro does not work in your case (the float value set over Modbus is not showed correctly on the LCD), then try to use another representation of format (change the macro).

alisitsyn commented 2 years ago

@brainstorm,

Any update on the issue? Does the above-described solution work for you?

brainstorm commented 2 years ago

A couple of days after your message I tried a few combinations of the macro that you outlined, but no matter how I reorder the bytes, the readout did not seem to make sense still. I'll make sure I have a stable input from a DC lab power supply and triple-check because I was using solar panels and the values where fluctuating quite a bit... the last couple of weeks it has been about moving apartments which did not help in having a stable setup, but I'll come back to this, promise, I really appreciate your input and I'll keep this ball rolling ;)

I'll continue working on branch https://github.com/brainstorm/esp-modbus-power-meter/tree/alisitsyn_suggestions_debug, I'm trimming down the example to iterate faster.

alisitsyn commented 2 years ago

Thank you for the update and take your time. I can suggest using the modbuspoll tool to investigate the device first. It allows to send test commands (prepared according to manual) to the device and get answers as well as log the functionality of the transaction. Once we get the log for the reply of the command we can understand the actual change in Modbus protocol of your device and adopt the esp-modbus implementation.

brainstorm commented 2 years ago

Thanks Alex! Is there an ESP/non-windows equivalent for that tool? Looks handy but don't have physical windows machines at reach atm.

I was actually writing a small function for that purpose within the firmware: scan all holding registers linearly, shouldn't be difficult, I reckon.

alisitsyn commented 2 years ago

There are many tools for host machine that supports Modbus. I can recommend the pymodbus small example script. There are tools for android that can be found and they are usually using the BT-serial adapter for RTU.

alisitsyn commented 2 years ago

@brainstorm, I am going to close the issue because do not have response for a long time. Please feel free to reopen issue once you have some feedback.

SaiQmon commented 1 year ago

I could participate this a bit, as a total rookie. I have very little understanding about floats/integers/hex whatsoever :D I have couple Arduino Minis in the table but so far I've tried to understand this with Raspberry Pi + CH340 RS485-USB adapter + Node-Red. My unit should be identical, part number YG899-9SY, ordered about 2020 from Aliexpress. It's up and running with 3 phase load connected.

I can get feedback from modbus, but I just cannot figure how to format the response and/or if I'm polling the right addresses?

Node-red

Based on the Chinese manual on Docs -folder + some nice hand made remarks, "Phase 1 voltage" should be at address 23+24. If I poll them I get one array and one buffer element:

image

image

image

Converting these to Float(le) with Buffer-Parser in Node-Red doesn't do any good either:

image

image

One thing I don't get is that why it forms two separate values from each item? If you combine two values into one 32bit float, shouldn't it be just one number?


I just found that in "Include" folder cid_tables.h -file there is different address (I think..):

{ CID_HOLD_DATA_6, STR("Volts_phase_1"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x29, 2, // HOLD_OFFSET(holding_data6), PARAM_TYPE_FLOAT, PARAM_SIZE_FLOAT, OPTS( 0, 400, .001 ), PAR_PERMS_READ },

That 0x29 refers to decimal address 41 - right? I've tried this as well, no success.

I've done this similar float-conversion with TUF2000-M Ultrasonic flowmeter with Modbus RTU and it works like a charm, combining two values into one float.

Any ideas what I'm missing here?

SaiQmon commented 1 year ago

Hi again, I figured it out!

I got a new manual, mostly in reasonable English. YG899-9SY_manual.pdf

There we can see e.g. Phase A voltage is in address 9+10 with 4-bytes, float.

image

Some testing in Node-Red shows it's actually Big-Indian Float.

image

I don't get why I have to use length of 1 in parser? But like this it works.

image


I get a lot of CRC-errors when reading over 10 addresses at once, most likely because the RS485 fieldbus is not terminated at the moment. I will hook up some 120Ohm resistor and try it again.

image https://www.kele.com/content/blog/2014/01/17/the-rs-485-network-terminator

alisitsyn commented 4 months ago

The support for extended data types was merged to master with commit [8480ff76]. Please take a look to updated documentation