Softwarematig een USB-module resetten: de Wemos D1 relais-module

· 5 min lezen

Iedereen die wel eens een ESP32 of ander board via USB flasht, kent het frustrerende moment: de module hangt, de seriële poort is weg of het board boot telkens in de verkeerde modus. De standaard remedie is fysiek: de USB-kabel loskoppelen, drie seconden wachten, weer aansluiten. Een hardwarematige reset — uitgevoerd met je handen.

Wij wilden dat softwarematig doen. Het resultaat is een kleine Wemos D1 Mini met een relais-shield die de voedingslijn van een aangesloten USB-module schakelt. Relais aan is USB afgekoppeld, relais uit is USB aangekoppeld. Een fysieke handeling, programmeerbaar gemaakt.

Het idee

Een relais is een mechanische schakelaar die je met een stroompje bedient. Het relais-shield van de Wemos D1 Mini is active-low: de relais-trip wordt bediend door een pin laag te trekken. De module zelf is een ESP8266-bordje ter grootte van een postzegel met een USB-aansluiting en een CH340 seriële chip — ideaal om via de seriële poort commando’s naar te sturen.

De opstelling:

ComponentFunctie
Wemos D1 Miniontvangt commando’s via USB-serieel
Relais-shieldschakelt de voedingslijn van de doel-module
Doel-module (bv. ESP32-C3)hangt aan de USB-poort via het relais

Een commando 1 over de seriële poort zet het relais aan — de doel-module verliest voeding en verdwijnt van de USB-bus. Een commando 0 koppelt hem weer aan. Daarmee is een power-cycle exact reproduceerbaar, zonder dat er iemand bij de kabel hoeft te komen.

De ontwerpfout die bijna alles brak: GPIO0

Het eerste ontwerp gebruikte GPIO0 voor het relais. Dat leek logisch — het is een gewone output-pin. Maar GPIO0 is op de ESP8266 een boot-strapping pin: bij het opstarten moet hij hoog zijn, anders boot het board in UART-downloadmodus (de flash-modus) en hangt het.

En hier zit de adder onder het gras: het relais-shield is active-low. Met het relais aan trekt de shield GPIO0 laag. Bij een reset of power-cycle met het relais in de aan-stand bootte ons board dus telkens in flash-modus — precies op het moment dat we hem nodig hadden om een reset uit te voeren.

De fix: het relais verhuizen naar D1 (GPIO5). Geen strapping-pin, geen verrassingen. De les: lees de pinout van je chip voordat je een pin kiest voor iets dat de boel kan resetten.

De firmware: bewust minimalistisch

De firmware is PlatformIO met het Arduino-framework, en bewust klein gehouden:

#define RELAY_PIN 5   // D1

void loop() {
  if (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '1') { digitalWrite(RELAY_PIN, HIGH); Serial.println("ON"); }
    else if (c == '0') { digitalWrite(RELAY_PIN, LOW); Serial.println("OFF"); }
  }
}

De LED op het board geeft de relais-stand weer. Elk commando beantwoordt de firmware met een bevestiging (ON/OFF) — zo weet het script zeker dat het commando echt is aangekomen.

Later kwam daar een identificatie-commando bij: ? antwoordt met het hardware-chip-ID van de ESP8266 (ID usbtoggle-relay B52BBC). De poortnaam van zo’n CH340 verandert namelijk per USB-poort en per reboot — maar het chip-ID is uniek per chip en blijft altijd gelijk. Zo kun je bij meerdere modules altijd bevestigen welke module je bedient.

Het script: meer dan een regel python

Het eerste relay-script was een bash-one-liner die python inline aanriep. Daar zaten drie problemen in:

  1. Het opende de seriële poort met DTR/RTS geassert — en op de CH340 is dat precies de combinatie die het board reset. Elke aanroep triggert dus een reset en de reply gaat verloren.
  2. Het faalde stil: lege output en exit-code 0, ook als er niets gebeurde.
  3. De poortnaam was hardcoded.

Het herschreven script is Python 3 met pyserial en pakt alle drie aan:

  • DTR/RTS expliciet uit vóór het openen van de poort. pyserial assert die lijnen standaard; door de states vóór open() te zetten blijft het board gewoon draaien bij elke aanroep.
  • Banner-wait als vangnet: mocht het board tóch resetten, dan gooit het script de boot-banner weg voordat het commando gaat — de reply kan nooit vervuild raken.
  • Echte foutafhandeling: usage-melding, duidelijke fout bij een ontbrekende poort, en een ERROR met exit-code 1 als het board niet antwoordt. Geen tracebacks, geen stille mislukkingen.

Het resultaat:

./relay 1   # relais aan  → USB afgekoppeld
./relay 0   # relais uit  → USB aangekoppeld
./relay id  # → ID usbtoggle-relay B52BBC

De test die ertoe doet

Het bewijs kwam toen we een ESP32-C3 aansloten en de stroomkring echt testten. Met het relais aan verdween de C3 keurig van de USB-bus. De kritieke test: relais aan, de USB-kabel loskoppelen en weer aansluiten (een echte power-cycle), en dan het board weer moeten kunnen bereiken. Met de oude GPIO0-bedrading was dit de hang-in-flash-modus-zaak; met GPIO5 bootte het board gewoon en antwoordde direct OFF.

En het mooiste: je hoort het klikken. Een softwarematige handeling met een mechanisch geluid — en een USB-module die als bij toverslag weer opduikt in de apparaatlijst.

Van handmatig naar geautomatiseerd

Omdat we dit ook vanuit onze AI-agent willen kunnen doen, hebben we de hele werkwijze vastgelegd in een skill: de agent kan de module vinden (via VID/PID en chip-ID), identificeren, en bij een hangende module de power-cycle uitvoeren — met de expliciete regel dat dit een last resort is, pas als softwarematige resets falen of niet bestaan. De fysieke handeling is daarmee volledig overgenomen door software.

De code

Het complete project — firmware, script, README en de skill — staat publiek op git.eddydevink.nl/eddy/usbtoggle. We zijn er blij mee: een kleine module, een paar regels code, en een probleem dat iedere maker kent is voorgoed opgelost.