arendst / Tasmota

Alternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at
https://tasmota.github.io/docs
GNU General Public License v3.0
22.16k stars 4.8k forks source link

Generic Modbus energy meter (xnrg_29_modbus) - handling of UINT32 and INT32 possibly wrong (word order) #17467

Closed r-werner closed 1 year ago

r-werner commented 1 year ago

PROBLEM DESCRIPTION

A clear and concise description of what the problem is. Generic Modbus energy meter (xnrg_29_modbus) allows to receive UINT32 and INT32 (4 byte) numerical values. The code needs to translate the data (4 bytes) received from the serial interface into native float.

When receiving numerical MODBUS values of "Datatype" 4 (called "4-byte unsigned" by the code comments) the current implementation is assuming a certain byte and word order (4 bytes are received in 2 words with 2 bytes per word).

But the current implementation does not match the expected behaviour of my solar inverter (SOLAX X3 MIC) resulting in wrong values (e.g. for "Energy Total") Several MODBUS specifications found online as well as other protocol implementations specify the following de-coding logic for INT32 values: 32-bit values must be read as two consecutive Modbus registers (register count = 2). The byte order (inside the single word) is MSB->LSB. In INT32 and UINT32 formats, the word order is LSW-> MSW.

Example: an int32 consisting of 4 bytes B3, B3, B1, B0 would be received in the order: B1, B0, B3, B2 This is because the 4 bytes are split across two MODBUS registers (e.g. A and A+1). The first register holds B1 and B0 and the next register holds B3 and B2 (both registers are read together resulting in 4 bytes to be decoded).

REQUESTED INFORMATION

Make sure your have performed every step and checked the applicable boxes before submitting your issue. Thank you!

- [X] Provide the output of this command: `Status 0`:
```lua
  STATUS 0 output here:
00:05:21.149 RSL: STATUS = {"Status":{"Module":18,"DeviceName":"Tasmota","FriendlyName":["Tasmota"],"Topic":"tasmota_511F8E","ButtonTopic":"0","Power":0,"PowerOnState":3,"LedState":1,"LedMask":"FFFF","SaveData":1,"SaveState":1,"SwitchTopic":"0","SwitchMode":[0,0,0,0,0,0,0,0],"ButtonRetain":0,"SwitchRetain":0,"SensorRetain":0,"PowerRetain":0,"InfoRetain":0,"StateRetain":0,"StatusRetain":0}}
00:05:21.159 RSL: STATUS1 = {"StatusPRM":{"Baudrate":9600,"SerialConfig":"8N1","GroupTopic":"tasmotas","OtaUrl":"http://ota.tasmota.com/tasmota/release/tasmota.bin.gz","RestartReason":"Power On","Uptime":"0T00:05:21","StartupUTC":"","Sleep":50,"CfgHolder":4617,"BootCount":8,"BCResetTime":"1970-01-01T00:00:00","SaveCount":20,"SaveAddress":"F8000"}}
00:05:21.166 RSL: STATUS2 = {"StatusFWR":{"Version":"12.3.1(tasmota)","BuildDateTime":"2022-12-21T12:48:18","Boot":31,"Core":"2_7_4_9","SDK":"2.2.2-dev(38a443e)","CpuFrequency":80,"Hardware":"ESP8266EX","CR":"362/699"}}
00:05:21.172 RSL: STATUS3 = {"StatusLOG":{"SerialLog":0,"WebLog":4,"MqttLog":0,"SysLog":0,"LogHost":"","LogPort":514,"SSId":["...",""],"TelePeriod":300,"Resolution":"558180C0","SetOption":["00008009","2805C80001000600003C5A0A192800000000","00400080","00006000","00004000","00000000"]}}
00:05:21.184 RSL: STATUS4 = {"StatusMEM":{"ProgramSize":634,"Free":368,"Heap":24,"ProgramFlashSize":4096,"FlashSize":4096,"FlashChipId":"16405E","FlashFrequency":40,"FlashMode":"DOUT","Features":["00000809","8F9AC787","04368001","000000CF","010013C0","C000F981","00004004","00001000","54000020","00000090"],"Drivers":"1,2,3,4,5,6,7,8,9,10,12,16,18,19,20,21,22,24,26,27,29,30,35,37,45,62","Sensors":"1,2,3,4,5,6"}}
00:05:21.193 RSL: STATUS5 = {"StatusNET":{"Hostname":"tasmota-....","IPAddress":"...","Gateway":"192.168.0.1","Subnetmask":"255.255.255.0","DNSServer1":"192.168.0.1","DNSServer2":"0.0.0.0","Mac":".....","Webserver":2,"HTTP_API":1,"WifiConfig":4,"WifiPower":17.0}}
00:05:21.199 RSL: STATUS6 = {"StatusMQT":{"MqttHost":"","MqttPort":1883,"MqttClientMask":"DVES_%06X","MqttClient":"DVES_511F8E","MqttUser":"DVES_USER","MqttCount":0,"MAX_PACKET_SIZE":1200,"KEEPALIVE":30,"SOCKET_TIMEOUT":4}}
00:05:21.208 RSL: STATUS7 = {"StatusTIM":{"UTC":"1970-01-01T00:05:21","Local":"1970-01-01T00:05:21","StartDST":"1970-01-01T00:00:00","EndDST":"1970-01-01T00:00:00","Timezone":"+00:00","Sunrise":"20:13","Sunset":"05:47"}}
00:05:21.213 RSL: STATUS9 = {"StatusPTH":{"PowerDelta":[0,0,0],"PowerLow":0,"PowerHigh":0,"VoltageLow":0,"VoltageHigh":0,"CurrentLow":0,"CurrentHigh":0}}
00:05:21.220 RSL: STATUS10 = {"StatusSNS":{"Time":"1970-01-01T00:05:21","ENERGY":{"TotalStartTime":"1970-01-01T00:00:00","Total":977.900,"Yesterday":0.000,"Today":0.000,"Power":0,"Voltage":0}}}
00:05:21.226 RSL: STATUS11 = {"StatusSTS":{"Time":"1970-01-01T00:05:21","Uptime":"0T00:05:21","UptimeSec":321,"Heap":24,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":0,"Wifi":{"AP":1,"SSId":"...","BSSId":":..:..","Channel":1,"Mode":"11n","RSSI":100,"Signal":-46,"LinkCount":1,"Downtime":"0T00:00:02"}}}


### TO REPRODUCE
_Steps to reproduce the behavior:_
1) build tasmota with USE_MODBUS_ENERGY to include "Generic Modbus energy meter"
2) connect RS485 (for hardware setup see documentation for SOLAX X1 sensor)
3) configure TX and RX for GPIO1 and GPIO3
4) upload and enable rule using an int32(e.g. rule3 on file#modbus do {"Name":"X3MIC","Baud":9600,"Config":8N1","Address":1,"Function":4,"Voltage":{"R":0x0404,"T":3,"M":10},"Power":{"R":0x040e,"T":3,"M":1},"Total":{"R":0x0423,"T":4,"M":1000}} endon)
5) wait for data to come in and compare transmitted bytes with expected int32 (float) value

### EXPECTED BEHAVIOUR
_A clear and concise description of what you expected to happen._
line 294 in xnrg_29_modbus.ino
current impl:
          uint32_t value_buff = ((uint32_t)buffer[3])<<24 | ((uint32_t)buffer[4])<<16 | ((uint32_t)buffer[5])<<8 | buffer[6];
possible fix?:
          uint32_t value_buff = ((uint32_t)buffer[5])<<24 | ((uint32_t)buffer[6])<<16 | ((uint32_t)buffer[3])<<8 | buffer[4];

But what about other hardware (inverter & sensor) implementations using a different byte (word) order ???
--> adding another Datatype?

### SCREENSHOTS
_If applicable, add screenshots to help explain your problem._

### ADDITIONAL CONTEXT
_Add any other context about the problem here._
see document 
1) from SOLAX: "SolaxPower External Communication Protocol X1&X3 Hybrid -G4  ModbusRTU V3.14- public version.pdf" (easy to find via Google)
2) https://www.bitzer.de/shared_media/html/ct-310/de-DE/441051147512417675.html

**(Please, remember to close the issue when the problem has been addressed)**
arendst commented 1 year ago

Thx for your elaborate info.

This driver is some work in progress based on user requests/support.

I'll add another datatype solving your issue soon.

arendst commented 1 year ago

Try latest dev release. It adds two new datatypes 6 and 8.

6 for swapped int32, 8 for swapped uint32 as you suggested.

Give it a try.

r-werner commented 1 year ago

Thanks for the super quick turn around!!! ...tried just now, but it is dark, no sun, no power and inverter is "sleeping" ...only returning 00 00 00 00... and with this I can't see any difference ;-) will try again tomorrow.

r-werner commented 1 year ago

tested and works as expected! :-) the following input value (received from the inverter): 01 04 04 EB EC 00 0E 8E 51 was successfully decoded to (incl. devision by 1000): 977.9000