Community
OpenEnergyMonitor

OpenEnergyMonitor Community

Logging SolarEdge inverter data using MODBUS over TCP

I’m currently trying to get data from our SolarEdge inverter into my local emoncms running on our emonpi. The main reason for doing this is to free up one of the two emonpi CT sensors to record the power being used by our heat pump.

First step was to update the firmware on our SolarEdge 3500 inverter, since older versions don’t support MODBUS over TCP.

I then enabled MODBUS support on the inverter, instructions available here: https://www.solaredge.com/sites/default/files/sunspec-implementation-technical-note.pdf

I’ve added the following to my emonhub config:

[[ModbusTCP]]
    # this interfacer retrieves register information from modbusTCP clients 
    # retrieve register information from modbus TCP documentation for your inverter.
    # Information here is designed for Fronius Symo 3 phase inverter.
    Type = EmonModbusTcpInterfacer
    [[[init_settings]]]
        modbus_IP = 192.168.178.36   # ip address of client to retrieve data from
        modbus_port = 502          # Portclient listens on
    [[[runtimesettings]]]
        # list of names of items being retrieved
#        rName = DC_Current_1,DC_Voltage_1,DC_Current_2,DC_Voltage_2,Power,AC_Current,Frequency,Temperature,Residual_Current,Total_Yield,Daily_Yield
        rName = I_AC_Current,I_AC_Current_SF,I_AC_Power,I_AC_Power_SF,I_AC_VA,I_AC_VA_SF,I_AC_VAR,I_AC_VAR_SF,I_AC_PF,I_AC_PF_SF,I_DC_Current,I_DC_Current_SF,I_DC_Voltage,I_DC_Voltage_SF,I_DC_Power,I_DC_Power_SF,I_Temp_Sink,I_Temp_Sink_SF
        # List of starting registers for items listed above
#        register = 30769,30771,30957,30959,30775,30795,30803,30953,31247,30513,30517
        register = 40072,40076,40084,40085,40088,40089,40090,40091,40092,40093,40097,40098,40099,40100,40101,40102,40104,40107
        # List of # of registers used for each item 
#        nReg = 1,1,1,1,1,1,1,1,1,1,1
        nReg = 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
        # Data type for each item above
#        rType = float32,float32,float32,float32,float32,unint32,unint32,float32,float32,uint64,uint64
        rType = uint16,int16,int16,int16,int16,int16,int16,int16,int16,int16,uint16,int16,uint16,int16,int16,int16,int16,int16
        # nodeid used to match with node definition in nodes section below. Can be set to any integer value not previously used.
        nodeId = 27
        # Channel to publish data to should leave as ToEmonCMS
        pubchannels = ToEmonCMS,
        # time in seconds between checks, This is in addition to emonhub_interfacer.run() sleep time of .01
        # use this value to set the frequency of data retrieval from modbus client
        interval = 1

and then a matching node section at the end of the file:

[[27]]
    nodename = inverter
    [[[rx]]]
        names = ac_total_current,ac_current_scale_factor,ac_power_value,ac_power_scale_factor,apparent_power,apparent_power_scale_factor,reactive_power,reactive_power_scale_factor,power_factor,power_factor_scale_factor,dc_current,dc_current_scale_factor,dc_voltage,dc_voltage_scale_factor,dc_power,dc_power_scale_factor,heat_sink_temperature,heat_sink_temperature_scale_factor
        datacodes = H,h,h,h,h,h,h,h,h,h,H,h,H,h,h,h,h,h
        scales = 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
        units = A,n,W,n,VA,n,VAR,n,%,n,A,n,V,n,W,n,C,n

I tried leaving units blank for scale factors, but it didn’t like that so I’ve just used ‘n’ for no unit.

Waiting for some sunshine tomorrow to see if I’m getting any sensible values through.

My understanding from this post: Solar PV generation, pulse counter & virtual feeds is that I need to perform the following calculation to get generated power:
I_AC_Power register x 10^I_AC_Power_SF

I don’t see a ^ operator in the input processes, I guess I need to add a custom input process - can anyone could point me in the direction of how to do that?

Also need to work out how to delete the input I accidentally created with my typo (dc_volate_scale factor)

All up and running now. I added an input process to apply the scale factor to the power being reported from the solaredge inverter and have moved the CT sensor to monitor the heat pump energy usage.

I now have a nice dashboard showing house and heat pump energy usage, as well as solar:

Hit a slight snag where it would occasionally take the value and scale for the power from different times resulting in the calculated power value being out by an order of magnitude or two. Fixed by retrieving the two modbus registers together and unpacking them.

To do this added I_AC_Power_Combined to emonhub.conf, as a uint32 spanning 2 registers:

[[ModbusTCP]]

    # this interfacer retrieves register information from modbusTCP clients 

    # retrieve register information from modbus TCP documentation for your inverter.

    # Information here is designed for Fronius Symo 3 phase inverter.

    Type = EmonModbusTcpInterfacer

    [[[init_settings]]]

        modbus_IP = 192.168.178.36   # ip address of client to retrieve data from

        modbus_port = 502          # Portclient listens on

    [[[runtimesettings]]]

        # list of names of items being retrieved

#        rName = DC_Current_1,DC_Voltage_1,DC_Current_2,DC_Voltage_2,Power,AC_Current,Frequency,Temperature,Residual_Current,Total_Yield,Daily_Yield

        rName = I_AC_Current,I_AC_Current_SF,I_AC_Power,I_AC_Power_SF,I_AC_VA,I_AC_VA_SF,I_AC_VAR,I_AC_VAR_SF,I_AC_PF,I_AC_PF_SF,I_DC_Current,I_DC_Current_SF,I_DC_Voltage,I_DC_Voltage_SF,I_DC_Power,I_DC_Power_SF,I_Temp_Sink,I_Temp_Sink_SF,I_AC_Power_Combined

        # List of starting registers for items listed above

#        register = 30769,30771,30957,30959,30775,30795,30803,30953,31247,30513,30517

        register = 40072,40076,40084,40085,40088,40089,40090,40091,40092,40093,40097,40098,40099,40100,40101,40102,40104,40107,40084

        # List of # of registers used for each item 

#        nReg = 1,1,1,1,1,1,1,1,1,1,1

        nReg = 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2

        # Data type for each item above

#        rType = float32,float32,float32,float32,float32,unint32,unint32,float32,float32,uint64,uint64

        rType = uint16,int16,int16,int16,int16,int16,int16,int16,int16,int16,uint16,int16,uint16,int16,int16,int16,int16,int16,uint32

        # nodeid used to match with node definition in nodes section below. Can be set to any integer value not previously used.

        nodeId = 27

        # Channel to publish data to should leave as ToEmonCMS

        pubchannels = ToEmonCMS,

        # time in seconds between checks, This is in addition to emonhub_interfacer.run() sleep time of .01

        # use this value to set the frequency of data retrieval from modbus client

        interval = 1

Added the same to the inverter node with datacode i for 32 bit integer:

[[27]]
    nodename = inverter
    [[[rx]]]
        names = ac_total_current,ac_current_scale_factor,ac_power_value,ac_power_scale_factor,apparent_power,apparent_power_scale_factor,reactive_power,reactive_power_scale_factor,power_factor,power_factor_scale_factor,dc_current,dc_current_scale_factor,dc_voltage,dc_voltage_scale_factor,dc_power,dc_power_scale_factor,heat_sink_temperature,heat_sink_temperature_scale_factor,ac_power_combined
        datacodes = H,h,h,h,h,h,h,h,h,h,H,h,H,h,h,h,h,h,i
        scales = 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
        units = A,n,W,n,VA,n,VAR,n,%,n,A,n,V,n,W,n,C,n,n

Then in my Modules/process/process_processlist.php added an input process to unpack the value and scale factor and calculate the power in Watts:

           array(
            "id_num"=>61,
            "name"=>_("value and scale"),
            "short"=>"v+s",
            "argtype"=>ProcessArg::NONE,
            "function"=>"value_and_scale",
            "datafields"=>0,
            "datatype"=>DataType::UNDEFINED,
            "unit"=>"",
            "group"=>_("Calibration"),
            "description"=>_("<p>Unpack combined value and scale factor, applying the scale factor.</p>")
         ),

and the function:

    public function value_and_scale($arg, $time, $value) {
        $scale = $value & 0xFFFF; // Right 16 bits are the scale
        if($scale >= 0x8000) { // Decode twos complement if negative
          $scale = -(($scale ^ 0xFFFF)+1);		
        }
        
        $val = $value >> 16; // Left 16 vits are the value

        // Apply scale factor to the value
        return $val * pow(10, $scale);
    }

Now logging correctly without spikes caused by incorrect scale values being applied, not that there’s much sun to log recently!

nice solution :slight_smile: