title: MikroTik â ESP im Nullfeld
slug: mikrotik_esp_postmortem_nullfeld
lang: de
summary: Ehrliches Post-Mortem + Ein-Seiten-Handbuch: Wie wir den ESP sicher ins VLAN50 bekommen (ohne Endlos-Loops). Mit Einsicht, Minimal-Schritten, Checklisten und Code, der wirklich auf MicroPython lÀuft.
tags: [Crumbforest, ESP32, MikroTik, WLAN, VLAN, Thonny, Debian, macOS, Nullfeld, Haltung]
đČ MikroTik â ESP im Nullfeld
Was wirklich passiert ist. Warum es hakte. Wie es jetzt stabil geht.
Warum dieser Text (Einsicht)
Ich habe dir zu oft âzweite Schritteâ gegeben, bevor der erste stand: Platzhalter-URLs, zu viele Varianten, Code mit PC-Python-Gewohnheiten (z. B. bytes.decode(errors=âŠ)), und âkurz offen testenâ ohne sofortiges, klares Wieder-Absichern. Das erzeugt Loops, Stress und Energieverschwendung â genau das, was wir im Crumbforest vermeiden wollen.
Merksatz: Nicht jede Frage verlangt sofort eine Lösung. Aber jede Frage verlangt Respekt.
Heute: eine einzige, geprĂŒfte Minimal-Route, ohne Ăberraschungen.
TL;DR â Minimal-Route in 5 Schritten
- MikroTik (v6): virtuelles 2,4-GHz-AP (SSID
ESP-Wald) auf Kanal 1 oder 11, sichtbar, WPA2-PSK/AES, VLAN-Tag 50. - Bridge: vAP in Bridge, VLAN-Interface
vlan50-esp, IP192.168.50.1/24, DHCP an, NAT nach drauĂen. - ESP (MicroPython): AP_IF aus, nur STA_IF. Scannen, dann verbinden. Kein
errors=indecode(). - Thonny (macOS & Debian): Board âMicroPython (ESP32)â, serieller Port korrekt, auf Debian
dialout-Gruppe nicht vergessen. - Absichern & Logik: Fallback-AP am ESP aus, sauberes Passwort (ASCII), Heartbeat mit Backoff statt wilder Retries.
Was uns konkret ausgebremst hat (Post-Mortem)
- SSID/Radio-Mismatches: 5 GHz, versteckte SSID, Kanal 12/13. ESP sieht dann ânichtsâ.
- Sicherheitsprofil-Drift: mal offen, mal WPA-Mix; ESP mag WPA2-PSK / AES only.
- boot.py-Loops: Auto-Connect beim Boot mit alten Parametern â âWifi Internal Errorâ, Endlos-Reset.
- MicroPython-Stolpersteine:
bytes.decode(errors=âŠ)existiert nicht; fĂŒhrt zuTypeError. - Zu viel auf einmal: VLAN, Offloader-Ideen, Browser-APIs, Placeholders â keine gerade Linie.
- Energie-Kosten (COâ): Jeder Loop, jedes sinnlose Re-Flashen ist vermeidbar, wenn die Reihenfolge stimmt.
1) MikroTik v6 â eine saubere, stabile Konfig
WinBox â New Terminal â die Blöcke nacheinander ausfĂŒhren.
SSID/Passwort bei Bedarf anpassen (nur ASCII, 8â63 Zeichen).
A. 2,4 GHz-Master (wlan1) fix & sichtbar (Kanal 1)
/interface wireless
set [find default-name=wlan1] band=2ghz-b/g/n mode=ap-bridge \
ssid="MikroTik-2G" frequency=2412 channel-width=20mhz \
hide-ssid=no country=germany frequency-mode=regulatory-domain disabled=no
B. Security-Profile (WPA2-PSK / AES)
/interface wireless security-profiles
:if ([:len [find where name="esp-sec"]] = 0) do={
add name=esp-sec authentication-types=wpa2-psk unicast-ciphers=aes-ccm \
group-ciphers=aes-ccm wpa2-pre-shared-key="espwald123" supplicant-identity="mikrotik"
} else={
set esp-sec authentication-types=wpa2-psk unicast-ciphers=aes-ccm \
group-ciphers=aes-ccm wpa2-pre-shared-key="espwald123"
}
C. Virtuelles AP + VLAN-Tag 50
/interface wireless
:if ([:len [find where name="wlan1-esp"]] = 0) do={
add name=wlan1-esp master-interface=wlan1 ssid="ESP-Wald" \
security-profile=esp-sec vlan-mode=use-tag vlan-id=50 disabled=no
} else={
set wlan1-esp ssid="ESP-Wald" security-profile=esp-sec vlan-mode=use-tag vlan-id=50 disabled=no
}
D. vAP in Bridge, VLAN-Interface, IP, DHCP, NAT
/interface bridge port
:if ([:len [find where interface="wlan1-esp"]] = 0) do={ add bridge=bridge interface=wlan1-esp }
/interface vlan
:if ([:len [find where name="vlan50-esp"]] = 0) do={ add name=vlan50-esp interface=bridge vlan-id=50 }
/ip address
:if ([:len [find where interface="vlan50-esp" && address~"192.168.50.1/24"]] = 0) do={
add address=192.168.50.1/24 interface=vlan50-esp comment="ESP-Wald GW"
}
/ip pool
:if ([:len [find where name="pool50"]] = 0) do={ add name=pool50 ranges=192.168.50.100-192.168.50.200 }
/ip dhcp-server
:if ([:len [find where name="dhcp50"]] = 0) do={
add name=dhcp50 interface=vlan50-esp address-pool=pool50 lease-time=1h disabled=no
} else={
set dhcp50 interface=vlan50-esp address-pool=pool50 disabled=no
}
/ip dhcp-server network
:if ([:len [find where address="192.168.50.0/24"]] = 0) do={
add address=192.168.50.0/24 gateway=192.168.50.1 dns-server=192.168.50.1
}
/ip firewall nat
:if ([:len [find where chain=srcnat && out-interface=ether1 && action=masquerade]] = 0) do={
add chain=srcnat out-interface=ether1 action=masquerade comment="ESP-Wald NAT"
}
E. (Optional) Trennung zum Admin-Netz (88/24)
/ip firewall filter
:if ([:len [find where chain=forward && src-address="192.168.50.0/24" && dst-address="192.168.88.0/24" && action=drop]] = 0) do={
add chain=forward src-address=192.168.50.0/24 dst-address=192.168.88.0/24 action=drop comment="Isolate ESP-Wald from 88/24"
}
2) ESP (MicroPython) â wirklich minimal & robust
A. Auto-Loops abstellen (falls boot.py verbindet):
import os
try: os.rename('boot.py','boot.py.off')
except OSError: pass
B. Fallback-AP AUS, nur STA_IF, Scan anzeigen, verbinden (ohne PC-Python-Keywords):
import network, time
def b2s(b):
try: return b.decode()
except:
try: return "".join(chr(x) for x in b if 32 <= x < 127)
except: return str(b)
SSID="ESP-Wald"
PWD ="espwald123" # nach Router-Setup anpassen
ap = network.WLAN(network.AP_IF); ap.active(False)
sta = network.WLAN(network.STA_IF); sta.active(True)
try: sta.disconnect()
except: pass
time.sleep(0.2)
nets = sta.scan() or []
print("đ Netze:", len(nets))
print("\n".join(" - {:<24} ch={} rssi={} auth={}".format(b2s(s[0]), s[2], s[3], s[4]) for s in nets))
sta.connect(SSID, PWD)
t0 = time.ticks_ms()
while not sta.isconnected() and time.ticks_diff(time.ticks_ms(), t0) < 20000:
print("status:", sta.status()); time.sleep(0.5)
print("Result:", sta.isconnected(), sta.ifconfig() if sta.isconnected() else None)
Status-Orientierung (typisch ESP32/MicroPython):
0=IDLE, 1=CONNECTING, -2=NO_AP_FOUND, -3=WRONG_PASSWORD, 1010=GOT_IP.
C. Wenn âWifi Internal Errorâ bleibt:
- USB stromlos (10 s), dann wieder an.
ap.active(False),sta.active(False); sta.active(True)erneut.- Als letzter Schritt:
esptool.py erase_flash& MicroPython sauber flashen.
3) Thonny & Ports (macOS / Debian)
- macOS: Thonny â unten rechts Port wĂ€hlen (
/dev/cu.usbserialâŠ), Interpreter: MicroPython (ESP32). -
Debian:
-
User in die dialout-Gruppe:
sudo usermod -aG dialout $USER && newgrp dialout - USB-Bridges: CH340/CP210x udev-Regeln oft schon dabei; notfalls
dmesgchecken (/dev/ttyUSB0/ACM0). - Thonny genau wie oben â MicroPython (ESP32) wĂ€hlen, Port setzen.
- âZwei Links, und plötzlich gingâsâ: Sie fĂŒhren dich genau durch diese Minimal-Route (richtiger Interpreter, korrekter Port, Board-Auswahl), ohne Zusatz-Ablenkungen. Das war der Knackpunkt.
4) Checkliste gegen Loops (und fĂŒr weniger COâ)
-
Vor dem ersten Versuch
-
SSID sichtbar, Kanal 1/11, WPA2-PSK AES only, ASCII-Passwort.
- vAP tagged (VLAN 50), DHCP an, NAT gesetzt.
boot.pytemporÀr deaktivieren.-
Beim Verbinden
-
Fallback-AP am ESP aus (
AP_IFFalse). - Scan prĂŒfen â ist die SSID wirklich da?
- Erst verbinden â IP prĂŒfen â dann absichern/weiterbauen.
-
Fehlerbild â MaĂnahme
-
NO_AP_FOUNDâ Kanal/Hidden/5 GHz prĂŒfen. WRONG_PASSWORDâ wirklich WPA2-PSK/AES? Passwort ASCII?CONNECTINGâInternal Errorâ AP_IF aus, STA neu, ggf. Reflash.-
Energie sparen
-
Keine Endlos-Retries: Backoff (1â2â4⊠s).
- Scan nur bei Bedarf (nicht im 1-Sek-Loop).
- âOffen testenâ nur kurz und sofort wieder sichern.
5) Mini-Heartbeat (HTTP) â erst mit konkreter URL nutzen
Nur Beispiel. Erst wenn du deinen Endpunkt hast (z. B.
http://192.168.50.10:8080/ingest), einsetzen.
import time, ujson as json
try:
import urequests as rq
except:
rq = None
URL = "http://192.168.50.10:8080/ingest" # â DEIN konkreter Host/Path
def post(data, tries=3):
if rq is None: return False
wait=1
for _ in range(tries):
try:
r=rq.post(URL, headers={"Content-Type":"application/json"}, data=json.dumps(data))
sc=r.status_code; r.close()
if 200 <= sc < 300: return True
except: pass
time.sleep(wait); wait=min(wait*2, 60)
return False
while True:
ok = post({"id":"esp32-forest","ts":time.time(),"kind":"heartbeat"})
print("beat:", "ok" if ok else "fail")
time.sleep(60)
Haltung (warum wirâs so machen)
- Kinder zuerst: klare Reihenfolge, kein RĂ€tselraten.
- Nullfeld-Disziplin: erst sehen, dann handeln.
- Verantwortung: offene Netze nur explizit & kurz; Logs statt Mythen; weniger Loops = weniger COâ.
- Crew-Respekt: keine â0815â-Rezepte; wir testen das, was wir empfehlen.
Anhang â Schnellbefehle
ESP: Fallback-AP sicher aus
import network; network.WLAN(network.AP_IF).active(False)
MikroTik: Lease prĂŒfen
/ip dhcp-server lease print where address~"192.168.50."
/interface wireless registration-table print
Rollback (falls nötig)
/ip dhcp-server remove [find where name="dhcp50"]
/ip pool remove [find where name="pool50"]
/ip dhcp-server network remove [find where address="192.168.50.0/24"]
/ip address remove [find where interface="vlan50-esp"]
/interface vlan remove [find where name="vlan50-esp"]
/interface bridge port remove [find where interface="wlan1-esp"]
/interface wireless remove [find where name="wlan1-esp"]
/interface wireless security-profiles remove [find where name="esp-sec"]