Antwortleitung (RX) verdrahtet und Flusskontrolle aktiviert

- WANT_RESPONSE=True: Anzeige-Antworttelegramm als Handshake, Fehlercode
  wird ausgewertet (Rueckweg verifiziert: 02 80 81 80 30 03)
- transport: Empfangspuffer vor jedem Senden leeren, damit read_response
  nur die frische Antwort sieht (Stoerbytes zwischen Telegrammen)
- run_display: Anzeige-/Antwortfehler abfangen statt abzustuerzen
- resp_test.py: Pruefwerkzeug fuer den Rueckkanal
- PORT auf /dev/cu.usbserial-120
- .gitignore ergaenzt, __pycache__ aus der Versionierung entfernt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-16 17:27:32 +02:00
parent 81e0e1a036
commit 4acec81657
16 changed files with 129 additions and 17 deletions
+10
View File
@@ -0,0 +1,10 @@
# Python-Bytecode
__pycache__/
*.py[cod]
# Virtuelle Umgebungen
venv/
.venv/
# Editor-/OS-Kram
.DS_Store
+21 -11
View File
@@ -85,14 +85,6 @@ selbst.
## Offene Punkte ## Offene Punkte
- **Antworttelegramm-Pfad (RX) noch offen, absichtlich zurückgestellt.**
`WANT_RESPONSE = False` ist Default, der Normalbetrieb läuft ohne Rückkanal.
Diagnose 2026-07-15: Adapter-Loopback ok, aber über den MAX232 und am echten
Gerät kommt keine Antwort zurück → der Empfangszweig (Anzeige Pin 3 TxD → MAX232
R_IN → R_OUT → Adapter RxD) leitet nicht. `loopback.py` hilft beim Eingrenzen.
Die Anzeige *antwortet* nachweislich (das alte MSP430-Programm nutzte die Antwort
als Handshake); es ist reine Verdrahtung. Wird der RX-Pfad fertig, `WANT_RESPONSE`
in `config.py` auf True stellen.
- **Echte Montierungs-IP:** In `config.py` steht `MOUNT_HOST` noch als Platzhalter. - **Echte Montierungs-IP:** In `config.py` steht `MOUNT_HOST` noch als Platzhalter.
Sobald die GM4000 im Netz erreichbar ist, dort die IP eintragen (Port 3490 ist Sobald die GM4000 im Netz erreichbar ist, dort die IP eintragen (Port 3490 ist
Vorgabe). Bis dahin läuft alles gegen den Mock (`run_display.py --mock`). Vorgabe). Bis dahin läuft alles gegen den Mock (`run_display.py --mock`).
@@ -127,12 +119,30 @@ diese Zelle überschreibt.
`CHARS_PER_LINE` aufgefüllt werden, sonst bricht das Füll-Leerzeichen um und `CHARS_PER_LINE` aufgefüllt werden, sonst bricht das Füll-Leerzeichen um und
beschädigt die andere Zeile. beschädigt die andere Zeile.
## Antwortleitung (RX)
Der Rückkanal ist verdrahtet und verifiziert (2026-07-16): Anzeige Pin 3 (TxD) →
MAX232-Empfängereingang (R_IN) → R_OUT → Adapter RxD. Die Anzeige quittiert jedes
Telegramm mit `02 80 81 80 30 03` (Fehlercode `0` = kein Fehler). `WANT_RESPONSE = True`
ist damit aktiv: Nach der Antwort darf sofort das nächste Telegramm folgen, der
Fehlercode wird ausgewertet. `resp_test.py` prüft den Rückweg (sendet mit Antwort-
Anforderung und zeigt die empfangenen Antworten).
Wichtig: `SerialTransport.write` leert vor jedem Senden den Empfangspuffer, damit
`read_response` nur die frische Antwort sieht (sonst können Störbytes zwischen den
Telegrammen das Parsen stören). `run_display` fängt Anzeige-Fehler ab und zeichnet
beim nächsten Durchlauf neu, statt abzustürzen.
Diagnose-Historie: Der Rückweg war anfangs falsch verdrahtet — Pin 3 lag an einem
MAX232-*Sender*ausgang statt einem Empfängereingang; zwei Ausgänge trieben
gegeneinander (verschliffener ~3-V-Pegel am Oszi). `loopback.py` grenzt so etwas ein.
## Erledigt ## Erledigt
- **Phase 1 + 2 am Gerät bestätigt (2026-07-15):** Koordinaten erscheinen korrekt, - **Phase 1 + 2 am Gerät bestätigt (2026-07-15/16):** Koordinaten erscheinen korrekt,
inkl. echtem Gradzeichen; im `run_display`-Betrieb aktualisieren beide Zeilen inkl. echtem Gradzeichen; im `run_display`-Betrieb aktualisieren beide Zeilen
laufend, sauber und ohne Flimmern. Protokoll, 19200 8E1, Zeichensatz und laufend, sauber und ohne Flimmern, mit Antwort-Handshake der Anzeige. Protokoll,
Zeilengeometrie stimmen. 19200 8E1, Zeichensatz und Zeilengeometrie stimmen.
- Schrift: Sperrschrift (`ESC z`, `CHARSET_SPACED = True`), 10 Zeichen/Zeile. - Schrift: Sperrschrift (`ESC z`, `CHARSET_SPACED = True`), 10 Zeichen/Zeile.
Die schmale `1` wirkt dadurch etwas luftig — Font-Eigenschaft, nur per eigenem Die schmale `1` wirkt dadurch etwas luftig — Font-Eigenschaft, nur per eigenem
Font (microSYST-PC-Software) änderbar. Font (microSYST-PC-Software) änderbar.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5 -6
View File
@@ -5,7 +5,7 @@ import migra
# -- Serielle Schnittstelle ----------------------------------------------- # -- Serielle Schnittstelle -----------------------------------------------
# Auslieferzustand der Anzeige laut Handbuch (Kap. 4.1) und identisch zur # Auslieferzustand der Anzeige laut Handbuch (Kap. 4.1) und identisch zur
# UART-Init des alten MSP430-Programms: 19200 8E1. # UART-Init des alten MSP430-Programms: 19200 8E1.
PORT = "/dev/cu.usbserial-1120" # Mac; auf dem ESP32-C3 spaeter die UART-Nummer PORT = "/dev/cu.usbserial-120" # Mac; auf dem ESP32-C3 spaeter die UART-Nummer
BAUDRATE = 19200 BAUDRATE = 19200
PARITY = "E" PARITY = "E"
BYTESIZE = 8 BYTESIZE = 8
@@ -15,11 +15,10 @@ STOPBITS = 1
DISPLAY_ADDR = 1 # Zieladresse der Anzeige (DA = 0x81) DISPLAY_ADDR = 1 # Zieladresse der Anzeige (DA = 0x81)
HOST_ADDR = 0 # unsere Quelladresse (SA = 0x80) HOST_ADDR = 0 # unsere Quelladresse (SA = 0x80)
USE_CHECKSUM = False # das alte Programm fuhr ohne Pruefsumme USE_CHECKSUM = False # das alte Programm fuhr ohne Pruefsumme
# Antworttelegramm als Flusskontrolle. Derzeit False, weil die RS232-Rueckleitung # Antworttelegramm als Flusskontrolle: Rueckweg verdrahtet und verifiziert
# (Anzeige Pin 3 TxD -> Adapter RxD) noch nicht verdrahtet ist. Ist der RX-Pfad # (2026-07-16), Anzeige liefert 02 80 81 80 30 03. Nach Erhalt der Antwort darf
# fertig, hier wieder auf True stellen -- dann wird der Fehlercode ausgewertet und # sofort das naechste Telegramm folgen; der Fehlercode wird ausgewertet.
# ohne feste Pause gesendet. WANT_RESPONSE = True
WANT_RESPONSE = False
RESPONSE_TIMEOUT = 1.0 # Sekunden (nur relevant, wenn WANT_RESPONSE = True) RESPONSE_TIMEOUT = 1.0 # Sekunden (nur relevant, wenn WANT_RESPONSE = True)
QUIET_TIME = 0.4 # Pause zwischen Telegrammen, wenn ohne Antwort gefahren wird QUIET_TIME = 0.4 # Pause zwischen Telegrammen, wenn ohne Antwort gefahren wird
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Antwortleitung testen: Telegramme MIT Antwort-Anforderung senden.
Sendet im 1-Sekunden-Takt ein festes Telegramm mit gesetztem Antwort-Bit
(FC = 0x81). Nach jedem Empfang muss die Anzeige auf Pin 3 ihr Antworttelegramm
senden (erwartet: 02 80 81 80 30 03). Bricht bei ausbleibender Antwort NICHT ab,
damit man am Oszi in Ruhe messen kann; liest eine evtl. eintreffende Antwort mit
und zeigt sie als Hex.
python3 resp_test.py # 60 s, 1 s Takt
python3 resp_test.py --seconds 120
"""
import argparse
import sys
import time
import config
import migra
from transport import SerialTransport
def main(argv=None):
p = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("--seconds", type=float, default=60.0)
p.add_argument("--interval", type=float, default=1.0)
p.add_argument("--port", default=config.PORT)
args = p.parse_args(argv)
du = migra.DataUnit().fill(config.BACKGROUND).charset(0, spaced=True)
du.cursor(0, 0); du.text("RESP TEST")
frame = migra.build_telegram(du, dest=config.DISPLAY_ADDR, src=config.HOST_ADDR,
checksum=False, response=True) # FC-Bit0 = 1
t = SerialTransport(port=args.port, baudrate=config.BAUDRATE, parity=config.PARITY,
bytesize=config.BYTESIZE, stopbits=config.STOPBITS, timeout=0.5)
print("Port :", args.port)
print("TX (%d B): %s" % (len(frame), " ".join("%02X" % b for b in frame)))
print("FC : 0x%02X (Bit0=1 -> Antwort anfordern)" % frame[3])
print("Erwartete Antwort auf Pin 3: 02 80 81 80 30 03")
print("Laufzeit %.0f s, Takt %.1f s. Abbruch mit Ctrl-C.\n" % (args.seconds, args.interval))
deadline = time.monotonic() + args.seconds
n = 0
got = 0
try:
while time.monotonic() < deadline:
n += 1
t._port.reset_input_buffer()
t.write(frame)
# kurz auf eine Antwort lauschen (blockiert das Oszi nicht)
end = time.monotonic() + 0.4
buf = bytearray()
while time.monotonic() < end:
c = t._port.read(1)
if c:
buf.extend(c)
if c[0] == migra.ETX:
break
elif buf:
break
if buf:
got += 1
print("#%02d RX: %s" % (n, " ".join("%02X" % b for b in buf)))
else:
print("#%02d RX: (nichts empfangen)" % n)
# bis zum naechsten Sekundentakt warten
rest = args.interval - 0.4
if rest > 0:
time.sleep(rest)
except KeyboardInterrupt:
print("\nabgebrochen")
finally:
t.close()
print("\n%d Telegramme gesendet, %d Antworten empfangen." % (n, got))
return 0
if __name__ == "__main__":
sys.exit(main())
+6
View File
@@ -17,6 +17,7 @@ import time
import config import config
import coords import coords
import migra
from display import open_display from display import open_display
from mount_client import MountClient, MountError from mount_client import MountClient, MountError
@@ -72,6 +73,11 @@ def main(argv=None):
last_shown = (ra_line, dec_line) last_shown = (ra_line, dec_line)
print("RA %-11s DEC %-12s -> %s | %s" print("RA %-11s DEC %-12s -> %s | %s"
% (ra_raw, dec_raw, ra_line, dec_line)) % (ra_raw, dec_raw, ra_line, dec_line))
except migra.MigraError as e:
# Anzeige-/Antwortfehler (z.B. gestoerte Antwort): nicht abstuerzen,
# nur melden und beim naechsten Durchlauf neu zeichnen.
print("Anzeige: %s" % e, file=sys.stderr)
last_shown = None
except (MountError, ValueError) as e: except (MountError, ValueError) as e:
print("Montierung: %s" % e, file=sys.stderr) print("Montierung: %s" % e, file=sys.stderr)
client.close() client.close()
+4
View File
@@ -40,6 +40,10 @@ class SerialTransport:
self._timeout = timeout self._timeout = timeout
def write(self, data): def write(self, data):
# Empfangspuffer vor dem Senden leeren: eventuelle Stoerbytes zwischen
# den Telegrammen verwerfen, damit read_response nur die frische Antwort
# auf DIESES Telegramm sieht.
self._port.reset_input_buffer()
self._port.write(data) self._port.write(data)
self._port.flush() self._port.flush()