Tutorial · Gevorderd · 1,5 uur

Bestuur een robot vanuit een browser met een ESP32-webserver

Maak van een ESP32 een wifi-toegangspunt dat zijn eigen bedieningspagina serveert, zodat elke telefoon je robot vanuit een browser kan besturen zonder app.

Inleiding

Een HC-05 vraagt om een seriële terminal-app en een koppelritueel. Een ESP32 kan iets beters: zelf zijn eigen netwerk zijn en zijn eigen bedieningspagina serveren. Je verbindt je telefoon met de wifi van de robot, opent een browser, en bestuurt. Niets te installeren, en het werkt vanaf elk apparaat met een browser.

De ESP32 heeft wifi en bluetooth op de chip, twee cores op 240 MHz, en genoeg RAM om een webpagina in het flash te houden en te serveren. Dat is de hele truc.

Verticaal stroomschema met vijf stappen. Een browser op een telefoon stuurt een HTTP GET-verzoek voor de route vooruit. De ESP32, die als soft-accesspoint fungeert of met een netwerk is verbonden, ontvangt het. De HTTP-server routeert het verzoek naar een handler. De handler stuurt de richtingspinnen en de PWM van de motordriver aan. De wielen draaien en de robot beweegt. Een notitie waarschuwt dat een watchdog de motoren stopt als er geen verzoeken meer binnenkomen.
Elke laag tussen een duim en een wiel. De watchdog is het belangrijkst: zonder hem blijft de robot rijden als de verbinding wegvalt. SVG downloaden

Toegangspunt of bestaand netwerk?

Twee modi, en de keuze is belangrijker dan hij lijkt.

Access point (AP) — de ESP32 maakt zijn eigen wifinetwerk en de telefoon meldt zich daarop aan.

  • Werkt overal, zonder router. De robot is zelfstandig.
  • De telefoon verliest tijdens het besturen zijn internetverbinding, en sommige telefoons schakelen stilletjes terug naar mobiele data en laten je dan vallen.
  • Het adres is altijd hetzelfde, meestal 192.168.4.1.

Station (STA) — de ESP32 meldt zich aan bij je bestaande wifinetwerk.

  • De telefoon houdt zijn internetverbinding.
  • Het IP-adres van de robot wordt door de router toegewezen en kan per herstart wisselen.
  • Onbruikbaar buiten het bereik van dat ene netwerk, wat demonstraties uitsluit.

Voor een robot die je meedraagt, is AP-modus vrijwel altijd de juiste keuze:

#include <WiFi.h>
#include <WebServer.h>

WebServer server(80);

void setup() {
  WiFi.softAP("RobotCar", "drive1234");   // password must be 8+ characters
  Serial.println(WiFi.softAPIP());        // 192.168.4.1
  server.on("/", handleRoot);
  server.on("/cmd", handleCommand);
  server.begin();
}

void loop() {
  server.handleClient();
  applyDeadman();                          // see below — this is not optional
}

De bedieningspagina serveren

De pagina staat als raw string in het flash. Houd hem klein — elke byte wordt vanaf een microcontroller geserveerd — en maak de bediening geschikt voor aanraking, want het hele punt is besturen vanaf een telefoon.

const char PAGE[] PROGMEM = R"(
<!doctype html><meta name=viewport content="width=device-width,initial-scale=1">
<style>
  body{font-family:system-ui;margin:0;display:grid;place-items:center;height:100vh}
  button{width:90px;height:90px;font-size:24px;margin:4px;touch-action:manipulation}
</style>
<div>
  <div style="text-align:center"><button data-c="F">▲</button></div>
  <div><button data-c="L">◀</button><button data-c="S">■</button><button data-c="R">▶</button></div>
  <div style="text-align:center"><button data-c="B">▼</button></div>
</div>
<script>
let held = null;
const send = c => fetch('/cmd?c=' + c);
for (const b of document.querySelectorAll('button')) {
  // Repeat while held, so releasing and losing signal look the same to the robot.
  b.onpointerdown = e => { held = b.dataset.c; send(held);
                           b.setPointerCapture(e.pointerId); };
  b.onpointerup = b.onpointercancel = () => { held = null; send('S'); };
}
setInterval(() => held && send(held), 100);
</script>
)";

void handleRoot() { server.send_P(200, "text/html", PAGE); }

touch-action: manipulation op de knoppen doet ertoe: zonder die wachten mobiele browsers ongeveer 300 ms om te zien of je een dubbeltik bedoelde, waardoor de robot kapot aanvoelt.

De doodsmanschakel, opnieuw

Alles wat de Bluetooth-tutorial zegt over het verliezen van de verbinding geldt hier, en in nog sterkere mate — het bereik van wifi eindigt plotseling in plaats van geleidelijk.

unsigned long lastCommandAt = 0;

void handleCommand() {
  char c = server.arg("c")[0];
  drive(c);
  lastCommandAt = millis();
  server.send(200, "text/plain", "ok");
}

void applyDeadman() {
  if (millis() - lastCommandAt > 400) stop();
}

De browser stuurt de ingedrukte toets elke 100 ms, dus een timeout van 400 ms verdraagt drie weggevallen verzoeken voor hij stopt. Die verhouding is wat je afstelt: te krap en de robot schokt op een zwakke verbinding, te ruim en hij rijdt door nadat je de verbinding kwijt bent.

Wanneer over te stappen op WebSockets

De fetch-aanpak hierboven opent voor elk commando een nieuwe HTTP-verbinding. Het is eenvoudig, het is makkelijk te debuggen in het netwerktabblad van de browser, en bij 10 commando’s per seconde is het prima.

Het houdt op prima te zijn zodra je telemetrie terug wilt. Sensorwaarden pollen met nóg meer HTTP-verzoeken verdubbelt het verkeer en voegt latency in beide richtingen toe. Een WebSocket houdt één verbinding open en laat beide kanten op elk moment spreken:

#include <WebSocketsServer.h>
WebSocketsServer ws(81);

void onWsEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t len) {
  if (type == WStype_TEXT) { drive(payload[0]); lastCommandAt = millis(); }
  if (type == WStype_DISCONNECTED) stop();   // the link closing IS a stop command
}

Die laatste regel is de echte upgrade. Met HTTP kan de robot een verbroken verbinding alleen uit stilte afleiden; een WebSocket vertelt je het moment dat hij sluit, dus de robot stopt meteen in plaats van na een timeout.

Begin met HTTP. Stap over op WebSockets wanneer je de robot terug wilt laten praten.

Voeding

De ESP32 is een 3,3 V-onderdeel en trekt stroom in scherpe pieken zodra de radio zendt — ruim boven 200 mA op het toppunt. Twee gevolgen:

Voed hem niet vanaf de 3,3 V-pin van de Arduino. Die spanningsregelaar kan de zendpieken niet leveren, en het symptoom is een print die telkens herstart wanneer de druk op het netwerk groot wordt, wat op een codefout lijkt maar er geen is.

De GPIO-pinnen zijn niet 5 V-tolerant. Alles wat vanuit 5 V-logica een signaal in de ESP32 stuurt, heeft een spanningsdeler of levelshifter nodig, precies als de RX-lijn van de HC-05.

Geef hem een degelijke voeding uit het accupakket via een eigen spanningsregelaar, met de motorvoeding gescheiden gehouden.

Controleer voor je gaat rijden

  1. Serveer de pagina. Verbind een telefoon met RobotCar, open http://192.168.4.1 en controleer of de knoppen verschijnen.
  2. Volg de commando’s. Print elk ontvangen commando over USB. Houd een knop ingedrukt en controleer of de herhalingen op ongeveer 10 per seconde binnenkomen.
  3. Verbreek de verbinding. Houd vooruit ingedrukt en zet daarna de wifi van je telefoon uit. De robot moet binnen de timeout stoppen.

Zet hem daarna in een robot: het project op afstand bestuurde robot bouwt het hele ding, met welke radio dan ook, op een chassis dat ook zelfstandig kan rijden.

Verken de graaf

Onderdeel van deze bouwprojecten

Projecten en leerroutes waarin deze tutorial voorkomt.

Verder lezen

Bronnen