Linux-RISC / Sungrow-Meter-cheater

Sungrow S100 DTSU666 power meter emulation using a Raspberry Pi
Apache License 2.0
6 stars 1 forks source link

S100 map #1

Open Nicknack1234 opened 1 year ago

Nicknack1234 commented 1 year ago

So using the information I have used modbus master to dump the first 100 registers. I will monitor the values throughout the day to generate a proper map. So far I can see register 24 is grid frequency and 6 is grid voltage. The S100 slave ID is 32 and the value of register 0 is 64 and doesn't seem to change.

image

Linux-RISC commented 1 year ago

Right, the map is what I need because I haven't got any S100.

If I had the map, I could simulate a S100 using a PLC with a 485 interface and later a Raspberry Pi with a 485.

I also could read these values from a Shelly EM using a RBPi and send them back to the inverter.

Linux-RISC commented 1 year ago

By the way, I'm using diagslave (https://www.modbusdriver.com/diagslave.html) on my RBPi and no success, it seems that the inverter is not requesting for slave 32:

./diagslave -a 32 -b 9600 -p none /dev/ttyUSB0 diagslave 3.4 - FieldTalk(tm) Modbus(R) Diagnostic Slave Simulator Copyright (c) 2002-2021 proconX Pty Ltd Visit https://www.modbusdriver.com for Modbus libraries and tools.

Protocol configuration: Modbus RTU, frame tolerance = 0ms Slave configuration: address = 32, master activity t/o = 3.00s Serial port configuration: /dev/ttyUSB0, 9600, 8, 1, none

Server started up successfully. Listening to network (Ctrl-C to stop) ......................................................................................................................................................................................................................................................................................................................

But if I change the slave number ... What is happening here? ./diagslave -a 254 -b 9600 -p none /dev/ttyUSB0 diagslave 3.4 - FieldTalk(tm) Modbus(R) Diagnostic Slave Simulator Copyright (c) 2002-2021 proconX Pty Ltd Visit https://www.modbusdriver.com for Modbus libraries and tools.

Protocol configuration: Modbus RTU, frame tolerance = 0ms Slave configuration: address = 254, master activity t/o = 3.00s Serial port configuration: /dev/ttyUSB0, 9600, 8, 1, none

Server started up successfully. Listening to network (Ctrl-C to stop) Slave 254: readHoldingRegisters from 64, 1 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 11, 12 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 98, 3 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 120, 1 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 357, 8 references Slave 254: readHoldingRegisters from 357, 8 references ... request for addresses 11, 98, 120 and 357 is repeated.

Linux-RISC commented 1 year ago

After answering the slave 254, a new device appeared, my default language is Spanish. You could check your inverter to generate a proper map. I don't understand why your device is 32 and mine 254, my inverter model is SG5.0RS.

Captura de pantalla de 2022-10-09 00-04-03

Captura de pantalla de 2022-10-09 00-06-30

Nicknack1234 commented 1 year ago

Yea ok some differences there. SG5D is my inverter, Its ID is 1 and my S100 is definitely 32, There are some different models of smart meter, maybe thats why you are seeing a different ID? I have checked the registers of my S100 that you are seeing and there is nothing there, All of my values are in 0-12.

Linux-RISC commented 1 year ago

Yes, the MODBUS ID changes according to smart meter model. I'm going to try to cheat the inverter.

I found the way to change the language: Captura de pantalla de 2022-10-09 08-10-25 Captura de pantalla de 2022-10-09 08-10-50

Linux-RISC commented 1 year ago

I have cheated the inverter!

I need to know what every MODBUS register is, check some false data:

Captura de pantalla de 2022-10-09 23-07-58

Captura de pantalla de 2022-10-09 23-08-31

Captura de pantalla de 2022-10-09 23-09-04

Linux-RISC commented 1 year ago

Project succeeded!! Please check the documentation to emulate a S100 meter

Captura de pantalla de 2022-10-21 22-29-39

lexleite commented 1 year ago

Hi. Is there any trick to make diagslave to get the inverter messages? Im trying to see which address my inverter is looking for the meter but i get no response...

Linux-RISC commented 1 year ago

Hi. Is there any trick to make diagslave to get the inverter messages? Im trying to see which address my inverter is looking for the meter but i get no response...

Yes, you can use ./diagslave -a 254 -b 9600 -p none /dev/ttyUSB0 to get the messages from the inverter.

If you need more detailed information, please use cheater.sh instead, it displays the entire request.

I'm still using Sungrow-Meter-cheater to send the power consumption to my SunGrow SG5.0RS :-), so that I obtain the right graph of production vs consumption.

lexleite commented 1 year ago

what i meant is, have you turned off the inverter and turn on to make it query the meter? or with the inverter already on, just by plugging it, makes the inverter detect the meter?

Linux-RISC commented 1 year ago

what i meant is, have you turned off the inverter and turn on to make it query the meter? or with the inverter already on, just by plugging it, makes the inverter detect the meter?

The inverter is always sending requests, just connect the Cheater (or meter) and will be recognized by the inverter.

lexleite commented 1 year ago

I am having issues on detecting the inverter requests. I am not sure if i am connecting the pins correctly or if inverter is not sending the requests at all. Do you know who requests the meter information? Is it the inverter or the dongle?

Linux-RISC commented 1 year ago

My inverter is a SG5.0RS, you should check the documentation of your inverter to check if the procedure is different: "Do you know who requests the meter information? Is it the inverter or the dongle?" --> The inverter, of course, it's a MODBUS master

https://github.com/Linux-RISC/Sungrow-Meter-cheater

answer=FE0310000000D80000000000000000000000D8E0DA (216 W) answering to fe03016400081020 answer=FE0310000000D80000000000000000000000D8E0DA (216 W) answering to fe03016400081020 answer=FE0310000000D80000000000000000000000D8E0DA (216 W) answering to fe03016400081020 answer=FE0310000000D80000000000000000000000D8E0DA (216 W) unknown request fffe030164000810 answering to fe03016400081020 answer=FE0310000000DA0000000000000000000000DA6659 (218 W) answering to fe03016400081020 answer=FE0310000000DB0000000000000000000000DBA518 (219 W) unknown request fffe030164000810 answering to fe03016400081020 answer=FE0310000000DB0000000000000000000000DBA518 (219 W) answering to fe03016400081020 ...

El dom, 15 ene 2023 a las 23:04, lexleite @.***>) escribió:

I am having issues on detecting the inverter requests. I am not sure if i am connecting the pins correctly or if inverter is not sending the requests at all. Do you know who requests the meter information? Is it the inverter or the dongle?

— Reply to this email directly, view it on GitHub https://github.com/Linux-RISC/Sungrow-Meter-cheater/issues/1#issuecomment-1383264533, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFBZRZG4AV3ZLJ2KRIBUSVLWSRX5NANCNFSM6AAAAAARAHKN7Y . You are receiving this because you commented.Message ID: @.***>

octera commented 1 year ago

Have you been able to to further on other register ? Thanks to your work I'm able to send my current power in register starting 356 to 363 (the answser to register 356+8 register). For the record the register are build in the following way : 356+357 = Power Phase A, big endian 358+359 = Power Phase B, big endian 360+361 = Power Phase C, big endian 362+363 = Power All Phases , big endian

Now I'm trying to understand registrers :

Did you have any insight to help me to decypher thoses register ?

For the record, the code I have made in Golang inspired from your bash one :

        var powerB = make([]byte, 4)
        binary.BigEndian.PutUint32(powerB, uint32(gridPower))
        // Copy in first position
        data[1] = powerB[0]
        data[2] = powerB[1]
        data[3] = powerB[2]
        data[4] = powerB[3]
        // Copy in 4th position
        data[13] = powerB[0]
        data[14] = powerB[1]
        data[15] = powerB[2]
        data[16] = powerB[3]
octera commented 1 year ago

@Nicknack1234 could you make a new screenshot of your registers please ? it will help me to understand how the index are increasing. I will be grateful! In the same way if you can do the registrer 97, 98, 99 and 119 it will be really great!

Linux-RISC commented 1 year ago

I suppose you have checked out the file cheater.sh

As you can see, Power is a cumulated value, the script should integrate in time the instant power and send it back to the inverter using registers 356-363. At this moment, the script just sends back the instant power.

I'm sorry but I still don't know the meaning of the rest of registers.

Please, let me know if you find out the meaning of these registers :-)

octera commented 1 year ago

Some findings but I don't know how to reset index of power in the cloud app, so i'm stuck on that and cannot test deeper... My guess : 10/11 : Phase A : index for energy consumtion 12/13 : Phase A : index for energy given to the grid 14/15 : Phase B : index for energy consumtion 16/17 : Phase B : index for energy given to the grid 18/19 : Phase C : index for energy consumtion 20/21 : Phase C : index for energy given to the grid

Nicknack1234 commented 1 year ago

Once I found register 10 was the instantaneous power consumption I stopped looking at the other values. I use the S100 Single phase and this is my code to get my house power consumption. The value shows live my house power consumption.

    rr = s100Client.read_holding_registers(10, unit=32)
    if ("Exception" not in str(rr)) and ("Error" not in str(rr) and ("ModbusIOException" not in str(rr))):
      inverter[str(addr)] = rr.registers[0]
      decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, byteorder=Endian.Big, wordorder=Endian.Big)
      houseuse = decoder.decode_16bit_int() / 10
      inverter["HouseUsagef"] = houseuse
   else:
      logger.info(str(rr))

Here is the data graphed, Yellow is my S100 register 10 value. Best way to figure out what each value does is just send them all to grafana and graph them all. Green is my sungrow inverter output and blue is my victron battery.

Screenshot_20230208_101512_Chrome

octera commented 1 year ago

@Nicknack1234 thanks for the feedback, it means my assumption for the register 10 to 21 is wrong... Can I ask you to provide me the current value you have for register 10 to 21, 97,98,99 and 119 ?

octera commented 1 year ago

I have been able to identify all register and make a cheater wich fully work with the mobile app. The register describe in the file below are in fact the same than the S100 is using, it's a 1to1 match! https://github.com/bohdan-s/Sungrow-Inverter/blob/main/Modbus%20Information/DTSU666%20Meter%20Communication%20Protocol_20210601.pdf

Just some information : the energy are not in Wh directly, but in Wh divided by 10. You can retrieve the grid index in the inverter itself at register 5098/5099, it is the same number that the one displayed in the mobile app.

Now the fun part : it appear that if this register has a too high value, it mess the mobile app. So we have to buffer overflow it to go back to a value near to 0. In order to do that we need to put 0 in the register 10/11, then a high value, then 0, then a high value, ... until you reach the buffer overflow by monitoring the register 5098/5099 from the inverter

Once done, you have to remove the inverter from the app for 10 minutes (to remove all bad data), then readd it

lexleite commented 1 year ago

I have been able to identify all register and make a cheater wich fully work with the mobile app. The register describe in the file below are in fact the same than the S100 is using, it's a 1to1 match! https://github.com/bohdan-s/Sungrow-Inverter/blob/main/Modbus%20Information/DTSU666%20Meter%20Communication%20Protocol_20210601.pdf

Just some information : the energy are not in Wh directly, but in Wh divided by 10. You can retrieve the grid index in the inverter itself at register 5098/5099, it is the same number that the one displayed in the mobile app.

Now the fun part : it appear that if this register has a too high value, it mess the mobile app. So we have to buffer overflow it to go back to a value near to 0. In order to do that we need to put 0 in the register 10/11, then a high value, then 0, then a high value, ... until you reach the buffer overflow by monitoring the register 5098/5099 from the inverter

Once done, you have to remove the inverter from the app for 10 minutes (to remove all bad data), then readd it

Hi octera. Which meter are you using??

octera commented 1 year ago

I'm from France, so here is my setup : Linky (Meter from energy provider) --> MQTT this provide me grid index, injection index, apparent power, injected power MQTT --> Home Automation (Jeedom) --> MQTT MQTT -> Cheater

octera commented 1 year ago

For people that may be interested, here is my Golang code to have a fully functinal cheater taking data from MQTT : https://github.com/octera/energy-center/blob/main/fakeSungrowMeter/fakeSungrowMeter.go

trobbinsfromoz commented 11 months ago

@Nicknack1234 can you indicate what your data acquisition rate is in your data graph posted on Feb 8 ? I'm about to get a SG5.0RS-ADA with S100 installed and am trying to get some idea of the measurement delay time and the measured data refresh rate that can be obtained, compared to actual instantaneous grid export/import power levels. That will help with the control of my domestic airconditioner.

Nicknack1234 commented 11 months ago

@trobbinsfromoz yea the quickest it can run is 6 second intervals. Any faster than that and the s100 returns nothing. The values don't seem to have any delay above the 6 second polling limit. I have my victron battery system polling the s100 every 6 seconds and I can turn on a kettle and have it output the difference in grid imports.

trobbinsfromoz commented 11 months ago

Superb info - thanks so much for that insight - exactly what I was after :-)

I can then use that data to log the delay and ramp rate of my Daikon aircon power consumption to changes in its control parameter changes/settings, as a first effort in working out a likely control strategy to keeping the grid power in export when there is PV and to use the house as a thermal storage battery (as I'll wait for EV battery integration into home energy storage to mature, given it will likely still take another 2 or so years).

Linux-RISC commented 7 months ago

After updating my SG5.0RS to the firmware version SUNSTONE-S_B000.V001.P029-20231019.zip the inverter is lo longer requesting the address 356+8 registers (164H, Active power of phase A,B,C and Total active power). In addition, the meter is not detected on the web yet. But we have a new player: fe03500000018105. Register 5000H? What is that? The script cheater.sh has been updated to answer 0, but no request for active power is received yet. Do you know the solution? Please let me know. Thanks in advance.

This is the log being generated right now: answering to fe03500000018105: 0, register 20480 (5000H), 1 register, FE03020000AC50 answering to fe03003f0001a009: slave 32 (20H), register 63 (3FH), 1 register, address, FE030200FE2DD0 unknown request 207300000001c370 answering to fe03500000018105: 0, register 20480 (5000H), 1 register, FE03020000AC50 answering to fe03003f0001a009: slave 32 (20H), register 63 (3FH), 1 register, address, FE030200FE2DD0 unknown request 207300000001c370 answering to fe03500000018105: 0, register 20480 (5000H), 1 register, FE03020000AC50 answering to fe03003f0001a009: slave 32 (20H), register 63 (3FH), 1 register, address, FE030200FE2DD0 unknown request 207300000001c370 answering to fe03500000018105: 0, register 20480 (5000H), 1 register, FE03020000AC50

pdf83 commented 2 months ago

@Linux-RISC,

Have you been able to clarify the issue with register 5000H ? actually I would like also to code a kind of smart meter emulator for Sungrow inverter. I have same model as yours (SG5.0RS).

I went through all the documentation I have found on the web to try to understand the behavior and the configuration on the smart meter. I think the answer is in the documentation from Sungrow referenced in a previous comment : https://github.com/bohdan-s/Sungrow-Inverter/blob/main/Modbus%20Information/DTSU666%20Meter%20Communication%20Protocol_20210601.pdf On the first section it is mentioned that for smart meter DTSU666 the slave address is 254 and that device type code (identification of the smart meter - either DTSU666 or S100) is located at register 5000. For Model DTSU666 you should read 0x20D5.

I think that your inverter try to identify the model of the smart meter by reading register 5000H. I have not be able to find what is the model type for S100 (if somebody has it I'm more than interested !). I guess that if you put 0x20D5 in this register the system will probably see DTSU666 smart meter. I have been able to find that slave id for S100 seems to be 32d and that slave id for DTSU666 seems to be 254d

Actually it seems that by default the inverter try to talk to slave id 32d but by reading register 3FH, there is a way to refine the slave id of the smart meter. It seems that this register is holding

hope this will help :-)

Linux-RISC commented 2 months ago

Not yet, but I'm going to study the documentation again to try to find a solution, I'm in a dead end.

Thanks for your support!

Linux-RISC commented 2 months ago

This is not working as expected: answering to fe03500000018105: $FE, register 20480 ($5000), 1 register, FE030220D5740F answering to fe03003f0001a009: slave $FE, register 63 ($3F), 1 register, address, FE030200016D90 answering to 207300000001c370: $FE, register 0 ($00), 1 register, 207300000001c3700000 answering to fe03500000018105: $FE, register 20480 ($5000), 1 register, FE030220D5740F answering to fe03003f0001a009: slave $FE, register 63 ($3F), 1 register, address, FE030200016D90 answering to 207300000001c370: $FE, register 0 ($00), 1 register, 207300000001c3700000 answering to fe03500000018105: $FE, register 20480 ($5000), 1 register, FE030220D5740F answering to fe03003f0001a009: slave $FE, register 63 ($3F), 1 register, address, FE030200016D90 answering to 207300000001c370: $FE, register 0 ($00), 1 register, 207300000001c3700000 answering to fe03500000018105: $FE, register 20480 ($5000), 1 register, FE030220D5740F

Perhaps the explanation for "Device type code" is not very clear: 1

I can't understand why the function 3F is used for two purposes: 2

Any idea or suggestion ?

pdf83 commented 2 months ago

as far as I know modbus used 16 bit registers. So register 3FH should contain 2 bytes

pdf83 commented 2 months ago

@Nicknack1234 have you been able to get the full map of the S100 smart meter ? are you able to retrieve the content of the register 0x3F, 0x95 (seems to same as 0x3F), 0x5000,

pdf83 commented 2 months ago

Perhaps the explanation for "Device type code" is not very clear: 1

I agree that documentation is confusing regarding addr 0x5000. However I do believe that this register should contains smart meter type code. this 0x5000 register is also used by inverter to store its device code. For example for SG5.0RS the code is 0x2606 in 0x5000 (see appendix 5 in https://www.scribd.com/document/650855326/Communication-Protocol-of-PV-Grid-Connected-String-Inverters-V1-1-53-EN)

image

I have checked it on my inverter and it matches

So I would suggest to respond FE030220D5740F to fe03500000018105 as you have already done and FE0302FE012DF0 to fe03003f0001a009 At this point inverter should believe that it is talking to smart meter DTSU666 and should proceed to send other requests

I can't find what is this 0x73 function code in the third request 207300000001c370. May be it is due to the fact that it didn't get expected value from the last answer It seems that it is now trying to communicate with slave id 0x20.

Let me know if you get more success with this...

Linux-RISC commented 2 months ago

Hi pdf83, thanks for your support!, the project is working again:

request: fe03003f0001a009: slave 254 ($FE), register 63 ($3F), 1 register | answer: address=254 ($FE), baud rate=1 (9600 bps) FE0302FE012DF0 request: fe03500000018105: slave 254 ($FE), register 20480 ($5000), 1 register | answer: device type coding=0x20D5 FE030220D5740F request: fe03016400081020: slave 254 ($FE),register 356 ($0164), 8 registers, Active power of phases A,B,C and Total active power | answer: (A,Total=-615 W, B,C=0 W) FE0310FFFFFD990000000000000000FFFFFD99DED2 request: fe0300770001201f: slave 254 ($FE), register 119 ($77), 1 register, Frequency | answer: 50 Hz, FE030200322D85 request: fe03000a000c71c2, register 10 ($0A), 12 registers, Current forward active total/spike/peak/flat/valley/... electric energy | answer: (0,0,0,0,0,0) FE03180000000000000000000000000000000000000000000000006F1F request: fe03003f0001a009: slave 254 ($FE), register 63 ($3F), 1 register | answer: address=254 ($FE), baud rate=1 (9600 bps) FE0302FE012DF0 request: 207300000001c370: slave $20, register 0 ($00), 1 register | answer: 207300000001c370 request: 20030000000d82be: slave 32 ($20), register 0 ($00), 13 registers | answer: 20031A0000000100000001000000010000000100000001000000010006767 request: 20030000000d82be: slave 32 ($20), register 0 ($00), 13 registers | answer: 20031A0000000100000001000000010000000100000001000000010006767 request: 20030000000d82be: slave 32 ($20), register 0 ($00), 13 registers | answer: 20031A0000000100000001000000010000000100000001000000010006767 ...

I have improved the debug messages and there are some details to be solved:

  1. The inverter requests too many times the request 20030000000d82be
  2. If the answer is 20031A0000000000000000000000000000000000000000000000000000CRC-16, the inverter get stuck requesting the same data
  3. If the aswer is (unrealistic) 20031A000000010000000100000001000000010000000100000001000CRC-16, it works but I think the frequency of updates is a bit low.

I suspect the answer is not the optimum and because of this the updates are a bit slow.

I you know how to improve this, please let me know.

Thanks in advance!

pdf83 commented 2 months ago

Hi @Linux-RISC,

Happy to learn that you are now able to communicate again with inverter. This means that we are progressing... I'm confused with these 2 requests targeting slave id 0x20:

Did you try to not answer to these requests ? This requests are most probably not for your meter. The physical smart meter should only answer to request targeting its slave id (0xFE).

Let me know.

Linux-RISC commented 2 months ago

Thanks again, @pdf83

The script cheater.sh no longer answers the "20*" requests.

I think the data are updated quite faster, I'll keep this behaviour.

pdf83 commented 2 months ago

Great. Thank you for sharing results. I found one project that seems to try to do the same thing in Java. I'm sharing this with you. This might helps for the register mapping. https://gitlab.com/john.j.cool/edge/iot/open-smart-meter/-/blob/main/src/main/java/jjc/edge/iot/model/SungrowSmartMeter.java?ref_type=heads

on my side I will try to start my project, based on your results, which will try to emulate Sungrow Smart Meter with ESP32 as a Modbus slave.

SilentButeo2 commented 4 weeks ago

Also looking at this and wanted to add my findings.

At start-up, the inverter will auto search for different power meters. It doesn't know what kind of meter is installed and will send every 10 second a dedicated request to known meters. Calls I see with no power meter connected (on my SH05.RS)

15:39:52,576 data: 0xfe 0x3 0x50 0x0 0x0 0x1 0x81 0x5
15:40:02,574 data: 0xfe 0x3 0x0 0x3f 0x0 0x1 0xa0 0x9
15:40:12,580 data: 0x20 0x73 0x0 0x0 0x0 0x1 0xc3 0x70

For the S100, it will be slaveid 0x20 and will use custom function 0x73 for that. The exact meaning I don't know, but if you answer to this, the inverter will start querying (every 200ms). If you stop answer to the 200ms queries (for x times?) the inverter will turn back to its power meter search mode and again send every 10 seconds the search queries.

Sending all 0 for those holding registers, my inverter doesn't seem to have troubles with this. It just signals 0 for all the values.

The S100 is the 1-phase meter from Sungrow. One of the other search call will be to the DTSU666, a general 3-phase power meter.

Because I had initial problems with my setup (3-phase/3-wire, instead of the more general 3-phase/4-wire) the default solution Sungrows gives with a single-phase inverter and S100 single-phase meter was not working. This because in that kind of setup, you can't use only 1 phase to detect if you are injecting or importing power. You need to have the full 3-phase power to do so. So the solution was to use a DTSU666 (and configure this in 3-phase/3-wire) and connect to the inverter.

Just to state, that a single phase inverter also will look for a 3-phase power meter(s).