12 Monate Crumbforest-Wachstum, sortiert nach Waldlogik. Struktur: - 454 Dokumente in 7 organischen Ebenen - Lichtung → Waldrand → Waldmitte → Wurzeln → Krone → Unterholz → Samen - INDEX.md für jede Ebene (eigene Stimme) - WALDKARTE.md (Master-Navigation) - crumbcodex.md (Das Versprechen zwischen Krümel & Bits) - lichtung/willkommen.md (Nullfeld-Einstieg für Kinder) Philosophie: - Waldlogik statt Ordnerlogik - Tiefe, Licht, Wurzeln - wie ein echter Wald - Schutz für Krümel, Tiefe für Gräber, Poesie für Atmende Repos verbunden: - OZM-Keks-Handbuch-v1 (Das Wissen) - Crumb-Core-v.1 (Das Herz) - 194.164.194.191 - crumbmissions (Das Spiel) Wuuuhuuuuu! 🦉✨
10 KiB
🌲 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.
- User in die dialout-Gruppe:
-
„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.
- Fallback-AP am ESP aus (
-
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"]