Portierung auf den XIAO ESP32-C3 (MicroPython)

Die Kette laeuft auf dem Geraet gegen den Mock auf dem Mac. Getestet mit
MicroPython 1.28.0 (ESP32_GENERIC_C3): alle Module importieren, Telegrammbau
und Koordinaten-Formatierung sind byte-identisch zum Mac (inkl. Gradzeichen),
Poll-Schleife 22 Abfragen in 20 s ohne Aussetzer, Ausfall und Reconnect
verhalten sich wie vorgesehen. Offen bleibt allein die RS232-Strecke zur
Anzeige -- der Steckbrettaufbau mit MAX3232 fehlt noch.

Gemeinsamer Kern statt zweier Programme:
- displayloop.py: die Poll-Schleife, herausgeloest aus run_display. Mac und
  ESP32 fahren jetzt denselben Ablauf; drumherum liegt nur die Kommandozeile
  (run_display.py) bzw. das WLAN-Setup (run_esp32.py).
- ticks.py: Timeouts fuer beide Plattformen. time.ticks_ms() laeuft nach ~12
  Tagen ueber; das bisherige "jetzt + Timeout" haette eine Warteschleife
  danach tagelang haengen lassen. Jetzt ueber ticks_diff.
- wifi.py, run_esp32.py, wifi_config.py (gitignored, Vorlage dabei), deploy.sh.

Pinbelegung aus dem KiCad-Schaltplan uebernommen, nicht geraten: UART1 mit
TX=GPIO21 (Pad 7/D6) und RX=GPIO20 (Pad 8/D7) ueber den MAX3232, LDR an
GPIO2 (Pad 1/A0). UART0 bleibt die REPL.

LDR-Kennlinie ist auf der Platine umgekehrt: der Teiler lautet
3V3-[2k]-A0-[LDR]-GND, die Spannung faellt also mit steigender Helligkeit --
andersherum als beim MSP430, auf den die Schwellen zugeschnitten sind. Wird
im Code gespiegelt (config.LDR_INVERTED, brightness.raw_to_ldr). Die
Schwellen selbst muessen am realen Aufbau noch nachgemessen werden.

Am Geraet gefundene Fehler, die den Attrappen-Tests entgehen mussten, weil
CPython sie nicht hat:
- MicroPython kennt kein str.ljust (auch kein rjust/center/zfill).
  display._emit_line fuellt von Hand auf.
- NOLINK_LINES hatte 10 Zeichen bei 9 Zeichen Zeilenbreite und erschien als
  "MONTIERUN". Jetzt "KEIN LINK"/"TELESKOP".
Dagegen: TestMicropythonVertraeglichkeit prueft die deployten Dateien statisch
auf solche Methoden und auf modulweites argparse/threading und haelt die
Dateiliste mit deploy.sh synchron; test_display.py prueft die Breite fester
Anzeigetexte.

Weitere Portabilitaet: mount_client nutzt getaddrinfo und eine Sendeschleife
statt sendall (MicroPython laesst dessen Verhalten mit Timeout offen);
argparse in brightness wandert in die Funktion; migra._digits und die
Hex-Ausgabe verlassen sich nicht mehr auf %0*d bzw. bytes.hex(). Beides kann
1.28 zwar doch -- am Geraet nachgeprueft, die Kommentare sagen es jetzt richtig.

Widerspruch in der Dokumentation bereinigt: die Zeichenbreite stand an vier
Stellen als 9 oder 10. Gemessen sind 9; die Erklaerung duerfte sein, dass die
Sperrschrift 7 px pro Zelle braucht (9*7 = 63 von 64 px) und nicht 6, wie es
eine 5x7-Matrix mit 1 px Luecke waere. Nachgeprueft ist das nicht -- dafuer
gibt es jetzt "probe.py pitch", das den Auto-Vorschub gegen feste
Pixelabstaende stellt, statt am Umbruch abzuzaehlen.

mount_mock.py --lan bindet auf 0.0.0.0 und nennt die einzutragende LAN-IP,
damit der ESP32 den Mock erreicht.

92 Tests gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-26 18:51:45 +02:00
parent d8a9953b73
commit 42fc7f52d4
19 changed files with 1702 additions and 129 deletions
+34 -2
View File
@@ -8,8 +8,13 @@ Client nicht haengt.
python3 mount_mock.py # feste Koordinaten, Port 3490
python3 mount_mock.py --ra 18:36:56 --dec +38:47:01
python3 mount_mock.py --move # RA laeuft (Sekunden ticken hoch)
python3 mount_mock.py --lan --move # auch vom ESP32 aus erreichbar
Nur fuer die Entwicklung (CPython). Auf dem ESP32 laeuft spaeter nur der Client.
Vorgabe ist 127.0.0.1, also nur lokal. Damit der ESP32 den Mock uebers WLAN
erreicht, braucht es --lan (bindet auf 0.0.0.0); die Adresse, die dann auf dem
ESP32 einzutragen ist, wird beim Start ausgegeben.
Nur fuer die Entwicklung (CPython). Auf dem ESP32 laeuft nur der Client.
"""
import argparse
@@ -172,20 +177,47 @@ class MountMock:
self.stop()
def lan_address():
"""Die eigene LAN-Adresse ermitteln -- die, die der ESP32 ansprechen muss.
Ueber einen UDP-Socket ans Gateway: der verschickt nichts, verraet aber,
welche lokale Adresse das Betriebssystem fuer den Weg nach draussen nimmt.
"""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(("192.168.1.1", 1))
return s.getsockname()[0]
except OSError:
return None
finally:
s.close()
def main(argv=None):
p = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("--host", default="127.0.0.1")
p.add_argument("--lan", action="store_true",
help="auf allen Schnittstellen lauschen (0.0.0.0), damit der "
"ESP32 den Mock uebers WLAN erreicht")
p.add_argument("--port", type=int, default=3490)
p.add_argument("--ra", default="18:36:56")
p.add_argument("--dec", default="+38:47:01")
p.add_argument("--move", action="store_true", help="RA laeuft mit der Zeit hoch")
args = p.parse_args(argv)
mock = MountMock(host=args.host, port=args.port, ra=args.ra, dec=args.dec,
host = "0.0.0.0" if args.lan else args.host
mock = MountMock(host=host, port=args.port, ra=args.ra, dec=args.dec,
move=args.move)
print("Mock-Montierung auf %s:%d (RA=%s DEC=%s%s)"
% (mock.host, mock.port, args.ra, args.dec, ", move" if args.move else ""))
if host == "0.0.0.0":
ip = lan_address()
if ip:
print("Vom ESP32 aus erreichbar unter: %s:%d" % (ip, mock.port))
print(" run_esp32.main(host=\"%s\", fake_display=True)" % ip)
print("Falls keine Verbindung zustande kommt: macOS fragt beim ersten Mal,")
print("ob python3 eingehende Verbindungen annehmen darf -- das muss erlaubt sein.")
print("Beenden mit Ctrl-C.")
mock.serve_forever()
return 0