Anschließen eines Solar-Hybrid-Wechselrichters an ioBroker

Um unsere Stromrechnung zu senken, haben wir eine Insel-Solaranlage installiert, deren zentrale Einheit ein PowMr 5600W All In One Hybrid-Wechselrichter ist.

Reiner Sinuswellen-Solar-Hybrid-Wechselrichter, 48 V DC, 220 V/230 V AC, 5600 W Nennleistung, integrierter 80 A MPPT-Laderegler im Wechselrichter kombiniert. Der Hybrid-Wechselrichter verfügt über ein LCD-Display, das Systemdaten und Betriebsstatus in Echtzeit anzeigt. Geeignet für 48 V Blei-Säure- oder Lithium-Batterien.

PowMr 5600W All In One hibrid inverter

Reiner Sinuswellen-Solar-Hybrid-Wechselrichter, 48 V DC, 220 V/230 V AC, 5600 W Nennleistung, integrierter 80 A MPPT-Laderegler im Wechselrichter kombiniert.

Der Hybrid-Wechselrichter verfügt über ein LCD-Display, das Systemdaten und Betriebsstatus in Echtzeit anzeigt.

Geeignet für 48 V Blei-Säure- oder Lithium-Batterien.

Um unsere Solaranlage bestmöglich zu nutzen, müssen wir den Wechselrichter überwachen. Wir müssen wissen, wie viel Strom das Solarpanel produziert oder wie viel Energie der Akku speichert.

Der PowMr 5600W All In One Hybrid-Wechselrichter verfügt auch über einen USB-Anschluss, ich habe dort ein Raspberry Pi 3-Modell angeschlossen, aber auch ein preisgünstiger Raspberry Pi Zero kann für diese Aufgabe perfekt sein.

Mit dem Python-Skript, das auf dem Raspberry Pi läuft, lesen wir die Daten aus dem Wechselrichter und fügen sie dem MQTT-Server hinzu, der auf iobroker läuft.

Der PowMr auch, wie viele ähnliche Wechselrichter (z.B. ESUN, Deye, etc.) verwendet das SRNE Modbus-RTU Kommunikationsprotokoll.

Damit das Python-Skript mit dem Wechselrichter kommunizieren kann, benötigen wir die MinimalModbus-Bibliothek. Wir können dies ganz einfach mit dem Paketinstallationsprogramm von Python tun. Falls noch nicht geschehen, installieren wir das pip-Paket-Installationsprogramm und installieren wir dann die MinimalModbus-Bibliothek.

Bash
sudo apt-get update 
sudo apt-get install python3-pip 
 
pip3 install -U minimalmodbus

Damit wir minimalmodbus verwenden können, müssen wir die Bibliothek zunächst in unser Python-Skript importieren.

Python
#!/usr/bin/python3

import minimalmodbus
...

Dann erstellen wir eine Instanz und legen die Einstellungen fest.

Python
...

#            minimalmodbus.Instrument(port, slave_address, mode)
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 1, 'rtu')

instrument.serial.baudrate = 9600
instrument.serial.bytesize = 8
instrument.serial.parity = 'N'
instrument.serial.stoppbits = 1
instrument.serial.timeout  = 0.2
instrument.mode = minimalmodbus.MODE_RTU
instrument.clear_buffers_before_each_transaction = True

Mit der Funktion read_register können wir die Register des Wechselrichters gelesen werden.

Python
# read_register(registeraddress, number_of_decimals, functioncode)
battery_remaining = instrument.read_register(0x100, 1, 3)*10
12V 100Ah LiFePO4 Lithiumbatterie, 1280Wh, 100A BMS, bis zu 15000 Ladezyklen.

12V 100Ah LiFePO4 Lithiumbatterie

1280 Wh, 100 A BMS, bis zu 15000 Ladezyklen.

Auch für Solaranlagen ist es eine gute Wahl.

Wir werden die paho-mqtt-Bibliothek verwenden, um mit dem mqtt-Server zu kommunizieren. Wenn wir dies noch nicht getan haben, installieren wir es wie folgt.

Bash
pip3 install paho-mqtt

Wir importieren auch die paho-mqtt-Bibliothek.

Python
#!/usr/bin/python3

import minimalmodbus
from paho.mqtt import client as mqtt_client
...

Geben wir Verbindungsdaten für den MQTT-Server ein.

Python
...
broker = '192.168.1.180'
port = 1883
username = ''
password = ''
client_id = 'PowMr-client'
...

Wir benötigen eine Funktion, um eine Verbindung zum MQTT-Server herzustellen.

Python
def connect_mqtt():
  def on_connect(client, userdata, flags, rc):
    if rc == 0:
      print("Connected to ioBroker!")
    else:
      print("Failed to connect, return code %d\n", rc)

  client = mqtt_client.Client(client_id)
  client.username_pw_set(username, password)
  client.on_connect = on_connect
  client.connect(broker, port)
  return client

Wir benötigen außerdem eine Funktion, um MQTT-Nachrichten zu veröffentlichen.

Python
def publish(client, topic, payload):
  result = client.publish(topic, payload)
  status = result[0]
  if status == 0:
    print(f"Send `{payload}` to topic `{topic}`")
  else:
    print(f"Failed to send message to topic {topic}")

Zum Schluss noch die Run-Funktion. Wir verbinden uns mit dem mqtt-Server, lesen die gewünschten Daten vom Wechselrichter aus und senden diese an den mqtt-Server.

Python
def run():
  client = connect_mqtt()
  client.loop_start()

  while(1):
    try:
      battery_remaining = instrument.read_register(0x100, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/battery_remaining", battery_remaining)
    except:
      print("Failed to read the battery_remaining")

Erstellen wir die Datei inverter.py.

Bash
nano inverter.py

Kopieren wir dann den Code unten.

Python
#!/usr/bin/python3
import time
import subprocess
import minimalmodbus
from paho.mqtt import client as mqtt_client

delay = 1

broker = '192.168.1.180'
port = 1883
username = ''
password = ''
client_id = 'PowMr-client'


instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 1, 'rtu')

instrument.serial.baudrate = 9600
instrument.serial.bytesize = 8
instrument.serial.parity = 'N'
instrument.serial.stoppbits = 1
instrument.clear_buffers_before_each_transaction = True
instrument.serial.timeout  = 0.2
instrument.mode = minimalmodbus.MODE_RTU


def connect_mqtt():
  def on_connect(client, userdata, flags, rc):
    if rc == 0:
      print("Connected to ioBroker!")
    else:
      print("Failed to connect, return code %d\n", rc)

  client = mqtt_client.Client(client_id)
  client.username_pw_set(username, password)
  client.on_connect = on_connect
  client.connect(broker, port)
  return client


def publish(client, topic, payload):
  result = client.publish(topic, payload)
  status = result[0]
  if status == 0:
    print(f"Send `{payload}` to topic `{topic}`")
  else:
    print(f"Failed to send message to topic {topic}")


def run():
  client = connect_mqtt()
  client.loop_start()
    
  while(1):
    try:
      battery_remaining = instrument.read_register(0x100, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/battery_remaining", battery_remaining)
    except:
      print("Failed to read the battery_remaining")
    time.sleep(delay)
        
    try:
      battery_voltage = instrument.read_register(0x101, 1, 3)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/battery_voltage", battery_voltage)
    except:
      print("Failed to read the battery_voltage")
    time.sleep(delay)
        
    try:
      battery_current = instrument.read_register(0x102, 1, 3, True)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/battery_current", battery_current)
    except:
      print("Failed to read the battery_current")
    time.sleep(delay)
        
    try:
      pv_power = instrument.read_register(0x109, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/pv_power", pv_power)
    except:
      print("Failed to read the pv_power")
    time.sleep(delay)
        
    try:
      pv_voltage = instrument.read_register(0x107, 1, 3)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/pv_voltage", pv_voltage)
    except:
      print("Failed to read the pv_voltage")
    time.sleep(delay)
        
    try:
      pv_current = instrument.read_register(0x108, 1, 3)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/pv_current", pv_current)
    except:
      print("Failed to read the pv_current")
    time.sleep(delay)
        
    try:
      battery_charge_status = instrument.read_register(0x10B, 1, 3)*10
      if battery_charge_status == 0: batt_charge_status = "0 - Ladung ausgeschaltet"
      elif battery_charge_status == 1: batt_charge_status = "1 - Schnelles Laden"
      elif battery_charge_status == 2: batt_charge_status = "2 - Laden mit konstanter Spannung"
      elif battery_charge_status == 3: batt_charge_status = "3 ---"
      elif battery_charge_status == 4: batt_charge_status = "4 - Schwimmende Ladung"
      elif battery_charge_status == 5: batt_charge_status = "5 ---"
      elif battery_charge_status == 6: batt_charge_status = "6 - Aktivierung der Li-Batterie"
      elif battery_charge_status == 7: batt_charge_status = "7 ---"
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/battery_charge_status", batt_charge_status)
    except:
      print("Failed to read the battery_charge_status")
    time.sleep(delay)
        
    try:
      inverter_current_status = instrument.read_register(0x210, 1, 3)*10
      if inverter_current_status == 0: inverter_status = "0 - Einschaltverzögerung"
      elif inverter_current_status == 1: inverter_status = "1 - Warten"
      elif inverter_current_status == 2: inverter_status = "2 - Initialisierung"
      elif inverter_current_status == 3: inverter_status = "3 - Sanfter Start"
      elif inverter_current_status == 4: inverter_status = "4 - Netzwerkbetrieb"
      elif inverter_current_status == 5: inverter_status = "5 - Wechselrichterbetrieb"
      elif inverter_current_status == 6: inverter_status = "6 - Wechselrichter für das Netz"
      elif inverter_current_status == 7: inverter_status = "7 - Netzwerk für den Wechselrichter"
      elif inverter_current_status == 8: inverter_status = "8 - Batterie aktiviert"
      elif inverter_current_status == 9: inverter_status = "9 - Ausschalten durch den Benutzer"
      elif inverter_current_status == 10: inverter_status = "10 - Fehler"
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/inverter_status", inverter_status)
    except:
      print("Failed to read the inverter_status")
    time.sleep(delay)
        
    try:
      load_current = instrument.read_register(0x219, 1, 3)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/load_current", load_current)
    except:
      print("Failed to read the load_current")
    time.sleep(delay)
        
    try:
      loadpower = instrument.read_register(0x21B, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/load_power_W", loadpower)
    except:
      print("Failed to read the load_power_W")
    time.sleep(delay)
        
    try:
      mains_charging_current = instrument.read_register(0x21E, 1, 3)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/mains_charging_current", mains_charging_current)
    except:
      print("Failed to read the mains_charging_current")
    time.sleep(delay)
        
    try:
      load_rate = instrument.read_register(0x21F, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/load_rate", load_rate)
    except:
      print("Failed to read the load_rate")
    time.sleep(delay)
        
    try:
      temp_dcdc = instrument.read_register(0x220, 1, 3, True)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/temp_dcdc", temp_dcdc)
    except:
      print("Failed to read the temp_dcdc")
    time.sleep(delay)
        
    try:
      temp_dcac = instrument.read_register(0x221, 1, 3, True)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/temp_dcac", temp_dcac)
    except:
      print("Failed to read the temp_dcac")
    time.sleep(delay)
        
    try:
      temp_trans = instrument.read_register(0x222, 1, 3, True)
      publish(client,"Photovoltaik-Anlage/Inverter/parameters/temp_transformer", temp_trans)
    except:
      print("Failed to read the temp_transformer")
    time.sleep(delay)
        
        
    # fault codes
    try:
      current_fault_code_1 = instrument.read_register(0x204, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/fault_codes/current_fault_code_1", current_fault_code_1)
    except:
      print("Failed to read the current_fault_code_1")
    time.sleep(delay)
        
    try:
      current_fault_code_2 = instrument.read_register(0x205, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/fault_codes/current_fault_code_2", current_fault_code_2)
    except:
      print("Failed to read the current_fault_code_2")
    time.sleep(delay)
        
    try:
      current_fault_code_3 = instrument.read_register(0x206, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/fault_codes/current_fault_code_3", current_fault_code_3)
    except:
      print("Failed to read the current_fault_code_3")
    time.sleep(delay)
        
    try:
      current_fault_code_4 = instrument.read_register(0x207, 1, 3)*10
      publish(client,"Photovoltaik-Anlage/Inverter/fault_codes/current_fault_code_4", current_fault_code_4)
    except:
      print("Failed to read the current_fault_code_4")
    time.sleep(delay)
        
    instrument.serial.close()


if __name__ == '__main__':
    run()

Wenn unsere Datei fertig ist, speichern wir sie. Jetzt müssen wir dafür sorgen, dass es beim Systemstart automatisch startet. Dazu benötigen wir eine weitere Datei.

Bash
sudo nano /etc/systemd/system/inverter.service

Kopieren wir die folgenden paar Zeilen in die Datei. Vergessen wir nicht, den Dateipfad korrekt einzugeben!

Bash
[Unit]
Description=inverter-service
Wants=network-online.target
After=network.target network-online.target

[Service]
Type=idle
User=username
ExecStartPre=/bin/sleep 30
ExecStart=/usr/bin/python3 /home/pi/inverter.py
Restart=always

[Install]
WantedBy=multi-user.target

Legen wir die Berechtigungen wie folgt fest.

Bash
sudo chmod 644 /etc/systemd/system/inverter.service

Starten wir den Systemctl-Hintergrunddienst neu.

Bash
sudo systemctl daemon-reload

Inverter.service aktivieren.

Bash
sudo systemctl enable inverter.service

Auf diese Weise wird inverter.py bei jedem Systemstart ausgeführt und die gelesenen Daten werden an den MQTT-Server gesendet.

Weitere Informationen zum SRNE-Modbus-Protokoll und einigen Registern finden wir im untenstehenden PDF-Dokument. Damit können wir die Datei „inverter.py“ nach unseren individuellen Bedürfnissen modifizieren.