Stromzähler
TYP: ISKRA MT691
Hier dreht sich alles um die ersten Schritte zum Auslesen und Verstehen des SML-Protokolls deines Stromzählers – mit einfacher Hardware und einem Mikrocontroller wie dem Arduino.
Was brauchst du?
Für den Einstieg benötigst du im Grunde drei Dinge:
Die Daten werden in zwei zentralen Tabellen gespeichert, um eine präzise und effiziente Verwaltung der Energieverbrauchs- und Einspeisewerte zu gewährleisten:
obis_180_280: Diese Tabelle speichert die täglichen Verbrauchs- und Einspeisewerte in Kilowattstunden (kWh). Sie dient der langfristigen Erfassung und Analyse des Energieverbrauchs und der Einspeisung über den Tagesverlauf.
obis_10070: Hier werden die aktuellen Verbrauchs- und Einspeisewerte in Watt gespeichert. Diese Tabelle ermöglicht eine Echtzeitüberwachung des Energieflusses und bietet eine detaillierte Ansicht der momentanen Verbräuche und Einspeisungen.
obis_180_280 sind besonders nützlich für langfristige Auswertungen, etwa für Monats- oder Jahresberichte.
Echtzeitdaten (in Watt) aus der Tabelle obis_10070 ermöglichen eine sofortige Rückmeldung über den aktuellen Energieverbrauch und die Einspeisung, was besonders für die Optimierung der Energieerzeugung und -nutzung von Bedeutung ist.
Beide Tabellen arbeiten Hand in Hand, um eine umfassende und flexible Analyse der Energieflüsse zu ermöglichen.
Den Arduino-Code können Sie direkt kopieren und in Ihre Arduino IDE einfügen. Bitte beachten Sie, dass Sie an insgesamt 5 Stellen Ihre persönlichen Daten wie WLAN-SSID, WLAN-Passwort, Serveradresse und Zugangspasswort eintragen müssen. Diese Stellen sind im Code durch ** YOUR VALUE ** gekennzeichnet und müssen individuell angepasst werden, damit die Verbindung und Datenübertragung korrekt funktioniert.
// WIFI-Konfiguration und Verbindungsaufbau
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
const char* ssid = "**YOUR SSID**"; // SSID des WLANs
const char* password = "**YOUR PASSWORD**"; // Passwort des WLANs
const char* host = "**YOUR DOMAIN**"; // Serveradresse zum Hochladen der Daten
const int httpsPort = 443; // HTTPS-Port für verschlüsselte Verbindung
// Serielle Schnittstelle (SoftwareSerial) für den IR-Kopf am Zähler
#include <SoftwareSerial.h>
SoftwareSerial SmlSerial(4, 5); // RX = GPIO4, TX = GPIO5 (nur RX wird verwendet)
// SML-Protokoll-Konstanten und OBIS-Kennungen (Kennzahlen für Zählerdaten)
byte bytesmlread; // Zwischenspeicher für eingelesene Bytes
const byte byt_Start_Seq[] = { 0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01 }; // Startsequenz eines SML-Telegramms
const byte byt_Stop_Seq[] = { 0x1B, 0x1B, 0x1B, 0x1B, 0x1A }; // Endsequenz eines SML-Telegramms
// OBIS-Kennungen zur Erkennung bestimmter Messwerte:
const byte byt_180_Seq[] = { 0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x00, 0xFF, 0x65, 0x00, 0x1C, 0x49, 0x04, 0x01, 0x62, 0x1E, 0x52, 0xFF}; // OBIS 1.8.0 (Bezug in kWh)
const byte byt_280_Seq[] = { 0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x00, 0xFF, 0x01, 0x01, 0x62, 0x1E, 0x52, 0xFF }; // OBIS 2.8.0 (Einspeisung in kWh)
const byte byt_10070_Seq[] = { 0x77, 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF, 0x01, 0x01, 0x62, 0x1B, 0x52, 0x00 }; // OBIS 10.7.0 (aktuelle Wirkleistung in Watt -Einspeisung/+Bezug)
// Variablen zur Ablaufsteuerung ("Zustandsmaschine")
uint8_t SequenceStep = 1; // Aktueller Sequenzschritt
uint8_t SequenceIndex = 0; // Index zum Abgleich der Bytes in Sequenzen
uint8_t intCountByteVal[3] = {0, 0, 0}; // Anzahl der relevanten Bytes für OBIS 1.8.0, 2.8.0, 10.7.0
uint8_t intAktLoopCount = 0; // Aktuelle Anzahl abgeschlossener Lesezyklen
uint8_t intSollLoopCount = 60; // Anzahl Lesezyklen, bevor ein Upload erfolgt (~1 Zyklus/Sekunde)
// Puffervariablen zum Speichern der Zählerwerte (HEX und dezimal)
byte byt_180_HEX[4]; // Zählerwert 1.8.0 (Bezug) in Hex
byte byt_280_HEX[4]; // Zählerwert 2.8.0 (Einspeisung) in Hex
byte byt_10070_HEX[4]; // Aktuelle Leistung (OBIS 10.7.0) in Hex
double dbl_180toDB = 0; // Zählerwert 1.8.0 (Bezug) in Dezimal (kWh)
double dbl_280toDB = 0; // Zählerwert 2.8.0 (Einspeisung) in Dezimal (kWh)
double dbl_10070toDB = 0; // Leistung OBIS 10.7.0 in Dezimal (Watt)
//Led`s
const int LedErrorPin = 12; //LED Error Pin
const int LedOkPin = 13; //LED OK Pin
void setup() {
//Serial.begin(115200); // Serielle Ausgabe zur Debug-Ausgabe aktivieren
WiFi.begin(ssid, password); // WLAN-Verbindung aufbauen
WiFi.setAutoReconnect(true); // Automatische WLAN-Wiederverbindung
WiFi.persistent(false); // Vermeidet Flash-Verschleiß
SmlSerial.begin(9600); // SML-Datenübertragung starten (IR-Kopf am Zähler)
pinMode(LedErrorPin, OUTPUT); // Setzt den LedErrorPin als Ausgang
pinMode(LedOkPin, OUTPUT); // Setzt den LedOkPin als Ausgang
}
void loop() {
//Fehlerausgabe wenn Step zu lange aktiv
ErrorCodeLed(SequenceStep);
// Schritt 1: Prüfen, ob WLAN verbunden ist
if ((SequenceStep == 1) && (WiFi.status() == WL_CONNECTED)) {
SequenceStep ++; // Weiter zur nächsten Sequenz 1-->2
digitalWrite(LedOkPin, HIGH); //LED Prozess SML Running OK
}
// Schritt 2-12: SML-Daten auslesen und OBIS-Werte extrahieren
if (SmlSerial.available()) { //wenn Daten vorhanden
bytesmlread = SmlSerial.read(); //Temp-Speicher der SML Nachricht
switch (SequenceStep) {
// Schritt 2: Prüfen auf Startsequenz
case 2:
if (bytesmlread == byt_Start_Seq[SequenceIndex]) {
SequenceIndex ++;
if (SequenceIndex == sizeof(byt_Start_Seq)) {
SequenceIndex = 0;
if (intAktLoopCount < intSollLoopCount-1) {
SequenceStep = 9;
}else{
SequenceStep ++; // Weiter zur nächsten Sequenz 2-->3
}
}
} else {
SequenceIndex = 0;
}
break;
// Schritt 3–5: OBIS 1.8.0 (Bezug)
case 3: // Suche Sequenz für Zählerwert OBIS 1.8.0
if ((bytesmlread == byt_180_Seq[SequenceIndex])||SequenceIndex == 11) { //Sequenz Index 12 wird Übersprungen byte nicht Konstant
SequenceIndex ++;
if (SequenceIndex == sizeof(byt_180_Seq)) {
SequenceIndex = 0;
bytesmlread = 0; //Leeren um sicherzustellen, dass das nächste Byte empfangen wurde
SequenceStep ++; // Weiter zur nächsten Sequenz 3-->4
}
} else {
SequenceIndex = 0;
}
break;
case 4: // Ermittle die Anzahl der Byte für Zählerwert OBIS 1.8.0
if (bytesmlread != -1) {
intCountByteVal[0] = rechteHexZiffer(bytesmlread)-1;
SequenceStep ++; // Weiter zur nächsten Sequenz 4-->5
}
break;
case 5: // Zählerwert 1.8.0 speichern
byt_180_HEX[SequenceIndex] = bytesmlread;
SequenceIndex ++;
if (SequenceIndex == intCountByteVal[0]) {
SequenceIndex = 0;
SequenceStep ++; // Weiter zur nächsten Sequenz 5-->6
}
break;
// Schritt 6–8: OBIS 2.8.0 (Einspeisung)
case 6: // Suche Sequenz für Zählerwert OBIS 2.8.0
if (bytesmlread == byt_280_Seq[SequenceIndex]) {
SequenceIndex ++;
if (SequenceIndex == sizeof(byt_280_Seq)) {
SequenceIndex = 0;
bytesmlread = 0; //Leeren um sicherzustellen, dass das nächste Byte empfangen wurde
SequenceStep ++; // Weiter zur nächsten Sequenz 6-->7
}
} else {
SequenceIndex = 0;
}
break;
case 7: // Ermittle die Anzahl der Byte für Zählerwert OBIS 2.8.0
if (bytesmlread != -1) {
intCountByteVal[1] = rechteHexZiffer(bytesmlread)-1;
SequenceStep ++; // Weiter zur nächsten Sequenz 7-->8
}
break;
case 8: // Zählerwert 2.8.0 Speichern
byt_280_HEX[SequenceIndex] = bytesmlread;
SequenceIndex ++;
if (SequenceIndex == intCountByteVal[1]) {
SequenceIndex = 0;
SequenceStep ++; // Weiter zur nächsten Sequenz 8-->9
}
break;
// Schritt 9–11: OBIS 10.7.0 (aktuelle Wirkleistung)
case 9: // Suche Sequenz für Zählerwert OBIS 10.7.0
if (bytesmlread == byt_10070_Seq[SequenceIndex]) {
SequenceIndex ++;
if (SequenceIndex == sizeof(byt_10070_Seq)) {
SequenceIndex = 0;
bytesmlread = 0; //Leeren um sicherzustellen, dass das nächste Byte empfangen wurde
SequenceStep ++; // Weiter zur nächsten Sequenz 9-->10
}
} else {
SequenceIndex = 0;
}
break;
case 10: // Ermittle die Anzahl der Byte für Zählerwert OBIS 10.7.0
if (bytesmlread != -1) {
intCountByteVal[2] = rechteHexZiffer(bytesmlread)-1;
SequenceStep ++; // Weiter zur nächsten Sequenz 10-->11
}
break;
case 11: // Zählerwert 10.7.0 speichern
byt_10070_HEX[SequenceIndex] = bytesmlread;
SequenceIndex ++;
if (SequenceIndex == intCountByteVal[2]) {
SequenceIndex = 0;
SequenceStep ++; // Weiter zur nächsten Sequenz 11-->12
}
break;
case 12: // Suche Stop-Sequence
if (bytesmlread == byt_Stop_Seq[SequenceIndex]) {
SequenceIndex ++;
if (SequenceIndex == sizeof(byt_Stop_Seq)) {
SequenceIndex = 0;
SequenceStep ++; // Weiter zur nächsten Sequenz 12-->13
}
} else {
SequenceIndex = 0;
}
break;
}
}
//Werte Übermitteln
if (SequenceStep == 13){
dbl_10070toDB += parseWattValue(byt_10070_HEX, intCountByteVal[2]);
intAktLoopCount ++;
if (intAktLoopCount == intSollLoopCount){
digitalWrite(LedOkPin, LOW); //LED Prozess Datensammlung abgeschlossen
//Werte zusammenstellen
dbl_10070toDB /= intAktLoopCount;
dbl_10070toDB = roundToDecimalPlaces(dbl_10070toDB,2);
dbl_180toDB = parseUnsignedValue(byt_180_HEX,intCountByteVal[0]);
dbl_180toDB /= 10000;
dbl_180toDB = roundToDecimalPlaces(dbl_180toDB,3);
dbl_280toDB = parseUnsignedValue(byt_280_HEX,intCountByteVal[1]);
dbl_280toDB /= 10000;
dbl_280toDB = roundToDecimalPlaces(dbl_280toDB,3);
// Use WiFiClientSecure class to create TLS connection
WiFiClientSecure client;
client.setInsecure();
if (client.connect(host, httpsPort)) {
client.print(String("GET ") + "/**URL TO YOUR PHP**.php?PW=**YOUR SALT PASSWORD**" +
"&P180=" + String(dbl_180toDB) +
"&P280=" + String(dbl_280toDB) +
"&P10070=" + String(dbl_10070toDB) +
" HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"User-Agent: BuildFailureDetectorESP8266\r\n" +
"Connection: close\r\n\r\n");
// Warten bis alles übertragen wurde (Server hat geantwortet)
while(client.connected() && !client.available()) {
yield(); // Watchdog füttern
}
client.stop(); // Verbindung sauber schließen
}
// Speicher Leeren für neue Werte
intAktLoopCount = 0;
dbl_180toDB = 0;
dbl_280toDB = 0;
dbl_10070toDB = 0;
}
memset(intCountByteVal, 0, sizeof(intCountByteVal)); // Reset Array relevanten Bytes
SequenceStep = 1; // Sequenz vollständig (Neustart)
}
}
//Funktionen
/*Parse 10.07.0 aktuelle Wirkleistung Bezug/Einspeisung */
long parseWattValue(byte* data, int length) {
if (length < 1 || length > 4) return 0; // Ungültige Länge
long result = 0;
// Bytes zusammenfügen (Big Endian)
for (int i = 0; i < length; i++) {
result <<= 8;
result |= data[i];
}
// Prüfen, ob der Wert negativ ist (höchstes Bit des ersten Bytes gesetzt?)
if (data[0] & 0x80) {
// Zweierkomplement-Sign-Erweiterung
// Anzahl der ungenutzten oberen Bits (32 - length*8)
int shift = (4 - length) * 8;
result = (result << shift) >> shift; // Sign-Erweiterung durch Links- und Rechtsschieben
}
return result;
}
/*Parse Zählerstände 1.8.0 und 2.8.0 (immer positiv)*/
unsigned long parseUnsignedValue(byte* data, int length) {
if (length < 1 || length > 4) return 0;
unsigned long result = 0;
for (int i = 0; i < length; i++) {
result <<= 8;
result |= data[i];
}
return result;
}
/*Rückgabe rechte Hex Ziffer als Integer*/
byte rechteHexZiffer(byte hexWert) {
return hexWert & 0x0F;
}
/*Zahlen Runden auf Nachkommastellen*/
double roundToDecimalPlaces(double value, int decimalPlaces) {
double scalingFactor = pow(10.0, decimalPlaces);
return round(value * scalingFactor) / scalingFactor;
}
/*Error Code durch LED ausgeben*/
void ErrorCodeLed(int intSmlStep) {
uint32_t static previousMillis = millis(); // speichert wie viele Sekunden seit der letzten Änderung vergangen sind
uint32_t static interval = 5000; // Interval zwischen zwei Änderungen
boolean static value = LOW; // Startwert der LED
int static intStep = 0; //Aktueller Schritt
int static intStepOld; // Alter Schritt speichern
int static BlinkCount = 0; //Speicher Anzahl aktuell geblinkt
if (intStep < 2) {
if (intSmlStep == intStepOld) {
intStep = 1;
} else {
intStepOld = intSmlStep;
intStep = 0;
previousMillis = millis();
}
}
if (intStep == 1) {
if (millis() - previousMillis > interval) {
previousMillis = millis(); // aktuelle Zeit abspeichern
intStep = 2;
interval = 500;
}
}
if (intStep == 2) {
if (millis() - previousMillis > interval) {
previousMillis = millis(); // aktuelle Zeit abspeichern
value = !value; // LED Zustand wecheln.
digitalWrite(LedErrorPin, value);// Wert auf den Ausgang schreiben
BlinkCount ++;
if (BlinkCount == intStepOld * 2) {
intStep = 0;
interval = 5000;
previousMillis = millis();
BlinkCount = 0;
}
}
}
}
Dieses ESP8266-Programm liest über einen IR-Kopf die SML-Daten eines digitalen Stromzählers aus und überträgt die Messwerte per HTTPS an einen Webserver. Die Steuerung erfolgt über eine Zustandsmaschine mit 13 Sequenzschritten. Dabei wird die aktuelle Wirkleistung (OBIS 10.7.0) kontinuierlich erfasst, während die Zählerstände (OBIS 1.8.0 und 2.8.0) nur einmal pro Upload-Zyklus verarbeitet werden. Dies reduziert die Datenmenge und erhöht die Effizienz.
intAktLoopCount) noch nicht erreicht ist, wird direkt zu Schritt 9 gesprungen – die Zählerstände 1.8.0 und 2.8.0 werden übersprungen und nur die aktuelle Leistung (10.7.0) erfasst.
intSollLoopCount (standardmäßig 60) erfolgreichen Durchläufen wird die mittlere Leistung berechnet und zusammen mit den Zählerständen per HTTPS an den Server gesendet. Danach werden alle Werte zurückgesetzt und der Zyklus beginnt von vorn.
Die gesamte Ablaufsteuerung basiert auf einer effizienten Zustandsmaschine, die sicherstellt, dass nur vollständige und valide SML-Telegramme verarbeitet werden. Die zyklische Mittelwertbildung der Leistung sorgt für geglättete Werte und reduziert kurzfristige Schwankungen. Die HTTPS-Übertragung erfolgt verschlüsselt und zuverlässig über die WiFiClientSecure-Klasse.
Den PHP-Code können Sie direkt kopieren und auf Ihren Server hochladen. Bitte beachten Sie, dass Sie an insgesamt 5 Stellen Ihre persönlichen Daten wie Datenbank-Name, Datenbank-Passwort, Datenbank-Benutzer, Datenbank-Host und Salt-Passwort eintragen müssen. Diese Stellen sind im Code durch ** YOUR VALUE ** gekennzeichnet und müssen individuell angepasst werden, damit die Verbindung und Datenübertragung korrekt funktioniert.
<?php
/*Fehler ausschalten*/
//error_reporting(E_ALL);
//ini_set("display_errors", 1);
error_reporting(0);
/* Datenbankverbindung */
/* Datenbankverbindung */
$username = "** YOUR USERNAME **";
$password = "** YOUR PASSWORD **";
$database = "**YOUR DATABASE NAME **";
$hostname = "** YOUR HOST IP + PORT **";
//connection to the database
$dbcon = mysqli_connect($hostname, $username, $password, $database);
mysqli_set_charset($dbcon, 'utf8');
/* Variablen */
//GET
$Get_PW = $_GET["PW"]; //Passwort zur Berechtigungsbestätigung
$Get_obis180 = $_GET["P180"]; //Zählerwert für OBIS 1.8.0
$Get_obis280 = $_GET["P280"]; //Zählerwert für OBIS 2.8.0
$Get_obis10070 = $_GET["P10070"]; //Zählerwert für OBIS 10.07.0
//Zeitstempel
$timestamp = time();
$aktTime = date("H:i:s", $timestamp);
$aktDate = date("Y-m-d", $timestamp);
$yesterday = date("Y-m-d", strtotime("-1 day"));
//Allgemeine Variablen
$PWsoll = "** YOUR SALT PASSWORD HASH **";
$switch_db_case = 0;
//Variablen Datenbankabfrage
$DBdataArray = array();
$dif_used_180 = 0;
$dif_used_280 = 0;
$dif_180 = 0;
$dif_280 = 0;
// Überprüfen, ob Passwort und Zählerwerte gesetzt sind
if ($Get_PW == $PWsoll && $Get_obis180 != "" && $Get_obis280 != "" && $Get_obis10070 != "") {
/* OBIS 10.07.0 in DB schreiben */
$statement = "INSERT INTO obis_10070 (obis_10070, TimeStamp) VALUES ('" . $Get_obis10070 . "', '" . $aktDate . " " . $aktTime . "')";
mysqli_query($dbcon, $statement);
/* OBIS 1.8.0 und 2.8.0 */
// Letzte Werte ermitteln
$statement = "SELECT * FROM obis_180_280 ORDER BY Date DESC LIMIT 2";
$sqlresult = mysqli_query($dbcon, $statement);
if ($sqlresult && mysqli_num_rows($sqlresult) > 0) {
// Schleife für alle Datensätze (in diesem Fall 2)
while ($dsatzSQL = mysqli_fetch_assoc($sqlresult)) {
// Speichern der Daten im Array
$DBdataArray[] = $dsatzSQL;
}
// Sicherstellen, dass genau 2 Datensätze im Array sind
if (count($DBdataArray) == 2) {
//Datenbank hat min zwei Einträg
if($DBdataArray[0]["date"] == $aktDate){
//5. Letzter Eintrag ist von Heute
$switch_db_case = 5;
}elseif ($DBdataArray[0]["date"] == $yesterday){
//6. Letzter Eintrag ist von Gestern
$switch_db_case = 6;
}else{
//7. Letzter Eintrag ist schon länger her (Bei Ausfall länger als 1 Tag).
$switch_db_case = 7;
}
} elseif(count($DBdataArray) == 1) {
//Datenbank hat nur einen Eintrag
if($DBdataArray[0]["date"] == $aktDate){
//2. Datenbank neu. Enthält nur Einträge von heute.
$switch_db_case = 2;
}elseif($DBdataArray[0]["date"] == $yesterday){
//3. Datenbank neu. Enthält nur Einträge von Gestern.
$switch_db_case = 3;
}else{
//4. Letzter Eintrag ist schon länger her (Bei Ausfall länger als 1 Tag).
$switch_db_case = 4;
}
}
} else {
//1. Neustart, noch kein Datensatz vorhanden
$switch_db_case = 1;
}
echo $switch_db_case;
// Datenbank anpassen
switch ($switch_db_case) {
case 1:
//OK
//Neustart kein Datensatz vorhanden
$statement = "INSERT INTO obis_180_280 (used_180, used_280, obis_180, obis_280, date) VALUES ( '0', '0', '" . $Get_obis180 . "', '" . $Get_obis280 . "', '" . $aktDate . "')";
mysqli_query($dbcon, $statement);
break;
case 2:
//OK
//Nur ein Eintrag mit heutigem Datum, Wert Updaten
$dif_used_180 = $DBdataArray[0]["used_180"] + ($Get_obis180 - $DBdataArray[0]["obis_180"]);
$dif_used_280 = $DBdataArray[0]["used_280"] + ($Get_obis280 - $DBdataArray[0]["obis_280"]);
$statement = "Update obis_180_280 SET obis_180=$Get_obis180, obis_280=$Get_obis280, used_180=$dif_used_180, used_280=$dif_used_280 WHERE Date='" . $aktDate . "'";
mysqli_query($dbcon, $statement);
break;
case 3:
//OK
//Nur ein Eintrag mit gestrigem Datum, Werte von heute eintragen.
$dif_used_180 = $Get_obis180 - $DBdataArray[0]["obis_180"];
$dif_used_280 = $Get_obis280 - $DBdataArray[0]["obis_280"];
$statement = "INSERT INTO obis_180_280 (used_180, used_280, obis_180, obis_280, date) VALUES ( '".$dif_used_180."', '".$dif_used_280."', '" . $Get_obis180 . "', '" . $Get_obis280 . "', '" . $aktDate . "')";
mysqli_query($dbcon, $statement);
break;
case 4:
//OK
//Nur ein Eintrag mit älterem Datum, Werte bis heute nachtragen.
$LatestDBDate = new DateTime($DBdataArray[0]["date"]);
$DayDiff = $LatestDBDate->diff(new DateTime($aktDate))->days;//-1
$dif_used_180 = round(($Get_obis180 - $DBdataArray[0]["obis_180"])/$DayDiff,3);
$dif_used_280 = round(($Get_obis280 - $DBdataArray[0]["obis_280"])/$DayDiff,3);
$dif_180 = $DBdataArray[0]["obis_180"];
$dif_280 = $DBdataArray[0]["obis_280"];
$statement = "INSERT INTO obis_180_280 (used_180, used_280, obis_180, obis_280, date) VALUES ";
for ($i = 1; $i <= $DayDiff-1; $i++ ){
$nextday = date("Y-m-d", strtotime("+$i day", strtotime($DBdataArray[0]["date"])));
$dif_180 += $dif_used_180;
$dif_280 += $dif_used_280;
$statement .= "('$dif_used_180', '$dif_used_280', '$dif_180', '$dif_280', '$nextday'), ";
}
$statement .= "('".$dif_used_180."', '".$dif_used_280."', '" . $Get_obis180 . "', '" . $Get_obis280 . "', '" . $aktDate . "')";
mysqli_query($dbcon, $statement);
break;
case 5:
//OK
//Zwei oder mehr Einträge. Der letzte Eintrag ist von heute.
$dif_used_180 = $Get_obis180 - $DBdataArray[1]["obis_180"];
$dif_used_280 = $Get_obis280 - $DBdataArray[1]["obis_280"];
$statement = "Update obis_180_280 SET obis_180=$Get_obis180, obis_280=$Get_obis280, used_180=$dif_used_180, used_280=$dif_used_280 WHERE Date='" . $aktDate . "'";
mysqli_query($dbcon, $statement);
break;
case 6:
//OK
//Zwei oder mehr Einträge. Der letzte Eintrag ist von gestern.
$dif_used_180 = $Get_obis180 - $DBdataArray[0]["obis_180"];
$dif_used_280 = $Get_obis280 - $DBdataArray[0]["obis_280"];
$statement = "INSERT INTO obis_180_280 (used_180, used_280, obis_180, obis_280, date) VALUES ( '".$dif_used_180."', '".$dif_used_280."', '" . $Get_obis180 . "', '" . $Get_obis280 . "', '" . $aktDate . "')";
mysqli_query($dbcon, $statement);
break;
case 7:
//OK
//Nur Werte mit älterem Datum, Werte bis heute nachtragen.
$LatestDBDate = new DateTime($DBdataArray[0]["date"]);
$DayDiff = $LatestDBDate->diff(new DateTime($aktDate))->days;
$dif_used_180 = round(($Get_obis180 - $DBdataArray[0]["obis_180"])/$DayDiff,3);
$dif_used_280 = round(($Get_obis280 - $DBdataArray[0]["obis_280"])/$DayDiff,3);
$dif_180 = $DBdataArray[0]["obis_180"];
$dif_280 = $DBdataArray[0]["obis_280"];
$statement = "INSERT INTO obis_180_280 (used_180, used_280, obis_180, obis_280, date) VALUES ";
for ($i = 1; $i <= $DayDiff-1; $i++ ){
$nextday = date("Y-m-d", strtotime("+$i day", strtotime($DBdataArray[0]["date"])));
$dif_180 += $dif_used_180;
$dif_280 += $dif_used_280;
$statement .= "('$dif_used_180', '$dif_used_280', '$dif_180', '$dif_280', '$nextday'), ";
}
$statement .= "('".$dif_used_180."', '".$dif_used_280."', '" . $Get_obis180 . "', '" . $Get_obis280 . "', '" . $aktDate . "')";
mysqli_query($dbcon, $statement);
break;
}
}
/* Verbindung zur SQL-DB trennen */
mysqli_close($dbcon);
?>
Diese PHP-Datei verarbeitet die vom ESP8266 übermittelten Zählerdaten und speichert sie in einer MySQL-Datenbank. Sie prüft die Gültigkeit der Anfrage, berechnet Differenzwerte und aktualisiert die Datenbank je nach Zustand. Die Datei ist so aufgebaut, dass sie auch bei Ausfällen oder Neustarts konsistente Daten liefert.
** YOUR VALUE **).P180: Zählerstand für OBIS 1.8.0 (Bezug)P280: Zählerstand für OBIS 2.8.0 (Einspeisung)P10070: aktuelle Wirkleistung (OBIS 10.7.0)PW: Passwort zur Authentifizierung (muss mit $PWsoll übereinstimmen)obis_10070 geschrieben – unabhängig vom Zustand der anderen Daten.Die Datei prüft, welche Einträge bereits in der Tabelle obis_180_280 vorhanden sind und entscheidet anhand des Datums, wie die neuen Werte verarbeitet werden. Es gibt sieben mögliche Fälle:
obis_10070: Speichert die aktuelle Leistung mit Zeitstempel.obis_180_280: Speichert Bezug und Einspeisung pro Tag sowie die berechnete Differenz (used_180, used_280).Die Datei enthält 5 Stellen, an denen persönliche Zugangsdaten eingetragen werden müssen. Diese sind im Code durch ** YOUR VALUE ** gekennzeichnet und müssen angepasst werden, damit die Verbindung zur Datenbank funktioniert.
Die PHP-Datei ist robust gegenüber Ausfällen und sorgt dafür, dass auch bei längerer Unterbrechung die Zählerdaten korrekt interpoliert und gespeichert werden. So bleibt die Datenhistorie vollständig und nachvollziehbar.