Hoofdstuk 13 van 14

Eindproject — Bouw je eigen Werkplaats-klimaatsensor

Je begon deze gids met een ESP32, een breadboard en een verzameling onderdelen die misschien nog niet allemaal vertrouwd aanvoelden. AI gebruikt zonder de…

Je begon deze gids met een ESP32, een breadboard en een verzameling onderdelen die misschien nog niet allemaal vertrouwd aanvoelden.

Sindsdien heb je geleerd hoe je:

een schakeling opbouwt;

code naar de ESP32 uploadt;

spanning en verbindingen controleert;

een BME280 uitleest;

met WiFi verbindt;

MQTT-berichten publiceert en ontvangt;

verbindingen na een storing herstelt;

fouten systematisch onderzoekt;

AI gebruikt zonder de controle uit handen te geven.

In dit eindproject brengen we al deze onderdelen samen.

We bouwen een Werkplaats-klimaatsensor die temperatuur, luchtvochtigheid en luchtdruk meet en via MQTT beschikbaar maakt. Wanneer WiFi of de MQTT-broker tijdelijk ontbreekt, blijft de sensor lokaal werken. Zodra de verbinding terugkomt, herstelt het project zichzelf.

Maar dit hoofdstuk wordt geen volledig voorgekauwd bouwrecept.

Tot nu toe liep ik bij iedere stap naast je. Nu geef ik je iets meer ruimte om zelf keuzes te maken, voorspellingen te doen en problemen op te lossen.

Dat is uiteindelijk waar De Werkplaats om draait:

Niet alleen een project nabouwen, maar begrijpen hoe het werkt en voldoende vertrouwen ontwikkelen om het zelf aan te passen.

De opdracht

Bouw een klimaatsensor met een ESP32-WROOM-32 en een BME280.

Het apparaat moet:

temperatuur meten;

relatieve luchtvochtigheid meten;

luchtdruk meten;

de meetwaarden in de seriële monitor tonen;

verbinding maken met een 2,4GHz-wifinetwerk;

meetwaarden via MQTT publiceren;

een online- en offlinestatus beschikbaar maken;

blijven meten wanneer WiFi ontbreekt;

blijven meten wanneer de MQTT-broker ontbreekt;

WiFi en MQTT automatisch herstellen;

ongeldige meetwaarden niet publiceren;

via MQTT een opdracht voor een directe meting kunnen ontvangen;

na een herstart zelfstandig verdergaan.

Het project is pas afgerond wanneer je niet alleen kunt laten zien dát het werkt, maar ook kunt uitleggen:

hoe de onderdelen met elkaar zijn verbonden;

welke taak ieder codeblok uitvoert;

hoe het programma reageert op storingen;

hoe je iedere belangrijke functie hebt getest;

welke beperkingen het project nog heeft;

wat je bij een volgende versie zou verbeteren.

Het systeem als geheel

Onze klimaatsensor bestaat uit verschillende blokken:

Omgeving → BME280 → I²C → ESP32 → WiFi → MQTT-broker → ontvanger

De voeding vormt daarnaast een eigen keten:

USB-voeding → ESP32 → 3,3 volt → BME280 → GND

Voor een MQTT-opdracht loopt de informatie in de andere richting:

Ontvanger → MQTT-broker → ESP32 → callback → nieuwe meting

Het project kan gedeeltelijk blijven werken wanneer een later blok uitvalt.

Als MQTT ontbreekt, kunnen de volgende onderdelen nog steeds werken:

Omgeving → BME280 → ESP32 → seriële monitor

Als WiFi ontbreekt, kan MQTT niet werken. De lokale meting hoeft daardoor echter niet te stoppen.

Dit onderscheid vormt een belangrijk onderdeel van het ontwerp.

Wanneer is het project geslaagd?

Vooraf bepalen we wat we onder een geslaagd project verstaan.

Gebruik hiervoor de volgende acceptatiecriteria.

OnderdeelHet project is geslaagd wanneer
VoedingDe ESP32 en BME280 blijven stabiel werken
SensorAlle drie de meetwaarden geldig worden uitgelezen
Lokale uitvoerDe waarden leesbaar in de seriële monitor verschijnen
WiFiDe ESP32 een IP-adres ontvangt
WiFi-uitvalDe sensor tijdens de uitval blijft meten
WiFi-herstelDe verbinding zonder reset terugkomt
MQTTDe ESP32 met een unieke client-ID verbindt
PublicerenAlle meetwaarden op de juiste topics verschijnen
Statusonline en na onverwachte uitval offline zichtbaar zijn
MQTT-uitvalDe sensor tijdens brokeruitval blijft meten
MQTT-herstelPubliceren na herstel automatisch wordt hervat
OpdrachtHet bericht meting een directe meting veroorzaakt
Ongeldige waardeEen ongeldige meting niet wordt gepubliceerd
DuurtestHet project gedurende de gekozen testperiode stabiel blijft
DocumentatieBedrading, instellingen, tests en beperkingen zijn vastgelegd

Je mag aanvullende eisen toevoegen. Verwijder de basiscriteria alleen wanneer je kunt uitleggen waarom ze niet bij jouw uitvoering passen.

Deel 1 — Maak eerst je eigen ontwerp

Voordat je de eerste draad aansluit, leg je een aantal keuzes vast.

Dit hoeft geen uitgebreid technisch document te worden. Eén pagina met duidelijke beslissingen is voldoende.

Geef het apparaat een naam

Kies een herkenbare hostnaam, bijvoorbeeld:

werkplaats-klimaatsensor

Gebruik je later meerdere sensoren, neem dan de ruimte of functie op:

werkplaats-klimaatsensor

woonkamer-klimaatsensor

schuur-klimaatsensor

De naam moet:

uniek zijn binnen je netwerk;

geen spaties bevatten;

gemakkelijk herkenbaar zijn;

niet te algemeen zijn.

Noteer jouw keuze:

Hostnaam:

________________________________________

Ontwerp de MQTT-topicstructuur

Kies een basispad voor alle berichten van het apparaat.

Bijvoorbeeld:

werkplaats/klimaatsensor

Daaronder kunnen de topics worden:

werkplaats/klimaatsensor/temperatuur

werkplaats/klimaatsensor/luchtvochtigheid

werkplaats/klimaatsensor/luchtdruk

werkplaats/klimaatsensor/wifi-rssi

werkplaats/klimaatsensor/status

werkplaats/klimaatsensor/opdracht

Noteer jouw topics:

Temperatuur:

________________________________________

Luchtvochtigheid:

________________________________________

Luchtdruk:

________________________________________

WiFi-signaal:

________________________________________

Status:

________________________________________

Opdracht:

________________________________________

Controleer of:

alles met kleine letters is geschreven;

de structuur consequent is;

de topics niet onnodig lang zijn;

ieder topic één duidelijke functie heeft.

Kies de meet- en publicatie-intervallen

De sensor hoeft niet even vaak te publiceren als hij meet.

Je kunt bijvoorbeeld:

iedere 2 seconden meten;

iedere 60 seconden publiceren.

Daardoor kan het apparaat lokaal veranderingen volgen zonder voortdurend MQTT-berichten te versturen.

Noteer je keuzes:

Meetinterval:

________________________________________

Publicatie-interval:

________________________________________

WiFi-herstelinterval:

________________________________________

MQTT-herstelinterval:

________________________________________

Schrijf bij ieder interval kort waarom je het hebt gekozen.

Een temperatuurmeting hoeft meestal niet iedere seconde via MQTT te worden verstuurd. Een alarmsignaal kan juist wel een snelle reactie vragen.

Bepaal wat zonder netwerk moet blijven werken

In dit project moet de sensor blijven meten wanneer WiFi of MQTT ontbreekt.

Denk daarnaast na over mogelijke toekomstige lokale taken:

een waarschuwingsled;

een zoemer;

een ventilatorregeling;

een display;

lokale opslag.

Noteer:

Deze functies moeten zonder netwerk blijven werken:

________________________________________

________________________________________

Dit onderscheid voorkomt dat een netwerkstoring later onbedoeld het volledige apparaat stillegt.

Bepaal de veilige toestand

Ons huidige project bestuurt nog geen krachtige belasting. Toch is het goed om deze vraag nu al te stellen:

Wat moet het apparaat doen wanneer een onderdeel uitvalt?

Voor onze klimaatsensor kan dat betekenen:

geen ongeldige waarden publiceren;

lokaal blijven meten zonder WiFi;

de laatste geldige waarde niet als nieuwe meting presenteren;

foutmeldingen tonen;

periodiek opnieuw proberen;

niet voortdurend opnieuw opstarten.

Noteer jouw veilige toestand:

Bij sensoruitval:

________________________________________

Bij WiFi-uitval:

________________________________________

Bij MQTT-uitval:

________________________________________

Deel 2 — Verzamel de onderdelen

Voor de basisuitvoering gebruiken we:

een ESP32-ontwikkelbord met ESP32-WROOM-32-module;

een BME280-sensormodule;

een USB-datakabel;

een breadboard;

jumperdraden;

een computer met Arduino IDE;

toegang tot een 2,4GHz-wifinetwerk;

toegang tot een MQTT-broker;

bij voorkeur een multimeter.

Daarnaast heb je de volgende software nodig:

ondersteuning voor ESP32-borden;

de Adafruit BME280 Library;

de Adafruit Unified Sensor Library;

PubSubClient.

Controleer voordat je verdergaat:

welk ontwikkelbord je precies hebt;

of de sensor werkelijk een BME280 is;

welke voedingsspanning de module accepteert;

welke pinnen op jouw module SDA en SCL zijn;

of de BME280 0x76 of 0x77 gebruikt;

of je MQTT-inloggegevens werken;

of je een bekende goede USB-datakabel gebruikt.

Deel 3 — Maak een aansluitschema

Maak vóór het bouwen een eenvoudige verbindingstabel.

Voor onze standaardopstelling wordt dat:

BME280ESP32Functie
VCC, VIN of 3V33V3Voeding
GNDGNDGemeenschappelijke massa
SDAGPIO 21I²C-gegevens
SCLGPIO 22I²C-klok

De exacte benamingen op de sensormodule kunnen verschillen.

Kopieer daarom niet alleen deze posities. Controleer de tekst op jouw eigen module en de documentatie van die uitvoering.

Teken vervolgens zelf de verbindingen. Een handgetekend schema is voldoende als het duidelijk laat zien:

welke onderdelen worden gebruikt;

welke pinnen met elkaar zijn verbonden;

welke voedingsspanning wordt gebruikt;

waar GND loopt;

welke GPIO’s voor I²C worden gebruikt.

Controle vóór het aansluiten

Beantwoord eerst:

Is de BME280 geschikt voor voeding met 3,3 volt?

Loopt SDA naar GPIO 21?

Loopt SCL naar GPIO 22?

Is GND aangesloten?

Zijn er geen losse draden die naastgelegen pinnen raken?

Is de USB-kabel nog losgekoppeld?

Pas daarna sluit je de ESP32 via USB aan.

Deel 4 — Bouw in fasen

We gaan het project niet in één keer volledig opbouwen.

Iedere fase krijgt:

één duidelijk doel;

een verwachte uitkomst;

een test;

een beslismoment.

Pas wanneer een fase aantoonbaar werkt, voegen we de volgende laag toe.

Fase 1 — Test alleen de ESP32

Doel

Aantonen dat:

de ESP32 voeding krijgt;

de computer het bord herkent;

code kan worden geüpload;

de seriële monitor werkt.

Test

Upload een eenvoudig programma met een herkenbare melding:

void setup() {

Serial.begin(115200);

delay(1000);

Serial.println();

Serial.println("Werkplaats-klimaatsensor gestart");

}

void loop() {

}

Verwachte uitkomst

Na uploaden en resetten verschijnt:

Werkplaats-klimaatsensor gestart

Ga alleen verder wanneer

uploaden zonder foutmelding lukt;

de juiste USB-poort is gekozen;

de tekst leesbaar verschijnt op 115200 baud.

Fase 2 — Test alleen de I²C-bus

Doel

Aantonen dat de ESP32 een apparaat op de I²C-bus ziet.

Test

Gebruik de I²C-scanner uit het hoofdstuk over sensoren.

Verwachte uitkomst

De scanner vindt:

0x76

of:

0x77

Leg vast

Gevonden I2C-adres:

________________________________________

Als er niets wordt gevonden

Controleer:

voeding op de module;

gezamenlijke GND;

SDA naar GPIO 21;

SCL naar GPIO 22;

jumperdraden;

pinvolgorde van de module;

soldeerverbindingen.

Voeg nog geen WiFi of MQTT toe zolang de sensor niet zichtbaar is.

Fase 3 — Lees de BME280 lokaal uit

Doel

Aantonen dat:

de sensorbibliotheek werkt;

het gevonden apparaat als BME280 wordt herkend;

temperatuur, luchtvochtigheid en luchtdruk geldig zijn.

Test

Gebruik het sensorprogramma uit het eerdere hoofdstuk en stel het gevonden I²C-adres in.

Verwachte uitkomst

De seriële monitor toont waarden zoals:

Temperatuur: 21.8 °C

Luchtvochtigheid: 48.2 %

Luchtdruk: 1009.5 hPa

Controleer niet alleen of er getallen staan

Vraag ook:

zijn de waarden aannemelijk;

veranderen ze logisch;

verschijnt nergens nan;

blijft de sensor gedurende minstens tien minuten beschikbaar;

wordt de temperatuur niet duidelijk beïnvloed door warmte van de ESP32?

Leg een referentiemeting vast

Tijdstip:

________________________________________

Temperatuur:

________________________________________

Luchtvochtigheid:

________________________________________

Luchtdruk:

________________________________________

Vergelijkingsinstrument:

________________________________________

Een verschil met een andere thermometer bewijst niet meteen dat de BME280 defect is. Let ook op plaatsing, reactietijd en warmte van het ontwikkelbord.

Fase 4 — Voeg WiFi toe

Doel

Aantonen dat:

de ESP32 met het juiste netwerk verbindt;

een IP-adres wordt ontvangen;

de sensor blijft meten zonder WiFi;

een weggevallen verbinding wordt hersteld.

Test

Voeg de failsafe-WiFi-code uit het eerdere hoofdstuk toe.

Controleer dat:

er geen oneindige verbindingslus ontstaat;

sensormetingen tijdens een poging blijven verschijnen;

na een time-out het programma verdergaat;

later opnieuw wordt geprobeerd.

Normale test

Start de ESP32 met beschikbaar WiFi.

Je verwacht:

WiFi verbonden.

IP-adres: ...

Signaalsterkte: ... dBm

Storingstest

Schakel het accesspoint tijdelijk uit, mits je daarmee geen belangrijke systemen verstoort.

Je verwacht:

een melding dat WiFi is verbroken;

blijvende lokale sensormetingen;

periodieke herstelpogingen;

herstel zonder reset wanneer WiFi terugkomt.

Ga alleen verder wanneer

WiFi niet alleen één keer kan verbinden, maar ook aantoonbaar van een storing kan herstellen.

Fase 5 — Voeg MQTT toe

Doel

Aantonen dat:

MQTT alleen probeert te verbinden wanneer WiFi beschikbaar is;

de client-ID uniek is;

de sensor blijft meten zonder broker;

de verbinding na brokeruitval wordt hersteld.

Test

Voeg eerst alleen de MQTT-verbinding toe. Publiceer nog geen echte meetwaarden.

Publiceer na verbinding tijdelijk:

mqttClient.publish(

"werkplaats/klimaatsensor/test",

"hallo",

false

);

Verwachte uitkomst

Een algemene MQTT-client ontvangt:

hallo

op:

werkplaats/klimaatsensor/test

Waarom eerst een vaste tekst?

Daarmee testen we alleen:

ESP32 → WiFi → MQTT-client → broker

De BME280, getalomzetting en publicatie-intervallen spelen bij deze test nog geen rol.

Fase 6 — Publiceer de meetwaarden

Doel

Temperatuur, luchtvochtigheid en luchtdruk op afzonderlijke topics publiceren.

Controleer per waarde

gebruikt de code de laatst geldige meting;

wordt het getal correct naar tekst omgezet;

staat er één waarde per topic;

komt het bericht aan;

klopt de eenheid bij de ontvanger;

wordt geen nan gepubliceerd?

Verwachte MQTT-berichten

Bijvoorbeeld:

werkplaats/klimaatsensor/temperatuur = 21.8

werkplaats/klimaatsensor/luchtvochtigheid = 48.2

werkplaats/klimaatsensor/luchtdruk = 1009.5

werkplaats/klimaatsensor/wifi-rssi = -54

Controleer de berichten eerst met een algemene MQTT-client voordat je Home Assistant configureert.

Fase 7 — Voeg status en opdrachten toe

Status

Publiceer na een geslaagde MQTT-verbinding:

online

op:

werkplaats/klimaatsensor/status

Stel als last will in:

offline

op hetzelfde topic.

Gebruik retained berichten voor de status.

Opdracht

Abonneer de ESP32 op:

werkplaats/klimaatsensor/opdracht

Laat het bericht:

meting

een directe sensormeting veroorzaken.

Belangrijke controle

Na iedere herverbinding met MQTT moet de ESP32 zich opnieuw op het opdrachttopic abonneren.

Test dit door:

een werkende opdracht te versturen;

de broker tijdelijk uit te schakelen;

de broker opnieuw te starten;

te wachten op de herverbinding;

opnieuw meting te versturen.

De opdracht moet ook na herstel aankomen.

Deel 5 — Schrijf eerst zelf de hoofdstructuur

Probeer vóór je de referentiecode bekijkt de hoofdopbouw zelf te maken.

Je programma heeft ongeveer deze onderdelen nodig:

void startWiFiConnection();

void maintainWiFi();

void connectToMqtt();

void maintainMqtt();

void readSensor();

void publishSensorValues();

void mqttCallback(

char* topic,

byte* payload,

unsigned int length

);

void setup();

void loop();

De hoofdloop hoort overzichtelijk te blijven:

void loop() {

maintainWiFi();

maintainMqtt();

// Periodiek meten

// Periodiek publiceren

}

Vraag jezelf bij iedere taak af:

moet dit één keer gebeuren;

moet dit voortdurend worden onderhouden;

mag dit alleen bij WiFi;

mag dit alleen bij MQTT;

moet dit ook zonder netwerk blijven werken?

Een mogelijke volgorde in setup()

Seriële monitor starten.

I²C starten.

BME280 zoeken.

WiFi voorbereiden.

Unieke MQTT-client-ID maken.

MQTT-server en callback instellen.

De eerste WiFi-poging starten.

De eerste sensormeting direct toestaan.

Een mogelijke volgorde in loop()

WiFi onderhouden.

MQTT onderhouden.

Controleren of het tijd is voor een meting.

Controleren of het tijd is voor een publicatie.

Andere lokale taken uitvoeren.

Plaats de sensormeting niet binnen een voorwaarde die WiFi of MQTT vereist.

Deel 6 — Maak je eigen testplan

Een testplan beschrijft vooraf:

welke functie je test;

hoe je de situatie maakt;

wat je verwacht;

wat je werkelijk ziet;

of de test is geslaagd.

Gebruik bijvoorbeeld de volgende tabel.

TestHandelingVerwachtingUitkomst
OpstartenESP32 inschakelenSensor en programma starten
SensorBME280 uitlezenDrie geldige waarden
Geen sensorSDA tijdelijk losFoutmelding, programma blijft actief
WiFi normaalAccesspoint beschikbaarIP-adres ontvangen
Geen WiFiAccesspoint uitschakelenMetingen gaan door
WiFi-herstelAccesspoint inschakelenAutomatische herverbinding
MQTT normaalBroker beschikbaarVerbinding en publicaties
Geen brokerBroker uitschakelenMetingen gaan door
MQTT-herstelBroker inschakelenPublicaties hervatten
StatusESP32 onverwacht uitschakelenStatus wordt offline
Opdrachtmeting publicerenDirecte meting
Opdracht na herstelBroker herstellen, opdracht sturenOpdracht komt opnieuw aan
Ongeldige waardeSensorcommunicatie verstorenGeen ongeldige publicatie
DuurtestLangdurig laten werkenGeen vastlopen of ongewenste reset

Vul de uitkomst tijdens de test in. Schrijf niet alleen “goed”, maar noteer wat je werkelijk hebt waargenomen.

Bijvoorbeeld:

Sensorwaarden bleven iedere twee seconden verschijnen.

WiFi verbond 7 seconden na herstel van het accesspoint.

Deel 7 — Voer bewust storingen in

Een betrouwbaar project test je niet alleen onder ideale omstandigheden.

We voeren daarom enkele gecontroleerde fouten in.

Breng veranderingen aan de bedrading alleen aan wanneer de voeding is losgekoppeld.

Storing 1 — BME280 ontbreekt

Koppel de USB-kabel los en verwijder tijdelijk de SDA-draad.

Start daarna de ESP32.

Verwachting

de BME280 wordt niet gevonden;

er worden geen ongeldige waarden gepubliceerd;

WiFi en MQTT mogen eventueel nog starten;

het programma blijft actief;

de fout wordt duidelijk gemeld.

Vraag

Hoe weet je uit de seriële uitvoer dat het probleem bij de sensor zit en niet bij WiFi?

Storing 2 — WiFi ontbreekt bij het opstarten

Schakel het accesspoint uit voordat de ESP32 start.

Verwachting

de BME280 blijft meten;

de eerste WiFi-poging loopt af;

het programma gaat verder;

later volgt een nieuwe poging;

na herstel komt WiFi terug zonder reset.

Vraag

Welke code voorkomt dat de ESP32 eindeloos op WiFi wacht?

Storing 3 — WiFi valt tijdens normaal gebruik weg

Start het project met werkende WiFi en MQTT. Schakel daarna het accesspoint uit.

Verwachting

WiFi-uitval wordt ontdekt;

MQTT is niet meer bruikbaar;

lokale metingen gaan door;

WiFi probeert opnieuw;

MQTT wacht totdat WiFi is hersteld;

daarna herstelt ook MQTT.

Vraag

Waarom moet MQTT niet blijven proberen zolang WiFi ontbreekt?

Storing 4 — De broker valt uit

Laat WiFi actief en schakel alleen de MQTT-broker uit.

Verwachting

WiFi blijft verbonden;

de sensor blijft meten;

MQTT-pogingen mislukken;

foutcodes worden getoond;

het programma start niet voortdurend opnieuw;

na herstel wordt opnieuw verbonden;

de ESP32 abonneert zich opnieuw;

publicaties worden hervat.

Vraag

Welke waarneming maakt duidelijk dat dit geen WiFi-probleem is?

Storing 5 — Verkeerd MQTT-wachtwoord

Plaats tijdelijk een verkeerd MQTT-wachtwoord in Arduino_Secrets.h en upload de code.

Verwachting

WiFi werkt;

MQTT wordt geweigerd;

lokale metingen gaan door;

de foutcode wijst op authenticatie of autorisatie;

de ESP32 probeert later opnieuw.

Herstel daarna het juiste wachtwoord en upload opnieuw.

Storing 6 — Verkeerd topic bij de ontvanger

Laat de ESP32 correct publiceren, maar configureer een testontvanger tijdelijk met een afwijkende topicnaam.

Verwachting

de ESP32 meldt succesvolle publicaties;

MQTT Explorer ziet de waarden op het juiste topic;

de foutief geconfigureerde ontvanger ziet niets.

Vraag

Waar ligt het laatste bevestigde werkende punt?

Het juiste antwoord is bij de broker. De sensorbedrading hoeft dan niet opnieuw te worden onderzocht.

Deel 8 — Controleer het herstel

Het verwijderen van de storing is nog niet hetzelfde als aantonen dat het herstel werkt.

Controleer na iedere storing:

Wordt de verbinding automatisch hersteld?

Is een reset nodig?

Wordt de status opnieuw online?

Worden meetwaarden opnieuw gepubliceerd?

Wordt opnieuw geabonneerd op opdrachten?

Zijn de lokale metingen tijdens de storing doorgegaan?

Is er ongewenste oude of ongeldige informatie gepubliceerd?

Blijven de intervallen correct werken?

Een betrouwbaar project keert niet alleen terug naar “verbonden”. Het hervat ook zijn normale taken.

Deel 9 — Voer een duurtest uit

Een project dat vijf minuten werkt, kan na enkele uren alsnog vastlopen.

Laat de klimaatsensor daarom langere tijd werken. Begin bijvoorbeeld met 24 uur.

Tijdens de duurtest controleer je:

blijft de ESP32 actief;

blijven meetwaarden veranderen;

blijft MQTT verbonden;

ontstaan onverwachte resets;

herstelt het project van een korte netwerkstoring;

blijft de sensor bereikbaar;

loopt de seriële uitvoer normaal door;

verandert het geheugengebruik of gedrag merkbaar;

worden er onrealistische meetwaarden gepubliceerd?

Noteer belangrijke gebeurtenissen

Maak een eenvoudige tabel.

TijdGebeurtenisWaarnemingHerstel
09:00Project gestartAlles verbondenNiet nodig
11:15Accesspoint herstartLokale metingen gingen doorWiFi na 18 s terug
15:30Broker herstartMQTT tijdelijk offlineNa 10 s hersteld
09:00 volgende dagEinde testGeen ongewenste resetGeslaagd

Een duurtest hoeft niet volledig handmatig te worden bekeken. De status in de broker, Home Assistant-logboeken en seriële uitvoer kunnen aanvullende informatie geven.

Wat is een mislukte duurtest?

Bijvoorbeeld:

de ESP32 is opnieuw opgestart zonder verklaarde reden;

sensormetingen stoppen;

MQTT herstelt niet;

opdrachten komen na herstel niet meer aan;

de status blijft onterecht online;

het publicatie-interval versnelt of stopt;

meetwaarden worden onrealistisch;

het apparaat wordt ongewoon warm.

Een mislukte duurtest betekent niet dat het hele project mislukt is. Het levert een nieuwe, concrete foutzoekopdracht op.

Deel 10 — Denk na over de plaatsing

De BME280 meet de omgeving direct rondom de sensor.

De plaatsing heeft daarom invloed op het resultaat.

Plaats de sensor niet zonder nadenken:

direct naast de ESP32;

boven een spanningsregelaar;

in direct zonlicht;

tegen een koude buitenmuur;

vlak bij een verwarming;

in een volledig gesloten behuizing;

in een sterke luchtstroom;

op een vochtige plaats waarvoor de module niet is beschermd.

Afstand tot de ESP32

De ESP32 produceert warmte. Daardoor kan de gemeten temperatuur hoger zijn dan de werkelijke kamertemperatuur.

Mogelijke verbeteringen zijn:

de BME280 op enige afstand plaatsen;

ventilatieopeningen in de behuizing maken;

de sensor thermisch scheiden van de ESP32;

de sensor buiten het hoofdcompartiment plaatsen;

de invloed gedurende langere tijd meten.

Voeg niet direct een softwarematige temperatuurcorrectie toe.

Controleer eerst of betere plaatsing de oorzaak vermindert. Een vaste correctie kan op één plaats kloppen en onder andere omstandigheden juist een nieuwe fout introduceren.

De antenne vrijhouden

De printantenne bevindt zich aan het uiteinde van de ESP32-WROOM-module.

Plaats deze niet direct tegen:

metaal;

een grote printplaat;

dikke kabelbundels;

een metalen behuizing;

een voedingsmodule die storing veroorzaakt.

Test de RSSI buiten en binnen de behuizing.

Noteer:

RSSI buiten de behuizing:

________________________________________

RSSI in de behuizing:

________________________________________

Een duidelijk verschil geeft informatie over de invloed van de constructie.

Deel 11 — Maak het project duurzaam

Een breadboard is uitstekend voor experimenten. Voor langdurig gebruik kan een definitievere uitvoering betrouwbaarder zijn.

Mogelijke vervolgstappen zijn:

een prototypeprint gebruiken;

connectoren toevoegen;

trekontlasting toepassen;

de voeding degelijk aansluiten;

de sensor mechanisch beschermen;

een geschikte behuizing maken;

aansluitingen labelen;

code en schema samen bewaren.

Denk ook na over onderhoud:

Kan de USB-aansluiting bereikbaar blijven?

Kun je de sensor vervangen?

Is de resetknop bereikbaar?

Kun je de behuizing openen zonder draden los te trekken?

Kun je zien welk apparaat het is?

Zijn de gebruikte broker- en topicgegevens gedocumenteerd?

Een project is niet alleen betrouwbaar wanneer het vandaag werkt. Het moet later ook begrijpelijk en onderhoudbaar blijven.

Deel 12 — Documenteer je project

Een Werkplaats-project is meer dan code.

Leg minimaal het volgende vast.

Projectnaam

________________________________________

Doel

________________________________________

________________________________________

Hardware

ESP32-bord:

________________________________________

Sensormodule:

________________________________________

Voeding:

________________________________________

Behuizing:

________________________________________

Pinverbindingen

FunctieESP32-pinOnderdeelpin
Voeding
GND
SDA
SCL

Software

Arduino IDE-versie:

________________________________________

ESP32-boardpakket:

________________________________________

Adafruit BME280 Library:

________________________________________

PubSubClient:

________________________________________

Codeversie:

________________________________________

Netwerkinstellingen

Noteer geen wachtwoorden.

Hostnaam:

________________________________________

MQTT-broker:

________________________________________

MQTT-poort:

________________________________________

MQTT-client-ID:

________________________________________

Topics

________________________________________

________________________________________

________________________________________

Intervallen

Sensor:

________________________________________

Publiceren:

________________________________________

WiFi opnieuw proberen:

________________________________________

MQTT opnieuw proberen:

________________________________________

Bekende beperkingen

Bijvoorbeeld:

temperatuur wordt enigszins beïnvloed door warmte van de ESP32;

MQTT op poort 1883 is niet versleuteld;

meetwaarden worden niet lokaal opgeslagen;

bij netwerkuitval worden gemiste publicaties niet later ingehaald;

de sensor is niet geschikt voor buitengebruik zonder bescherming.

Noteer jouw beperkingen:

________________________________________

________________________________________

________________________________________

Testresultaten

Bewaar:

de ingevulde acceptatietabel;

foutmeldingen;

resultaten van storingstests;

duurtest;

foto’s van de bedrading;

definitief aansluitschema.

Deel 13 — Een eigen uitbreiding kiezen

Wanneer de basisuitvoering stabiel werkt, voeg je één eigen uitbreiding toe.

Kies niet meteen vijf nieuwe functies. Eén goed uitgewerkte uitbreiding leert je meer dan een grote verzameling half geteste mogelijkheden.

Uitbreiding 1 — Een waarschuwingsled

Laat een led lokaal aangeven wanneer de luchtvochtigheid boven een gekozen grens komt.

Bepaal vooraf:

welke GPIO je gebruikt;

welke weerstand nodig is;

welke grenswaarde logisch is;

wat er gebeurt als de sensor uitvalt;

of de led ook zonder WiFi moet werken.

Deze uitbreiding maakt duidelijk waarom lokale werking onafhankelijk van het netwerk belangrijk is.

Uitbreiding 2 — Een display

Toon de meetwaarden op een klein I²C-display.

Controleer:

welk I²C-adres het display gebruikt;

of dit conflicteert met de BME280;

of beide apparaten dezelfde SDA- en SCL-lijnen kunnen delen;

hoe vaak het scherm moet worden bijgewerkt;

wat het display toont wanneer de sensor ontbreekt.

Uitbreiding 3 — MQTT Discovery

Laat de ESP32 zijn sensoren automatisch bij Home Assistant aanmelden.

Onderzoek daarbij:

discovery-topics;

unieke identifiers;

device-informatie;

beschikbaarheid;

retained configuratieberichten;

verwijderen van oude configuraties.

Voeg deze functie pas toe nadat de gewone MQTT-topics aantoonbaar werken.

Uitbreiding 4 — Metingen lokaal bewaren

Bewaar meetwaarden tijdelijk wanneer MQTT ontbreekt en verstuur ze later alsnog.

Daarbij ontstaan nieuwe ontwerpvragen:

hoeveel waarden worden bewaard;

waar worden ze opgeslagen;

wat gebeurt er als de opslag vol is;

hoe voorkom je slijtage van flashgeheugen;

hoe geef je oorspronkelijke meettijdstippen door;

wat gebeurt er na een stroomonderbreking?

Dit is een mooie uitbreiding, maar duidelijk complexer dan alleen de laatste waarde publiceren.

Uitbreiding 5 — Een tweede sensor

Voeg een tweede meetpunt toe.

Controleer:

gebruikt de sensor hetzelfde I²C-adres;

kan het adres worden aangepast;

is een multiplexer nodig;

hoe onderscheid je de topics;

hoe herken je welke sensor is uitgevallen?

Uitbreiding 6 — Een behuizing ontwerpen

Maak een behuizing waarin:

de ESP32 veilig is gemonteerd;

de antenne voldoende vrij blijft;

lucht langs de BME280 kan stromen;

warmte van de ESP32 de sensor zo weinig mogelijk beïnvloedt;

de USB-aansluiting bereikbaar is;

draden niet onder spanning staan.

Test vóór en na plaatsing:

temperatuurverschil;

luchtvochtigheid;

RSSI;

stabiliteit van de verbinding.

Deel 14 — Gebruik AI bij je uitbreiding

AI mag je helpen bij het ontwerpen van de uitbreiding, maar blijf de methode uit het vorige hoofdstuk gebruiken.

Een goede vraag is bijvoorbeeld:

Ik gebruik een ESP32-WROOM-32 met een BME280 op GPIO 21 en 22. De huidige code meet lokaal en onderhoudt WiFi en MQTT zonder blokkerende lussen. Ik wil een waarschuwingsled toevoegen die ook zonder netwerk werkt. Stel drie geschikte GPIO’s voor en benoem per pin eventuele opstartbeperkingen. Verander nog geen code.

Of:

Voeg alleen een lokale waarschuwingsled op GPIO 23 toe. De led moet aangaan boven 70% luchtvochtigheid en uitgaan onder 68%, zodat hij niet snel heen en weer schakelt. Laat WiFi-, MQTT- en sensorfuncties verder ongewijzigd. Leg iedere wijziging uit.

Controleer daarna:

is GPIO 23 werkelijk vrij;

klopt de bedrading;

is een voorschakelweerstand opgenomen;

blijft de functie zonder netwerk werken;

zijn de gekozen grenswaarden passend;

ontstaat geen nieuwe blokkerende code?

AI kan een voorstel doen. Jouw meting en test bepalen of het ontwerp werkt.

Deel 15 — Referentie-uitwerking

Bekijk deze uitwerking pas nadat je zelf een poging hebt gedaan.

De code hieronder gebruikt dezelfde basis als de eerdere hoofdstukken, maar past de topicstructuur en intervallen aan voor het eindproject.

Het bestand Arduino_Secrets.h bevat:

#define SECRET_SSID "NaamVanJeWiFi"

#define SECRET_PASS "JeWiFiWachtwoord"

#define SECRET_MQTT_SERVER "192.168.1.50"

#define SECRET_MQTT_USER "esp32_sensor"

#define SECRET_MQTT_PASS "JeMQTTWachtwoord"

De hoofdcode:

#include <Wire.h>

#include <Adafruit_Sensor.h>

#include <Adafruit_BME280.h>

#include <WiFi.h>

#include <PubSubClient.h>

#include "Arduino_Secrets.h"

// Projectinformatie

const char* projectVersion = "1.0";

const char* hostName = "werkplaats-klimaatsensor";

// WiFi

const char* ssid = SECRET_SSID;

const char* wifiPassword = SECRET_PASS;

// MQTT

const char* mqttBroker = SECRET_MQTT_SERVER;

const int mqttPort = 1883;

const char* mqttUser = SECRET_MQTT_USER;

const char* mqttPassword = SECRET_MQTT_PASS;

// MQTT-topics

const char* topicTemperature =

"werkplaats/klimaatsensor/temperatuur";

const char* topicHumidity =

"werkplaats/klimaatsensor/luchtvochtigheid";

const char* topicPressure =

"werkplaats/klimaatsensor/luchtdruk";

const char* topicRssi =

"werkplaats/klimaatsensor/wifi-rssi";

const char* topicStatus =

"werkplaats/klimaatsensor/status";

const char* topicCommand =

"werkplaats/klimaatsensor/opdracht";

// BME280

Adafruit_BME280 bme;

const int sdaPin = 21;

const int sclPin = 22;

const uint8_t bmeAddress = 0x76;

// Netwerkclients

WiFiClient wifiClient;

PubSubClient mqttClient(wifiClient);

// Intervallen

const unsigned long sensorInterval = 2000;

const unsigned long publishInterval = 60000;

const unsigned long wifiConnectTimeout = 10000;

const unsigned long wifiRetryInterval = 30000;

const unsigned long mqttRetryInterval = 10000;

const unsigned long statusInterval = 30000;

// Beschikbaarheid

bool sensorAvailable = false;

bool sensorValuesValid = false;

bool wifiWasConnected = false;

bool wifiAttemptActive = false;

// Meetwaarden

float currentTemperature = 0.0;

float currentHumidity = 0.0;

float currentPressure = 0.0;

// Tijdregistratie

unsigned long lastSensorRead = 0;

unsigned long lastPublish = 0;

unsigned long lastStatusReport = 0;

unsigned long wifiAttemptStarted = 0;

unsigned long lastWifiAttempt = 0;

unsigned long lastMqttAttempt = 0;

// MQTT-identiteit

String mqttClientId;

void startWiFiConnection() {

Serial.print("Verbinden met WiFi: ");

Serial.println(ssid);

WiFi.begin(ssid, wifiPassword);

wifiAttemptActive = true;

wifiAttemptStarted = millis();

lastWifiAttempt = millis();

}

void maintainWiFi() {

if (WiFi.status() == WL_CONNECTED) {

if (!wifiWasConnected) {

wifiWasConnected = true;

wifiAttemptActive = false;

Serial.println();

Serial.println("WiFi verbonden.");

Serial.print("IP-adres: ");

Serial.println(WiFi.localIP());

Serial.print("RSSI: ");

Serial.print(WiFi.RSSI());

Serial.println(" dBm");

// MQTT na WiFi-herstel direct laten proberen

lastMqttAttempt =

millis() - mqttRetryInterval;

}

return;

}

if (wifiWasConnected) {

wifiWasConnected = false;

wifiAttemptActive = false;

if (mqttClient.connected()) {

mqttClient.disconnect();

}

Serial.println();

Serial.println("*** WiFi verbroken ***");

Serial.println("Lokale metingen gaan door.");

startWiFiConnection();

return;

}

if (wifiAttemptActive) {

if (

millis() - wifiAttemptStarted >=

wifiConnectTimeout

) {

wifiAttemptActive = false;

WiFi.disconnect(false, false);

Serial.println("WiFi-poging verlopen.");

Serial.println("Lokale taken gaan door.");

}

return;

}

if (

millis() - lastWifiAttempt >=

wifiRetryInterval

) {

startWiFiConnection();

}

}

void mqttCallback(

char* topic,

byte* payload,

unsigned int length

) {

String message;

for (unsigned int i = 0; i < length; i++) {

message += static_cast<char>(payload[i]);

}

message.trim();

Serial.println();

Serial.println("MQTT-bericht ontvangen");

Serial.print("Topic: ");

Serial.println(topic);

Serial.print("Bericht: ");

Serial.println(message);

if (strcmp(topic, topicCommand) != 0) {

return;

}

if (message == "meting") {

Serial.println("Directe meting aangevraagd.");

lastSensorRead =

millis() - sensorInterval;

lastPublish =

millis() - publishInterval;

} else {

Serial.println("Onbekende opdracht.");

}

}

void connectToMqtt() {

if (WiFi.status() != WL_CONNECTED) {

return;

}

Serial.print("Verbinden met MQTT als ");

Serial.print(mqttClientId);

Serial.print("... ");

bool connected = mqttClient.connect(

mqttClientId.c_str(),

mqttUser,

mqttPassword,

topicStatus,

0,

true,

"offline"

);

if (!connected) {

Serial.println("mislukt.");

Serial.print("MQTT-foutcode: ");

Serial.println(mqttClient.state());

return;

}

Serial.println("gelukt.");

mqttClient.publish(

topicStatus,

"online",

true

);

if (mqttClient.subscribe(topicCommand)) {

Serial.print("Geabonneerd op: ");

Serial.println(topicCommand);

} else {

Serial.println(

"Abonneren op opdrachttopic mislukt."

);

}

// Na herstel de huidige waarde direct publiceren

lastPublish =

millis() - publishInterval;

}

void maintainMqtt() {

if (WiFi.status() != WL_CONNECTED) {

if (mqttClient.connected()) {

mqttClient.disconnect();

}

return;

}

if (mqttClient.connected()) {

mqttClient.loop();

return;

}

if (

millis() - lastMqttAttempt >=

mqttRetryInterval

) {

lastMqttAttempt = millis();

connectToMqtt();

}

}

void readSensor() {

if (!sensorAvailable) {

return;

}

float temperature =

bme.readTemperature();

float humidity =

bme.readHumidity();

float pressure =

bme.readPressure() / 100.0F;

if (

isnan(temperature) ||

isnan(humidity) ||

isnan(pressure)

) {

sensorValuesValid = false;

Serial.println();

Serial.println(

"*** Ongeldige sensormeting ***"

);

return;

}

currentTemperature = temperature;

currentHumidity = humidity;

currentPressure = pressure;

sensorValuesValid = true;

Serial.println();

Serial.println("Klimaatmeting");

Serial.print("Temperatuur: ");

Serial.print(currentTemperature, 1);

Serial.println(" °C");

Serial.print("Luchtvochtigheid: ");

Serial.print(currentHumidity, 1);

Serial.println(" %");

Serial.print("Luchtdruk: ");

Serial.print(currentPressure, 1);

Serial.println(" hPa");

Serial.println("--------------------");

}

bool publishFloat(

const char* topic,

float value

) {

char payload[16];

snprintf(

payload,

sizeof(payload),

"%.1f",

value

);

bool result = mqttClient.publish(

topic,

payload,

true

);

Serial.print(topic);

Serial.print(" = ");

Serial.print(payload);

Serial.println(

result ? " gepubliceerd" : " mislukt"

);

return result;

}

void publishSensorValues() {

if (!mqttClient.connected()) {

return;

}

if (!sensorValuesValid) {

Serial.println();

Serial.println(

"Geen geldige waarden om te publiceren."

);

return;

}

Serial.println();

Serial.println("MQTT-publicatie");

publishFloat(

topicTemperature,

currentTemperature

);

publishFloat(

topicHumidity,

currentHumidity

);

publishFloat(

topicPressure,

currentPressure

);

publishFloat(

topicRssi,

static_cast<float>(WiFi.RSSI())

);

Serial.println("--------------------");

}

void reportStatus() {

Serial.println();

Serial.println("Systeemstatus");

Serial.print("Sensor: ");

Serial.println(

sensorAvailable ? "beschikbaar" : "niet beschikbaar"

);

Serial.print("Meetwaarden: ");

Serial.println(

sensorValuesValid ? "geldig" : "ongeldig"

);

Serial.print("WiFi: ");

Serial.println(

WiFi.status() == WL_CONNECTED

? "verbonden"

: "niet verbonden"

);

Serial.print("MQTT: ");

Serial.println(

mqttClient.connected()

? "verbonden"

: "niet verbonden"

);

Serial.println("--------------------");

}

void setup() {

Serial.begin(115200);

delay(1000);

Serial.println();

Serial.println(

"Werkplaats-klimaatsensor"

);

Serial.print("Codeversie: ");

Serial.println(projectVersion);

// I2C en BME280

Wire.begin(sdaPin, sclPin);

if (bme.begin(bmeAddress)) {

sensorAvailable = true;

Serial.println("BME280 gevonden.");

} else {

Serial.println("BME280 niet gevonden.");

Serial.println(

"Het programma blijft actief."

);

}

// WiFi voorbereiden

WiFi.setHostname(hostName);

WiFi.mode(WIFI_STA);

WiFi.setAutoReconnect(true);

// Unieke MQTT-client-ID

mqttClientId =

"MQTT-" + WiFi.macAddress();

mqttClientId.replace(":", "");

Serial.print("MQTT-client-ID: ");

Serial.println(mqttClientId);

// MQTT voorbereiden

mqttClient.setServer(

mqttBroker,

mqttPort

);

mqttClient.setCallback(mqttCallback);

// Eerste taken direct toestaan

lastSensorRead =

millis() - sensorInterval;

lastPublish =

millis() - publishInterval;

lastStatusReport =

millis() - statusInterval;

lastMqttAttempt =

millis() - mqttRetryInterval;

// Eerste WiFi-poging

startWiFiConnection();

}

void loop() {

maintainWiFi();

maintainMqtt();

if (

millis() - lastSensorRead >=

sensorInterval

) {

lastSensorRead = millis();

readSensor();

}

if (

mqttClient.connected() &&

millis() - lastPublish >=

publishInterval

) {

lastPublish = millis();

publishSensorValues();

}

if (

millis() - lastStatusReport >=

statusInterval

) {

lastStatusReport = millis();

reportStatus();

}

}

Controleer de referentiecode kritisch

Ook deze referentiecode mag je niet blindelings uploaden.

Controleer:

gebruikt jouw BME280 0x76 of 0x77;

komen SDA en SCL overeen met jouw bedrading;

kloppen de topics;

is de hostnaam uniek;

klopt het brokeradres;

zijn de intervallen geschikt;

bestaan alle gebruikte bibliotheken;

komt de MQTT-client-ID correct tot stand;

wil je na de opdracht meting zowel meten als direct publiceren?

Een referentie is een uitgangspunt. Jouw hardware en ontwerpkeuzes blijven bepalend.

Deel 16 — Beoordeel je eindresultaat

Vul na de bouw de acceptatiecriteria opnieuw in.

OnderdeelGeslaagd?Bewijs of waarneming
Stabiele voeding
Geldige sensormetingen
Seriële uitvoer
WiFi-verbinding
Meten zonder WiFi
Automatisch WiFi-herstel
MQTT-verbinding
Publicatie van alle waarden
Online/offline-status
Meten zonder broker
Automatisch MQTT-herstel
Opdracht ontvangen
Opnieuw abonneren na herstel
Geen ongeldige publicaties
Duurtest
Documentatie compleet

Een vinkje zonder test is een aanname.

Noteer daarom bij ieder onderdeel welke waarneming aantoont dat het werkt.

Beantwoord de eindvragen

Wat was het laatste onderdeel dat niet direct werkte?

Hoe heb je het probleem afgebakend?

Welke hypothese bleek juist?

Welke test leverde de belangrijkste informatie?

Welke aanname bleek niet te kloppen?

Wat blijft werken zonder WiFi?

Wat blijft werken zonder MQTT?

Hoe weet de broker dat het apparaat offline is?

Waarom is de MQTT-client-ID uniek?

Wat gebeurt er na herstel van de broker?

Welke beperking heeft jouw huidige uitvoering?

Welke verbetering zou je als eerste toevoegen?

Wanneer je deze vragen in je eigen woorden kunt beantwoorden, heb je meer gebouwd dan een klimaatsensor.

Je begrijpt het systeem.

Wat je met dit eindproject hebt bereikt

Je Werkplaats-klimaatsensor kan:

informatie uit de fysieke wereld meten;

deze informatie lokaal zichtbaar maken;

zelfstandig met een netwerk verbinden;

meetwaarden via MQTT delen;

opdrachten ontvangen;

uitval van WiFi en MQTT opvangen;

verbindingen automatisch herstellen;

ongeldige waarden tegenhouden;

de eigen beschikbaarheid melden;

blijven functioneren wanneer niet ieder onderdeel beschikbaar is.

Maar de belangrijkste opbrengst zit niet in de temperatuurwaarde op het scherm.

Je hebt een volledig project leren bekijken als een verzameling samenwerkende blokken. Je weet dat een storing in MQTT niet automatisch een defecte sensor betekent. Je weet dat een brandende voedingsled geen dataverbinding bewijst. Je weet dat geldige code nog geen veilige schakeling garandeert.

En je hebt geleerd dat een fout niet het einde van een project is.

Een fout is het punt waarop de werkelijkheid afwijkt van je verwachting. Zodra je dat verschil kunt waarnemen, afbakenen en testen, heb je een nieuwe ingang om verder te komen.

Deze klimaatsensor is daarom geen eindpunt.

Zie hem als een basis die je kunt:

aanpassen;

uitbreiden;

verbeteren;

documenteren;

opnieuw gebruiken in een eigen project.

Dat is hoe een voorbeeld uitgroeit tot gereedschap.

En dat is hoe je van nabouwen naar zelfstandig maken gaat.