Integration of a Fronius Symo Gen 24 plus Inverter via Modbus TCP

Hi Francesco,
glad to hear that this was useful for someone out there :slight_smile:

I’ve googled to look for custom data type and structure but I didn’t find any specific explanation.
Could you please point me to a tutorial or manual to understand how it works ?

I believe this question is specifically about how to unpack the data that has been received from the inverter. I was using the Python documentation about binary structures - see here: struct — Interpret bytes as packed binary data — Python 3.12.2 documentation - plus some heavy trial and error.

At the end of the document there is a reference table of Format Characters - you will recognize those in my code. There are also some examples included how to do shortcuts: e.g. 8x means “8 padding bytes in a row”

I hope this was helpful, let me know if you have further questions.
Kind regards,
Oscar

Hi Oscar,

many thanks for taking time to reply. I’m still need to study your script and also the Python doc… (I’m newbie). I’ve succeed to include Energy by reading directly the register (40285) then tempating by multiplying it for its multiplier…

Then to learn from your code:

  • you read 88 (registers, 16bit) from modbus TCP
  • data type: custom is required to keep the read values as they are…
  • structure: “>hhhh8x H16xHHH16x H16xHHH16x H16xHHH16x H16xHHH16x” directive let you to unpack each according to its type (short, unsigned short,…) as described for each in Fronius modbus table.
    It’s correct to say ? “>” big endian, the first 4x “h” means firs parameters are unsigned short,…
    so then in each index you will have the parameters decodified and ready accordingly…
    am I correct ?
    Still I can’t understand “8x” or “16x”. They might represent a parameter made of bit … but it doesn’t seem to match with Fronius table or even the list you provided becasue only ID might be this type of data… what I miss ?

Regards
Francesco

Thank you for this !!
I was able to get the string power finally on my setup with Symo 10.
Can you please share how to get the voltages also please? DC voltage for strings and AC voltage for phases :slight_smile:

Or even better the codes/sensors for all the data from Modbus :slight_smile: - or at least everything you have.
I know it is a lot, and i’ve tried for at least 8 hours to do it myself, but it is well over my head …
Now i am using the API requests but i’d like to switch everything to modbus as it seems much better and faster.

Thank you

Hi Oscar @soly3141

Did you get any further with your plans to control the power flow to/from the battery? I’m in the phase to choose inverter and that is a hard requirement for me that I can control dynamically when to charge and discharge the battery (and preferable from Home Assistant).

/Henrik

Hi,

I’m glad it helped. It is really not that hard once you figure it out for the first time - but the first time indeed - not that easy.
I followed a document called Modbus_TCP_RTU.pdf, downloaded from Fronius after registration.
Check the data you need:
i.e. AC power.
Search and find, keep in mind in my example I use int+SF (whole numbers + scale factor. If you have 50 as data and -2 as SF, data must be multiplied by 10 on the power of SF - in our example 50* 10^-2 = 50 * 0.01 = 0.5W)

After some trying and using modbus testing tool, you realize the doc is off by 1 register address as counting starts from 0 → so you’re looking for 40083 as start
you need 2 registers → count: 2
and you need an unsigned integer as data (watts never go below 0 ), and a signed as scale factor → structure: “Hh”
(source: struct — Interpret bytes as packed binary data — Python 3.10.4 documentation )

Then do the math in the template sensor by using the above dataset and split that az every colon char.

The MPPT values are trickier as those are not often together:
image


Remember, registers are off by 1, so startaddress + line address - 1
We start with register 40257
We need 38 registers in total
data is like Signed integer(h) + 32 junk(32x) + unsigned integer(H) + 38 junk(38x) + unsigned integer(H)
Junk registers will be skipped simply.
I’m not sure how this works with a smart meter, but from the docs it seems if you have monitoring tied to the inverter, you even could get consumption data out the same way.

You may also use float, for me hat did not work out somehow. Int+SF is rock solid if you read both values in the same entity. If it is divided to different modbus entities, there will be spikes as they won’t always align.

Hi Henrik @hossians,
I did, it’s working for me but might need some polishing before ready to release. At the moment, I’m managing when and how quickly the battery will be charged. Due to regulations here, my AC output to the grid is limited by 70% of the total kWp installed. Im fetching forecast data for PV production from solcast.com. If the forecast is above a certain threshold I limit battery charging to the peak hours of the day to make sure the system is always 100% productive.

The main part is to control the charging flow, this is done with the following command:

service: modbus.write_register
data:
  unit: 1
  value: '500' # charging value in Watt 
  hub: fronius1 # name of your modus hub
  address: 40366 # rmodbus register to control charging "InWRte" (-1 offset observed in the register numbering) 

If you want to control discharge, you can use address 40365 “OutWRte”

In my automation, it looks like this

# Solar Battery Charging Configuration
- alias: WRBatteryStoreExcessEnergy7k
  trigger:
    platform: numeric_state
    entity_id: sensor.derived_energy_pv_output_total
    above: 7000
    for:
      minutes: 2
  condition:  "{{ states('sensor.solcast_forecast')|int > 40 }}" 
  action:
    service: modbus.write_register
    data:
       unit: 1
       hub: fronius1
       address: 40366
       value: '1200'

The improvement I would still do is to implement some state information that charging has already been configured if this has triggered once - otherwise, in case of clouds or other impacts to your solar production, the condition is met multiple times in a short period and the register is written more often than necessary.

1 Like

Thank you Oscar! That is really good to know, then I can proceed with sourcing the Fronius inverter.
I’m planning to buy and sell “per hour” as we can see very big swings in the price over a 24h period. I don’t have the 70% limitation in Sweden so the battery and the inverter will be the limiting factor for selling, but buying we have a monthly power tariff so that is a little bit more tricky. Will have to do some nice calculations to find the optimal sell/buy rates and thresholds…
The list of registers that is writeable, where did you find them?

/Henrik

Thank you very much for the detailed explanation:) I tried to follow it, but , at my copy/paste level… no luck :sweat_smile:

I was hoping you have them already in the format you showed earlier , where my copy/paste/adapt/change_name would actually shine :laughing:

Cheers

Hi @Momo - after the upgrade to 2022.4 I noticed that the modbus values are not updated anymore. The reason was a change in the handling of data types in the modbus integration (see Remove generic data types INT, UINT, FLOAT in modbus by janiversen · Pull Request #67268 · home-assistant/core · GitHub). I will update my examples in the intial post in the next couple of hours. Which version are you running?

KRs, Oscar

Hi @soly3141 :slight_smile:

I’m running 2022.4.1.

The power values of MPPT1 and MPPT2 i have copied from @ipetrovits still update just fine .

This is how the updated reading section looks like. The changes are only in the first three sensors (reading_energy_main_meter, reading_energy_inverter_ac_output, reading_energy_battery_soc_scaled)

modbus:
  - type: tcp
    host: 192.168.x.x
    port: 502
    name: fronius1
    sensors:
      - name: reading_energy_main_meter
        slave: 200
        scan_interval: 2
        count: 2
        address: 40097
        data_type: float32
        unit_of_measurement: W
      - name: reading_energy_inverter_ac_output  
        unit_of_measurement: W
        slave: 1
        count: 2
        data_type: float32
        address: 40091
        scan_interval: 2
      - name: reading_energy_battery_soc_scaled
        slave: 1
        count: 1
        data_type: uint16
        address: 40361
        scan_interval: 2
      - name: reading_inverter_multiple_raw
        slave: 1
        count: 88
        address: 40265
        scan_interval: 2
        data_type: custom
        # Registers and positions in custom structure
        # 0  DCA_SF
        # 1  DCV_SF
        # 2. DCW_SF DC Energy scaling factor
        # 3. DCWH_SF
        # 4  1_ID
        # 5  1_DCA
        # 6. 1_DCV
        # 7. 1_DCW Energy string 1
        # 8  2_ID
        # 9. 2_DCA
        # 10 2_DCV
        # 11 2_DCW Energy string 2
        # 12 3_ID
        # 13 3_DCA
        # 14 3_DCV
        # 15 3_DCW Energy to battery, charging
        # 16 4_ID
        # 17 4_DCA
        # 18 4_DCV
        # 19 4_DCW Energy from battery, discharging
        structure: ">hhhh8x H16xHHH16x H16xHHH16x H16xHHH16x H16xHHH16x" 
2 Likes

Super:) Thank you !!

But now i’ll start from the top as newbie if thats ok.

Modbus should be set as float or int+SF?

Your meter address is 200 - where can i find that address in the web interface ? What i found on my system is SmartMeter Modbus address: 1

The slave 1 - i assume this is the first invertor. In case i have 2 invertors daisy chained and one smartmeter, i should make the same config for slave 2, correct?

Is there any way to retrieve the MPPT DC value ? Currently i get it using json API from history :frowning:

Thank you again and i hope you do not mind all the questions.

Hi, I have only a few mins so i’ll cherry pick from your questions, hoping others with multiple inverters and smartmeters will be able to help you in the rest (i have only 1 symo without smartmeter)

  • Modbus should be set as float or int+SF?
    Up to you, it was easier for me to find out that the register addresses are misaligned with the documentation (debugging standard integers and translating from hex was faster), and then i got stuck with this config. One caveat, if you read int+SF, those must be read in the same query. If you define multiple modbus entities, those will be separate queries. I thought that would be an edge case when during the readings the scale factor changes, it turned out to be occuring multiple times a day causing spikes in the data, messing up stats. You also will need a template entity to do the math.
    I did not change to float, because not all data can be read in float format, there is some registers still in int+sf format regardless of the default setting.
    i.e. MPPT data is in a different register if you’re in float mode, but the data is still in int+sf format.

  • Is there any way to retrieve the MPPT DC value ? Currently i get it using json API from history
    Realtime MPPT DC values only available via custom modbus at the moment. Btw i had issues with the json api - values not refreshing in time etc. so this was the reason for modbus at all.

Can you please elaborate ?

Modbus is not working yet with the new Update…isn´t it?

It should work, at least for me it does :wink:

If you have issues, did you try analysing your logs to find errors related to your modbus setup?

KRs

1 Like

okay, but the new release removes “int”, “uint” and “float” as data_types.

i have this one in my Modbus configuration :frowning:

modbus:
  - type: tcp
    host: 10.0.0.245
    port: 502
    name: fronius1
    sensors:
      - name: reading_energy_main_meter        
        slave: 200
        scan_interval: 2
        count: 2
        address: 40097
        data_type: float
        unit_of_measurement: W
      - name: reading_energy_inverter_ac_output          
        unit_of_measurement: W
        slave: 1
        count: 2
        data_type: float
        address: 40091
        scan_interval: 2
      - name: reading_energy_battery_soc_scaled        
        slave: 1
        count: 1
        data_type: uint
        address: 40361
        scan_interval: 2
      - name: reading_inverter_multiple_raw        
        slave: 1
        count: 88
        address: 40265
        scan_interval: 2
        data_type: custom
        # Registers and positions in custom structure
        # 0  DCA_SF
        # 1  DCV_SF
        # 2. DCW_SF DC Energy scaling factor
        # 3. DCWH_SF
        # 4  1_ID
        # 5  1_DCA
        # 6. 1_DCV
        # 7. 1_DCW Energy string 1
        # 8  2_ID
        # 9. 2_DCA
        # 10 2_DCV
        # 11 2_DCW Energy string 2
        # 12 3_ID
        # 13 3_DCA
        # 14 3_DCV
        # 15 3_DCW Energy to battery, charging
        # 16 4_ID
        # 17 4_DCA
        # 18 4_DCV
        # 19 4_DCW Energy from battery, discharging
        structure: ">hhhh8x H16xHHH16x H16xHHH16x H16xHHH16x H16xHHH16x"
1 Like

Hi @Vaioraner,
I have updated the documentation some time ago to adapt to the new data type handling: Integration of a Fronius Symo Gen 24 plus Inverter via Modbus TCP or this earlier post, specifically about the data type changes Integration of a Fronius Symo Gen 24 plus Inverter via Modbus TCP - #45 by soly3141

Does this help? KRs,
Oscar

1 Like

OMG! I am an Ideot :slight_smile:

you made my Day!

Thanks Oscar!
vielen Dank!

lg wolfgang

You can get past data from REST API, which is lagging and not realtime, but also had trouble with it, it was not stable.
Official and a 3rdparty integration is working well, but I havent seen any with MPPT values - therefore I made my system read all info from the inverter directly - via Modbus, where this data is available to read.
Possible that there will be an integration in the future handling this, maybe there is one already in development, but native modbus works rock solid. All the past errors with this interface was caused by breaking changes introduced…

1 Like