Skip to Content

← Fronius SunSpec Modbus TCP Monitoring on a Raspberry Pi

Automated agricultural irrigationRaspberry Pi (Docker)Modbus TCPAcquisition

Fronius SunSpec Modbus TCP Monitoring on a Raspberry Pi — full example

Read solar power from a Fronius inverter and Smart Meter over SunSpec Modbus TCP with Python on a Raspberry Pi, and irrigate only on solar surplus.

Complete, runnable program for the Raspberry Pi (Docker) (fronius-sunspec-modbus-reading.py): wiring header, requirements and integration notes included.

Download the full project pack — freeThis example + the related ones + bill of materials

Read-only preview.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# COMPLETE EXAMPLE — Reading a Fronius inverter over SunSpec Modbus TCP
#
# Hardware:  Raspberry Pi (Docker) + Fronius inverter (SunSpec Modbus TCP)
# Based on:  automated agricultural irrigation project
#
# Requirements:
#   pip install pysunspec2
#   - Enable "Modbus TCP" on the inverter (Settings -> Modbus -> TCP slave,
#     data type "float", port 502).
#   - The Smart Meter must be registered on the inverter so that it exposes
#     SunSpec model 211 (three-phase meter, float32).
#
# What it does:
#   1. Scans the SunSpec map of the inverter (auto-discovered models).
#   2. Reads generated AC power (model 113, three-phase float inverter).
#   3. Reads power consumed at the grid connection point via Smart Meter (model 211).
#   4. Computes the available solar surplus to decide whether to start irrigation.
#
# In production this script runs in a loop inside a container and publishes
# the values to Node-RED; the standalone version is shown here.

import time
import sys

from sunspec2.modbus.client import SunSpecModbusClientDeviceTCP

# ---------------------------------------------------------------------------
# Configuration (adjust to your installation)
# ---------------------------------------------------------------------------
INVERTER_IP = "192.168.1.100"   # IP of the Fronius inverter on the local network
INVERTER_PORT = 502             # Standard Modbus TCP port
SLAVE_ID = 1                    # Unit ID of the inverter (Fronius uses 1 by default)

SURPLUS_THRESHOLD_W = 4000      # Minimum surplus to authorize irrigation (W)
READ_PERIOD_S = 10              # How often the inverter is read, in seconds


def connect():
    """Opens the Modbus TCP connection and scans the device's SunSpec map."""
    d = SunSpecModbusClientDeviceTCP(
        slave_id=SLAVE_ID, ipaddr=INVERTER_IP, ipport=INVERTER_PORT
    )
    d.scan()  # auto-discovers the available SunSpec models
    return d


def read_powers(d):
    """Returns (generated_W, consumed_W) reading models 113 and 211.

    Model 113: three-phase inverter with float32 registers.
    Model 211: three-phase float32 Smart Meter (grid connection point).
    On the meter, W > 0 means grid import; W < 0, export.
    """
    inv = d.inverter_three_phase_float[0]
    inv.read()                       # refreshes the whole model block
    generated = inv.W.value          # W generated by the inverter

    met = d.meter[0]
    met.read()
    consumed = met.W.value           # W consumed (sign depends on configuration)

    return generated, consumed


def main():
    try:
        d = connect()
    except Exception as e:
        print(f"ERROR: could not connect to the inverter at {INVERTER_IP}: {e}")
        sys.exit(1)

    print(f"Connected to Fronius {INVERTER_IP}:{INVERTER_PORT} (slave {SLAVE_ID})")
    print("SunSpec models detected:", list(d.models.keys()))

    while True:
        try:
            generated, consumed = read_powers(d)
        except Exception as e:
            # Failed read: typical at dusk, when the inverter shuts down.
            # We do not abort: we retry on the next cycle.
            print(f"Warning: read failed ({e}); retrying in {READ_PERIOD_S}s")
            time.sleep(READ_PERIOD_S)
            continue

        surplus = generated - max(consumed, 0)

        print(
            f"Generation: {generated:8.0f} W | "
            f"Consumption: {consumed:8.0f} W | "
            f"Surplus: {surplus:8.0f} W"
        )

        # Simple self-consumption decision: irrigation is only authorized when
        # the solar surplus covers the pump power. The actual command to the
        # VFD is sent over Modbus TCP (see the Altivar 320 example).
        if surplus >= SURPLUS_THRESHOLD_W:
            print(" -> Sufficient surplus: irrigation AUTHORIZED")
        else:
            print(" -> Insufficient surplus: irrigation on hold")

        time.sleep(READ_PERIOD_S)


if __name__ == "__main__":
    main()
Download the full project pack — freeThis example + the related ones + bill of materials