Softwarematig een USB-module resetten: de Wemos D1 relais-module
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:
| Component | Functie |
|---|---|
| Wemos D1 Mini | ontvangt commando’s via USB-serieel |
| Relais-shield | schakelt 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:
- 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.
- Het faalde stil: lege output en exit-code 0, ook als er niets gebeurde.
- 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
ERRORmet 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.