IoT Verklikker

Januari 2018: Een simpele IoT (Internet of Things) toepassing met de ESP8266: een lampje waarbij de kleur je energiegebruik op dat moment weergeeft.

Energieverklikker
De energie-verklikker, met wat verschillende kleuren:
bij veel gebruik (rood) tot aan productie (groen-blauw).

Het idee

Om je bewust te worden van je energiegebruik is het handig gelijk 'feedback' (terugkoppeling) te krijgen: je ziet wat je gebruikt. Dat kan natuurlijk met een tablet of zo, maar die moet je toch weer pakken.

Ik heb daarom een 'IoT'-lampje gemaakt dat op ieder moment door de kleur weergeeft wat mijn energiegebruik op dat moment is. Gebruik ik veel energie, dan wordt het rood, leveren mijn zonnepanelen veel op dan wordt'ie blauw-groen, en een heel kleurenpalet daar tussenin, zie de foto's links. De grote afbeelding is bij neutraal gebruik (was haast 0 Watt, bewolkte hemel dus het beetje energie dat geleverd werd ging de computer in: groen met een beetje rood).

Werkt leuk, en je ziet gelijk 'wat je huis doet'. Zet de magnetron aan: vuurrood... En zo ook voor zowat alle andere apparaten die warmte (moeten) produceren. Wasmachine, koffiezetter, broodrooster. Al je LED-lampen zijn daarentegen nauwelijks zichtbaar als kleurverandering, gebruik is verwaarloosbaar vergeleken met een magnetron. Maar, ze staan natuurlijk wel langer aan, dus telt toch ook op.

De kleur wordt elke 10 seconden bijgewerkt (beperking van de smart meter), rap genoeg om snel te zien wat er gebeurt.


MQTT dataflow
MQTT dataflow van de smart meter naar de verklikker

De globale werking

IoT: Internet of Things... Alles via het internet aan elkaar geknoopt. Zo werkt dit ook ongeveer:

  • Mijn 'slimme meter' geeft elke 10 seconden het elektriciteitsgebruik op zijn seriële P1-poort,
  • dit wordt opgevangen via een serieel-naar-USB kabeltje op de USB-poort van mijn Raspberry, en in de MQTT database gepubliceerd,
  • mijn draadloze lampje is 'geabonneerd' op de MQTT-waarde voor de energie, krijgt daardoor elke 10 seconden van de Raspberry een update,
  • en past zijn kleur op de waarde van het energiegebruik aan.

Overigens is het bij mij meer 'Intranet of Things': de informatie blijft in huis, en gaat niet naar externe servers. De slimme meter is in de meterkast met een serieel kabeltje aan de Raspberry verbonden, en die publiceert met MQTT de binnenkomende data alleen op het eigen netwerk... Minder kans op hackers, wat niet aan het grote internet hangt kan ook niet gehackt worden (de kans is in ieder geval een heel stuk kleiner).

Het lampje

De controller (op de bovenste foto zelf niet te zien) is een kastje van ongeveer 9x5x3 cm. Het is gebaseerd op een Arduino-achtig printje met de ESP8266 WiFi-processor. Eerst ontwikkeld op de Wemos omdat deze makkelijk op mijn breadboard kan, daarna overgezet naar een goedkope 'serial WiFi' print, zie mijn Arduino-pagina. Eigenlijk is deze module bedoeld als 'draadloze seriële poort' voor de Arduino, maar de module is zelf krachtiger dan de hele Arduino dus kan het werk prima zelf af... Ik had nog een 3x1 Watt RGB-LED liggen op een koelvin, dus die als lamp gebruikt. De meeste onderdelen komen sowieso uit de 'rommelbak'.

Opbouw van de verklikker
De opbouw van de Energie-verklikker (schema beneden)

Hiernaast is het open kastje met inhoud te zien (schema onderaan deze pagina). Bovenin rechts de 'serial WiFi' print, plus een drukknopje ('ronde' printje links van de print in het kastje, met het grijs-witte draadje). In de onderste helft rechts de USB-aansluiting, en in het midden een handgefreesd printje met 3 MOSFET transistoren, plus drie (vier...) weerstanden om de stroom door de LED's te beperken. En links op de foto de LED op koelvin, met een kabeltje met connector. Het geheel is wat houtje-touwtje in elkaar gezet, maar is dan ook een zondagmiddag projectje (OK; iets meer, zeker als je deze beschrijving mee telt).

De software

De ESP8266 Arduino-omgeving wordt geleverd met de hele internet-library voor WiFi; hoef ik dus niet veel zelf aan te doen. Dat maakt dit project niet al te moeilijk. De basis-opbouw van mijn eigen programma is als volgt:

  • Initialiseer de hardware, timers en zo voort
  • Log in op het lokale netwerk en de MQTT server
  • Abonneer op de MQTT service: waarde van energiegebruik, en de tijd
  • Ga in een eindeloze loop, en wacht op MQTT berichten:
    • Bij een binnenkomend bericht wordt een callback-functie aangeroepen, die het bericht in de rij (queue) zet
      • Op deze manier wordt het zware werk vanuit de callback naar het hoofdprogramma uitbesteed. Houdt callbacks en interrupt-routines zo licht mogelijk!
    • Als er een energiebericht in de queue staat (elke 10 seconden): bereken de kleurwaarde en zet de PWM-waardes voor de LEDs
    • Als er een tijdsbericht in de queue staat: bereken de gewenste helderheid
      • In de nacht de LEDS vrij gedimd, overdag bij zonnig weer (hoge energie-leveringswaarde) juist extra helder

Veel meer is het niet. Ik had al wat gespeeld met de Sonoff, dus wist de grote lijnen van het werken met de ESP8266 al. Maar, zitten wat leuke details in wat betreft gebruik van MQTT en zo, komt ooit nog...

De details moet ik nog opschrijven; wordt zeker vervolgt... Voor wie de source al wil: SerialWiFiEnergy.ino

Opzetten MQTT client

OK, een stukje vast (om voor mezelf te documenteren)... Het aanmelden aan de MQTT server gebeurt met onderstaande stukjes code:

WiFiClient espClient;
PubSubClient mqttClient(mqtt_server, mqtt_port, mqttCallback, espClient);

...

if (mqttClient.connect("Serwi1 Client",      // Name of the system
      mqtt_user, mqtt_password,              // Login data for the MQTT server
      mqtt_topicLog, 0, false, "Serwi1 died...")) // plus the 'last will' in case we terminate
{
    mqttClient.subscribe(mqtt_topicPwm);     // Subscribe on the interesting topics, e.g.
    mqttClient.subscribe(mqtt_topicTime);    // "Kees/Time" for current time
    mqttClient.publish(mqtt_topicLog, "Serwi1 online"); // There we are...    
}

Wat gebeurt hier zoal (let op, gebeurt hierdoor ook een en ander automatisch 'achter de schermen' op de Raspberry):

  • Er wordt een PubSubClient genaamd mqttClient aangemaakt, met:
    • de gegevens van de MQTT server (IP adres van de Raspberry, poort-nummer van de mosquitto MQTT server)
    • de te gebruiken callback functie mqttCallback voor als er berichten binnenkomen
    • een link naar de WiFiClient
  • De connect functie van het eerder aangemaakte mqttClient wordt gebruikt om aan te melden bij de MQTT server.
    • Bij het aanmelden kan je een 'laatste wil' meegeven. Als je systeem crasht (of de stekker wordt er gewoon uitgetrokken) zal de MQTT server dit merken, en je laatste wil publiceren naar de systemen die daarop geabonneerd zijn. Als dus andere systemen van je afhankelijk zijn, kunnen ze de fout netjes opvangen. Hier wordt dan dus "Serwi1 died..." gepubliceerd naar mqtt_topicLog ("Kees/Log").
  • Na het aanmelden kan je je abonneren (subscribe) op onderwerpen (topics) waarin je bent geïnteresseerd, hier de energiewaarde en de tijd
  • En bij aanmelding publiceer ik dat het systeem online is naar mijn Log-topic, zodat andere systemen op mijn netwerk (als ze geïnteresseerd zijn) dit te weten komen
    • Bijvoorbeeld: op de Raspberry loopt een Python-script geabonneerd op "Kees/Log" die alle binnenkomende berichten op dit topic wegschrijft naar een log file, samen met tijd/datum.
    • Ook op de MQTT Dash op mijn tablet krijg ik de log-meldingen te zien
  • Let op: weggelaten in bovenstaande code is de 'else' bij het if-statement, met de fout-afhandeling.

Callbacks en Queue

Een ander cruciaal deel is de 'callback', de routine die aangeroepen wordt als er een bericht binnenkomt. Het enige wat ik daarin doe is het binnengekomen bericht in een queue (wachtrij) stoppen, het echte afhandelen gebeurt in het hoofdprogramma. Zoals gezegd, ik houd callbacks en interrupts liefst altijd zo kort en snel mogelijk. In het kort (even zonder uitleg van de circulaire queue data structuur, hopelijk spreekt de code voor zichzelf):

void mqttCallback(char* topic, byte* payload, unsigned int length)
{                     // lightweigth IRQ: only store message, don't execute
    unsigned int payloadLength = min(length, (unsigned int)QUEUEMESSLEN-1);
   
    mqtt_writeptr++;  // next empty entry in circular queue
    if(mqtt_writeptr >= QUEUESIZE) { mqtt_writeptr = 0; }
   
    strncpy(mqttQueue[mqtt_writeptr].topic, topic, QUEUEMESSLEN-1);
    strncpy(mqttQueue[mqtt_writeptr].payload, (char *)payload, payloadLength);
    mqttQueue[mqtt_writeptr].payloadlen = payloadLength;
    mqttQueue[mqtt_writeptr].payload[payloadLength] = (char)0; // terminator
}

En in het hoofdprogramma wordt dan de queue-inhoud verwerkt, waarbij process_queue() mijn code is die de afzonderlijke berichten inspecteert en verwerkt:

void loop()            // 'Arduino main', is NOT a loop itself
{
    if (!mqttClient.connected()) { reconnect_subscribe(); }
    mqttClient.loop(); // does not loop; just executes one handling
    process_queue();   // process any new messages arrived
    other_stuff();     // my own main program code, administration, timing etc
}

Verdere code-fragmenten en details nog een andere keer uit te werken...

Het schema

Basis is de 'Serial Wifi' print, met een ESP-07 module (met ESP8266 chip en flash-geheugen). De print voegt daar nog een 5Volt naar 3.3Volt regulator en level converters voor de seriële aansluiting aan toe, plus een reset-schakelaar, etc. Al met al een eenvoudige module, direct vanuit USB te voeden. Het heeft als nadeel dat de I/O aansluitingen niet mooi op pinnen/connectors zitten. Ik heb dus een en ander direct op de aansluitingen van de ESP-07 module gesoldeerd.

Schema van de IoT lamp
Schema van de Energie-verklikker (zie foto boven voor de opbouw)

Het schema laat de Serial Wifi module zien, met daarop de ESP-07. Deze laat een externe Wifi-antenne toe, maar ik gebruik hier de antenne op de module. De module kan met 5 Volt gevoedt worden, en is hier direct op een printje met micro-USB connector aangesloten (rechts).

De drie LEDs worden gestuurd via drie MOSFETs vanwege de stroomsterkte, 330 mA is te veel om direct door de module te leveren. De drie MOSFETs (3x BSO304SN, geschikt voor 3 Volt aansturing) worden door de ESP8266 aangestuurd (groen/geel/oranje draadjes op de foto's), op GPIO 12, 13 en 14 (let op de volgorde op de module...). Dit gebeurt met behulp van PWM (puls-breedte modulatie) voor de helderheid van de drie kleuren. De ESP8266 processor maakt niet zo'n heel mooie PWM, maar voor de aansturing van LEDs prima.

MOSFET op printjeDe SMD mosfets zitten op een 'gefreesd' printje (dremel met cirkelzaagblad, dus alleen rechte lijnen), om makkelijker te kunnen bedraden en om eventuele hitte af te voeren. Nu zal dit met deze LEDs niet zo nodig zijn, maar ik gebruik die module misschien nog wel eens voor iets anders. De gates zitten met 100 kOhm naar aarde: als een draad los schiet gaat de FET 'uit'. De onderste FET op het plaatje links heb ik schematisch weergegeven, beter te zien hoe de gefreesde lijnen lopen.

De rode LED heeft wat minder spanning nodig dan de groene en de blauwe, heeft daarom een hogere weerstand gekregen om op 330 mA uit te komen: 10 Ohm ipv 6.8 Ohm. De 10 Ohm is gemaakt door een weerstand van 15 Ohm parallel te schakelen aan een van 33 Ohm (rommelbak...). De LEDs worden overigens nooit allemaal op 100% aangestuurd, in de praktijk is het gebruik minder (maximaal twee LED's ivm de gekozen kleuren). De helderheid is ook nog afhankelijk van de tijd van de dag. De voeding van de LEDs komt via een zekering voor het geval er iets mis gaat. Is een 500 mA 'poly-fuse' zekering, herstelt zichzelf als de overbelasting wordt opgeheven.

De schakelaar S1 (die op het moment geen functie heeft, maar tijdens het testen wel is gebruikt) zit direct op GPIO 0 en aarde. De pull-up weerstand zit in de ESP8266 (softwarematig aangezet). GPIO 0 wordt ook gebruikt om de ESP-07 in programmeermode te zetten, maar is daarna vrij bruikbaar.

Op de opbouw-foto is te zien dat ik de 5 Volt naar de Serial Wifi los kan koppelen (rode draadje rechts naar de 4-pins connector van de Serial WiFi): dan kan ik makkelijk mijn programmer er op aan sluiten, zonder dat de LEDs ongecontroleerd aan kunnen gaan (de externe USB voeding is dan niet aangesloten).

En verder

Ik denk dat ik het software-gedeelte dat het energiegebruik naar de kleur omzet er uit haal, en op de Raspberry laat lopen (als een Python script). De Raspberry geeft dan ruwe RGB-waardes door naar mijn lampje. Op die manier kan ik makkelijk de functie van de lamp wijzigen, mocht ik iets anders willen laten zien.

En misschien nog eens die vermogensweerstanden bij de LEDs wegwerken. Zou toch mogelijk moeten zijn die door de goede waarde spoel (plus diode) te vervangen, zodat je een geschakelde voeding hebt. Wordt tenslotte al met PWM aangestuurd. Aan de andere kant, om zoveel vermogen gaat het ook niet, en als het schakelen misgaat en de mosfet constant aan gaat, krijg je een veel te grote stroom door de LEDs...