Digitalen Stromzähler ins openHAB
Für einen weiteren Stromanschluss in der Garage habe ich einen digitalen Zähler bekomme den ich gerne auslesen möchte um den Stromverbrauch der Wallbox zu analysieren. Dafür natürlich ein Wemos D1 Mini mit einem (modifizierten) TCRT5000 und einem BME280 für ein paar Klimadaten.
Material
ESP8266-12F * Affiliatelink
Jumper-Kabel * Affiliatelink
TCRT5000 * Affiliatelink
BME280 * Affiliatelink (aktuell sehr teuer, müsst ihr mal nach günstigeren suchen oder evtl. drauf verzichten)
Anschlüsse
![](https://blog.gwarg.de/wp-content/uploads/2023/02/Stromzaehler_Steckplatine-1024x683.png)
Stromzähler
Ich habe einen Stromzähler mit IR-LED-Ausgang mit 10.000 Imp/kWh. Meine Wallbox in der Garage hat eine Nennleistung 11kW, man erlaube mir meine Gedanken auf die Leistung einzugrenzen 🙂
Also, 11 kW * 10.000 Imp/kWh = 110.000 Imp/h = 1833 Imp/Min = 30,56 Imp/Sek.
Warum die Gedanken: kommt gleich beim Arduino-Sketch.
Um die Diode am Zähler anzubringen hatte ich Glück, dass zwischen Zähler und Rückwandplatte noch ein wenig Platz war und ich ein gedrucktes Teil dort anbringen konnte wodurch die Diode genau auf die IR-LED „guckt“. Damit ist die Erfassung perfekt (glaube ich jedenfalls).
Arduino
Der Code beginnt wie immer mit den Variablen. Dann Setup mit dem üblichen, WLAN, MQTT, etc.
Ich habe im loop auf alle Ausgaben verzichtet um das Programm möglichst schnell zu lassen. Wie man sieht gibt es am Ende des loops kein „delay()“. Hintergrund ist, dass ich durch ein delay durchaus einen Impuls verpassen könnte. Also möglichst oft die Eingänge prüfen.
Die Frage ist: Wie schnell ist eigentlich der Wemos D1 mini den ich verwende? Ich hab einen einfachen Sketch erstellt um das zu prüfen. Ergebnis: 5.054.487 Loops/Minute, also ca. 85 Loops/ms. Sollte also für meinen Stromzähler reichen.
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <ArduinoOTA.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <time.h>
// #############################################
// WLAN-Zugang
// #############################################
#define wifi_ssid "Garagen-WLAN"
#define wifi_password "Garagen-Passwort"
// #############################################
// MQTT-Server (Openhab)
// #############################################
#define mqtt_server "192.168.2.2"
#define mqtt_user "openhabian"
#define mqtt_password ""
// #############################################
// NTP-Client
// #############################################
#define MY_NTP_SERVER "192.168.2.1"
#define MY_TZ "CET-1CEST,M3.5.0/02,M10.5.0/03"
// #############################################
// MQTT-Topics
// ############################################
// BME280
#define topic_temperature "garage/temperature"
#define topic_humidity "garage/humidity"
#define topic_pressure "garage/pressure"
#define topic_dewpoint "garage/dewpoint"
#define topic_heatindex "garage/heatindex"
// IR-LED Stromzähler
#define topic_energy_daily "stromGarage/tagesstand"
#define topic_energy_diff "stromGarage/diff"
// Connections
WiFiClient espClient;
PubSubClient client(espClient);
// Sensoren
Adafruit_BME280 bme; // I2C
// PIN-Assignment
int Digital_Eingang = 16; // Binärer Wert
// Variablen zur Laufzeit
long lastMsg = 0; // Timestamp der letzten Messung
int DigitalLast = 0; // Energie-Impuls im letzten Loop
int daily_count_energy = 0; // Tagesstand
int daily_count_diff = 0; // Tagesstand
time_t now; // Zeiten:
tm tm; // ||
void setup ()
{
pinMode (Digital_Eingang, INPUT);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
while(!Serial); // time to get serial running
unsigned status;
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
while (1);
}
configTime(MY_TZ, MY_NTP_SERVER);
ArduinoOTA.setHostname("ESPGarageStrom");
ArduinoOTA.setPassword("OTAupdate");
ArduinoOTA.begin();
}
void loop ()
{
if (!client.connected()) {
reconnect();
}
// ## ###############
// ## Stromzähler
// ## ###############
int Digital = digitalRead (Digital_Eingang);
if(Digital==1 && Digital!=DigitalLast) {
// Zählimpuls
daily_count_energy++;
daily_count_diff++;
}
if (Digital!=DigitalLast) {
DigitalLast = Digital;
}
time(&now); // read the current time
localtime_r(&now, &tm); // update the structure tm with the current time
// Um Null Uhr den Zähler wieder zurück setzen!
if (tm.tm_hour == 0 && tm.tm_min == 0 && tm.tm_sec == 0) {
// Ein letztes mal setzen!
client.publish(topic_energy_daily, String(daily_count_energy).c_str(), true);
daily_count_energy = 0;
}
// ## ###############
// ## Temperaturen
// ## ###############
long now = millis();
if (now - lastMsg > (1 * 60 * 1000)) {
// Nur jede Minute Messwerte ermitteln!
lastMsg = now;
float bme_temp = bme.readTemperature();
float bme_humidity = bme.readHumidity();
float bme_pressure = bme.readPressure() / 100.0F;
float T = (bme_temp * 9 / 5) + 32; // Convert back to deg-F for the RH equation
float RHx = bme_humidity; // Short form of RH for inclusion in the equation makes it easier to read
float heat_index = (-42.379+(2.04901523*T)+(10.14333127*RHx)-(0.22475541*T*RHx)-(0.00683783*sq(T))-(0.05481717*sq(RHx))+(0.00122874*sq(T)*RHx)+(0.00085282*T*sq(RHx))-(0.00000199*sq(T)*sq(RHx))-32)*5/9;
if ((bme_temp <= 26.66) || (bme_humidity <= 40)) heat_index = bme_temp; // The convention is not to report heat Index when temperature is < 26.6 Deg-C or humidity < 40%
float dew_point = 243.04*(log(bme_humidity/100)+((17.625*bme_temp)/(243.04+bme_temp)))/(17.625-log(bme_humidity/100)-((17.625*bme_temp)/(243.04+bme_temp)));
client.publish(topic_temperature, String(bme_temp).c_str(), true);
client.publish(topic_humidity, String(bme_humidity).c_str(), true);
client.publish(topic_pressure, String(bme_pressure).c_str(), true);
client.publish(topic_dewpoint, String(dew_point).c_str(), true);
client.publish(topic_heatindex, String(heat_index).c_str(), true);
client.publish(topic_energy_daily, String(daily_count_energy).c_str(), true);
client.publish(topic_energy_diff, String(daily_count_diff).c_str(), true);
daily_count_diff = 0;
}
}
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(wifi_ssid);
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.print(WiFi.localIP());
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("outTopic", "ESP01 alive");
// ... and resubscribe
client.subscribe("inTopic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
Zusätzlich habe ich noch einen BME280 angeschlosen um die Temperatur in der Garage ebenfalls messen zu können.
Der Loop läuft durch, wenn sich der Wert ändert, werden interne Zähler hochgesetzt. Einmal einen Tageszähler der Nachts noch mal per MQTT gesendet und dann wieder auf 0 gesetzt wird. Per MQTT werden damit nur jede Minute Werte übertragen: Klimadaten, Tageswert und Minutenwert des Stromzählers.
openHAB
Im openHAB werden dann die weiteren Auswertungen gemacht die man so glaubt zu brauchen:
var Number Letzter_abgelesener_Zaehlerstand = 37.5
// Ab 01.10.2022: 19.269ct
var Number Strompreis_Monat = 81.12 // € pro Jahr
var Number Strompreis_Tag = 22.22465753424658 // ct pro Tag
var Number Strompreis_kWh = 33.33 // ct pro kWh
rule "Increment Strommeter"
when
Item MQTT_Garage_Energie changed
then
// Initialer Start - die beiden folgenden Zeilen auskommentieren und anschließend einen Zählimpuls auslösen damit das Regelwerk einmal durchlaufen wird!
// postUpdate(Strom_Garage_Heute, 0.0)
// postUpdate(Strom_Garage_Gestern, 0.0)
// postUpdate(Strom_Garage_Stand, 0.0)
// Aktuellen Zaehlerstand setzten - die folgenden drei Zeilen auskommentieren und anschließend einen Zählimpuls auslösen damit das Regelwerk einmal durchlaufen wird!
if (Strom_Garage_Stand.state != NULL && Strom_Garage_Stand.state < Letzter_abgelesener_Zaehlerstand) {
postUpdate(Strom_Garage_Stand, (Letzter_abgelesener_Zaehlerstand))
}
// Kommender Wert:
// MQTT_Garage_Energie := Tageswert bisher (in Impulsen)
// 10.000 Impulse := 1kWh
// -> 1 Impuls := 0,0001 kWh || 0,1 Wh || 100mWh
postUpdate(Strom_Garage_Heute, (Strom_Garage_Heute.state as DecimalType + (MQTT_Garage_EnergieDiff.state as DecimalType / 10))) // 0,1Wh
postUpdate(Strom_Garage_Stand, (Strom_Garage_Stand.state as DecimalType + ((MQTT_Garage_EnergieDiff.state as DecimalType / 10) / 1000)) // 0,1Wh
postUpdate(Stromkosten_Garage_Heute, ((Strom_Garage_Heute.state as DecimalType * Strompreis_kWh) + Strompreis_Tag) / 100)
end
rule "Reset Tagesverbrauch"
when
Time is midnight
then
postUpdate(Strom_Garage_Gestern, Strom_Garage_Heute.state)
postUpdate(Strom_Garage_Heute, 0.0)
postUpdate(Stromkosten_Garage_Gestern, Stromkosten_Garage_Heute.state)
postUpdate(Stromkosten_Garage_Heute, (Strompreis_Tag / 100))
end
Das wars dann auch schon. Damit kann man dann noch schöne Grafiken in Grafane bauen wenn man will:
![](https://blog.gwarg.de/wp-content/uploads/2023/02/Garage-1024x300.png)
Schreibe einen Kommentar