der Ertrag einer Hybrid-Insel-Wechselrichter PV-Anlage ergibt sich aus der für die Insel gelieferten Energie abzüglich der aus dem Netz in Zeiten niedriger PV-Leistung und Batteriestandes bezogenen Energie.
Um das zu ermitteln, habe ich vor den AC-Eingang meines Wechselrichters und nach dem AC-Ausgang je einen SDM230M Stromzähler eingebunden: Die Geräte sind recht günstig (ca. 40€) und verfügen über einen Modbus RTU Anschluss. Sie können recht einfach parallel an ein Modbus TCP WLAN Gateway, angeschlossen werden und können so von der Solaranzeige ausgelesen werden. Ich habe beide Geräte mit einem Elfin EW11 in einer Unterverteilung montiert. Der Raspi mit der Solaranzeige liest die Stromzählerdaten via WLAN.
Für den SDM230 gibt es im Solaranzeige-Image eine Reglerdatei. Leider hat der Code dort für mich nicht funktioniert. Es könnte sein, dass die Firmware des SDM230 verändert wurde. Nach einer Reihe von Experimenten bei denen mir der CAS Modbus Scanner gute Dienste geleistet hat, habe ich selber Code geschrieben, den ich in meine user_device.php (siehe hier; dort habe ich auch das Elfin EW11 WLAN Gateway beschrieben) integriert habe.
Der Code verlangt keine Einträge in der INI Datei. Die Parametrisierung findet in der user_device.php Datei statt:
Code: Alles auswählen
// 1.6 Must-Energy Wechselrichter / SDM230 Stromzähler
//
$SDM_IP = "x.x.x.x"; // IP-Adresse des TCP Gateway zum SDM230
$SDM_Port = "502"; // Modbus-TCP Standard-Port
$SDM_Adresse1 = "02"; // Stromzähler Netzstrom; Modbus Geräte ID, wie am Display des Geräts eingestellt
$SDM_Adresse2 = "03"; // Stromzähler Solarstrom; Modbus Geräte ID, wie am Display des Geräts eingestellt
$SDM_InfluxDB = "Insel";
$SDM_Measurement = "Einspeisung";
Code: Alles auswählen
// 6. Must Energy Hybrid-Insel-Wechselrichter / SDM 230 Stromzähler via Modbus TCP auslesen
//
// Das Auslesen der SDM 230 Stromzähler
// erfolgt via Elfin EW11 Modbus/WLAN Interface und das Modbus TCP Protokoll
//
// Dazu wird an die RS 485/Modbus RTU Schnittstelle des SDM230
// ein Modbus TCP WLAN Adapter angeschlossen (z.B. Elfin EW11). Damit
// kann die Solaranzeige den Stromzähler via WLAN auslesen. Es gibt 2 Stromzähler
// links, Modbus-Adresse 2: misst Netzbezug des Hybrid-Wechselrichters
// rechts, Modbus-Adresse 3: misst den vom Wechselrichter für die Insel generierten Strom
//
$aktuelleDaten = array();
$RemoteDaten = true;
$Device = "ME"; // ME = Smart Meter
$Version = "";
$Start = time(); // Timestamp festhalten
$funktionen->log_schreiben("------------- Start user_device.php SDM230, Wechselrichter -------------------------- ","|--",5);
$WR_IP = $SDM_IP;
$WR_Port = $SDM_Port;
try {
$USB1 = fsockopen( $WR_IP, $WR_Port, $errno, $errstr, 10 ); // 10 Sekunden Timeout
if (!is_resource($USB1)) {
$funktionen->log_schreiben( "Kein Kontakt zum Elfin Modbus TCP Gateway ".$WR_IP." Port: ".$WR_Port, "XX ", 3 );
$funktionen->log_schreiben( "Exit.... ", "XX ", 3 );
throw new Exception("Konnte TCP-Connection zum Elfin nicht öffnen. ");
}
$FunktionsCode = "04";
$RegisterAnzahl = "0002"; // in hexstring
$DatenTyp = "Float32" ;
$Timebase = 10000;
// 6.1 Stromzähler Netzstrom auslesen
$funktionen->log_schreiben( "Trace 6.1 - Stromzähler Netzstrom auslesen", " ", 7 );
$GeraeteAdresse = str_pad( dechex( $SDM_Adresse1 ), 2, "0", STR_PAD_LEFT ); // Modbus Geräte ID
$SDMregisters = array(
"Netz_Spannung_V" => "0000",
"Netz_Strom_A" => "0006",
"Netz_Bezug_W" => "0012",
"Netz_Scheinleistung_VA" => "0018",
"Netz_Blindleistung_VAr" => "0024",
"Netz_Leistungsfaktor" => "0030",
"Netz_Frequenz_Hz" => "0070",
"Netz_Bezug_KWh" => "0072",
"Netz_total_W" => "0084",
"Netz_max_W" => "0088",
"Netz_Strombedarf_A" => "0258",
"Netz_max_Strombedarf_A" => "0264",
"Netz_total_KWh" => "0342",
"Netz_total_seit_Reset_KWh" => "0384");
foreach ($SDMregisters AS $feldname => $adresse) {
$rc = $funktionen->modbus_tcp_lesen( $USB1, $GeraeteAdresse, $FunktionsCode, $adresse, $RegisterAnzahl, $DatenTyp, $Timebase ) ;
$funktionen->log_schreiben($adresse.": ".$feldname."=".$rc["Wert"]," ",8);
$aktuelleDaten[$feldname] = $rc["Wert"];
}
// 6.2 Stromzähler Insel auslesen
$funktionen->log_schreiben( "Trace 6.2 - Stromzähler Insel auslesen", " ", 7 );
$GeraeteAdresse = str_pad( dechex( $SDM_Adresse2 ), 2, "0", STR_PAD_LEFT ); // Modbus Geräte ID
$SDMregisters = array(
"Insel_Spannung_V" => "0000",
"Insel_Strom_A" => "0006",
"Insel_Bezug_W" => "0012",
"Insel_Scheinleistung_VA" => "0018",
"Insel_Blindleistung_VAr" => "0024",
"Insel_Leistungsfaktor" => "0030",
"Insel_Frequenz_Hz" => "0070",
"Insel_Bezug_KWh" => "0072",
"Insel_total_W" => "0084",
"Insel_max_W" => "0088",
"Insel_Strombedarf_A" => "0258",
"Insel_max_Strombedarf_A" => "0264",
"Insel_total_KWh" => "0342",
"Insel_total_seit_Reset_KWh" => "0384");
foreach ($SDMregisters AS $feldname => $adresse) {
$rc = $funktionen->modbus_tcp_lesen( $USB1, $GeraeteAdresse, $FunktionsCode, $adresse, $RegisterAnzahl, $DatenTyp, $Timebase ) ;
$funktionen->log_schreiben($adresse.": ".$feldname."=".$rc["Wert"]," ",8);
$aktuelleDaten[$feldname] = $rc["Wert"];
}
// 6.3 abgeleitete Daten berechnen
$funktionen->log_schreiben( "Trace 6.3 - abgeleitete Daten berechnen", " ", 7 );
$aktuelleDaten["Solar_Leistung_W"] = $aktuelleDaten["Insel_Bezug_W"] - $aktuelleDaten["Netz_Bezug_W"];
$aktuelleDaten["Solar_Ertrag_KWh"] = $aktuelleDaten["Insel_Bezug_KWh"] - $aktuelleDaten["Netz_Bezug_KWh"];
// 6.4 Query für das Schreiben in die influxDB zusammenstellen
$funktionen->log_schreiben( "Trace 6.4 - Query zusammenstellen", " ", 7 );
$zq = "";
foreach($aktuelleDaten as $key => $value) {
$zq .= ",".$key."=".$value;
}
$zq = $SDM_Measurement." ".substr($zq,1)." ".$zentralerTimestamp."\n";
$aktuelleDaten["ZusatzQuery"] = $zq;
$funktionen->log_schreiben("Zentraler Timestamp: ".$zentralerTimestamp," ",8);
$aktuelleDaten["zentralerTimestamp"] = $zentralerTimestamp;
$FehlermeldungText = "";
$aktuelleDaten["Regler"] = $Regler;
$aktuelleDaten["Objekt"] = $Objekt;
$aktuelleDaten["Firmware"] = "SDM230";
//$aktuelleDaten["zentralerTimestamp"] = ($aktuelleDaten["zentralerTimestamp"]+10);
// 6.5 optionale Nachbearbeitung der Smart-Meter Daten
$funktionen->log_schreiben( "Trace 6.5 - Nachbearbeitung", " ", 7 );
if ( file_exists ("/var/www/html/SDM230_meter_math.php")) {
include 'SDM230_meter_math.php'; // Falls etwas neu berechnet werden muss.
}
$funktionen->log_schreiben(var_export($aktuelleDaten,1)," ",8);
// 6.6 Smart-Meter Daten optional an den hauseigenen MQTT Broker übergeben
$funktionen->log_schreiben( "Trace 6.6 - MQTT", " ", 7 );
if ($MQTT) {
$funktionen->log_schreiben("MQTT Daten zum [ $MQTTBroker ] senden."," ",1);
require($Pfad."/mqtt_senden.php");
}
// 6.7 Zeitpunkte definieren
$funktionen->log_schreiben( "Trace 6.7 - Zeit", " ", 7 );
$aktuelleDaten["Timestamp"] = time();
$aktuelleDaten["Monat"] = date("n");
$aktuelleDaten["Woche"] = date("W");
$aktuelleDaten["Wochentag"] = strftime("%A",time());
$aktuelleDaten["Datum"] = date("d.m.Y");
$aktuelleDaten["Uhrzeit"] = date("H:i:s");
// 6.8 Smart-Meter Daten in influxDB ablegen
$funktionen->log_schreiben( "Trace 6.8 - Daten in InfluxDB", " ", 7 );
$aktuelleDaten["InfluxAdresse"] = $InfluxAdresse;
$aktuelleDaten["InfluxPort"] = $InfluxPort;
$aktuelleDaten["InfluxUser"] = $InfluxUser;
$aktuelleDaten["InfluxPassword"] = $InfluxPassword;
$aktuelleDaten["InfluxDBName"] = $InfluxDBName;
$aktuelleDaten["InfluxDaylight"] = $InfluxDaylight;
$aktuelleDaten["InfluxDBLokal"] = $SDM_InfluxDB;
$aktuelleDaten["InfluxSSL"] = $InfluxSSL;
$aktuelleDaten["Demodaten"] = false;
if ($InfluxDB_remote) {
// Daten sollen in remote DB geschrieben werden
if ($RemoteDaten) {
$rc = $funktionen->influx_remote_test();
if ($rc) {
$rc = $funktionen->influx_remote($aktuelleDaten);
if ($rc) {
$RemoteDaten = false;
}
} else {
$RemoteDaten = false;
}
}
if ($InfluxDB_local) {
// Daten sollen zusätzlich lokal abgelegt werden
$rc = $funktionen->influx_local($aktuelleDaten);
}
} else {
// Daten sollen nur lokal abgelegt werden
$rc = $funktionen->influx_local($aktuelleDaten);
}
$funktionen->log_schreiben( "Ende Abschnitt 6 - SDM230", " ", 5 );
} catch(\Throwable $ex) {
$USB1 = false;
$funktionen->log_schreiben( "Socket==false. Kein Kontakt zum Smart-Meter ".$WR_IP." Port: ".$WR_Port, "XX ", 3 );
$funktionen->log_schreiben( "Exit.... ", "XX ", 3 );
for ($Ui = 1; $Ui <= count( $User_Key ); $Ui++) {
$rc = $funktionen->send_cached_message( $API_Token[$Ui], $User_Key[$Ui], $Device."-Gerät SDM230/Elfin EW11 ".$WR_IP.":".$WR_Port." nicht erreichbar.".$errno.$errstr, 0, "", $Messengerdienst[$Ui], "YmdH" );
}
}
In Grafana habe ich dann das so dargestellt: