stanus74 / home-assistant-saj-h2-modbus

Home Assistant Component to read SAJ H2 Inverter Modbus Data
MIT License
8 stars 3 forks source link

Insight in charging ekd-modified HS2 (Ampere.StoragePro) using Ampere.IQ (kiwigrid VoyagerX) #18

Open jsjhb opened 3 days ago

jsjhb commented 3 days ago

This is not an actual problem, this is just an information dump!

Introduction

I can give some insight in how loading from grid is done with the Energiekonzepte Deutschland (ekd) version of the SAJ HS2 (Ampere.StoragePro) using their energy management solution Ampere.IQ (remarketed Kiwigrid VoyagerX).

Findings

It seems, ekd has modified th HS2 to have some additional modbus registers, starting from 0x8300, for ease of access, I presume. It should be possible to identify the native HS2 registers to reach the same goal.

There seem to be two different loading procedures, one pure loading from grid without additional available PV power, and the second one with additional PV power.

Simple grid load

The "night mode" starts with writing one register 0x3644 (13892) to "100"- "BatOnGridDisDepth". This is followed by writing 7 registers (function 16) from 0x8300 (33536) every 3 seconds, until the configured SOC is reached: 0x8300: 3 0x8301: 2 0x8302: 2764 0x8303: 200 0x8304: 600 0x8305: 10000 0x8306: 10000 All values are int16. If the desired max. SOC of the battery is reached before the end of the "cheap" price window, two registers are written separately: 0x8300: 0 0x3647: 0 As they are written at the same time, it seems reasonable, that they are doing the same, setting the inverter mode - here from "passive" to "self use". At the end of the cheap price window, i.e. when Power should be consumed from the battery or eventually available PV power, one register is written: 0x3644: 20 again, int16, "BatOnGridDisDepth"

The value for 0x8302 seems to be the power drawn from the net for loading. In the Ampere.IQ app there are two possible settings, "schonend"(de)="gently"(en) and "schnell"(de)="fast"(en). In case "fast" is selected, the value sent is "7372". This roughly corresponds to the inverter power drawn in Watt (W), which ranges in my case between 2730W and 2840W in case of gently loading. Regardless of this value being constant for the whole loading event, the power actually drawn is varied by the inverter, possibly by a temperature (either battery or inverter seems reasonable), and the actual SOC of the battery. The power drawn is automatically adjusted by the inverter, so above SOC of 95% power drawn reduces to around 1700W, and above 98% to around 800W. Temperature has a similar effect, however the values sent are constant over the loading event.

Loading with PV and grid simultaneously - variant 1

In contrast to the night mode loading during daylight with available PV power values sent differ. At the start of the loading window register 0x3644 (13892) is set to "100"- "BatOnGridDisDepth". Then 5 registers are sent every 1 second: 0x8300: 1 0x8301: 2 0x8302: 3027 0x8303: 3027 0x8304: 600 The vaules for 0x8302 and 0x8303 are adjusted for available PV power to keep the battery power at around 3000-3500W. At the end again 3 seperate registers are written: 0x8300: 0 0x3647: 0 to reset the inverter app mode and 0x3644: 20 to set the minimal battery SOC during self-use mode.

Loading with PV and grid simultaneously - variant 2 (perhaps since Update rolled out 2024-11-19 - 2024-11-22)

Currently loading during time with available PV power resorts to the method of the night mode, with register 0x8302 sent to "4724 ".

There has been a malfunction with the Ampere.IQ on 2024-11-19, noticeable for me at 2024-11-19T19:00:00+01:00, when loading suddenly commenced, and was not interruptible. After that directing manual loading through the Ampere.IQ app was not successful, a support call apparently yielded in a fix. There seem to have been a software upgrade on either the Ampere.IQ device, or the server backend. Since then the grid loading with PV uses the simpler variant 2 or simple grid load, with a different power limit.

Discussion of the registers

0x3644 - BatOnGridDisDepth, min SOC of battery in grid connected mode, set usually to 20% as blackout reserve. Is set to 100% for passive mode charging, to prevent disacharging, even when charging is complete, but energy price is low. 0x3647 - AppMode - 0=self-use mode, 1=time mode, 2=backup mode, 3=passive mode 0x8300 - seems to be re-used by Ampere.IQ to set the AppMode (0x8300 is set at start, is reset at end, simultaneously with 0x3647 ), if set to "1" aka. time mode it is unclear, if 0x8303 is influenced here 0x8301 - is set to "2", never changed. 0x8302 - set to power, unclear which power is steered, probably upper inverter power limit (3027, 4727,7327, or variable according to available PV power) 0x8303 - either set to 200 in simple load, or set to same variable value as 0x8302 0x8304 - set to 600 0x8305 - set to 10000, could correlate to 100%, or max. inverter power, depending on model? 0x8306 - set to 10000, similar to 0x8305

Conclusion, and directions of research

As first step the registers in question should be monitored during grid charging, to find correlations between the undocumented and perhaps proprietary registers 0x8300 ff.

Grid charging could be easily achieved by setting 3 registers, 0x3644, 0x3647 and the register 0x8302 - here identification of the official SAJ-documented register is necessary. Close monitoring of SOC of battery is advised. Setting of 2 registers 0x3644 and 0x3647 should stop charging.

Putting the system in passive mode (0x3647=3) temporary and setting 0x3644 either to actual SOC of battery, or to 100 and setting of 0x8302 (or official equivalent) to "0" would sort of disable the system and draw power from the net, e.g if energy price is at a low level, but charging loss would exceed price gain until next cheaper price window.

Ampere.IQ frequently reads vital data in parallel, much more frequent as the current HA integration does. There are groups of registers, which are read at a rate of approx. 1s, and others at 2s, and some in a larger interval. I have no clue, how that could be replicated in HA, if needed, but for close monitoring of SOC of battery during grid charge the query interval of 30s seems to long. There is information like the long-time production values, which do not need to be queried every 30s, an interval of 5min or even longer should suffice. This would probably require a rewrite of the integration to query different groups of registers with a distinct interval.

UDicke commented 3 days ago

Thank you very much for this research and detailed description! I'm happy that we didn't order the Ampere.IQ Box. HA together with evcc and the integration from @stanus74 works brilliant! 😊

stanus74 commented 10 hours ago

Thanks for this information. The Modbus protocol for the H2 does not contain any register information for 8300H. I have read registers from 8300H and values are output. I will write to the German support of SAJ to find out more

jsjhb commented 4 hours ago

Yesterday (2024-11-28) I noticed new values in 8300H ff.: between 14:57 and 23:00 (+01:00) 8301H was set to "0" instead of "2", and both 8302H and 8303H were set to "10000". Simultaneously EPEX spot prices were rather high, throughout that period the quantile was above 0.60, marking the highest prices for the day. Today (2024-11-29) prices were even higher, but there was no change in the values 8301-8303H from the usual "2", "2764", "200".

It is possible, that ekd tries several steering methods with the Ampere.IQ, and would give a hint for not grid loading the battery during phases with high prices. speculation: Setting 8301H to "0" could prevent loading. 8302H and 8303H could be upper and lower limits of battery loading power in watts. "10000" could be a "disable" setting.

Nevertheless, I am monitoring the "official" writable registers, and there are so far no connected registers, apart from 3647H, which changes "magically" with the setting of 8300H at start of a load cycle, but is reset at at the same time manually together with 8300H (two registers written back-to-back).

From my understanding of the writable registers apart from 8300H ff., we could set the BatOnGridDisDepth to "100", AppMode to "3" (passive), PassiveBatteryChargePower to a reasonable level between 0.0 and 1.0 (Ampere.IQ loading suggest something like 0.3 and 0.75), and enable PassiveChargeEnable. Monitor SOC of battery to the desired level and disable PassiveChargeEnable.

jsjhb commented 4 hours ago

Made a pull request (#19) with my modifications for monitoring. DO NOT COMMIT!