Koppeling met WiFi
Onze ESP32 kan inmiddels temperatuur, luchtvochtigheid en luchtdruk meten met de BME280. De meetwaarden verschijnen in de seriële monitor, maar blijven…
Onze ESP32 kan inmiddels temperatuur, luchtvochtigheid en luchtdruk meten met de BME280. De meetwaarden verschijnen in de seriële monitor, maar blijven voorlopig beperkt tot de computer waarop de ESP32 via USB is aangesloten.
In dit hoofdstuk verbinden we de ESP32 met ons WiFi-netwerk.
Daarmee zetten we een belangrijke stap. De ESP32 wordt niet langer alleen een losse microcontroller met een sensor, maar een apparaat dat meetgegevens via het netwerk beschikbaar kan maken.
Toch maken we de werking van de sensor niet afhankelijk van WiFi.
Als het accesspoint wordt uitgeschakeld, het netwerk tijdelijk buiten bereik is of het wachtwoord niet klopt, moet de ESP32 zijn lokale taken blijven uitvoeren. De sensor moet dus gewoon worden uitgelezen en het programma mag niet eindeloos blijven wachten op een verbinding.
We bouwen de WiFi-koppeling daarom volgens drie uitgangspunten:
- De ESP32 probeert verbinding te maken.
- Als dat niet lukt, gaat het programma verder.
- De ESP32 probeert het later automatisch opnieuw.
Deze werkwijze sluit aan op de failsafe-basis die ik voor mijn eigen ESP32-projecten gebruik. De uitgebreidere versie op mijn website herstelt ook MQTT-verbindingen en ondersteunt OTA-updates. In dit hoofdstuk richten we ons uitsluitend op WiFi.
WiFi is een aanvulling, geen voorwaarde
Veel eenvoudige WiFi-voorbeelden bevatten een constructie zoals:
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Deze code probeert verbinding te maken en blijft in de while-lus zolang dat niet lukt.
Dat is overzichtelijk voor een eerste demonstratie, maar minder geschikt voor een zelfstandig apparaat.
Als het netwerk niet beschikbaar is, komt het programma nooit voorbij deze lus. De ESP32 kan dan bijvoorbeeld ook niet verder met:
- sensoren uitlezen;
- uitgangen besturen;
- knoppen controleren;
- meetwaarden lokaal opslaan;
- foutmeldingen afhandelen;
- andere taken uitvoeren.
WiFi is in ons project een manier om informatie te delen. Het is niet de reden waarom de sensor bestaat.
Daarom willen we de volgende scheiding maken:
De sensor blijft meten. WiFi wordt gebruikt wanneer het beschikbaar is.
Wat betekent verbonden met WiFi?
Een WiFi-verbinding bestaat uit verschillende stappen.
De ESP32 moet:
- de WiFi-radio starten;
- het opgegeven netwerk vinden;
- zich met het juiste wachtwoord aanmelden;
- door het netwerk worden geaccepteerd;
- meestal via DHCP een IP-adres ontvangen.
Pas wanneer deze stappen zijn geslaagd, meldt:
WiFi.status()
de toestand:
WL_CONNECTED
De ESP32 is dan verbonden met het lokale netwerk.
Dat betekent nog niet automatisch dat internet beschikbaar is. De router kan bijvoorbeeld wel een lokaal IP-adres uitdelen terwijl de internetverbinding is uitgevallen.
Voor MQTT binnen je eigen netwerk kan een lokale WiFi-verbinding voldoende zijn. Voor een externe internetdienst is daarnaast een werkende internetverbinding nodig.
De ESP32 als client
De ESP32 kan zelf een WiFi-netwerk aanmaken, maar kan ook verbinding maken met een bestaand netwerk.
In deze gids gebruiken we de ESP32 als client, ook wel station genoemd.
Daarvoor gebruiken we:
WiFi.mode(WIFI_STA);
STA staat voor station mode.
De ESP32 gedraagt zich dan als een apparaat binnen het bestaande netwerk, vergelijkbaar met een telefoon, laptop of slimme thermostaat.
Het bestaande accesspoint of de router verzorgt het netwerk.
Een 2,4GHz-netwerk
De klassieke ESP32-WROOM-32 gebruikt WiFi in de 2,4GHz-band. Een netwerk dat uitsluitend op 5 GHz werkt, kan daarom niet door deze ESP32 worden gebruikt.
Veel routers bieden zowel 2,4 als 5 GHz aan. Beide frequenties kunnen dezelfde netwerknaam hebben. Dat werkt vaak zonder problemen, zolang de router ook daadwerkelijk een 2,4GHz-netwerk aanbiedt.
Lukt de verbinding niet, controleer dan:
- of 2,4GHz-WiFi is ingeschakeld;
- of het netwerk binnen bereik is;
- of nieuwe apparaten verbinding mogen maken;
- of de netwerknaam correct is overgenomen;
- of het wachtwoord correct is;
- of het netwerk geen extra aanmeldpagina gebruikt.
Een openbaar netwerk waarop je na het verbinden eerst een webpagina moet openen, is niet zonder aanvullende code geschikt voor deze eenvoudige aansluiting.
Netwerknaam en wachtwoord
Om verbinding te maken, heeft de ESP32 twee gegevens nodig:
- de SSID;
- het WiFi-wachtwoord.
SSID staat voor Service Set Identifier. In de praktijk is dit de naam van het WiFi-netwerk.
We zouden deze gegevens rechtstreeks in het hoofdprogramma kunnen plaatsen:
const char* ssid = "MijnWiFi";
const char* password = "MijnWachtwoord";
Dat werkt, maar maakt het gemakkelijk om een wachtwoord per ongeluk mee te kopiëren wanneer je de code met iemand deelt.
Daarom bewaren we de WiFi-gegevens in een afzonderlijk tabblad.
Een apart bestand voor geheime gegevens
Maak in de Arduino IDE een nieuw tabblad aan. Afhankelijk van de gebruikte versie kan dat via de drie puntjes rechtsboven of via het menu voor nieuwe bestanden.
Geef het bestand exact deze naam:
Arduino_Secrets.h
Plaats daarin:
#define SECRET_SSID "NaamVanJeWiFi"
#define SECRET_PASS "JeWiFiWachtwoord"
Vervang de voorbeeldtekst door je eigen netwerknaam en wachtwoord.
Bijvoorbeeld:
#define SECRET_SSID "WerkplaatsWiFi"
#define SECRET_PASS "voorbeeld-wachtwoord"
Gebruik daarbij de aanhalingstekens.
In het hoofdprogramma voegen we het bestand toe met:
#include "Arduino_Secrets.h"
Daarna kunnen we de waarden gebruiken zonder het echte wachtwoord in het hoofdprogramma te plaatsen:
const char* ssid = SECRET_SSID;
const char* password = SECRET_PASS;
Het afzonderlijke bestand is vooral een hulpmiddel om de gegevens niet per ongeluk te publiceren. Het wachtwoord is nog steeds nodig in het programma dat naar de ESP32 wordt geüpload.
Wanneer je de code in Git of op een website plaatst, deel je Arduino_Secrets.h daarom niet mee.
Een naam voor de ESP32
Naast de WiFi-gegevens geven we de ESP32 een hostnaam:
const char* hostName = "werkplaats-sensor";
Deze naam kan door de DHCP-client worden gebruikt om het apparaat binnen het netwerk herkenbaar te maken. In de apparatenlijst van je router zie je dan mogelijk werkplaats-sensor in plaats van alleen een onbekend MAC- of IP-adres.
Gebruik voor de hostnaam bij voorkeur:
- kleine letters;
- cijfers;
- koppeltekens;
- geen spaties;
- geen accenten of bijzondere tekens.
Wanneer je meerdere sensoren bouwt, geef je ieder apparaat een unieke naam, bijvoorbeeld:
werkplaats-sensor
woonkamer-sensor
schuur-sensor
De hostnaam moet worden ingesteld voordat WiFi wordt gestart.
Een blokkerende en niet-blokkerende aanpak
Bij een blokkerende aanpak wacht de ESP32 in een lus totdat WiFi beschikbaar is.
Bij onze niet-blokkerende aanpak starten we een verbindingspoging en keren daarna direct terug naar het hoofdprogramma.
In loop() controleren we regelmatig:
- is de ESP32 inmiddels verbonden;
- duurt de poging te lang;
- is een bestaande verbinding weggevallen;
- is het tijd voor een nieuwe poging?
Tussen deze controles door blijft de ESP32 de sensor uitlezen.
De WiFi-verbinding krijgt dus wel aandacht, maar neemt het programma niet volledig over.
Tijd bijhouden met millis()
Om zonder lange delay() te werken, gebruiken we:
millis()
Deze functie geeft aan hoeveel milliseconden zijn verstreken sinds de ESP32 is gestart.
We kunnen bijvoorbeeld onthouden wanneer een verbindingspoging begon:
wifiAttemptStarted = millis();
Later vergelijken we de huidige tijd met dat moment:
if (millis() - wifiAttemptStarted >= wifiConnectTimeout) {
// De poging duurt te lang
}
De ESP32 hoeft tijdens die periode niet stil te staan. Hij kan ondertussen andere code blijven uitvoeren.
We gebruiken dezelfde methode om:
- iedere twee seconden de sensor uit te lezen;
- een verbindingspoging na tien seconden te beëindigen;
- na dertig seconden een nieuwe poging te starten.
Onze gekozen tijden
In dit voorbeeld gebruiken we:
const unsigned long sensorInterval = 2000;
const unsigned long wifiConnectTimeout = 10000;
const unsigned long wifiRetryInterval = 30000;
Dat betekent:
- iedere 2 seconden een sensormeting;
- maximaal 10 seconden voor één WiFi-poging;
- na een mislukte poging 30 seconden wachten tot de volgende poging.
Deze waarden zijn geen universele regels. Ze maken het gedrag voor onze oefening duidelijk en voorkomen dat de ESP32 voortdurend nieuwe verbindingspogingen start.
Een sensor die jarenlang op batterijen moet werken, vraagt bijvoorbeeld om een andere strategie dan een ESP32 die permanent via USB wordt gevoed.
Het complete programma
Onderstaande code bouwt voort op het BME280-programma uit het vorige hoofdstuk.
De sensor blijft iedere twee seconden meten, ongeacht de toestand van WiFi.
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <WiFi.h>
#include "Arduino_Secrets.h"
// WiFi-instellingen
const char* ssid = SECRET_SSID;
const char* password = SECRET_PASS;
const char* hostName = "werkplaats-sensor";
// BME280-instellingen
Adafruit_BME280 bme;
const int sdaPin = 21;
const int sclPin = 22;
const uint8_t bmeAddress = 0x76;
// Tijdsinstellingen
const unsigned long sensorInterval = 2000;
const unsigned long wifiConnectTimeout = 10000;
const unsigned long wifiRetryInterval = 30000;
// Status van de sensor
bool sensorAvailable = false;
// Status en tijdregistratie voor WiFi
bool wifiWasConnected = false;
bool wifiAttemptActive = false;
unsigned long lastSensorRead = 0;
unsigned long wifiAttemptStarted = 0;
unsigned long lastWifiAttempt = 0;
void startWiFiConnection() {
Serial.print("Verbinden met WiFi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
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("Signaalsterkte: ");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
}
return;
}
if (wifiWasConnected) {
wifiWasConnected = false;
wifiAttemptActive = false;
Serial.println();
Serial.println("*** WiFi-verbinding verbroken ***");
Serial.println("De sensor blijft lokaal werken.");
startWiFiConnection();
return;
}
if (wifiAttemptActive) {
if (millis() - wifiAttemptStarted >= wifiConnectTimeout) {
wifiAttemptActive = false;
WiFi.disconnect(false, false);
Serial.println("WiFi-verbinding niet gelukt.");
Serial.println("Het programma gaat verder.");
Serial.println("Over 30 seconden proberen we het opnieuw.");
}
return;
}
if (millis() - lastWifiAttempt >= wifiRetryInterval) {
startWiFiConnection();
}
}
void readSensor() {
if (!sensorAvailable) {
return;
}
float temperatuur = bme.readTemperature();
float luchtvochtigheid = bme.readHumidity();
float luchtdruk = bme.readPressure() / 100.0F;
if (isnan(temperatuur) ||
isnan(luchtvochtigheid) ||
isnan(luchtdruk)) {
Serial.println("Ongeldige meetwaarde ontvangen.");
return;
}
Serial.println();
Serial.println("BME280-meting");
Serial.print("Temperatuur: ");
Serial.print(temperatuur, 1);
Serial.println(" °C");
Serial.print("Luchtvochtigheid: ");
Serial.print(luchtvochtigheid, 1);
Serial.println(" %");
Serial.print("Luchtdruk: ");
Serial.print(luchtdruk, 1);
Serial.println(" hPa");
if (WiFi.status() == WL_CONNECTED) {
Serial.print("WiFi: verbonden, IP ");
Serial.println(WiFi.localIP());
} else {
Serial.println("WiFi: niet verbonden");
}
Serial.println("--------------------");
}
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println("Werkplaats-sensor starten...");
// BME280 starten
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.");
}
// Hostnaam instellen voordat WiFi wordt gestart
WiFi.setHostname(hostName);
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
// Eerste WiFi-poging starten
startWiFiConnection();
// Zorg dat de eerste sensormeting direct wordt uitgevoerd
lastSensorRead = millis() - sensorInterval;
}
void loop() {
maintainWiFi();
if (millis() - lastSensorRead >= sensorInterval) {
lastSensorRead = millis();
readSensor();
}
}
Wat gebeurt er in setup()?
In setup() starten we eerst de seriële monitor:
Serial.begin(115200);
Daarna starten we de I²C-verbinding en controleren we of de BME280 aanwezig is:
Wire.begin(sdaPin, sclPin);
if (bme.begin(bmeAddress)) {
sensorAvailable = true;
}
Het verschil met het vorige hoofdstuk is dat het programma niet meer in een oneindige while-lus stopt wanneer de sensor niet wordt gevonden.
In plaats daarvan bewaren we de toestand:
sensorAvailable = false;
Daardoor blijft de rest van het programma actief. De WiFi-code kan nog steeds worden getest en de seriële monitor blijft foutmeldingen tonen.
Voor een echt meetproject hebben we natuurlijk weinig aan een ontbrekende sensor, maar tijdens het foutzoeken is het nuttig dat één probleem niet automatisch alle andere onderdelen onzichtbaar maakt.
De volgorde bij het starten van WiFi
We gebruiken:
WiFi.setHostname(hostName);
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
De hostnaam wordt als eerste ingesteld. Dat is belangrijk, omdat de officiële ESP32 WiFi-implementatie voorschrijft dat setHostname() wordt aangeroepen voordat WiFi wordt gestart.
Daarna zetten we de ESP32 in stationmodus:
WiFi.mode(WIFI_STA);
Met:
WiFi.setAutoReconnect(true);
vragen we de WiFi-bibliotheek om ook zelf te proberen een verloren verbinding te herstellen.
Daarnaast houden we in onze eigen code de verbinding in de gaten. Daarmee blijft het gedrag zichtbaar en kunnen we zelf bepalen hoe vaak een nieuwe poging wordt gestart.
Een verbinding starten
De functie:
startWiFiConnection()
bevat alles wat nodig is om een nieuwe poging te beginnen.
Eerst tonen we met welk netwerk de ESP32 probeert te verbinden:
Serial.print("Verbinden met WiFi: ");
Serial.println(ssid);
Daarna starten we de verbinding:
WiFi.begin(ssid, password);
Deze opdracht hoeft niet te wachten totdat de volledige verbinding is opgebouwd. De WiFi-hardware en bibliotheek gaan op de achtergrond verder.
Wij bewaren vervolgens het tijdstip waarop de poging begon:
wifiAttemptStarted = millis();
lastWifiAttempt = millis();
Ook onthouden we dat er op dit moment een actieve poging loopt:
wifiAttemptActive = true;
Daarna eindigt de functie en kan de ESP32 andere taken uitvoeren.
De verbinding onderhouden
In iedere uitvoering van loop() roepen we aan:
maintainWiFi();
Deze functie kijkt naar de huidige toestand en kiest welke actie nodig is.
Er zijn vier belangrijke situaties:
- WiFi is verbonden.
- Een bestaande verbinding is weggevallen.
- Een verbindingspoging is bezig.
- Er is geen verbinding of actieve poging.
Door deze situaties afzonderlijk te behandelen, voorkomen we dat steeds opnieuw WiFi.begin() wordt aangeroepen.
Situatie 1 — WiFi is verbonden
De eerste controle is:
if (WiFi.status() == WL_CONNECTED)
Als de ESP32 verbonden is en dit de eerste keer is dat we die nieuwe toestand zien, tonen we:
- dat de verbinding is gelukt;
- het ontvangen IP-adres;
- de signaalsterkte.
Dat doen we alleen wanneer:
wifiWasConnected
nog false is.
Daarna zetten we:
wifiWasConnected = true;
Zolang de verbinding actief blijft, wordt dezelfde melding niet bij iedere uitvoering van loop() herhaald.
Het IP-adres
Na een succesvolle verbinding tonen we:
WiFi.localIP()
Dat levert bijvoorbeeld op:
192.168.1.74
Dit IP-adres is het netwerkadres dat de ESP32 van de router heeft ontvangen.
Het adres kan na een herstart veranderen wanneer de router DHCP gebruikt. Later kunnen we in de router eventueel een vaste DHCP-reservering maken.
Voor MQTT is een vast IP-adres voor de ESP32 meestal niet noodzakelijk. De ESP32 maakt straks zelf verbinding met de broker. Een herkenbare hostnaam blijft wel handig bij beheer en foutzoeken.
De signaalsterkte
Met:
WiFi.RSSI()
vragen we de ontvangen signaalsterkte op.
RSSI staat voor Received Signal Strength Indicator. De waarde wordt meestal in negatieve dBm weergegeven.
Je kunt bijvoorbeeld zien:
-45 dBm
of:
-82 dBm
Een waarde dichter bij nul betekent in het algemeen een sterker signaal.
Als praktische indicatie:
| RSSI | Algemene indruk |
|---|---|
| ongeveer -30 tot -50 dBm | sterk |
| ongeveer -50 tot -67 dBm | doorgaans goed |
| ongeveer -67 tot -75 dBm | bruikbaar, maar minder ruim |
| lager dan ongeveer -75 dBm | zwak of mogelijk instabiel |
Deze grenzen zijn geen harde garanties. Antennes, muren, storing en tijdelijke veranderingen in de omgeving kunnen invloed hebben.
De RSSI-waarde helpt ons vooral wanneer een apparaat soms wel en soms niet verbinding maakt.
Situatie 2 — De verbinding valt weg
Stel dat de ESP32 verbonden was en het accesspoint daarna wordt uitgeschakeld.
Dan wordt:
WiFi.status() == WL_CONNECTED
onwaar, terwijl:
wifiWasConnected
nog waar is.
De code weet daardoor dat er een bestaande verbinding is weggevallen.
We tonen:
*** WiFi-verbinding verbroken ***
De sensor blijft lokaal werken.
Daarna starten we direct één nieuwe verbindingspoging.
De sensorlus blijft ondertussen doorgaan.
Dit is belangrijk voor apparaten die een lokale taak hebben. Een thermostaat, niveausensor of ventilatieregeling mag niet volledig stoppen alleen omdat het netwerk tijdelijk niet beschikbaar is.
Situatie 3 — Een poging duurt te lang
Terwijl een verbindingspoging actief is, controleren we hoelang die al duurt:
if (millis() - wifiAttemptStarted >= wifiConnectTimeout)
Onze time-out is ingesteld op tien seconden:
const unsigned long wifiConnectTimeout = 10000;
Als binnen die tijd geen verbinding tot stand komt, beëindigen we de poging:
WiFi.disconnect(false, false);
De twee waarden false betekenen hier dat we:
- de WiFi-radio niet volledig uitschakelen;
- de opgeslagen netwerkinstellingen niet wissen.
Daarna melden we:
WiFi-verbinding niet gelukt.
Het programma gaat verder.
Over 30 seconden proberen we het opnieuw.
Er volgt geen oneindige while-lus en ook geen herstart van de ESP32.
Situatie 4 — Later opnieuw proberen
Als er geen verbinding en geen actieve poging is, controleert de code hoeveel tijd sinds de vorige poging is verstreken:
if (millis() - lastWifiAttempt >= wifiRetryInterval)
Na dertig seconden wordt opnieuw uitgevoerd:
startWiFiConnection();
Dit blijft zich herhalen:
Proberen → maximaal tien seconden bewaken → doorgaan → dertig seconden wachten → opnieuw proberen
Zodra het netwerk weer beschikbaar is, kan de eerstvolgende poging slagen.
De sensor blijft onafhankelijk werken
In loop() staat naast de WiFi-controle ook:
if (millis() - lastSensorRead >= sensorInterval) {
lastSensorRead = millis();
readSensor();
}
Iedere twee seconden wordt de sensor uitgelezen.
Deze code staat niet binnen een voorwaarde die een WiFi-verbinding vereist. Daardoor blijft hij actief wanneer:
- het netwerk bij het opstarten ontbreekt;
- het wachtwoord onjuist is;
- het accesspoint opnieuw wordt gestart;
- de ESP32 buiten bereik komt;
- de router tijdelijk uitvalt.
Binnen readSensor() tonen we alleen aanvullend wat de actuele WiFi-toestand is:
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi: verbonden");
} else {
Serial.println("WiFi: niet verbonden");
}
De meting zelf wordt in beide gevallen uitgevoerd.
Waarom we geen delay(30000) gebruiken
We zouden na een mislukte poging kunnen schrijven:
delay(30000);
De ESP32 zou dan dertig seconden wachten voordat hij het opnieuw probeert.
Maar tijdens die delay() wordt onze gewone programmacode niet uitgevoerd. De sensor wordt dan ook dertig seconden niet uitgelezen.
Met millis() houden we dezelfde wachttijd bij zonder het programma stil te zetten.
Dit verschil wordt steeds belangrijker naarmate een project meer taken krijgt. Straks moet de ESP32 bijvoorbeeld tegelijk:
- een sensor uitlezen;
- WiFi bewaken;
- MQTT onderhouden;
- berichten versturen;
- mogelijk opdrachten ontvangen.
Een programma met meerdere lange wachttijden reageert dan traag en wordt moeilijker betrouwbaar te maken.
Waarom unsigned long wordt gebruikt
De waarde van millis() bewaren we in variabelen van het type:
unsigned long
Bijvoorbeeld:
unsigned long lastSensorRead = 0;
unsigned betekent dat de waarde niet negatief kan zijn. long biedt ruimte voor een groot geheel getal.
Na een lange tijd bereikt millis() uiteindelijk zijn maximale waarde en begint de teller opnieuw bij nul. De vorm:
millis() - vorigTijdstip >= interval
blijft daar bij het juiste gegevenstype goed mee omgaan.
Vergelijk daarom liever het verstreken verschil dan een toekomstig absoluut tijdstip.
Geen verbinding is geen reden voor een reset
Sommige projecten starten de ESP32 opnieuw wanneer WiFi niet beschikbaar is.
Dat kan in specifieke situaties een bruikbare laatste herstelactie zijn, maar het moet niet de standaardoplossing zijn.
Als het accesspoint een uur buiten gebruik is, zal een steeds herstartende ESP32:
- zijn lokale taken onderbreken;
- voortdurend opnieuw opstarten;
- mogelijk meetgegevens verliezen;
- de werkelijke oorzaak niet oplossen;
- foutzoeken moeilijker maken.
Onze code behandelt ontbrekende WiFi daarom als een verwachte toestand:
Het netwerk is nu niet beschikbaar. De lokale taak gaat door en we proberen het later opnieuw.
Dat is een veel rustigere basis voor een betrouwbaar apparaat.
Het programma uploaden
Controleer vóór het uploaden:
- Arduino_Secrets.h bestaat als afzonderlijk tabblad;
- de netwerknaam correct is geschreven;
- het wachtwoord correct is geschreven;
- de BME280 nog steeds juist is aangesloten;
- het juiste ESP32-bord is geselecteerd;
- de juiste USB-poort is geselecteerd.
Compileer en upload daarna het programma.
Open de seriële monitor op:
115200 baud
Bij een succesvolle start kun je iets zien als:
Werkplaats-sensor starten...
BME280 gevonden.
Verbinden met WiFi: WerkplaatsWiFi
BME280-meting
Temperatuur: 21.8 °C
Luchtvochtigheid: 47.9 %
Luchtdruk: 1009.4 hPa
WiFi: niet verbonden
--------------------
WiFi verbonden.
IP-adres: 192.168.1.74
Signaalsterkte: -51 dBm
De eerste sensormeting kan verschijnen voordat WiFi volledig is verbonden. Dat is precies het gedrag dat we willen.
Test 1 — Normale verbinding
Laat het WiFi-netwerk ingeschakeld en start de ESP32.
Controleer:
- wordt de BME280 gevonden;
- verschijnen de meetwaarden;
- wordt een WiFi-poging gestart;
- verschijnt uiteindelijk WiFi verbonden;
- krijgt de ESP32 een geldig IP-adres;
- blijft de sensor daarna meten?
Schrijf op wat je verwachtte en wat er werkelijk gebeurde.
Test 2 — Starten zonder WiFi
Schakel voor deze test tijdelijk het betreffende accesspoint of 2,4GHz-netwerk uit. Doe dit alleen wanneer je daarmee geen belangrijke apparaten of andere gebruikers verstoort.
Start daarna de ESP32 opnieuw.
Je verwacht:
- de sensor wordt gestart;
- de meetwaarden blijven verschijnen;
- de WiFi-verbinding lukt niet;
- na tien seconden verschijnt een foutmelding;
- het programma blijft actief;
- na dertig seconden begint een nieuwe poging.
De belangrijkste waarneming is dat de sensormetingen tijdens dit hele proces doorgaan.
Schakel het WiFi-netwerk daarna weer in. De ESP32 moet bij een volgende poging alsnog verbinding maken, zonder dat je op reset hoeft te drukken.
Test 3 — WiFi laten wegvallen
Start de ESP32 met werkende WiFi en wacht totdat een IP-adres verschijnt.
Schakel daarna tijdelijk het accesspoint uit.
Je verwacht:
- de ESP32 merkt dat de verbinding is verbroken;
- er verschijnt een melding in de seriële monitor;
- de sensor blijft meten;
- de ESP32 probeert opnieuw te verbinden;
- de pogingen stoppen steeds na de time-out;
- na herstel van het accesspoint komt de verbinding automatisch terug.
Dit is een belangrijke test. We controleren niet alleen of een project onder ideale omstandigheden kan starten, maar ook of het van een storing kan herstellen.
Test 4 — Verkeerd wachtwoord
Je kunt in Arduino_Secrets.h tijdelijk een verkeerd wachtwoord invullen en de code opnieuw uploaden.
De ESP32 mag dan niet verbinden, maar moet wel blijven meten en periodiek opnieuw proberen.
Herstel daarna het juiste wachtwoord en upload de code opnieuw.
Let op: omdat de WiFi-gegevens onderdeel zijn van de geüploade code, ontdekt de ESP32 niet vanzelf dat jij het wachtwoord alleen in de Arduino IDE hebt gecorrigeerd. De aangepaste code moet opnieuw worden geüpload.
Wanneer er geen IP-adres verschijnt
Als de ESP32 niet verbindt, controleer dan systematisch:
Wordt de juiste netwerknaam gebruikt?
De SSID is hoofdlettergevoelig. Ook spaties maken deel uit van de naam.
Deze namen zijn verschillend:
WerkplaatsWiFi
werkplaatswifi
Werkplaats WiFi
Klopt het wachtwoord?
Controleer hoofdletters, cijfers en bijzondere tekens.
Kopieer bij twijfel het wachtwoord uit een betrouwbare bron. Let op onbedoelde spaties vóór of achter de tekst.
Is 2,4 GHz beschikbaar?
Een klassieke ESP32-WROOM-32 kan geen netwerk gebruiken dat uitsluitend op 5 GHz werkt.
Is het signaal sterk genoeg?
Plaats de ESP32 tijdelijk dichter bij het accesspoint. Als de verbinding daar wel lukt, kan bereik of storing een rol spelen.
Is het netwerk verborgen?
Een verborgen SSID kan aanvullende aandacht vragen. Maak het netwerk tijdens het eerste testen indien mogelijk zichtbaar.
Laat de router nieuwe apparaten toe?
MAC-filters, clientlimieten, bedrijfsnetwerken en gastnetwerken kunnen een nieuwe verbinding blokkeren.
Is de voeding stabiel?
WiFi veroorzaakt pieken in het stroomverbruik. Een slechte USB-kabel of instabiele voeding kan ervoor zorgen dat de ESP32 tijdens het verbinden opnieuw opstart.
Kijk in de seriële monitor of de opstarttekst steeds opnieuw verschijnt. Dat wijst eerder op een reset dan op alleen een mislukkende aanmelding.
De status nauwkeuriger onderzoeken
Voor onze hoofdcode is de controle op WL_CONNECTED voldoende om te bepalen of we netwerkfuncties mogen gebruiken.
Tijdens het foutzoeken kun je ook de numerieke status tonen:
Serial.print("WiFi-status: ");
Serial.println(WiFi.status());
Zo kun je zien of de toestand verandert tijdens een verbindingspoging.
Een statusnummer zonder context is echter nog geen volledige diagnose. Combineer de status met andere waarnemingen:
- verschijnt het netwerk op een telefoon of laptop;
- klopt het wachtwoord;
- ontvangt de ESP32 ooit een IP-adres;
- is er voldoende signaal;
- start de ESP32 onverwacht opnieuw op?
Een enkele foutcode is een aanwijzing, geen complete verklaring.
Verbonden blijven is onderdeel van het ontwerp
Een project is niet betrouwbaar omdat het één keer verbinding kan maken.
Een betrouwbaar netwerkapparaat moet ook rekening houden met:
- een herstartend accesspoint;
- tijdelijk slecht bereik;
- een router die een nieuw IP-adres uitdeelt;
- een verbroken verbinding;
- een netwerk dat bij het opstarten nog niet gereed is;
- een server die later beschikbaar komt dan de ESP32.
Daarom staat de WiFi-controle niet alleen in setup().
setup() wordt maar één keer uitgevoerd. Netwerkverbindingen kunnen tijdens de volledige levensduur van het apparaat veranderen. De controle hoort daarom ook bij het terugkerende werk in loop().
Een kleine oefening
Pas de code zo aan dat de actuele RSSI iedere dertig seconden wordt getoond wanneer WiFi verbonden is.
Voeg bovenaan toe:
const unsigned long rssiInterval = 30000;
unsigned long lastRssiCheck = 0;
Plaats daarna in loop():
if (WiFi.status() == WL_CONNECTED &&
millis() - lastRssiCheck >= rssiInterval) {
lastRssiCheck = millis();
Serial.print("WiFi RSSI: ");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
}
Voorspel wat er gebeurt wanneer WiFi ontbreekt.
De code binnen deze voorwaarde wordt dan niet uitgevoerd. De sensor en de periodieke herstelpogingen blijven wel actief.
Hiermee oefenen we een belangrijk patroon:
Voer een netwerktaak alleen uit wanneer het netwerk beschikbaar is.
Voorbereiding op MQTT
In dit hoofdstuk gebruiken we WiFi nog niet om de meetwaarden daadwerkelijk te versturen.
We hebben wel de voorwaarden daarvoor opgebouwd:
- de BME280 levert bruikbare variabelen;
- de ESP32 kan verbinding maken met het netwerk;
- de verbinding wordt voortdurend bewaakt;
- een storing stopt de sensormeting niet;
- de verbinding wordt na uitval opnieuw opgebouwd.
In het volgende hoofdstuk voegen we MQTT toe.
Ook daar gebruiken we dezelfde gedachte:
MQTT mag alleen verbinden wanneer WiFi beschikbaar is, maar een ontbrekende MQTT-broker mag de rest van het programma niet blokkeren.
We krijgen dan twee afzonderlijke verbindingen:
ESP32 → WiFi-netwerk → MQTT-broker
Als WiFi ontbreekt, kan MQTT niet werken. Als WiFi wel werkt maar de broker ontbreekt, moet de ESP32 onderscheid kunnen maken tussen die twee situaties.
Wat je uit dit hoofdstuk kunt meenemen
We hebben de ESP32 met WiFi verbonden zonder de lokale werking afhankelijk te maken van het netwerk.
Daarbij heb je ontdekt dat:
- de ESP32-WROOM-32 een 2,4GHz-WiFi-netwerk gebruikt;
- de ESP32 in WIFI_STA-modus als client met een bestaand netwerk verbindt;
- SSID de naam van het netwerk is;
- geheime gegevens in een afzonderlijk Arduino_Secrets.h-bestand kunnen worden geplaatst;
- een herkenbare hostnaam het beheer gemakkelijker maakt;
- WiFi.begin() een verbindingspoging start;
- WiFi.status() de actuele verbindingstoestand laat zien;
- WL_CONNECTED betekent dat de ESP32 met het lokale netwerk is verbonden;
- WiFi.localIP() het ontvangen IP-adres toont;
- WiFi.RSSI() een indicatie van de signaalsterkte geeft;
- een oneindige wachtlus de rest van het programma kan blokkeren;
- millis() ons tijd laat bewaken zonder lange delay();
- een verbindingspoging na een ingestelde tijd kan worden beëindigd;
- de ESP32 daarna periodiek opnieuw kan proberen;
- een weggevallen verbinding automatisch kan worden hersteld;
- de sensor blijft werken wanneer WiFi ontbreekt;
- verbinding met WiFi niet automatisch betekent dat ook internet of een server bereikbaar is;
- netwerkstoringen normale toestanden zijn waarop we ons programma kunnen voorbereiden.
De belangrijkste stap in dit hoofdstuk is niet alleen dat de ESP32 nu met WiFi kan verbinden.
Het is dat de ESP32 ook weet hoe hij zich moet gedragen wanneer dat niet lukt.
Dat onderscheid maakt van een werkende demonstratie uiteindelijk een betrouwbaar project.
Technische naslag
Dit hoofdstuk is gebaseerd op het failsafe-principe uit mijn eigen ESP32-code voor WiFi, MQTT en OTA, teruggebracht tot WiFi en uitgebreid met niet-blokkerende herstelpogingen. De functies en actuele voorwaarden van WiFi.begin(), WiFi.status(), setHostname() en automatische herverbinding staan in de officiële Arduino-ESP32 WiFi-documentatie van Espressif.