Some help with Fox Solar Inverter and Modbus

I’m trying to get some data out of our Fox Solar PV and Batteries directly into EmonCMS.
I’ve got some PHP hacky scripts up and running that steal the data out of the FoxCloud application however I’d like to get the data directly if possible.

Initially I went with a HF5111S which is a Modbus → TCP thing. I was hoping I could connect it to the MQTT directly, however it doesn’t seem to work.

I’ve got a successful modbus setup currently working using a few sdm120 meters and a USB->Modbus adaptor so I though I could use that, however I don’t understand enough about Modbus to set the config up. From the config, I need something like this

    [[SDM120]]
        Type = EmonHubMinimalModbusInterfacer
        [[[init_settings]]]
            device = /dev/ttyUSB0
            baud = 2400
        [[[runtimesettings]]]
            pubchannels = ToEmonCMS,
            read_interval = 10
            nodename = sdm120
            # prefix = sdm_
            [[[[meters]]]]
            ...
                [[[[[fox]]]]]
                    address = 247
                    registers = 0, 6, 12, 18, 30, 70, 72, 74, 76
                    names = V, I, P, VA, PF, FR, EI, EE, RI
                    precision = 2, 3, 1, 1, 3, 3, 3, 3, 3

However I don’t understand the registers or names. I assume names map directly to registers.

A screenshot from the manual

Should my config look like this? (for example)

                    registers = 11000, 6, 11001
                    names = pv1Voltage, pv1current

The registers are likely to be either 0,1,2,3 etc or 1,2,3,4.

Number of registers and names in each array must match.

Register 1 is at address xxx0 (offset by one) which can be confusing.

You need to be sure about the address as well.

Node-RED is an easy way to do some Modbus testing.

I’ll assume this is a wired connection?

Morning Brian,

Thanks for the info - that confirms what I’ve been reading over the last few days.
It is a wired connection, a random RS485 → TCP adaptor I’ve found. I can to successfully connect to it via emonhub, however I know my config is wrong. I can’t connect to it via modpoll which others seem to suggest, I guess it is a combination of the command line config I can’t get right. What pallete would you recommend for Node-RED?

Via MODBUSTCP interfacer?

Just search for Modbus packages - I’m using a TCP one I’m sure (for testing) to my MVHR system. I actually get the data using HomeAssistant and send it from there to emoncms but Node-Red was useful for testing.

Will do.

I saw a HomeAssistant plugin for Fox, however I have a severe allergy to HomeAssistant so I am trying to avoid it!

If from use a few years ago, it has changed somewhat. All my home automation runs on it (with suitable fall back mechanisms for essential services!).

So I’ve finally got data out to me via the RS485 → TCP. Tested with modpoll

Doing some more reading on EmonHub, it doesn’t look like I can just bang in some new config under the SDM120. I feel like I am being directed to the EmonModbusTcpInterfacer.

With a config like this

[[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.1.188 # ip address of client to retrieve data from
        modbus_port = 502 # Portclient listens on
    [[[runtimesettings]]]
        # List of starting registers for items listed above
          register = 11023
        # nodeid used to match with node definition in nodes section below. Can be set to any integer value not previously used.
          nodeId = 12
        # 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 = 10

Where 11023 is a random register I know has a value

image

I see this in emomHub - I suspect my config may be wrong at this point?

Config

 [[12]]
        nodename = fox_inverter
        [[[rx]]]
            # list of names of items being retrieved
            # This example retrieves the Inverter status, AC power in watts being produced, AC Lifetime KWh produced,
            # KWh produced for current day,....
            names = SOC
            datacodes = f
            scales = 0
            units = V

You have to define an equal number of registers and of names which comes from if len(rNames) != len (registers):

I only have one register and one name at this point?

@TrystanLea @glyn.hudson can you help?

I think this is looking for the register number, not the register address.

Register 1 is at address 0, register 2 at address 1 etc.

Try 11022 as the register (just a guess).

Possibly also try a second register as well.

Finally it says

Sometimes a trailing comma is needed (yes I know that isn’t in the docs).

Just guessing as I don’t use this interfacer :slight_smile:

That made it error differently :slight_smile:

Looking at this HA code, it shows some correct addresses such as 31001

So we could be down to trying to understand the register number / register address. I didn’t appreciate there was a difference between the two.


This doc confirms the register references - Data Register Reference H1 AC1 · StealthChesnut/HA-FoxESS-Modbus Wiki · GitHub

I don’t understand if the module is looking for a register number / address and if the reference above is a number or address.

Hello @kb6673 I think you may need a comma after those entries to ensure these are interpreted as arrays:

 [[12]]
        nodename = fox_inverter
        [[[rx]]]
            names = SOC,
            datacodes = f,
            scales = 0,
            units = V,

Getting closer. No errors about the config now. I think I am now possibly stuck on the register addresses / numbers?

I don’t fully understand that last error message

Not sure about that one, the code section this seems to be coming from is:

try:
    self.rVal = self._con.read_holding_registers(register - 1, qty, unit=unitId)
    assert self.rVal.function_code < 0x80
except Exception as e:
    self._log.error("Connection failed on read of register: %s : %s", register, e)
    self._modcon = False
    #missing datas will lead to an incorrect encoding
    #we have to drop the payload
    return

Can the docs be amended, please?

Why are you picking that number? Do you have a source document? sorry missed the link. - these look like addresses rather than register numbers.

Also, your successful test in message Some help with Fox Solar Inverter and Modbus - #7 by kb6673 you used TCP and starting 11000 address.

Looking at modpoll I think it may be better (and easier for you) if you used that and output the data via MQTT directly to emoncms (base topic emon). Simply create a script and a service file to run it automatically.

@TrystanLea - the modpoll package looks interesting as you can use a config file to specify different types of registers in different ranges - a significant limitation of the existing interfacer. For simplicity perhaps a modpoll interfacer might work.

I thought that too. I don’t know enough modbus to find the registers.

Looking at modpoll I think it may be better

I was using a different modpoll however I think I’ve found the one you found that will publish to MQTT directly.

I think you found this one, also called modpoll.

Oddly enough I started this journey with a PHP script that called modpoll, parsed the output and posted to emon’s MQTT. It felt a bit fragile so I thought I’d try to use the built in ModbusTcp

A step closer…

Looking at my line datacodes = f,which the docs refer to as “f: float, 4 bytes”

This screenshot suggests the data type is int16

Are data codes and data types the same thing? If so, any suggestions on what I should set for int16?

Almost - I’d say ‘h’ is the datacode for the data type of a 2-byte, 16-bit signed integer. There’s a full list in here:

Watch how the negative numbers are coded. I think this is hardcoded in the interfacer as @glyn.hudson was having issues with a particular device previously.

Tried using h The number is never negative, so I don’t think I need to worry about that

Current config

    Type = EmonModbusTcpInterfacer
   [[[init_settings]]]
        modbus_IP = 192.168.1.254 # ip address of client to retrieve data from
        modbus_port = 502 # Portclient listens on
    [[[runtimesettings]]]
        # List of starting registers for items listed above
        register = 11000, 11000
        # nodeid used to match with node definition in nodes section below. Can be set to any integer value not previously used.[
        nodeId = 12
        # 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 = 10

...
    [[12]]
        nodename = fox_inverter
        [[[rx]]]
            # list of names of items being retrieved
            names = batterysoc, batterysoc
            datacodes = h, h,
            scales = 0, 0,
            units = V, V,

Log

2023-10-20 13:50:09,286 INFO     ModbusTCP  Not connected, retrying connect {'modbus_IP': '192.168.1.254', 'modbus_port': '502'}
2023-10-20 13:50:09,307 INFO     ModbusTCP  Opening modbusTCP connection: 502 @ 192.168.1.254
2023-10-20 13:50:09,308 DEBUG    ModbusTCP  expected bytes number after encoding: 4
2023-10-20 13:50:09,309 DEBUG    ModbusTCP  datacode h
2023-10-20 13:50:09,309 DEBUG    ModbusTCP  reading register #: 11000, qty #: 1, unit #: 1
...
2023-10-20 13:50:12,314 ERROR    ModbusTCP  Connection failed on read of register: 11000 : 'ModbusIOException' object has no attribute 'function_code'

May have go back to my original plan and parse modpoll with PHP.

That will multiply whatever came in by zero, result: zero. Putting zeros there is nonsense, the sensible values are 1.0, then at least you’ll see the raw value interpreted as a signed 16-bit integer.