Měření spotřeby elektřiny
Dva články teoretických úvodů máme za sebou. Dneska se podíváme na praktické měření elektřiny. Tedy na moje konkrétní řešení.
Připomeňme si základní přehled. Na začátku máme impulzní elektroměr, ten posílá impulzy do Arduina. To je sečte a přes WiFi a MQTT pošle na server, tam si to přechroupe NodeRED a pošle do InfluxDB a nakonec nám data zobrazí Grafana.

Elektroměry s impulzním výstupem jsem našel a objednal na internetu a nechal elektrikáře nasadit do rozvaděče. Jsou to obyč třífázové elektroměry, cena okolo tisícovky. Já konkrétně kupoval tento. Jeden elektroměr měří celou část topení v technické místnosti (zahrnuje tepelné čerpadlo, záložní elektro kotel, oběhové čerpadlo, akumulační nádrž s patronou), druhý pak měří úplně všechno ostatní.
Dají se pořídit i jednofázové v řádu jednotek stokorun. Také se dají pořídit různé moduly co komunikují už na nějakýho hotových protokolech jako například Modbus. Já jsem se nechtěl učit ještě další novou technologii, takže jsem sáhnul po obyčejných impulzech. Ještě si dejte pozor na počet impulzů za kWh. Myslím že 1000 imp/kWh je pro rodinný dům nebo byt ideální. Dělají se tuším i 500 nebo 800 imp/kWh. Do méně impulzů bych nešel.

Princip je poměrně jednoduchý. Každou 1 Wh se vytvoří na kontaktech impulzního výstupu impulz, který jsme schopni pomocí Arduino detekovat. Musíme tedy propojit Arduino s elektroměrem. Píši pro zjednodušení Arduino, ale používám modul Wemos D1 mini. Dá se programovat přes Arduino IDE a má v sobě v základu už WiFi modul.
Malá odbočka ohledně instalace. Modul mi přišlo nejvhodnější umístit přímo do rozvaděče na DIN lištu. Zakoupil jsem proto držák na DIN lištu, k němu přišrouboval univerzální plošný spoj a něj to pak celé pájel. Ještě jsem potřeboval nějaké napájení, to jsem vyřešil 5V spínaným zdrojem na DIN lištu, který se napojil na jistič přímo v rozvaděči. Napojení na elektroměry nebo napájení je pak řešeno přes svorky, aby bylo možné celý plošný spoj v případě potřeby snadno demontovat. Pájet bych to nedoporučoval.
Celkové schéma zapojení je poměrně jednoduché, ale já ho nemám, ani si většinou nedělám. K Arduinu přivedete z 5V zdroje napájení a pak dle následujícího schématu připojíte impulzní výstup elektroměrů na vybrané piny na Arduinu. Tato zapojení většinou konzultuji s bratrem, který se přeci jen vyzná v elektronice o trochu víc a kterému i tímto děkuji. Níže tedy zapojení které mi doporučil. Tomu rezistoru se říká pull up rezistor.

Toto schéma je mou mantrou (a může být i vaší), protože naprosto totožně jsem pak zapojoval měřák na vodu, jen jsem použil rezistor s vyšším odporem. A podobně byste zapojovali jakékoli zařízení na kterém byste chtěli měřit impulzy.
Pokud tedy vše napájíte na plošný spoj, připevníte na DIN lištu, může výsledek vypadat třeba jako na obrázku níže. Samotný modul je na pin headeru (nevím jak se to řekne česky) a dá se tudíž sundavat. To je docela fajn, když chci upravovat software, tak ho jednoduše sundám, odnesu si k počítači a po úpravě opět vrátím. Můžu se ale klidně i píchnout microUSB kabelem do Arduino přímo když je v rozvaděči.


Mám tam ještě přípravu na napojení na HDO relé, abych mohl zaznamenávat kdy běží jaký tarif. Zatím to ale nemám dozapojené. Nicméně to bude užitečná informace, díky které:
- mohu kontrolovat a mít přehled kdy jde který tarif
- mohu přesněji počítat cenu za elektřinu
Mám tedy vyřešen kompletně hardware, elektroměry mám připojeny na piny D3 a D4. Kupodivu jsem to všechno trefil napoprvé a na straně hardware nebyly žádné problémy. Na „historky z natáčení“ se tak můžete těšit u zapojování měřiče vody. O serveru jsme se bavili v minulém článku a ten mi též běží. Je čas na trochu toho kódění. Přikládám kód pro Arduino který používám, níže ho pak trochu rozeberu.
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#define PULSEPIN1 D3
unsigned int pulse1Count;
#define PULSEPIN2 D4
unsigned int pulse2Count;
volatile unsigned long delayStart = 0;
// Update these with values suitable for your network.
const char* ssid = "YourWifiName";
const char* password = "YourWifiPassword";
const IPAddress mqtt_server(192, 168, 0, 1);
const char* mqtt_user = "ha_mqtt";
const char* mqtt_password = "YourMQTTPassword";
const String mqtt_client_name = "WemosD1Mini_EnergyMeter";
const char* mqtt_topic = "sensors/electricity";
const char* device_network_name = "EnergyMeter";
const int measurment_delay = 15; // delay in seconds
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.hostname(device_network_name);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
WiFi.printDiag(Serial);
}
void sendMessage()
{
if(!client.connected())
{
return;
}
snprintf (msg, MSG_BUFFER_SIZE, "{\"pulses1\": %d, \"pulses2\": %d, \"hdo\": false}", pulse1Count, pulse2Count);
Serial.print("Publish message: ");
Serial.println(msg);
client.publish(mqtt_topic, msg);
pulse1Count = 0;
pulse2Count = 0;
lastMsg = millis();
}
void reconnect() {
if((millis() - delayStart) >= 5000 || millis() < (delayStart - 1000))
{
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(mqtt_client_name.c_str(), mqtt_user, mqtt_password)) {
Serial.println("connected");
sendMessage();
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delayStart = millis();
}
}
}
ICACHE_RAM_ATTR void countPulse1()
{
pulse1Count++;
}
ICACHE_RAM_ATTR void countPulse2()
{
pulse2Count++;
}
void setup() {
Serial.begin(9600);
attachInterrupt(PULSEPIN1, countPulse1, FALLING);
attachInterrupt(PULSEPIN2, countPulse2, FALLING);
setup_wifi();
client.setServer(mqtt_server, 1883);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
if (millis() - lastMsg > (1000 * measurment_delay) || millis() < (lastMsg - 1000)) {
sendMessage();
}
}
Kromě standardní esp knihovny pro WiFi budeme potřebovat ještě knihovnu pro MQTT protokol. Tedy knihovnu, který za nás bude řešit komunikaci s MQTT brokerem.
Úvodní část obsahuje nastavení WiFi, serveru apod. Důležité je mít nastavenou správně adresu MQTT serveru. To jsem zapomněl zmínit u serveru – nastavte si v rámci sítě na svém routeru statickou IP adresu pro server, aby se vám náhodou někdy v budoucnosti nezměnila. MQTT server může i požadovat ověření v podobě jména a hesla.
Po spuštění (ve funkci setup) se použije funkce attachInterrupt, která dělá to, že pokud na nastaveném pinu dojde ke změně (dle typu nastavení) tak se provede zadaná funkce. Tzn. v tomto případě, pokud na PULSEPIN1 (D3) přijde impulz a napětí začne klesat (FALLING), zavolá se funkce countPulse1. Tato funkce pak připočítá impulz a to je vše. Všimněte si, že u metod coutPulse1/2 je ICACHE_RAM_ATTR. Proč to tam je si už nepamatuju, ale řešilo to nějaký problém, kvůli kterému to nešlo jinak zkompilovat. Ono vůbec, nečekejte ode mě nějakou velkou hloubku, mě stačí když to funguje.
Dále se pak se pak řeší poměrně standardně připojení k WiFi a následně připojení k MQTT serveru. Tam ještě zadáváte port, na kterém vám MQTT broker běží.
Při běhu aplikace (funkce loop) pak akorát řeším, zda jsme připojeni k MQTT serveru a pokud ne tak se snažíme se znovu připojit. Obnova WiFi připojení se řeší nějak sama v dané knihovně. Nejsem si jist jak přesně to funguje, ale k výpadkům WiFi mi došlo několikrát a vždy se vše bez problémů samo obnovilo. K výpadku MQTT serveru (přestože WiFi funguje) může snadno dojít například při aktualizaci.
A nakonec, pokud uběhne určitý čas (v mém případě 15s), tak odešleme počet impulzů na server a počítadlo vynulujeme. Data se posílají ve formátu JSON, což je opět poměrně standard a posílají se do určitého MQTT topicu. Zprávu posílám i když je počet pulzů 0 a to z toho důvodu, abych na straně serveru mohl dobře kontrolovat, že data chodí a spojení funguje, i když by třeba nebyl aktuálně žádný odběr.
Odeslaná zpráva (JSON) pak vypadá třeba takto:
{"pulses1": 2, "pulses2": 1, "hdo": false}
Tímto mám vyřešenou část hardware i software část v rozvaděči a můžu se plně přesunou k pohodlí počítače. Data zpracovávám v NodeRED, kde jsem si vytvořil následující schéma (flow). Je možné, že abyste viděli nody pro MQTT nebo Influx, budete muset doinstalovat nějaká rozšíření pro NodeRED.

Začnete nodem úplně vlevo, který vám sbírá data z MQTT z určitého topicu. Nastavení nodu vypadá následovně. Když vytváříte vůbec první MQTT node, tak si nastavíte MQTT server, následně už stačí jen vybrat z nabídky přednastavených serverů.

Data který tento node přijme pak putují do dalších nodů, tentokrát nodů s javascript funkcemi, kde si mohu data zpracovat. Úplně první node je debug node, který mi může vypisovat obsah zpráv do konzole. Dále když vezmu žluré nody shora: První node mi zpracovává informaci o HDO a posílá do Home Assistant senzoru (to teď nebudeme rozebírat). Druhý a třetí vybírá údaje pro daný elektroměr a posílá je do InfluxDB. Třetí je sčítá, abychom znali celkovou spotřebu. Tu bychom si pak mohli klidně sčítat pak až v Grafaně, ale co kdyby se to jednou třeba počítalo jinak, takže tu celkovou raději jednoduše sčítám. Poslední node pak počítá cenu. Tam to zatím obyčejně násobím částkou. Nicméně by bylo dobré pak zaznamenávat spotřebu i podle tarifů (mimochodem dělají se rovnou i dvoutarifní elektroměry, ale je to dražší, tak jsem si řekl, že si to vyřeším na straně software). Počítat cenu rovnou a ne až pak dál v Grafaně mi přijde chytré kvůli tomu, že se pak cena elektřiny může změnit a pokud máme spotřebu zaznamenanou a násobenou cenou v dané době, neovlivní nám aktuální změna ceny historická data, což by byla škoda.
Níže je javascript kód funkce pro první elektroměr. msg.payload je vždy zpráva, která přišla z předchozího node. V mém případě tedy z MQTT node, tedy JSON, který jsem vytvořili a poslali z Arduina. Z něj si vytáhneme údaj o počtu impulzů pro první elektroměr. Následně údaje uložíme opět do msg.payload a vrátíme celý objekt msg, který pak putuje podle nastavení NodeRED flow do dalšího nodu.
p = JSON.parse(msg.payload);
var total = 0;
if(Number.isInteger(p.pulses1) && p.pulses1 > 0)
{
total += p.pulses1;
}
msg.payload = total;
return msg;
Poslední node je InfluxDB node. Tam velmi podobně jako v případě MQTT nejprve musím nastavit InfluxDB server. Údaje jsem získal při konfiguraci nebo InfluxDB má případně web admin, kde se dají nastavovat databáze, uživatelé atp. Když mám server tak pak už jen velmi jednoduše nastavím pod jaké měření chci data ukládat. V mém případě jdou počty impulzů z elektroměru 1 do měření topení.

Jeden praktický poznatek: původně jsem jako cílový node používal Home Assistant senzor, ale z nějakého důvodu se mi pak některé impulzy ztrácely a nebylo to vůbec málo, třeba i polovina. Nedohledal jsem proč se to tak děje, asi tam dochází k nějaké optimalizaci a při větším množství dat, které se reportují často možná Home Assistant vyhazuje některá data? Je to ale divné, protože na webu jsem viděl příklady jak tím někdo měří elektřinu, tak možná to měří jiným způsobem. Například pro měření teploty to žádný problém není, data se posílají mnohem méně často a i se méně mění. U přesného měření spotřeby je to ale problém. Po změně na posílání dat přímo do InfluxDB je vše v pořádku. A zde je vidět krása NodeRED a MQTT, stačilo změnit jen cílový NODE bez zásahu do software na Arduinu. To si vesele dál posílá data do stále stejného topicu a je mu jedno co s nimi dál bude.
A už jdeme do finále. Tímto mám vše hotovo a data se mi již počítají a ukládají na server. K zobrazování dat používám Grafanu. O tom se můžu možná více rozepsat někdy příště, i když mám pocit, že jsem zatím jen nahlédl pod pokličku. V principu si nastavíte zdroj dat (v mém případě opět zadáte InfluxDB server) a pak si vytváříte jednotlivé grafy a dashboardy.

Grafana mám hromadu nastavení, je to asi celkem robustní nástroj. Výše vidíte jak vypadá nastavení pro spotřebu topení. Nahoře máte vždy náhled grafu, dole pak editujete daný dotaz do databáze. Když máte graf hotový, tak si jej pak můžete s dalšími grafy skládat do všemožných dashboardů nebo je sdílet a podobně.
To je pro dnešek vše. Kdyby nějaká část byla nejasná, mohu ji rozebrat detailněji v nějakém dalším článku, dejte kdyžtak vědět. Příště mám v plánu se podívat na měření spotřeby vody.
9 thoughts on “Měření spotřeby elektřiny”
Dobrý den, našel jsem na webu to vaše bastlení ,a musím říct , že jste opravdu profík. to měření elektřiny s dvěma elektroměry je parádní věc.
a hodně mě tohle tema zajímá. jsem ale lajk a doma jsem si nainstaloval 3 elktroměry ( hospo.budova, bojler, elktro kotel).
Rád bych se zeptal , zda je možnost mi s tímto nějak pomoci ,a poradit jak by se dala spotřeba energie sledovat a zaznamenávat .
Na kolik to tak vyjde tuhle srandu udělat:-)
děkuji za odověd
Tomáš
Dobrý den,
já osobně vám s tím bohužel nepomůžu víc než že si to tu přečtete. Není to moje zaměstnání. Pokud vás to nějak extra nebaví, tak nemá cenu se do toho pouštět, ale objednat si to u někoho, což ale vyjde na nějaké peníze. Pokud si to budete dělat sám tak ty součástky jsou levné, nejdražší je ten Rpi počítač okolo cca 2tis kč. A pak záleží jak si ceníte svého času.
Každopádně jak mi radil elektrikář – nejjednodušší je přece vzít si notýsek a tam si každé ráno jít všechny hodnoty opsat 🙂
Díky za super popsaný postup. Realizace trvala asi dvě hodiny. Ještě jednou díky. Protože programování není mojí silnou stránkou nedal by se zveřejnit kód do nodredu s celkovou spotřebou ? Dále jsem zjistil že muj elektroměr vysílá 800 imp/kWh kde přenastavit hodnotu když Váš elektroměr vysílá 1000 imp/kWh.
Děkuji za odpověď.
Celková spotřeba je jednoduchá, jen tam oba elektroměry sčítám, tedy:
p = JSON.parse(msg.payload);
var total = 0;
if(Number.isInteger(p.pulses1) && p.pulses1 > 0)
{
total += p.pulses1;
}
if(Number.isInteger(p.pulses2) && p.pulses2 > 0)
{
total += p.pulses2
}
msg.payload = total;
return msg;
já přidávám jeden impulz = 1Wh
ve vašem případě je jeden impulz = 1/800kWh = 1,25Wh
Tzn. že v node red upravíte tuto část:
total += p.pulses1;
na:
total += p.pulses1 * 1.25;
(na všech místech kde se počítají pulzy)
Mohl bych se zeptat na pár detailů k tomu zapojení MeanWell HDR-15-5 zdroje ? Pokud to chápu správně, pro napájení mikrokontroléru stačí pouze +V výstup, tedy -V zůstane nezapojený ? Dále by mě zajímalo, jaký vodič je dobré použít pro tento výstup. Zkoušel jsem AWG22 a multimetr mi při měření napětí mezi +V ze zdroje a GND (vytaženém z mikrokontroleru) dává šum kolem nuly.
Ne, pro napájení použijete oba výstupy. Plus i mínus. Pro napájení jsem použil nějakou obyčejnou dvoulinku co jsem měl doma, takže by vám tento vodič měl fungovat.
Taky děkuji za skvělý návod. Máme velmi podobně zapojeno jako Vy a měříme spotřebu všeho co se dá 🙂 Člověk pak zjistí, kde mu občas energie uniká, ale dokud to nezměří, tak neví.
Návod super, jen bych poradil pro ty co se s tím nechtějí až tak moc bastlit, nemají místo v rozvaděči nebo chtějí víc informací, vzít třeba Shelly Pro 3EM (popř i starší Shelly 3EM), je to 3-fázový elektroměr na DIN lištu pouze na 1 pozici (o velikosti obyčejného jističe). Je to spolehlivější, umí to všechno na co si člověk vzpomene WIFI/LAN/Bluetooth, lokální i cloudový přístup, 2 měsíční retenci dat pro úplný offline, relé pro spínání libovolného stykače, webové rozhraní, MODBUS, MQTT a přímou integraci s chytrou domácností, Node-RED atd. Jistá výhoda je i ve způsobu zapojení, na přívodní fáze se dají CT svorky tudíž není ani nutné měnit hotovou kabeláž. Díky tomu umí měřit akumulovanou energii, okamžité napětí, proud a účiník na každé fázi.
Souhlas, tehdy asi ještě nebyly nebo jsem nenašel / nevěděl. Každopádně od Shelly mám pár dalších modulů aktuálně na ovládání bojleru a řízení nějakých dalších věcí a je to super výrobek, určitě bych to příště řešil také tak.