Automatización IoT

Monitorización de inversor solar por MQTT con SunSpec Modbus y Python

Lee los datos de producción en vivo de tu inversor fotovoltaico por SunSpec Modbus TCP o una API local, publica potencia, energía diaria y estado en MQTT y pon los datos solares al alcance de todos los dashboards y automatizaciones de la casa.

Paneles solares en un tejado y un inversor de pared enviando datos a un dashboard en la nube para la monitorización de inversor solar
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

La monitorización de un inversor solar por MQTT libera tus datos de producción de la app del fabricante y los pone a disposición de dashboards, domótica y lógica de desplazamiento de cargas en tiempo real. En este tutorial leerás la potencia, la energía y el estado de un inversor mediante el estándar SunSpec Modbus TCP con Python, publicarás JSON limpio en un broker cada pocos segundos y conocerás los errores que hacen tropezar a la mayoría de los primeros intentos. También se cubren rutas alternativas mediante API HTTP locales y RS485.

Tres formas de sacar datos de un inversor

Los portales de los fabricantes sirven para echar un vistazo mensual, pero suelen actualizarse lentamente y dependen de internet. El acceso local es más rápido, funciona sin conexión y es lo que necesitas para automatizaciones como hacer funcionar una bomba de calor con los excedentes.

Método Inversores típicos Ventajas Inconvenientes
SunSpec Modbus TCP Muchos inversores de string de las grandes marcas Mapa de registros estandarizado, solo LAN, rápido A menudo hay que activarlo en los ajustes de instalador
API HTTP local del fabricante Algunas marcas exponen JSON en la LAN Fácil de leer con cualquier cliente HTTP Distinta en cada fabricante, puede requerir tokens
Modbus RTU sobre RS485 Muchos inversores híbridos y económicos Funciona sin puerto de red Requiere un adaptador RS485 y el mapa de registros del fabricante

SunSpec es el mejor punto de partida si tu inversor lo admite, porque el mismo código funciona con distintas marcas. La SunSpec Alliance define "modelos": bloques de registros con una disposición fija. Los modelos de inversor 101 (monofásico), 102 (fase partida) y 103 (trifásico) comparten la misma disposición de campos, que es en lo que se basa el código siguiente.

Activar Modbus TCP en el inversor

Cada marca esconde este ajuste de forma distinta, pero el patrón es el mismo:

  1. Asigna al inversor una IP fija mediante una reserva DHCP.
  2. En el menú de instalador o de puesta en marcha, activa Modbus TCP (a veces llamado "SunSpec" o "Modbus slave").
  3. Anota el puerto (casi siempre 502) y el unit ID (a menudo 1, a veces 126 u otro valor).
  4. Elige el formato de registros SunSpec / float o int+SF si el inversor lo pregunta; esta guía usa el formato de entero más factor de escala, que es el más compatible.
  5. Si tu inversor tiene una opción de "solo lectura" para Modbus, actívala. Solo necesitas leer.

Algunos inversores aceptan un único cliente Modbus TCP a la vez. Si ya hay conectado un gestor energético o el controlador de un wallbox, tu script puede ser rechazado o desconectar al otro cliente, así que decide qué sistema es el dueño de la conexión.

Cómo funcionan los registros SunSpec

Un dispositivo SunSpec empieza con el marcador SunS en una dirección base, normalmente 40000. Tras el marcador viene una cadena de modelos, cada uno de los cuales comienza con un ID de modelo y una longitud. Recorres la cadena hasta encontrar el modelo 101, 102 o 103 y luego lees los campos en desplazamientos fijos:

Desplazamiento en el modelo Campo Tipo Significado
2 A uint16 Corriente AC, escalada por A_SF (desplazamiento 6)
10 PhVphA uint16 Tensión de la fase A, escalada por V_SF (desplazamiento 13)
14 W int16 Potencia AC, escalada por W_SF (desplazamiento 15)
16 Hz uint16 Frecuencia, escalada por Hz_SF (desplazamiento 17)
24–25 WH acc32 Energía acumulada en Wh, escalada por WH_SF (desplazamiento 26)
31 DCW int16 Potencia DC, escalada por DCW_SF (desplazamiento 32)
33 TmpCab int16 Temperatura del armario, escalada por Tmp_SF (desplazamiento 37)
38 St enum16 Estado de funcionamiento (4 = MPPT, produciendo)

Los desplazamientos se cuentan desde el registro del ID de modelo. Cada valor se multiplica por 10 elevado a su factor de escala con signo, de modo que un W en bruto de 51234 con W_SF de -1 significa 5123,4 W. Los registros no implementados devuelven 0x8000 (con signo) o 0xFFFF (sin signo) y deben ignorarse.

El publicador en Python

Instala las bibliotecas:

pip install "paho-mqtt>=2.0" "pymodbus>=3.7"

El script localiza la base SunSpec, recorre los modelos, lee el bloque del inversor y publica JSON. Usa primero el servidor MQTT público gratuito, pensado solo para pruebas y no para datos de producción, con un prefijo aleatorio porque cualquiera puede leer esa zona.

import json
import time
import uuid
import paho.mqtt.client as mqtt
from pymodbus.client import ModbusTcpClient

INVERTER_IP = "192.168.1.60"
INVERTER_PORT = 502
POLL_SECONDS = 10

PREFIX = f"connectmq/demo/{uuid.uuid4().hex[:8]}/solar/inverter1"
STATE_NAMES = {1: "off", 2: "sleeping", 3: "starting", 4: "mppt",
               5: "throttled", 6: "shutting_down", 7: "fault", 8: "standby"}


def s16(v):
    return None if v == 0x8000 else (v - 0x10000 if v & 0x8000 else v)


def u16(v):
    return None if v == 0xFFFF else v


def scaled(value, sf):
    if value is None or sf is None:
        return None
    return round(value * (10 ** sf), 3)


def kwh(wh_raw, sf):
    if not wh_raw or sf is None:      # un valor acc32 de 0 significa no implementado
        return None
    return round(wh_raw * (10 ** sf) / 1000, 3)


def find_inverter_model(client):
    for base in (40000, 39999, 50000, 0):
        rr = client.read_holding_registers(base, count=2)
        if rr.isError() or rr.registers != [0x5375, 0x6E53]:   # "SunS"
            continue
        addr = base + 2
        for _ in range(30):
            rr = client.read_holding_registers(addr, count=2)
            if rr.isError():
                break
            model_id, length = rr.registers
            if model_id == 0xFFFF:
                break
            if model_id in (101, 102, 103):
                return addr, length, model_id
            addr += 2 + length
    raise RuntimeError("No SunSpec inverter model found - check Modbus settings and unit ID")


def read_inverter(client, addr, length):
    rr = client.read_holding_registers(addr, count=length + 2)
    if rr.isError():
        raise IOError(rr)
    r = rr.registers
    wh_raw = (r[24] << 16) | r[25]
    return {
        "ac_power_w": scaled(s16(r[14]), s16(r[15])),
        "ac_current_a": scaled(u16(r[2]), s16(r[6])),
        "ac_voltage_v": scaled(u16(r[10]), s16(r[13])),
        "frequency_hz": scaled(u16(r[16]), s16(r[17])),
        "energy_total_kwh": kwh(wh_raw, s16(r[26])),
        "dc_power_w": scaled(s16(r[31]), s16(r[32])),
        "temp_c": scaled(s16(r[33]), s16(r[37])),
        "state": STATE_NAMES.get(r[38], str(r[38])),
    }


mq = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                 client_id=f"solar-{uuid.uuid4().hex[:6]}")
mq.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
mq.connect("broker.connectmq.com", 1883, keepalive=60)
mq.loop_start()
mq.publish(f"{PREFIX}/status", "online", qos=1, retain=True)
print("Publishing to", PREFIX)

mb = ModbusTcpClient(INVERTER_IP, port=INVERTER_PORT, timeout=3)
model_addr = model_len = None

while True:
    try:
        if not mb.connected:
            mb.connect()
        if model_addr is None:
            model_addr, model_len, model_id = find_inverter_model(mb)
            print(f"Found SunSpec model {model_id} at {model_addr}")
        data = read_inverter(mb, model_addr, model_len)
        data["ts"] = int(time.time())
        mq.publish(f"{PREFIX}/telemetry", json.dumps(data), qos=1, retain=True)
        print(data)
    except Exception as exc:
        print("Read failed:", exc)
        mb.close()
        model_addr = None
    time.sleep(POLL_SECONDS)

Si tu inversor usa un unit ID distinto de 1, pásalo en cada lectura con el argumento device_id (llamado slave en versiones antiguas de pymodbus). Observa la salida desde otra máquina:

mosquitto_sub -h broker.connectmq.com -p 1883 -i solar-viewer-2290 -t "connectmq/demo/+/solar/#" -v

Un mensaje típico tiene este aspecto:

{"ac_power_w": 4183.0, "ac_current_a": 6.05, "ac_voltage_v": 231.4, "frequency_hz": 50.01,
 "energy_total_kwh": 18342.611, "dc_power_w": 4301.0, "temp_c": 41.2, "state": "mppt", "ts": 1760603520}

Errores habituales en la monitorización de inversores solares

La mayoría de los problemas tienen unas pocas causas:

  • Direcciones desplazadas en uno. La documentación mezcla numeración de registros desde 0 y desde 1. Por eso el script prueba tanto 40000 como 39999.
  • Los factores de escala cambian. Algunos inversores modifican W_SF dinámicamente. Lee siempre el factor de escala en la misma petición que el valor, como hace el código al leer el bloque completo.
  • La noche. Muchos inversores apagan su placa de comunicaciones por la noche o informan del estado "sleeping" con valores vacíos. Trata los tiempos de espera agotados tras la puesta de sol como algo normal y conserva el último valor retenido.
  • Frecuencia de sondeo. Cada 5 a 10 segundos es más que suficiente para dashboards y control de excedentes. Sondear varias veces por segundo no aporta nada y puede alterar a algunos inversores.
  • Contador frente a inversor. El inversor conoce la producción, no el consumo ni la exportación. Para la lógica de autoconsumo también necesitas un contador de red; la guía del monitor de energía doméstica cubre esa otra mitad.
  • Los inversores híbridos con baterías suelen exponer los datos de la batería en modelos propios del fabricante en lugar de los SunSpec estándar. Consulta monitorización de baterías domésticas para publicar valores del BMS.

Usar los datos

Una vez que la producción está en MQTT, cualquier consumidor puede usarla sin volver a tocar el inversor. Home Assistant puede asignar el JSON a sensores con su integración MQTT, Node-RED puede accionar un enchufe inteligente cuando la producción supera un umbral y Telegraf puede guardarlo todo en una base de datos de series temporales para informes mensuales de rendimiento. Como el mensaje de telemetría es retenido, un dashboard que se abre a mediodía ve la producción actual de inmediato.

Las integraciones funcionan así a propósito: el broker es el centro y cada herramienta se conecta a él como cliente MQTT. Si la idea es nueva para ti, la explicación de qué hace un broker MQTT describe el modelo publicación/suscripción en el que se basa.

Pasar a producción con un broker privado

Los datos solares no son secretos como las contraseñas, pero sí revelan patrones de ocupación y detalles de la instalación, y el broker público es compartido. Para una instalación permanente, ejecuta el publicador en una Raspberry Pi o un servidor doméstico y envía los datos a un broker MQTT en la nube mediante TLS. Con ConnectMQ, crea un broker en el portal, copia las credenciales generadas y sustituye las líneas de conexión:

mq = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
mq.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
mq.tls_set()
mq.will_set("home/solar/inverter1/status", "offline", qos=1, retain=True)
mq.connect("broker.connectmq.com", 8883, keepalive=60)

Un broker en la nube también te permite ver la producción desde el móvil sin exponer nada de tu red doméstica, porque la Raspberry Pi solo establece una conexión saliente. La guía de automatización IoT, más amplia, muestra cómo encajan los datos solares en los flujos energéticos de toda la vivienda.

Monitoriza tu producción solar con ConnectMQ

ConnectMQ es un broker MQTT gestionado con TLS en el puerto 8883, acceso por WebSocket para dashboards en el navegador, un explorador de topics en vivo y reglas de alerta por umbral, por ejemplo para avisarte cuando un inversor informa de un estado de fallo durante el día. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito.

Preguntas frecuentes

¿Todos los inversores admiten SunSpec Modbus?

No. Muchas marcas consolidadas de inversores de string lo admiten, a menudo tras activarlo en los ajustes de instalador, mientras que algunos inversores híbridos y económicos solo ofrecen mapas Modbus RTU propios del fabricante o API en la nube. Busca "SunSpec" en la documentación de tu modelo.

¿Con qué frecuencia debo sondear el inversor?

Cada 5 a 10 segundos basta para dashboards en vivo y automatizaciones basadas en excedentes. Un sondeo más rápido rara vez aporta valor y algunos inversores responden mal a él.

¿Por qué falla mi script por la noche?

Muchos inversores apagan su interfaz de comunicaciones cuando no hay entrada fotovoltaica. Considera normales los tiempos de espera nocturnos y apóyate en el último mensaje MQTT retenido.

¿Puedo controlar el inversor por MQTT?

Algunos inversores aceptan registros de control SunSpec para limitar la potencia, pero escribir en un inversor puede incumplir las normas de conexión a red. Mantén este tutorial en modo solo lectura salvo que tu instalador confirme qué está permitido.

Artículos relacionados

Guía para configurar Zigbee2MQTT: del coordinador al panel

Instala Zigbee2MQTT con Docker, configura el coordinador y la conexión MQTT, empareja y renombra dispositivos, contrólalos por MQTT y consulta sus estados en un panel, probando primero en un servidor MQTT público gratuito.

8 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.