← Monitorización Fronius SunSpec Modbus TCP en Raspberry Pi
Riego agrícola automatizadoRaspberry Pi (Docker)Modbus TCPAdquisición
Monitorización Fronius SunSpec Modbus TCP en Raspberry Pi — ejemplo completo
Lee la potencia solar de un inversor Fronius y su Smart Meter por SunSpec Modbus TCP con Python en una Raspberry Pi, y riega solo con excedente fotovoltaico.
Programa completo y ejecutable para el Raspberry Pi (Docker) (fronius-sunspec-modbus-reading.py): incluye cabecera de conexionado, requisitos y notas de integración.
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales
Vista de solo lectura.
#!/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()
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales