Hey zusammen,
Ich möchte euch ein weiteres Projekt von mir für euch bereitstellen.
Ich wollte meine Energieverbräuche in meinem Zuhause aufzeichnen und habe mir einen eigenen ziemlich genauen Smart-Meter gebaut, mit dem ich alle Ströme und Leistungen die ich in jedem Stromkreis habe, ermitteln kann.
Funktioniert via WLAN und MQTT.
MQTT Channels und Server müssen bereitgestellt werden. Integration erfolgt dann in Home via NodeRed.
Bei der Integration dass man in der EVE App die Daten von Home ersichtlich hat, bin ich noch am tüfteln. Lösungen und Ideen sind gern willkommen.
Mithilfe dieses How-Tos bekommt man mal alle Messwerte digital in ein System wie Nodered.
Es werden alle 10 Sekunden jeweils der maximale Strom und Leistung des Sensors innerhalb der Sendeintervallspanne per MQTT übertragen.
Läuft bei mir 24h/7Tage seit über 2 Monaten, ohne nur einen Mucks. Ziemlich stabil und zuverlässig programmiert und entwickelt. :´-D
Hauptmaterial:
1x ESP32 Dev Module
1x Powersupply für ESP32 Modul
1x Gehäuse (größe variiert je nach benötigter Menge an Stromwandlern)
Diese Komponenten können auch in variabler Stückzahl (X Stück) gekauft werden:
1x Kopfhörerbuchse (3,5mm Klinke, egal ob Mono oder Stereo - einfacher ist Mono)
1x SCT-013-020 Stromumbauwandler Messbereich 0-20A Sekundärseitig 0-1V
1x Kondensator Elektrolyt (ELKO) 10µF / min. 10V
2x Widerstand 20kOhm
--
Kleinmaterial:
Steckbrücken
Isolierter Draht
Lochrasterplatine
Stiftleiste (Buchse auf Lötpin)
Lötzinn
--
Werkzeug:
Lötkolben
Seitenschneider
Abisolierzange
Der Aufbau der Grundschaltung für je ein Stromumbauwandler sieht wie folgt aus:
Quelle: https://community.openenergymo…b5921b00817cddcdebc79.jpg
Hierzu verwenden wir die obere Schaltung.
Der Widerstand R1 wird nicht eingebaut! Dieser ist bereits in den SCT Wandler mit eingebaut.
R2 und R3 werden mit den 20kOhm Widerständen ausgetauscht. C1 mit dem 10µF Kondensator.
Vom ESP nehmen wir dann statt den 5V den 3,3V Pin. Die Masse wird am ESP Massepin angeschlossen.
Der Eingang der noch als letztes übrig ist, legen wir auf einen der Analogeingänge des ESP32 die nicht vom WLAN verwendet werden!
Das ESP 32 DEV Modul hat diverse Eingänge für Analogmessungen. Jedoch sind davon nicht alle belegbar, wenn man WLAN - was wir benötigen - verwenden möchte. Die Pins sind am DEV Modul via Matrix-Multiplexer für mehrer Funktionen frei Konfigurierbar.
Haben wir dann alles soweit X-Mal verlötet (je nachdem wie viele Stromkreise ihr überwachen möchtet) und vorbereitet, geht es an die Programmierung.
/*********
Adrian Zilly
Energy Meter ESP32 Dev Module
Hardware Lib: ESP32 Dev Module
v2.0
*********/
//Import Librarys
#include "EmonLib.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoOTA.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <SPI.h>
#include <driver/adc.h>
//Instanzen definieren
const char* nome1 = "Herd";
EnergyMonitor emon1; // 3AC Strom L1 //1AC - Herd
const char* MQTTChannelStrom1 = "EnergymeterStrom1"; //<================================== Anpassen - MQTT Channel-Name Strom 1
const char* MQTTChannelPower1 = "EnergymeterPower1"; //<================================== Anpassen - MQTT Channel-Name Leistung 1
const char* nome2 = "Wohnzimmer";
EnergyMonitor emon2; // 3AC Strom L2 //1AC - Wohnzimmer
const char* MQTTChannelStrom2 = "EnergymeterStrom2"; //<================================== Anpassen - MQTT Channel-Name Strom 2
const char* MQTTChannelPower2 = "EnergymeterPower2"; //<================================== Anpassen - MQTT Channel-Name Leistung 2
const char* nome3 = "Bad";
EnergyMonitor emon3; // 3AC Strom L3 //1AC - Bad
const char* MQTTChannelStrom3 = "EnergymeterStrom3"; //<================================== Anpassen - MQTT Channel-Name Strom 3
const char* MQTTChannelPower3 = "EnergymeterPower3"; //<================================== Anpassen - MQTT Channel-Name Leistung 3
const char* nome4 = "Flur";
EnergyMonitor emon4; // 1AC - Flur
const char* MQTTChannelStrom4 = "EnergymeterStrom4"; //<================================== Anpassen - MQTT Channel-Name Strom 4
const char* MQTTChannelPower4 = "EnergymeterPower4"; //<================================== Anpassen - MQTT Channel-Name Leistung 4
const char* nome5 = "Kind";
EnergyMonitor emon5; // 1AC - Kind
const char* MQTTChannelStrom5 = "EnergymeterStrom5"; //<================================== Anpassen - MQTT Channel-Name Strom 5
const char* MQTTChannelPower5 = "EnergymeterPower5"; //<================================== Anpassen - MQTT Channel-Name Leistung 5
const char* nome6 = "Schlafzimmer";
EnergyMonitor emon6; // 1AC - Schlafzimmer
const char* MQTTChannelStrom6 = "EnergymeterStrom6"; //<================================== Anpassen - MQTT Channel-Name Strom 6
const char* MQTTChannelPower6 = "EnergymeterPower6"; //<================================== Anpassen - MQTT Channel-Name Strom 6
//Werden mehr Instanzen benoetigt, die 4 letzten Zeilen kopieren und einfuegen, dann Variablen anpassen
//GPIOs fuer Strom Sensoren definieren
#define L1 36 //PIN 5 GPIO 36 - Klinke 1 links von Kabel
#define L2 39 //PIN 8 GPIO 39 - Klinke 2 links von Kabel
#define L3 34 //PIN 10 GPIO 34 - Klinke 3 links von Kabel
#define Lx1 35 //Pin 11 GPIO 35 - Klinke 4 links von Kabel
#define Lx2 32 //Pin 12 GPIO 32 - Klinke 6 links von Kabel
#define Lx3 33 //Pin 13 GPIO 33 - Klinke 5 links von Kabel
//Werden mehr Instanzen benoetigt, die letzte Zeile kopieren und einfuegen, dann Variable mit GPIO Eingang anpassen
//Set Calibration
#define C1 20
#define C2 20
#define C3 20
#define Cx1 20
#define Cx2 20
#define Cx3 20
//Werden mehr Instanzen benoetigt, die letzte Zeile kopieren und einfuegen, dann Variablennamen anpassen und Kalibrierung bei 20 lassen
//Spannung am Verteiler (gemessen mit Multimeter)
double Voltage = 230.0; //<================================== so lassen oder ggf. Anpassen durch Messen !230VAC Lebensgefahr!
//WLAN Setup
const char* SSID = "ICHHABEDASGEILSTEWLAN"; //<================================== Anpassen - WLAN Netzwerk Name!
const char* PSK = "SUPERSICHERESPASSWORT"; //<================================== Anpassen - WLAN Passwort!
int WLANTimeout = 30;
int WLANCounts = 0;
//MQTT Channels
const char* MQTTChannel = "Energymeter";
//Variablen definieren
double IrmsL1;
double IrmsL2;
double IrmsL3;
double IrmsL1x1;
double IrmsL2x1;
double IrmsL3x1;
//Werden mehr Instanzen benoetigt, die letzte Zeile kopieren und einfuegen, dann Variablen anpassen bsp. IrmsL1x2
double PowerL1;
double PowerL2;
double PowerL3;
double PowerL1x1;
double PowerL2x1;
double PowerL3x1;
//Werden mehr Instanzen benoetigt, die letzte Zeile kopieren und einfuegen, dann Variablen anpassen bsp. PowerL1x2
//Sende Variablen (Max Wert)
double SendIrmsL1;
double SendIrmsL2;
double SendIrmsL3;
double SendIrmsL1x1;
double SendIrmsL2x1;
double SendIrmsL3x1;
//Werden mehr Instanzen benoetigt, die letzte Zeile kopieren und einfuegen, dann Variablen anpassen bsp. SendIrmsL1x2
double SendPowerL1;
double SendPowerL2;
double SendPowerL3;
double SendPowerL1x1;
double SendPowerL2x1;
double SendPowerL3x1;
//Werden mehr Instanzen benoetigt, die letzte Zeile kopieren und einfuegen, dann Variablen anpassen bsp. SendPowerL1x2
//Timer Setup
int SendTimer = 10; //Sende Timer in s
//Timer definieren
long crontimer;
//Client Define
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
WiFi.mode(WIFI_STA);
Serial.println(SSID);
WiFi.begin(SSID, PSK);
while (WiFi.status() != WL_CONNECTED && WLANTimeout > WLANCounts) {
delay(500);
WLANCounts++;
Serial.print("\n Wifi Connecting..");
}
if (WLANCounts >= WLANTimeout) {
ESP.restart();
}
else {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
WLANCounts = 0;
}
}
void GetStrom ()
//Calculate Irms - Falls nicht sauber 0.0 uebertragen werden bei geschlossenem Wandler,
//Korrekturwert eintragen (Markiert mit <> ) ===> IrmsL1 = emon1.calcIrms(1480) - <0.30>;
{
IrmsL1 = emon1.calcIrms(1480) - 0.30; //<================================== ggf. Anpassen - Kalibrierung siehe Kommentar!
if (nome1 == "none") {
IrmsL1 = 0;
}
else {
IrmsL1 = IrmsL1;
}
Serial.print("L1: ");
Serial.println(IrmsL1);
IrmsL2 = emon2.calcIrms(1480) - 0.10; //<================================== ggf. Anpassen - Kalibrierung siehe Kommentar!
if (nome2 == "none") {
IrmsL2 = 0;
}
else {
IrmsL2 = IrmsL2;
}
Serial.print("L2: ");
Serial.println(IrmsL2);
IrmsL3 = emon3.calcIrms(1480) - 0.19; //<================================== ggf. Anpassen - Kalibrierung siehe Kommentar!
if (nome3 == "none") {
IrmsL3 = 0;
}
else {
IrmsL3 = IrmsL3;
}
Serial.print("L3: ");
Serial.println(IrmsL3);
IrmsL1x1 = emon4.calcIrms(1480) - 0.17; //<================================== ggf. Anpassen - Kalibrierung siehe Kommentar!
if (nome4 == "none") {
IrmsL1x1 = 0;
}
else {
IrmsL1x1 = IrmsL1x1;
}
Serial.print("L1x1: ");
Serial.println(IrmsL1x1);
IrmsL2x1 = emon5.calcIrms(1480) - 0.0; //<================================== ggf. Anpassen - Kalibrierung siehe Kommentar!
if (nome5 == "none") {
IrmsL2x1 = 0;
}
else {
IrmsL2x1 = IrmsL2x1;
}
Serial.print("L2x1: ");
Serial.println(IrmsL2x1);
//Copy
IrmsL3x1 = emon6.calcIrms(1480) - 0.0; //<================================== ggf. Anpassen - Kalibrierung siehe Kommentar!
if (nome6 == "none") {
IrmsL3x1 = 0;
}
else {
IrmsL3x1 = IrmsL3x1;
}
Serial.print("L3x1: ");
Serial.println(IrmsL3x1);
//Werden mehr Instanzen benoetigt, ab Zeile "Copy" bis Anfang diesem Kommentar kopieren und einfuegen,
//dann Variablen anpassen
if (IrmsL1 > SendIrmsL1) {
SendIrmsL1 = IrmsL1;
}
if (IrmsL2 > SendIrmsL2) {
SendIrmsL2 = IrmsL2;
}
if (IrmsL3 > SendIrmsL3) {
SendIrmsL3 = IrmsL3;
}
if (IrmsL1x1 > SendIrmsL1x1) {
SendIrmsL1x1 = IrmsL1x1;
}
if (IrmsL2x1 > SendIrmsL2x1) {
SendIrmsL2x1 = IrmsL2x1;
}
if (IrmsL3x1 > SendIrmsL3x1) {
SendIrmsL3x1 = IrmsL3x1;
}
//Werden mehr Instanzen benoetigt, letzten 3 Zeilen bis Anfang diesem Kommentar kopieren und einfuegen, dann Variablen anpassen
Serial.println("----------SEND: ");
Serial.print("L1: ");
Serial.println(SendIrmsL1);
Serial.print("L2: ");
Serial.println(SendIrmsL2);
Serial.print("L3: ");
Serial.println(SendIrmsL3);
Serial.print("PowerL1: ");
Serial.println(SendPowerL1);
}
void GetPower ()
//Calculate Leistung
{
PowerL1 = IrmsL1 * Voltage;
PowerL2 = IrmsL2 * Voltage;
PowerL3 = IrmsL3 * Voltage;
PowerL1x1 = IrmsL1x1 * Voltage;
PowerL2x1 = IrmsL2x1 * Voltage;
PowerL3x1 = IrmsL3x1 * Voltage;
//Werden mehr Instanzen benoetigt, letzte Zeile bis Anfang diesem Kommentar kopieren und einfuegen, dann Variablen anpassen
if (PowerL1 > SendPowerL1) {
SendPowerL1 = PowerL1;
}
if (PowerL2 > SendPowerL2) {
SendPowerL2 = PowerL2;
}
if (PowerL3 > SendPowerL3) {
SendPowerL3 = PowerL3;
}
if (PowerL1x1 > SendPowerL1x1) {
SendPowerL1x1 = PowerL1x1;
}
if (PowerL2x1 > SendPowerL2x1) {
SendPowerL2x1 = PowerL2x1;
}
if (PowerL3x1 > SendPowerL3x1) {
SendPowerL3x1 = PowerL3x1;
}
//Werden mehr Instanzen benoetigt, letzte 3 Zeilen bis Anfang diesem Kommentar kopieren und einfuegen, dann Variablen anpassen
}
void TxData()
{
client.publish(MQTTChannelStrom1, String(SendIrmsL1).c_str(), true);
client.publish(MQTTChannelStrom2, String(SendIrmsL2).c_str(), true);
client.publish(MQTTChannelStrom3, String(SendIrmsL3).c_str(), true);
client.publish(MQTTChannelStrom4, String(SendIrmsL1x1).c_str(), true);
client.publish(MQTTChannelStrom5, String(SendIrmsL2x1).c_str(), true);
client.publish(MQTTChannelStrom6, String(SendIrmsL3x1).c_str(), true);
//Werden mehr Instanzen benoetigt, letzte Zeile bis Anfang diesem Kommentar kopieren und einfuegen, dann Variablen anpassen
client.publish(MQTTChannelPower1, String(SendPowerL1).c_str(), true);
client.publish(MQTTChannelPower2, String(SendPowerL2).c_str(), true);
client.publish(MQTTChannelPower3, String(SendPowerL3).c_str(), true);
client.publish(MQTTChannelPower4, String(SendPowerL1x1).c_str(), true);
client.publish(MQTTChannelPower5, String(SendPowerL2x1).c_str(), true);
client.publish(MQTTChannelPower6, String(SendPowerL3x1).c_str(), true);
//Werden mehr Instanzen benoetigt, letzte Zeile bis Anfang diesem Kommentar kopieren und einfuegen, dann Variablen anpassen
Serial.println("================GESENDET==============");
}
void reconnect() {
while (!client.connected()) {
Serial.println("Reconnecting MQTT...");
if (!client.connect("Energymeter")) {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" retrying in 1 seconds");
delay(1000);
}
}
client.subscribe(MQTTChannel);
Serial.println("MQTT Connected...");
}
void setup()
{
Serial.begin(115200);
WiFi.disconnect();
delay(500);
setup_wifi();
client.setServer("IP.VON.MQTTSERVER.EINTRAGEN", 1883); //<================================== Anpassen!, ggf. auch Port
emon1.current(L1, C1); //(input pin, calibration)
emon2.current(L2, C2); //(input pin, calibration)
emon3.current(L3, C3); //(input pin, calibration)
emon4.current(Lx1, Cx1); //(input pin, calibration)
emon5.current(Lx2, Cx2); //(input pin, calibration)
emon6.current(Lx3, Cx3); //(input pin, calibration)
//Werden mehr Instanzen benoetigt, letzte Zeile bis Anfang diesem Kommentar kopieren und einfuegen, dann Variablen anpassen
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_11);
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);
adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11);
adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);
adc1_config_channel_atten(ADC1_CHANNEL_5, ADC_ATTEN_DB_11);
//Werden mehr Instanzen benoetigt, letzte Zeile bis Anfang diesem Kommentar kopieren und einfuegen, dann Variablen anpassen
analogReadResolution(10);
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_FS
type = "filesystem";
}
Serial.println("Start updating " + type);
});
ArduinoOTA
.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.setHostname("ESP32-Energymeter");
ArduinoOTA.setPassword("GEILESPASSWORT"); //<================================== Anpassen Over Air (WLAN) Programmierung
ArduinoOTA.begin();
}
void loop()
{
//Check Alles verbunden?
if (WiFi.status() != WL_CONNECTED) {
setup_wifi();
}
if (!client.connected()) {
reconnect();
}
delay(1000);
Serial.println("Reloop------- ");
//Ausfuehrung Zentralprogramm
GetStrom(); //Calculate Irms
GetPower(); //Calculate Leistung
//-------------------Timer LORA Update-------------------
if (millis() / 1000 > crontimer + SendTimer) {
TxData(); //Daten uebertragen
//SendeAmps wieder auf 0 für neues Update
SendIrmsL1 = 0;
SendIrmsL2 = 0;
SendIrmsL3 = 0;
SendIrmsL1x1 = 0;
SendIrmsL2x1 = 0;
SendIrmsL3x1 = 0;
SendPowerL1 = 0;
SendPowerL2 = 0;
SendPowerL3 = 0;
SendPowerL1x1 = 0;
SendPowerL2x1 = 0;
SendPowerL3x1 = 0;
//Werden mehr Instanzen benoetigt, letzte Zeile bis Anfang diesem Kommentar kopieren und einfuegen, dann Variablen anpassen
crontimer = millis() / 1000;
}
//-------------------Timer LORA Update-------------------
//Nebenprigramme
ArduinoOTA.handle();
client.loop();
}
Alles anzeigen
Falls ihr Fragen habt, dürft ihr diese gern stellen. Ich beantworte diese euch schnellstmöglich.
Grüße,
Euer Adrian