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
- **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.
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`).
@@ -127,12 +119,30 @@ diese Zelle überschreibt.
`CHARS_PER_LINE` aufgefüllt werden, sonst bricht das Füll-Leerzeichen um und
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
- **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
laufend, sauber und ohne Flimmern. Protokoll, 19200 8E1, Zeichensatz und
Zeilengeometrie stimmen.
laufend, sauber und ohne Flimmern, mit Antwort-Handshake der Anzeige. Protokoll,
19200 8E1, Zeichensatz und Zeilengeometrie stimmen.
- Schrift: Sperrschrift (`ESC z`, `CHARSET_SPACED = True`), 10 Zeichen/Zeile.
Die schmale `1` wirkt dadurch etwas luftig — Font-Eigenschaft, nur per eigenem
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 -----------------------------------------------
# Auslieferzustand der Anzeige laut Handbuch (Kap. 4.1) und identisch zur
# 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
PARITY = "E"
BYTESIZE = 8
@@ -15,11 +15,10 @@ STOPBITS = 1
DISPLAY_ADDR = 1 # Zieladresse der Anzeige (DA = 0x81)
HOST_ADDR = 0 # unsere Quelladresse (SA = 0x80)
USE_CHECKSUM = False # das alte Programm fuhr ohne Pruefsumme
# Antworttelegramm als Flusskontrolle. Derzeit False, weil die RS232-Rueckleitung
# (Anzeige Pin 3 TxD -> Adapter RxD) noch nicht verdrahtet ist. Ist der RX-Pfad
# fertig, hier wieder auf True stellen -- dann wird der Fehlercode ausgewertet und
# ohne feste Pause gesendet.
WANT_RESPONSE = False
# Antworttelegramm als Flusskontrolle: Rueckweg verdrahtet und verifiziert
# (2026-07-16), Anzeige liefert 02 80 81 80 30 03. Nach Erhalt der Antwort darf
# sofort das naechste Telegramm folgen; der Fehlercode wird ausgewertet.
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
+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 coords
import migra
from display import open_display
from mount_client import MountClient, MountError
@@ -72,6 +73,11 @@ def main(argv=None):
last_shown = (ra_line, dec_line)
print("RA %-11s DEC %-12s -> %s | %s"
% (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:
print("Montierung: %s" % e, file=sys.stderr)
client.close()
+4
View File
@@ -40,6 +40,10 @@ class SerialTransport:
self._timeout = timeout
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.flush()