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.
| Onderdeel | Het project is geslaagd wanneer |
|---|---|
| Voeding | De ESP32 en BME280 blijven stabiel werken |
| Sensor | Alle drie de meetwaarden geldig worden uitgelezen |
| Lokale uitvoer | De waarden leesbaar in de seriële monitor verschijnen |
| WiFi | De ESP32 een IP-adres ontvangt |
| WiFi-uitval | De sensor tijdens de uitval blijft meten |
| WiFi-herstel | De verbinding zonder reset terugkomt |
| MQTT | De ESP32 met een unieke client-ID verbindt |
| Publiceren | Alle meetwaarden op de juiste topics verschijnen |
| Status | online en na onverwachte uitval offline zichtbaar zijn |
| MQTT-uitval | De sensor tijdens brokeruitval blijft meten |
| MQTT-herstel | Publiceren na herstel automatisch wordt hervat |
| Opdracht | Het bericht meting een directe meting veroorzaakt |
| Ongeldige waarde | Een ongeldige meting niet wordt gepubliceerd |
| Duurtest | Het project gedurende de gekozen testperiode stabiel blijft |
| Documentatie | Bedrading, 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:
| BME280 | ESP32 | Functie |
|---|---|---|
| VCC, VIN of 3V3 | 3V3 | Voeding |
| GND | GND | Gemeenschappelijke massa |
| SDA | GPIO 21 | I²C-gegevens |
| SCL | GPIO 22 | I²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.
| Test | Handeling | Verwachting | Uitkomst |
|---|---|---|---|
| Opstarten | ESP32 inschakelen | Sensor en programma starten | |
| Sensor | BME280 uitlezen | Drie geldige waarden | |
| Geen sensor | SDA tijdelijk los | Foutmelding, programma blijft actief | |
| WiFi normaal | Accesspoint beschikbaar | IP-adres ontvangen | |
| Geen WiFi | Accesspoint uitschakelen | Metingen gaan door | |
| WiFi-herstel | Accesspoint inschakelen | Automatische herverbinding | |
| MQTT normaal | Broker beschikbaar | Verbinding en publicaties | |
| Geen broker | Broker uitschakelen | Metingen gaan door | |
| MQTT-herstel | Broker inschakelen | Publicaties hervatten | |
| Status | ESP32 onverwacht uitschakelen | Status wordt offline | |
| Opdracht | meting publiceren | Directe meting | |
| Opdracht na herstel | Broker herstellen, opdracht sturen | Opdracht komt opnieuw aan | |
| Ongeldige waarde | Sensorcommunicatie verstoren | Geen ongeldige publicatie | |
| Duurtest | Langdurig laten werken | Geen 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.
| Tijd | Gebeurtenis | Waarneming | Herstel |
|---|---|---|---|
| 09:00 | Project gestart | Alles verbonden | Niet nodig |
| 11:15 | Accesspoint herstart | Lokale metingen gingen door | WiFi na 18 s terug |
| 15:30 | Broker herstart | MQTT tijdelijk offline | Na 10 s hersteld |
| 09:00 volgende dag | Einde test | Geen ongewenste reset | Geslaagd |
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
| Functie | ESP32-pin | Onderdeelpin |
|---|---|---|
| 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.
| Onderdeel | Geslaagd? | 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.