Olav E Skrevet 25. januar 2021 Del Skrevet 25. januar 2021 (endret) Quote Jeg savnet den gamle forbruksmåleren på kjøkkenveggen som viser strømforbruket. Selv om jeg kan sjekke forbruket som leses via HAN porten via telefonen/nettside, så var ikke dataene like tilgjengelige. Har derfor laget en prototype med hjelp av en nodemcu, neopixel LED-ring og en 0.96 tommers OLED skjerm. Hver LED illustrer 1kW i strømtrekk. Antall watt vises på første linje. Spotpris for aktuell time blir hentet fra Tibber APIet og vises i andre linje. Estimert kostnad per time med nåværende strømtrekk inkluderer nettleiedelen på 42,61øre/kWh til BKK. Oppdateringsfrekvensen er hvert 2.sekund (hver gang AMS måleren sender ut "Act_Pow_P_Q1_Q4"). Backend systemer består av følgende: Raspberry Pi leser modbus til USB adapter er koblet til HAN porten ved hjelp av test_rx / han-port-1.15 nevnt i "Lesing av HAN - The Easy Way (TM) - WIP" tråden. Output fra test_rx blir matet til MQTT ved hjelp av et python-script. Node-Red henter spotpris fra Tibber i starten av hver time og sender til MQTT. NodeMCU heter dataene fra MQTT kjører beregninger på kostnad og viser dette på OLED-skjermen og via neopixlene. Todo: Printe ut et nytt kabinett med høy WAF slik at fruen blir glad. Prototypen ble tegnet i en fei. Lage presence detection på boksen slik at OLED skjermen ikke blir brent ut, alternativt bytte ut skjermen (stor e-paper/e-ink skjerm står på ønskelisten). Forsøke å skrive om koden til hente sanntidsforbruk direkte fra Tibber APIet slik at andre kan lage sitt eget watt-o-meter ved hjelp av en Nodemcu/ESP uten noe annet enn en Tibber pulse. Om noen vet om noe kode som henter ut sanntidsmålingene fra tibber og som kan kjøres på en esp/nodemcu så send meg gjerne i den retningen. Byggetråd for forbruksmåler / display for lettere å visualisere strømforbruket i tilfelle noen har interesse av å lage noe lignende. Tips for videreutvikling og hjelp/kommentarer til kode blir satt pris på. Jeg skal få lagt opp flere bilder og beskrivelse av komponenter og koblingsdiagram. Backend oppsettet mitt bærer litt preg av at veien blir til mens du går. Det er ikke nødvendig å bruke både python-scriptet og Node-red, det går fint an å hente spotprisen via python , eller benytte seg av node-red for å lese modbus-til-usb adapteret som enkelte i "Lesing av HAN - The easy way" tråden har gjort. Men nåværende oppsett er stabilt, så jeg kommer ikke til å skrive det om enda. 1) Node-red blir brukt til å hente spotpris fra Tibber, inject-noden er et cron basert og trigges 2 ganger i timen (hvert 30 minutter, i tilfelle første trigger feiler). // Cron utrykket trigger en "Post to tibber" med følgende innhold: msg.payload = { "query" : "{viewer {homes {currentSubscription {priceInfo {current {total startsAt }}}}}}" }; msg.headers = { "Authorization" : "Bearer din-tibber-token-aabbccdd-1234", "Content-Type" : "application/json" }; return msg; 2) Funksjon "set spotpris as global variable": Setter spotprisen til en global variabel ("strompris") i node-red for bruk i en annen flow. // Lagrer/setter spotprisen fra Tibber i en global variabel som kan brukes i MQTT flow senere. var newMsg = {payload: msg.payload.data.viewer.homes[0].currentSubscription.priceInfo.current.total }; global.set("strompris",newMsg.payload); return newMsg; // Henter forbruksdata via MQTT på "ams/Act_Pow_P_Q1_Q4", // disse dataene kommer somsagt på influx line protocol format // hos meg. // Eksempel: "Act_Pow_P_Q1_Q4,name=Act_Pow_P_Q1_Q4 value=2047" // // Jeg ønsker kun verdien, så jeg splitter på '='. // Denne kombineres med spotprisen som blir hentet ut fra // global "strompris" variabel i node-red og sendes ut // til ny MQTT topic som NodeMCU/forbruksmåleren bruker. // Jeg brukte ';' som delimiter mellom power og spotpris. // Innholdet i "Combine AMS power and spotpris" funksjonen var values = msg.payload.split('='); var power = values[2]; var spotpris = global.get("strompris"); msg = {payload: power +";" + spotpris}; return msg; Forbruket leses via HAN porten fra AMS ved hjelp av test_rx og en modbus til USB adapter. Jeg har brukt følgende script fra brukeren Berland her på forumet (Håper det går greit å legger ut modifisert versjon her). I tillegg til å sende JSON til MQTT så ønsket jeg å sende influx line protocol syntax til MQTT for enkelt å sende til influxdb ved bruk samme python script/Node-red eller Telegraf. # Modified from https://www.hjemmeautomasjon.no/forums/topic/2873-lesing-av-han-the-easy-way-tm-wip/?do=findComment&comment=39623 import subprocess import paho.mqtt.client as mqtt import json import sys import requests from requests.exceptions import HTTPError # If the binary is still active for some reason, kill it subprocess.call("killall test_rx >/dev/null", shell=True) proc = subprocess.Popen(['./test_rx','-n'], stdout=subprocess.PIPE) def on_connect(client, userdata, flags, rc): pass def on_disconnect(client, userdata, rc=0): sys.exit(1) client = mqtt.Client() client.on_connect = on_connect client.on_disconnect = on_disconnect client.connect('mqttserver', 1883) print "Connected" cumulativejson = '' cumulativeInfluxLine = '' try: for line in iter(proc.stdout.readline, ''): line = line.rstrip() cumulativejson += line if line[-1:] == '}': try: amsJson = json.loads(cumulativejson) client.publish('power/ams', cumulativejson) for key in amsJson: value = amsJson[key] if isinstance(value, int) or isinstance(value, float): cleanValue = str(value).replace(" ","") influxLineData = '%(key)s,name=%(key)s value=%(cleanValue)s' % locals() mqttTopic = 'ams/' + key client.publish(mqttTopic, influxLineData) cumulativejson = '' except UnicodeDecodeError: # Sometimes we get something strange # on the serial line, just ignore it. cumulativejson = '' continue except Exception as e: print(e) proc.terminate() sys.exit(1) # Let systemd restart us Koden som kjøres på NodeMCUen kan også sees her: https://github.com/Olavae/mqtt-power-meter/blob/main/MqttPowerSpotMeter.ino // // Power usage display with current spot price using OLED and Neopixel. // Uses a NodeMCU board with wifi to recieve a single MQTT message on // the form powerUsage;spotprice using ';' as a delimiter/IFS then // splits the message into two variables to visualise them. // #include <ESP8266WiFi.h> #include <PubSubClient.h> #include <Adafruit_NeoPixel.h> // Following 4 are needed for SSD1306 oled display #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define PIXELPIN D3 // Datapin for neopixel #define NUMPIXELS 24 // Number of neopixels #define DELAYVAL 500 // Time (in milliseconds) to pause between pixels #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) //OLED display (Uses GPIO5 (D1) (SCL) and GPIO4 (D2) (SDA) as standard Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Neopixels / ws2812 compatible leds Adafruit_NeoPixel pixels(NUMPIXELS, PIXELPIN, NEO_GRB + NEO_KHZ800); // Update these with values suitable for your network. const char* ssid = "Your-Wifi-Network"; const char* password = "wifi-passphrase"; const char* mqtt_server = "10.0.0.5"; // Your MQTT broker // Set your variable for "nettleie" to your power company. const float nettleie = 42.61; // BKK nettleie variabel del, Price in øre. const char* powerPriceTopic = "strompris"; // Your MQTT topic where you publish "powerUsage;spotpris" const char* delimiter = ";"; 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.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()); } void callback(char* topic, byte* payload, unsigned int length) { char incomming[length]; for (int i=0; i<length; i++){ incomming[i] = (char)payload[i]; } // Split incomming message into Power and spot price with delimiter // First token (Power) char* incToken = strtok(incomming,delimiter); char* incommingPower = incToken; // Second token (Spot price) incToken = strtok(NULL,delimiter); char* incommingSpot = incToken; // Convert into easier to manage data types. long power = atol(incommingPower); float kronepris = atof(incommingSpot); int pris = round((kronepris*100)); // Go from Krone to øre. // Calculate price per hour based on current power usage float timepris = (((kronepris*100) + nettleie)*power)/100000; // Number of LEDS which will light up, round to nearest kW. int powerInt = (int)(round((float)(power/1000.0))); // Light up Neopixel, colours suits my normal power usage. // Green is OK ( power < 5kW ) // Yellow is hmm ( 5kW < power < 10kW ) // Red gets expensive fast ( Power > 10kW ) pixels.clear(); for(int i=0;i<powerInt;i++) { if (i<=4) { pixels.setPixelColor(i, pixels.Color(0,50,0)); } else if ((i >= 5) && (i <= 9)) { pixels.setPixelColor(i, pixels.Color(50,50,0)); } else if (i >= 10) { pixels.setPixelColor(i, pixels.Color(50,0,0)); } } pixels.show(); // Light up pixels // OLED display, 3 lines with medium/large text. // -------- // | Power | // | Spot | // | perHour| // -------- display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.setCursor(0,10); display.print(power); display.println(" W"); display.print(pris); display.println(" 0re"); // Fake a norwegian Ø, haven't gotten Adafruit_gfx to display non ascii chars. display.print(timepris); display.println(" kr/t"); display.display(); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Create a random client ID String clientId = "ESP8266Client-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { Serial.println("connected"); // ... and resubscribe client.subscribe(powerPriceTopic); // Topic where you publish your meter values } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void setup() { pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output Serial.begin(115200); if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64 (Default address) can also be 0x3D Serial.println(F("SSD1306 allocation failed")); for(;;); // Don't proceed, loop forever } display.display(); delay(300); // Wait and show adafruit splashscreen, they provided a nice library // Clear the buffer display.clearDisplay(); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); } Komponenter NodeMCU kompatibelt utviklerkort, mitt er basert på ESP8266, har ikke testet med ESP32 versjoner. NodeMCU bruker 3.3V, men gir deg 5V på VIN-pin om du benytter deg av USB-kontakten for å gi kortet strøm. Neopixel krever 5V. OLED skjerm bruker 3.3V. Neopixel WS2812b kompatibel ring. https://www.kjell.com/no/produkter/elektro-og-verktoy/arduino/tilbehor/luxorparts-adresserbar-rgb-led-ring-24x-led-p87933 SSD1306 0.96 tommers OLED display 128x64. 470 ohm motstand mellom NodeMCU og neopixel datapin. (Ligger loddet under krympestrømpen på bildene nedenfor). 7 koblingskabler. Kabinettboks (Ikke tegnet enda, kan legge med STL-fil for 3dprinting når jeg er fornøyd). Jeg var utålmodig og fant et sett fra Kjell & co som inneholdt OLED-skjermen og 24-leds-neopixel, mostander og koblingskabler. Det er rimeligere å kjøpe fra andre steder. Merk at settet ikke inneholder NodeMCU kortet som trengs. https://www.kjell.com/no/produkter/elektro-og-verktoy/arduino/arduino-pakke/playknowlogy-startpakke-for-arduino-eksperiment-p88211 Jeg har startet på arbeidet med å skrive om koden for å bruke websockets, (testet ulike bibliotek, men denne her virker mest lovende til nå. https://github.com/gilmaimon/ArduinoWebsockets) for å hente forbruket direkte fra Tibber APIet i håp om å lage et stand-alone display for dem som bruker Tibber pulse, men som ikke ønsker å kjøre noe backend systemer hjemme med MQTT oppsett. PS: Jord-kabel mellom SSD1306/OLED display og NodeMCU er brun, jeg slurvet her, men den er tegnet inn i rett farge (svart) i koblingsdiagrammet. Endret 26. januar 2021 av Olav E La til mer info. Skrivefeil 5 2 Siter Lenke til kommentar Del på andre sider Flere delingsvalg…
Anbefalte innlegg
Bli med i samtalen
Du kan publisere innhold nå og registrere deg senere. Hvis du har en konto, logg inn nå for å poste med kontoen din.