Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grott server - I can not read/write registers above 1023 #162

Open
Sdahl1234 opened this issue Sep 13, 2022 · 26 comments
Open

Grott server - I can not read/write registers above 1023 #162

Sdahl1234 opened this issue Sep 13, 2022 · 26 comments

Comments

@Sdahl1234
Copy link

It's not possible to read/write registers above 1023 from the inverter.

I have a 3600 SPH inverter and to force the inverter to charge the battery I need to access register 1043.

I'm running latest beta.

Turning back to growatt's server its possible

image

rany2 added a commit to rany2/grott that referenced this issue Sep 13, 2022
* Modbus documentation has a mention but I assumed it was for serial only
@rany2
Copy link

rany2 commented Sep 13, 2022

If you change the lines that check if value is > 1023 to something like 2**16-1, does it work as you expect it to?

2**16-1 is the largest register value the format allows.

Edit: for reference check rany2/grott@193640a

@Sdahl1234
Copy link
Author

Yes, it works.
Now i can force my inverter to charge the battery from grid

rany2 added a commit to rany2/grott that referenced this issue Sep 13, 2022
* Seems like there is a usecase for those
@osos
Copy link

osos commented Sep 13, 2022

Yes, it works. Now i can force my inverter to charge the battery from grid

What are your steps to switch between load-first, grid-first and battery-first (with/-out ac charge) ?

@Sdahl1234
Copy link
Author

My plan was to switch on 1043 and then change register 1044 to 1 but i can't change the registers, only read. The PUT cmd returns "OK" but the value is not changed

image

@Sdahl1234
Copy link
Author

Sdahl1234 commented Sep 14, 2022

I found another document where some other registers is documented
https://pdfcoffee.com/qdownload/growatt-inverter-modbus-rtu-protocolii-v120-english-pdf-free.html

I can only modify start and stop time if the timer is OFF
Set 1102 off "0" //turn off timer
Set 1092 on "1" //turn on AC Charge (not testet because its already on)
Set the start/stop time (the values are hex, 14:00 = 0E00 hex =3584 dec )
Set 1102 on "1" //turn timer ON

image

image

@rany2
Copy link

rany2 commented Sep 14, 2022

@johanmeijer does the datalogger require some kind of challenge response when dealing with values >1023? I know that growatt sends a massive packet sometimes but I have no idea what that does. Interested to see what you have to say

@Sdahl1234
Copy link
Author

I have no clue.
Some registers are only possible to alter if another register has a given value.
Often when I used growatt's webinterface to change registers I would get an error but the value did change.

@johanmeijer
Copy link
Owner

Hmm Strange.

First I do not see the registers you mentioned (1023 / 1043) in my documentation and also not in document I downloaded via the link you included above.

image

So I am not sure what document you use.

But that is not the problem. If you can change the register values from the growatt site it should be also possible from Grottserver. The only way to find out what is happing. is to trace it. Can you make sure that in grott.ini minrecl=1 (or 0) and then set the register via the growatt sit and capture the grott log?
Then we can see what growatt is doing.

Setting registers is tricky when we do not know for sure if it are the right registers. The only way to find if we do not have the right documentation is trace the conversation when we perform it via the growatt site.

@johanmeijer
Copy link
Owner

johanmeijer commented Sep 14, 2022

If it can be done with growatt server API that would help also. But until know I am not been abe to find a good description of the growatt API.

@Sdahl1234
Copy link
Author

It was version 1.05 of the document where the missing registers was documented.

I have to wait until the weekend to try making the logs. I'm testing some stability with very low update interval and turning on/off the time switch for battery first.
But it would be nice to see what registers growatt server modify when changing to/from- load/battery/grid- first.

@catch56
Copy link
Contributor

catch56 commented Feb 14, 2023

@Sdahl1234 I think we might be trying to change the same things on a similar inverter - do you have an SPH? And did you manage to get this working? I've put in an PR to increase the register limit, but then got to the same point as you with an 'OK' return but no obvious change in the register. My plan so far was to leave the times at 00:00-23:59 then only enable/disable the slot (1102) and stop charge soc (1091). Then 10% is 'prevent discharge', over 10% would charge, disabling the slot would allow discharge. But early days of testing this.

@catch56
Copy link
Contributor

catch56 commented Feb 14, 2023

@johanmeijer here's (hopefully) what you were asking for.

This is the result of submitting this screen on the ShinePhone app (ShineWifi-S datalogger on an SPH6000) with those settings configured.

Screenshot_20230214_094724_ShinePhone

- Growatt packet received:
		  <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('local-ip', 49202), raddr=('isp-ip', 5279)>
	 - Growatt original Data:
		 \x00\x0d\x00\x06\x02\x41\x01\x36\x0d\x22\x2c\x4f\x22\x33\x26\x77\x36\x37\x77
		 \x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72
		 \x21\x20\x22\x20\x37\x02\x2a\x5f\x35\x51\x74\x74\x47\x72\x6f\x77\x61\x74\x74
		 \x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x76\x76\x7a\x4e\x5f\x40\x75\x18
		 \x6c\x0d\xd3\x72\x6a\x77\x63\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f
		 \x77\x61\x74\x74\x4f\x0e\x6f\x7a\x6f\x7b\x74\x54\x72\x6f\x77\x61\x74\x74\x47
		 \x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74
		 \x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77
		 \x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72
		 \x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74
		 \x47\x72\x6f\x77\x61\x74\x74\x60\x06\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61
		 \x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f
		 \x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47
		 \x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74
		 \x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x49\xed\x6f\x77
		 \x63\xf7\x74\x47\x61\xe8\x7e\xe0\x74\x74\x47\x72\x6f\x64\x61\x74\x74\x47\x72
		 \x6f\x76\xed\x74\x83\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74
		 \x47\x72\x6f\x77\x61\x74\x0d\xd2\x08\x7e\x77\x61\x74\x74\x47\x72\x6f\x77\x61
		 \x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f
		 \x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47
		 \x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74
		 \x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77
		 \x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72
		 \x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74
		 \x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61
		 \x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x6a\x57\x77\x61\x74\x77\x47\x72\x64
		 \x5b\x61\x74\x74\x4c\x72\x6f\x7c\xf9\x74\x74\x2d\x41\x6f\x77\xd1\x3b\x76\x54
		 \x72\x6f\x77\x11\x74\x6b\x47\x16\x6f\x77\x4e\xea\x74\x47\x7d\x71\x77\xe9\x74
		 \xfb\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77
		 \x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x72
		 \x6f\x77\x61\x74\x74\x47\x72\x6f\x77\x61\x74\x74\x47\x2c\x5e
	 - Grott automatic protocol detection
	 - Grott data record length 585
	 - layout   :  T060136XSPH
	 - no matching record layout found, try generic
	 - Record layout used :  T060136XSPH
	 - Growatt data decrypted V2
	 - Grott Growatt data decrypted
	 - Growatt plain data:
		 000d0006024101364a50433843475230445800000000000000000000000000000000000000004
		 e574354434558304230000000000000000000000000000000000000000017020e092d2f027918
		 79940005000200000000000000000000000000000000087c000d0e0f001300000000000000000
		 00000000000000000000000000000000000000000000000000000000000000000000000000000
		 00000000000000000000000000000000000000000000000000000000000000000000000000000
		 00000000027740000000000000000000000000000000000000000000000000000000000000000
		 00000000000000000000000000000000000000000000000000000000000000000000000000000
		 000000000000000000000000e9f00000283000013870981000000000013000000000000018c00
		 f7000000000000000000000000000000000000000079957a11000000000000000000000000000
		 00000000000000000000000000000000000000000000000000000000000000000000000000000
		 00000000000000000000000000000000000000000000000000000000000000000000000000000
		 00000000000000000000000000000000000000000000000000000000000000000000000000000
		 000000000000000000000000000000000000000000000018380000000300000b2c0000000b000
		 00b9800006a330000b04f021300000070001f006400002f9e00000f1e0088008f000000000000
		 00000000000000000000000000000000000000000000000000000000000000000000000000000
		 000000000005e31

Screenshotting the relevant section from this PDF
https://pdfcoffee.com/qdownload/growatt-inverter-modbus-rtu-protocolii-v120-english-pdf-free.html

Screenshot from 2023-02-14 09-55-55

So registers 1090-1108

@Sdahl1234
Copy link
Author

I got mine working on my 5 month old fork of this repo. Only change was to allow register above 1023.
I enabled AC charge in the first slot at set it to 00:00 - 23:59 (I did this on the inverter) and I'm only using register 1102 to switch on/off. When the slot is turned off the inverter goes back to normal mode. No need to change register 1091.
Home Assistant is controlling the on/off based on some conditions or if I force it in a period of time.
It has been rock solid.
johanmeijer did an amazing work on this.

@catch56
Copy link
Contributor

catch56 commented Feb 15, 2023

Getting closer with this, but still having some trouble.

I can change register 1102 'successfully', but when I do so, it appears to also reset the time slot to 00:00 to 00:00.

I verified this by switching grott to point to grottserver vs growatt then reading the results from the growatt website advanced settings page after setting commands.

I then tried the 'advanced set' on the website, and I get the same result from there. So it seems like if you don't submit the time registers and the on/off register at the same time, the times get reset.

I was able to change the charge rate without affecting the other values, both via the grottserver API and website, so that at least is independent, but there seems to be an interdependency with the slot settings.

Trying to change for example the end time of battery first slot 1 (also via the website) is also showing 'success' but then not actually changing them, although maybe that's me getting the format wrong.

Adding before/during/after screenshots to show what's happening.

Before: slot off, times correct
Screenshot from 2023-02-15 16-50-46

Setting the slot to on via the website (same effect as via grottserver)
Screenshot from 2023-02-15 16-51-21

After: slot on, times reset to 00:00 - 00:00
Screenshot from 2023-02-15 16-51-53

I'm wondering if this needs the slot status, start time and end time to all be set simultaneously, essentially simulating a form submission. However, if so, not sure how that would work with grottserver which expecting one register and one value.

@catch56
Copy link
Contributor

catch56 commented Feb 16, 2023

More investigation and made some decent progress at last.

Documenting here so hopefully the next person trying to do this with an SPH inverter saves some time!

@Sdahl1234 if you don't mind, it would be great if you could confirm you're purely setting the battery first slot 1 to on/off and that this doesn't affect the slot times.

i.e. curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1102&value=1&inverter=XXXXXX" in isolation.

For me this changes the value of that register OK, but the time slot (and only that time slot) resets to 00:00 - 00:00.

Setting just the end of the time slot to 23:59 also wasn't working for me.

However, I found a workaround. I found some multireg code in grottserver, but couldn't really figure out the API for it from the python, not a python developer at all.

But, reading up about modbus, there are apparently some implementations that have a window of a few seconds to send related registers together, and if you send them within that window, it's fine, but if not they get rejected. Possibly growatt's is responding to only getting one part of the window in isolation by resetting to 00:00.

So... I tried this:

$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1102&value=1&inverter=XXXXXXXX"
$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1100&value=0000&inverter=XXXXXXX"
$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1101&value=5947&inverter=XXXXXXX"

All within a window of about 10 seconds.

Trying to set the hex value with format=hex doesn't seem to work.

And it works :) I have to send the time slot on/off before the times, not after. There might be a more elegant way to do it, but working is better than not working.

(per the above 23:59 = 173B (hex) = 5947 (back to decimal from squished together hex). Thanks for mentioning this above because I'd never have figured it out, it's like 2+2 = 22.

Still need to set this up in hass now, but along with the change in my PR I think I'm up and running.

I did find one more thing that looks interesting.

There is a 'Stop SOC' setting for load first, described here: https://www.energetica-india.net/articles/guidance-for-operation-modes-of-the-sph-series-inverter

This is available as an explicit setting on the growatt website:

Screenshot from 2023-02-16 10-09-56

It's mentioned in the modbus documentation (v120) as being at register 3082. However, that appears to be wrong. If I set it via the dedicated form element for the setting, that seems to work fine, if I 'advanced read' from register 3082 or set it via 'advanced write', it's always 0. So I think it's actually written/read to a different modbus register than the one in the documentation. And I can't find any hint of what that register is. Which is shame, because that'd be a more semantic way to prevent discharge when you just want to conserve the battery than using the charging settings with stop soc = 10%.

@Sdahl1234
Copy link
Author

@catch56
This is how I did it:
Setting the values physical on the inverter instead of using the app.
Enable the battery first on the inverter.
Verify that you do charge from grid
Change to Load first
Verify that you do NOT charge from grid
change the 1102 registry to "1" from GrottServer
Verify that you do charge from grid
change the 1102 registry to "0" from GrottServer
Verify that you do NOT charge from grid

Hope this helps

@catch56
Copy link
Contributor

catch56 commented Feb 19, 2023

@Sdahl1234 thank you. I wasn't able to get that working, but setting mode/soc/times in quick succession is working successfully for me now.

@catch56
Copy link
Contributor

catch56 commented Feb 20, 2023

@johanmeijer thanks for merging the PR.

I'd be happy to put in a PR to add some SPH examples to the wiki - might result in less support requests later, but also wondered whether you actually want to put high level documentation for grottserver on there yet. I think this can be closed otherwise.

@Sdahl1234
Copy link
Author

@catch56 You could post your examples in this thread for a start. I think a lot of users are struggling with this.

@catch56
Copy link
Contributor

catch56 commented Feb 20, 2023

@Sdahl1234 Good point, the stuff I posted is a bit buried.

I have a ShineWifi-S and SPH6000 inverter, with grott and grottserver. I've been using this modbus reference:
https://pdfcoffee.com/qdownload/growatt-inverter-modbus-rtu-protocolii-v120-english-pdf-free.html

In all examples, XXXXXXX should be replaced with the serial number of your inverter.

To enable battery first slot 1:

Register 1102 is "BatFirst on/off Switch 1" and accepts 1 or 0

Register 1100 is "Bat First Start Time 1" and accepts two separate hex values for the h and m values, which are then combined into a single string (which I'll call 'hex smushed' in lieu of a proper format name) which is then further converted to decimal. I was only able to set the time using the hex-smushed-converted-to-decimal value.

Credit goes to Sdahl1234 for figuring that bit out.

i.e. 00:00 = 00 00 (hex) = 0000 (hex smushed) = 0000 (decimal)

23:59 = 17 3B (hex) = 173B (hex smushed) = 5947 (hex smushed converted back to decimal).

Register 1101 is Bat First End Time 1 and accepts the same format as the start time.

For me at least, setting the time slot state by itself resets the time slot times to 00:00-00:00, so didn't work for enabling it. I found that I had to send the time slot status, then the start and end times in fairly quick succession, in that order.

To turn battery first on (all day, I'm not actually using the times for anything except getting the mode changes to stick):

$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1102&value=1&inverter=XXXXXXXX"
$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1100&value=0000&inverter=XXXXXXX"
$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1101&value=5947&inverter=XXXXXXX"

To turn battery first off:

$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1102&value=0&inverter=XXXXXXXX"
$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1100&value=0000&inverter=XXXXXXX"
$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1101&value=5947&inverter=XXXXXXX"

Note that the following should also work to switch the slot off because the times don't matter when it's off, it's only switching it on that requires setting the times every time.

$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1102&value=0&inverter=XXXXXXXX"

To set the 'battery stopped SOC' setting - in this case to stop at 95%. This is independent of the slot being enabled or disabled, so doesn't need to be sent alongside anything else - but if you want to switch from load first to AC charging, you'll need to enable the slot AND change the stopped SOC setting for it to actually start charging.

$ curl -X PUT http://127.0.0.1:5782/inverter?command=register&register=1091&value=95&inverter=XXXXXXXX"

I also set 'AC charge enabled' to on via the shine app before making other changes, and just leave it enabled, but you could use register 1092 to do this via the grottserver API.

This allows me to switch between three 'modes':

  1. Slot disabled -> regular 'load first' mode where the inverter feeds load then battery then grid.

  2. Slot enabled and battery stop soc = 10% -> since the inverter won't allow the soc to get under 10% and prevents discharge when AC charging is enabled, this just holds whatever charge is in the battery at that time.

  3. Slot enabled and battery stop soc = 95% -> charge the battery up to 95% from the grid and keep it there.

I have a variable tariff where the price changes every 30 minutes, and a fixed export tariff. I'm using a home assistant automation to set load first when the import rate is high, prevent discharge when the import rate is near the export rate (or if it's unlikely to last until the next high rate), and top up the charge when the import rate is cheaper than the export rate.

I have a feeling that anyone trying to do this in home assistant and reading this thread will be more familiar with it than me - I've only been using it a couple of weeks expressly for this purpose and my home assistant commands/sensors are still in flux. Might come back and add some of that when I'm happier with it though.

@johanmeijer
Copy link
Owner

@johanmeijer thanks for merging the PR.

I'd be happy to put in a PR to add some SPH examples to the wiki - might result in less support requests later, but also wondered whether you actually want to put high level documentation for grottserver on there yet. I think this can be closed otherwise.

I think it is a good idea to start with a wiki page on Grottserver. I am a little bit short in free time at this moment so if you want to start it, it is OK with me.

@mvdh02
Copy link

mvdh02 commented Jun 2, 2023

@catch56 I'm trying to get this to work with my SPA3000 inverter with a connected 10kwh battery. I'm running Grott on a RPI with Domoticz. This works fine and all the read values are comming in (with Node-red). Because of the energy prices i'm looking for a solution to charge on command, discharge on command, or "normal mode". The rules here above seem the solution. But when i type in this rule in my RPI (command) nothing happens. Can you explian how to use / start these lines?? (I've change the IP en XXXX to the correct serial number ;-0) Thanks in advance.

@catch56
Copy link
Contributor

catch56 commented Jun 3, 2023

@mvdh02 I never got around to trying to write up stub documentation here, but you're on the right track.

  1. You need to ensure you're using grott server, not just grott. grottserver is necessary to be able to write back to the inverter.

  2. Make sure the SPA3000 inverter uses the same modbus register as the SPH6000 (which is what I'm running), (googling growatt inverter modbus protocol SPA3000 should find one).

  3. Before trying to write any values, you should make sure you're able to use grottserver's API to read values first. e.g. curl -X GET http://127.0.0.1:5782/inverter?command=register&register=1091&inverter=XXXXXXXX"

First of all make sure you're getting a valid value from the command, and secondly make sure the value you're getting matches what you would expect from the modbus documentation (i.e. 100 vs. 0 vs 5947 per the examples above). Then, once you've tried setting values, you can try reading them back to see if they've changed, and then compare that against the inverter behaviour like whether it's charging from AC or not.

  1. It's not always obvious if you're getting errors or success messages, especially with PUT commands, see Grott server: make error messages valid JSON with status code headers? #298 for some discussion.

  2. It's also possible that the SPA3000 has different settings for time of use charge/discharge via the app/website - if so the approach I took with the SPH might need adopting.

Since I wrote this up, I've found one more 'mode' which is to set 'discharge' with a 'stop SOC' of 100%, this prevents the battery charging from solar, maximising export (for use with UK Octopus' flux tariff), but without actually discharging from the battery except to meet load. I discovered via this that even when this is set, if it's extremely bright sunshine and the solar array exceeds the 6kw capacity of the inverter (happens for at most about 30 minutes per day for me), then it charges the battery with the excess, which is a nice feature.

@osos
Copy link

osos commented Sep 9, 2023

I have successfully performed the 3 writes after each other with a 3 seconds delay between:

$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1100&value=0000&inverter=XXXXXXX"
$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1101&value=5947&inverter=XXXXXXX"
$ curl -X PUT "http://127.0.0.1:5782/inverter?command=register&register=1102&value=1&inverter=XXXXXXXX"

The values read fine, however my SPH-3600 seems not to enter "Battery First" mode according to register 1044. Are there any other registers to be set to switch to battery first?

@osos
Copy link

osos commented Sep 9, 2023

Please also be aware that there is an updated protocol paper available, version 1.24 (https://www.scribd.com/document/592776725/Growatt-Inverter-Modbus-RTU-Protocol-II-V1-24-English-new)

@osos
Copy link

osos commented Sep 14, 2023

The values read fine, however my SPH-3600 seems not to enter "Battery First" mode according to register 1044. Are there any other registers to be set to switch to battery first?

Turns out it announce itself as "Battery first" until the battery is at 100%, thereafter is announce a "Load first" mode despite still not using the battery for the load

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants