Projekte - Stromzähler auslesen...
In meiner Wohnung habe ich einen Stromzähler mit Rücklaufsperre für die Stromeinspeisung. Der Zähler hat auch eine Infrarot-Schnittstelle, welche den aktuellen Stromverbrauch in Form eines gepulsten Signals ausgeben soll. Das hat mich natürlich interessiert....
Übersicht: Einführung...Hardware...Software...Ergebnisse...Allerdings...Fazit...
Einführung...
Für mein Balkonkraftwerk habe ich mir kürzlich einen Speicher zugelegt, um die als "kostenlose Spende" an meinen Stromversorger verschwendete Überproduktion zur Mittagszeit etwas besser auf den Tag zu verteilen und selbst zu nutzen.
Dieser hat ca. 1 kWh Kapazität ( ist bis auf 5 Speicherblöcke erweiterbar ) und wird zwischen Balkonkraftwerk und Inverter "eingehängt". Der vorhandene Inverter wird dabei weiterhin genutzt, der Speicher selbst hat also keinen eigenen Inverter.
Mit dem Hintergedanken an eine verbrauchsorientierte Steuerung von Speicher und Inverter habe ich mich daher mal mit dem Thema Verbrauchserfassung beschäftigt und bei Web-Recherchen erfahren, das mein Stromzähler ACE3000 Typ 260-C20D-R1-A den aktuellen Verbrauch durch gepulste Signale auf seiner Infrarot-Schnittstelle ausgibt.
Auf dem Gerät steht die Information 500 Imp. / kWh, weitere Informationen habe ich dazu erst mal nicht gefunden.
Ist mir zu kompliziert..... =>.... zurück zur Startseite
Hardware...
Natürlich gibt es im Netz bereits fertige Geräte und Anwendungen dazu - aber ich will es ja mal wieder "selbst bauen und programmieren"... Dann kann ich es auch später besser an meine Eigenbau-Haussteuerung anpassen.
Da der Zähler ja ein Infrarot-Signal senden soll, wird ein entsprechender Lesekopf benötigt - glücklicherweise werfe ich nichts weg und hatte also noch einen alten Brandmelder rumliegen. Wer im Netz nach Funktion von Brandmeldern "googelt", erfährt das in dem dargestellten "Labyrinth" eine LED-Sender-Diode und eine LED-Empfänger-Diode angeordnet sind, welche im Normalzustand "einander nicht sehen".

Wenn dann Rauch in das "Labyrinth" kommt, wird dadurch das Licht des Senders für den Empfänger sichtbar - und das "Geblöcke" geht los.
Für die Auswertung des Signals habe ich wieder einen ESP32 vorgesehen, allerdings wird hier noch eine kleine Schaltung benötigt.

Was sehen wir?..
Ausgehend vom 3.3 Volt (+) des ESP32 begrenzen wir die fliessenden Ströme mit dem R 1kOhm-Widerstand. Unser PNP-Transistor schaltet ja auf Durchgang - wenn an der Basis Strom "wegfliesst" und dies passiert, wenn die IR-Diode ein Lichtsignal erhält und diesen Stromweg freigibt....
Dann erhält unser PIN12 am ESP32 Spannung und wird auf den Zustand HIGH gesetzt. Damit er aber Zustand LOW nicht "flattert" ( siehe PullUP- bzw. PullDown-Schaltungen ), wird er durch den Widerstand R 51kOhm mit GND verbunden und somit im "Leerlauf" auf GND => definitiv LOW gezogen.
Hinweis: Bevor Ihr diese Schaltung ausprobiert und lötet - wartet noch ein wenig s.u...
Ist mir zu kompliziert..... =>.... zurück zur Startseite
Software...
Unsere so erfassten Signale können wir somit auswerten, indem wir den Zeitraum zwischen den Pulsen z.B. mit unserem ESP32 messen....
Dazu habe ich mir ein kleines Programm geschrieben und übertrage die ermittelten Daten per Bluetooth an meinen Laptop...
Die "älteren Entwicklungsschritte" sind im Programm jeweils auskommentiert, auch das Problem mit den Integer/Real-Variablen bei Berechnungen ist wieder aufgetreten..
(Achtung - der Text [code].....[/code] gehört NICHT zum Programm..)
[code]
/*
* ESP32 ermittelt mittels IR-Photodiode die Impulse
* des Stromzaehlers und sendet die Ergebnisse über Bluetooth
*/
#include "BluetoothSerial.h"
BluetoothSerial SerialBT;
//*********** zuerst mal Grundwerte definieren
int MESS_PIN = 12; // der LESE-Pin
unsigned long lastmicro, micro; // für die Zeitmessung
float zeit; // die erforderliche Zeit für die gewünschte Anzahl Signale
void setup() {
Serial.begin(115200); // zum seriellen Monitor Frequenz einstellen
if (!SerialBT.begin("ESP32_Zaehler")){
Serial.println("Keine Verbindung"); //Bluetooth device name
}
else
{
// *********** erste Meldung über Bluetooth
SerialBT.println("Start....");
}
// den LESE-PIN aktivieren, per Pull-Down runtergezogen auf 0
pinMode(MESS_PIN, INPUT);
// *********** Zeitenwerte voreinstellen
micro = micros();
lastmicro = micro;
} // ende setup
void loop() {
/*
// versuch 1 - kommt da was....
// warten auf einen datensatz....
while ( 0 == digitalRead(MESS_PIN));
// wenn was kommt => jeweils melden...
SerialBT.println("An");
// Ergebniss Antwort An kommt in Blöcken - kann hier aber auch noch
// Terminproblem mit Bluetooth beinhalten
*/
/*
// versuch 2 - kommen blockweise immer mehrere PING - z.B. 450 Impulse, dann Ruhe ?
// warten auf einen datensatz....solange LOW...
while ( 0 == digitalRead(MESS_PIN));
// los gehts...Voreinstellungen, wir kommen mit HIGH rein
zeit=0; // hier PING-Zählervariable
// 1sek = 1.000.000 microsekunden...=> 0.1 sek = 1000 Mikrosekunden
// wenn mehrere PING kommen, haben sie einen Abstand < 0,5 sek....
while (1 == digitalRead(MESS_PIN)){
zeit=zeit+1; // Hochzählen
lastmicro = micros();
// solange das darauffolgende LOW zwischen der PING-Sequenz nicht länger als 0.5 sek. dauert..
// der ESP würde sonst mit "Prozessorgeschwindigkeit" durchballern - und nicht Impulse zaehlen....
// wenn der LOW-Zustand dann länger dauert, ist die Sequenz beendet...
while ( ( 0 == digitalRead(MESS_PIN) ) && ( micros()-lastmicro < 5000) ); // einfach warten
}
SerialBT.println(zeit);
// Ergebniss - es wird immer etwa 1500 gezählt, allerdings scheinen die Abstände bei höherem
// Stromverbrauch etwas kürzer zu sein....
*/
/*
// versuch 3 - Prüfung Abstandsverkürzung PING's bei höherem Stromverbrauch
// Variable "Nullen"...
zeit=0.00000;
// wir müssen uns immer zuerst "eintakten"....das Senden über Bluetooth
// unterbricht zeitlich die Abarbeitung des Programmdurchlaufs und wir
// überspringen somit ggf. einen Pulsdurchgang....
// warten auf eine Unterbrechung....solange LOW...HIGH...dann
while ( 0 == digitalRead(MESS_PIN));
while (1 == digitalRead(MESS_PIN));
// los gehts...
lastmicro = micros(); // Startzeit
while (0 == digitalRead(MESS_PIN)); // Solange LOW = Abstand der Pulssignale
while (1 == digitalRead(MESS_PIN)); // zzgl. Länge des jeweiligen Pulssignals
micro=micros(); // Endzeit
// ACHTUNG !!!! Integer mit Integer berechnet bleibt Integer, auch wenn die
// Zielvariable dann float ist => immer xx.0000 als Ergebniss d.h. mind. ein
// Rechenoperand muss float als Wert sein !!!
// 1.000.000 Mikro-Sekunden = 1 sek...
zeit= ((micro-lastmicro)/1000000.00000); // Zeitabstand zwischen den Pulsen in sek
// wir haben - angeblich - 500 Pulse für 1000 Watt im Bezugsraum einer Stunde ( 3600 sek ) ...
// d.h. bei 1000 Watt hätten wir einen Abstand von 7.20 sek zwischen den Impulsen
// das Pulssignal selbst ist anscheinend etwa 1500 microsekunden lang....und wird ja mitgemessen
float bezug = 1.00000 * (3600 / 500); // für 1000 Watt (REAL!)
float watt = ( bezug / zeit ) * 1000.00000; // Bezugszeit für 1000 W / gemessene Zeit z.B. 1sek/10Sek = 0.1*1000W
SerialBT.print("aktuelle Leistung [W] : ");
SerialBT.print(watt,2);
SerialBT.print(" / Pulsabstand [sek] : ");
SerialBT.println(zeit,2);
// Ergebniss
// Das "Ruhe-Pulssignal" bei geschätzt 100-200W "Grundlast" ohne spezielle Verbraucher erzeugt
// einen Signalabstand von etwa 14 sek - was bei 500 Imp dann ABER etwa 500 W "Grundlast" entsprechen würde....
// Also habe ich bis auf einen RasPi, die FritzBox und einige StandBy-Uhren (wie z.B. am Herd) alles ausgeschaltet
// und komme auf einen Signalabstand von 68 sek - was bei 500 Imp dann noch etwa 125 W "Grundlast" entsprechen
// würde....bei 1000 Imp wären es dann ca. 60 Watt "Grundlast"...
// Also habe ich die Ergebnisse mittels Verbrauchsmessgerät an meinem Mini-Ofen getestet, dieser
// zieht lt. Messgerät etwa 700 Watt....Später dann am Wasserkocher 2000W...
// Ausgehend von einem Start-Puls-Abstand von 8,6 sek verringert sich der Abstand auf ca. 3.5 Sekunden
// a) bei 500 Imp. und einer Grundlast ( 8,6sek ) von 850W => +700W = 1550W => Pulsabstand 4.7 sek
// b) bei 1000 Imp. und einer Grundlast ( 8,6sek ) von 425W => +700W = 1125W => Pulsabstand 3,3 sek
// FAZIT: die Impulse/1000W ( und Stunde) scheinen eher 1000 Imp. statt 500 Imp. zu sein
// Allerdings sind da immer noch Differenzen...welche ggf. aus dem Rechenweg resultieren
// zeit ist float und somit in den Nachkommastellen begrenzt....wird also irgendwann gerundet...
// Im Versuch 4
*/
// versuch 4 - wie 3 aber anderer Rechenweg
// Variable "Nullen"...
zeit=0.00000;
// wir müssen uns immer zuerst "eintakten"....das Senden über Bluetooth
// unterbricht zeitlich die Abarbeitung des Programmdurchlaufs und wir
// überspringen somit ggf. einen Pulsdurchgang....
// warten auf eine Unterbrechung....solange LOW...HIGH...dann
while ( 0 == digitalRead(MESS_PIN));
while (1 == digitalRead(MESS_PIN));
// los gehts...
lastmicro = micros(); // Startzeit
while (0 == digitalRead(MESS_PIN)); // Solange LOW = Abstand der Pulssignale
while (1 == digitalRead(MESS_PIN)); // zzgl. Länge des jeweiligen Pulssignals
micro=micros(); // Endzeit
// sicherheitsfunktion - die micros()-schleife wird irgendwann zurück auf NULL gesetzt
// dann wäre ggf. lastmicro > micro => ergebnisse schrott ggf. Absturz
if (micro > lastmicro) {
// ACHTUNG !!!! Integer mit Integer berechnet bleibt Integer, auch wenn die
// Zielvariable dann float ist => immer xx.0000 als Ergebniss d.h. mind. ein
// Rechenoperand muss float als Wert sein !!!
// ACHTUNG !!!! siehe Berechnung => Bezug...
// 1.000.000 Mikro-Sekunden = 1 sek...
// zeit= ((micro-lastmicro)/1000000.00000); // Zeitabstand zwischen den Pulsen in sek
zeit= ( (micro-lastmicro) ); // Zeitabstand zwischen den Pulsen in MIKROSEKUNDEN => jetzt INTEGER
// Beispiel 2600W gemessen => 1,385 sek => 1.385 Mill...
// stunde ist 3600 sek * 1.000.000 Mikrosekunden / sekunde
// bezug wird somit eigentlich immer integer (xx Millionen)
// FALSCH - float bezug = 1000000.0 * (3600 / 1000); // für 1000 Watt - statt 500 Imp jetzt also 1000 Imp (REAL!)
float bezug = 1000000.0 * (3600.0 / 1000.0); // für 1000 Watt - statt 500 Imp jetzt also 1000 Imp (REAL!)
// => 3,6 Mill...= 3.600.000 aber s.u....
// Das Ergebniss war falsch.... daher habe ich mir die beiden Rechenwerte mal angesehen
// Bezug war nicht 36xxxx sondern 30xxxx - erst bei Ergänzung der xx.0 Stellen wurde der
// richtige Bezug mit 36xxx vom Programm berücksichtigt
// SerialBT.print (bezug);
// SerialBT.print ("/");
// SerialBT.println(zeit);
// jetzt also mikrosekunde/mikrosekunde => z.B. 0.2xxxxxxxxx * 1000 = 200W
// erst jetzt wird gerundet...
float watt = ( bezug / zeit ) * 1000.00000; // Bezugszeit für 1000 W / gemessene Zeit z.B. 1sek/10Sek = 0.1*1000W
// 3,6 MIll für 1000W / 1,385 Mill = 2,5993 * 1000W = 2599 W
// 1.000.000 Mikro-Sekunden = 1 sek...
// jetzt erst zeit in sek. umrechnen für die Ausgabe
zeit= ((micro-lastmicro)/1000000.00000); // Zeitabstand zwischen den Pulsen in sek
SerialBT.print("aktuelle Leistung [W] : ");
SerialBT.print(watt,2);
SerialBT.print(" / Pulsabstand [sek] : ");
SerialBT.println(zeit,2);
}// micro > lastmicro
} // ende loop
[/code]
ist mir zu kompliziert..... =>.... zurück zur Startseite
Ergebnisse...
Und so sieht dann das Ergebniss über Bluetooth aus...

Tja, "soweit so gut", wir haben jetzt unsere Ergebnisse und die passen auch halbwegs.
Wir sollten jetzt aber noch eine Zeitsteuerung einbauen, da in Abhängigkeit vom aktuellen Stromverbrauch auch die Bluetooth-Pakete immer schneller gesendet werden - und wir brauchen ja nicht alle paar 1/10-tel Sekunden einen neuen Datensatz. Ausserdem kann es dann passieren, das wir mit dem Zeitbedarf der Bluetooth-Übertragung "ins Gehege" kommen könnten.
ist mir zu kompliziert..... =>.... zurück zur Startseite
ALLERDINGS...
Ich habe das System bisher mit einer Akku-Powerbank betrieben und das hat ja auch ganz gut geklappt. Der Versuch der Stromversorgung des ESP32 mit einem kleinen Netzteil (Handy-Ladegerät) ist aber "in die Hose gegangen".
Plötzlich habe ich nur noch 0.0000 sek zwischen den Signalen.

Wahrscheinlich ist meine elektronische Schaltung "zu hoch-ohmig" und fängt bereits die Puls-Signale der Stromversorgung durch das kleine Netzteil mit ein. Und dies, obwohl ich einen kleinen Kondensator zwischen 3.3V und GND des ESP32 gehängt habe.
Dann habe ich den PullDown-Widerstand von 51 kOhm auf 10 kOhm reduziert und jetzt funktioniert es - die Qualität des Netzteils ist dabei aber anscheinend ebenfalls von Bedeutung.
Ein NoName (aus FernOst) Netzteil mit 2A Output funktioniert, allerdings gibt es gelegentlich Messfehler - ein Nokia-Netzteil (ebenfalls 2A Output) dagegen läuft stabiler und bisher ohne Messfehler...
Eventuell kann der Widerstand auch noch etwas verkleinert werden - muss ich gelegentlich noch ausprobieren.
ist mir zu kompliziert..... =>.... zurück zur Startseite
Fazit.....
Nun ja - es läuft also, was ich aber damit später anfangen werden, weiss ich aktuell noch nicht...
Probiert es doch einfach aus...