GWBASIC vs. Python - serielle Uebertragung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Zellsius
User
Beiträge: 8
Registriert: Donnerstag 12. Mai 2022, 19:34

Hallo Forum

Ich schreibe hier und heute zum ersten Mal.
( Soll ich mich explizit vorstellen? - in manchen Foren ist das ueblich.....)

Mein Problem ist aus meiner Sicht kein Problem.
Leider ist es fuer mich ein grosses Problem :-)

Folgender *uralter* und sehr simpler GW-BASIC-Code liegt mir vor und funktioniert auf entsprechender Hardware einwandfrei:

100 OPEN "COM1:2400,N,8,2,CS10000,DS" FOR RANDOM AS #1
130 CLS
200 INPUT " Loknr. (1-80) "; LN
210 INPUT " Geschw. (0-15) "; GE
300 PRINT #1, CHR$(GE); CHR$(LN);

Das Ganze soll nun auf einem aktuellen Laptop mittels Python realisiert werden.

Dazu habe ich nach vielen Stunden folgenden Code bereitgestellt, der - wie kann es auch anders sein - nicht funktioniert.

import serial
interface=serial.Serial('COM6', 2400, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_TWO, timeout=10)
lok= hex(42)
print (lok)
speed = hex(5)
print (speed)
interface.write(b'\r0x5')
interface.write(b'\r0x2a')
print(b'\r0x5\r')
print(b'\r0x2a\r')
interface.close()


Alles an Hilfe ist willkommen, vielen Dank :-)

Matze
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Serial-Objekte öffnet man wie Dateien innerhalb eines with-Statements.
Ein "funktioniert nicht" ist keine gute Fehlerbeschreibung. Hier kann man raten, was Du eigentlich übertragen willst. Woher hast Du "\r0x5" her?

Code: Alles auswählen

import serial
with serial.Serial('COM6',
        2400,
        bytesize=serial.EIGHTBITS,
        parity=serial.PARITY_NONE,
        stopbits=serial.STOPBITS_TWO,
        timeout=10) as interface:
    lok = 42
    speed = 5
    interface.write(bytes([speed, lok]))
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Zellsius: Die beiden "\r" bei den gesendeten Daten sind komisch, weil die an der Stelle nicht dem entsprechen was das BASIC-Programm macht. Da werden erst zwei Daten-Bytes gesendet und dann sendet das PRINT noch einen Wagenrücklauf *nach* den beiden Datenbytes. Also das Beispiel von Sirius3 noch um eine 13 nach `speed` und `lok` ergänzt.

Edit: Gnarf, stimmt gar nicht, das BASIC-Programm sendet gar keinen Wagenrücklauf, also 100% das Beispiel von Sirius3.

GW-BASIC-Programme kann man übrigens auch mit Python ausführen lassen: http://robhagemans.github.io/pcbasic/
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Zellsius
User
Beiträge: 8
Registriert: Donnerstag 12. Mai 2022, 19:34

Hallo Sirius3 und _blackjack_

Was soll ich sagen?
Der Code von Sirius3 funktionierte auf Anhieb.

Klasse Sache das :-)

Vielen, vielen Dank
Zellsius
User
Beiträge: 8
Registriert: Donnerstag 12. Mai 2022, 19:34

Hallo nochmals :-)

Alles funktioniert nun soweit einwandfrei - wenn da nicht dieses immer wieder auftretende *Aber.........* waere.

Durch die Verwendung des with-Statements wird die Verbindung nach jedem Aufruf wieder getrennt bzw. geschlossen und muss beim naechsten Auslesen der Daten neu geoeffnet werden.

Mein Programm hat jedoch die Hauptaufgabe, ZWEI COM-Ports staendig geoeffnet zu haben.
Vom einen wird gelesen, in den anderen wird geschrieben.
Jedes Mal, wenn ich die Ports neu oeffne, dauert das eine gewisse Zeit ( einer davon ist Bluetooth) und es kommt hin und wieder vor, dass da noch irgendwelche Zeichen im Buffer waren und die Verbindung nicht wieder hergestellt werden konnte.
Meinetwegen duerfen die Ports jedoch staendig an meinen Code gebunden sein, sie werden anderweitig nicht benoetigt.

Gibt es da eine clevere Vorgehensweise?
Gerne sende ich auch meinen bisherigen Code - aber dann bitte als Nachricht oder auch per Email.

Vielen Dank nochmals fuer die bisherige ( einfache aber geniale) Hilfe.

Matze
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Zellsius: ``with`` sorgt dafür, dass ”augeräumt” wird, wenn der Programmfluss den ``with``-Block verlässt. Du musst den Code halt so schreiben, dass der Block erst am Programmende verlassen wird, beziehungsweise zu einem Zeitpunkt wo die Verbindung nicht mehr benötigt wird.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
sparrow
User
Beiträge: 4165
Registriert: Freitag 17. April 2009, 10:28

@Zellsius: Was spricht gegen den Code hier im Forum? Ohne wird es schwierig. So kann man nur sagen: Wenn der Port nicht geschlossen werden darf, muss eben alles, was du tust, in den with-Block.
Zellsius
User
Beiträge: 8
Registriert: Donnerstag 12. Mai 2022, 19:34

Hallo _blackjack_ , hallo sparrow

Gegen die Veroeffentlichung des Codes steht: meine Blamage


Ihr zwei habt mich herausgefordert, das habt ihr nun davon.

Anbei der ( vollkommen laienhafte ) Code.

Edit:
Der Code wurde nicht uebermittelt.
Ich muss schauen, woran das lag..................
Zellsius
User
Beiträge: 8
Registriert: Donnerstag 12. Mai 2022, 19:34

import serial
import time
impulse =0
Gesamtstrecke= 2680
Weg_bis_Bruecke = 1320
Bremstraegheit = 4 # das muss noch mit der Geschwindigkeitsstufe verknuepft werden
lok = 16
speed = 3
licht = 16
funktion = 0
with serial.Serial('COM1',2400,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_TWO,timeout=10) as interface:
interface.write(bytes([speed+licht+funktion, lok]))
with serial.Serial('COM6',9600,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,timeout=10) as wagen:
erhaltenes_Zeichen = wagen.read()
while impulse < Weg_bis_Bruecke:
erhaltener_String = str(wagen.readline())
gekuerzter_String = erhaltener_String[3 : -6]
impulse = int(gekuerzter_String)
print(impulse)
speed = 0
with serial.Serial('COM1',2400,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_TWO,timeout=10) as interface:
interface.write(bytes([speed+licht+funktion, lok]))
print ("Brücke erreicht!")
print ("Warte 10 Sekunden......")
time.sleep(2) # die ersten 2 von 10 Sekunden........
speed = 3
with serial.Serial('COM1',2400,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_TWO,timeout=10) as interface:
interface.write(bytes([speed+licht+funktion, lok]))
with serial.Serial('COM6',9600,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,timeout=10) as wagen:
erhaltenes_Zeichen = wagen.read()
while impulse < Gesamtstrecke:
erhaltener_String = str(wagen.readline())
gekuerzter_String = erhaltener_String[3 : -6]
impulse = int(gekuerzter_String)
print(impulse)
print ("Jetzt fahre ich zum Startpunkt")

speed = 0
with serial.Serial('COM1',2400,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_TWO,timeout=10) as interface:
interface.write(bytes([speed+licht+funktion, lok]))
print ("Komplette Runde gedreht!")
print ("Programm erfolgreich durchlaufen.")
Benutzeravatar
sparrow
User
Beiträge: 4165
Registriert: Freitag 17. April 2009, 10:28

Ich bin mal so frei und poste deinen Code in Code-Tags, damit die Einrückung erhalten bleibt.
Code-Tags werden automatisch eingefügt, wenn du den </>-Button im "vollständigen Editor" drückst.

Code: Alles auswählen

import serial
import time
impulse =0
Gesamtstrecke= 2680
Weg_bis_Bruecke = 1320
Bremstraegheit = 4  #   das muss noch mit der Geschwindigkeitsstufe verknuepft werden
lok = 16
speed = 3
licht = 16
funktion = 0
with serial.Serial('COM1',2400,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_TWO,timeout=10) as interface:
     interface.write(bytes([speed+licht+funktion, lok]))
with serial.Serial('COM6',9600,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,timeout=10) as wagen:
        erhaltenes_Zeichen = wagen.read()
        while impulse < Weg_bis_Bruecke:
           erhaltener_String = str(wagen.readline())
           gekuerzter_String = erhaltener_String[3 : -6]  
           impulse = int(gekuerzter_String)
           print(impulse)
speed = 0
with serial.Serial('COM1',2400,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_TWO,timeout=10) as interface:
     interface.write(bytes([speed+licht+funktion, lok]))   
print ("Brücke erreicht!")
print ("Warte 10 Sekunden......")
time.sleep(2)  # die ersten 2 von 10 Sekunden........
speed = 3
with serial.Serial('COM1',2400,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_TWO,timeout=10) as interface:
     interface.write(bytes([speed+licht+funktion, lok]))
with serial.Serial('COM6',9600,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,timeout=10) as wagen:
        erhaltenes_Zeichen = wagen.read()
        while impulse < Gesamtstrecke:
           erhaltener_String = str(wagen.readline())
           gekuerzter_String = erhaltener_String[3 : -6]
           impulse = int(gekuerzter_String)
           print(impulse)
print ("Jetzt fahre ich zum Startpunkt")

speed = 0
with serial.Serial('COM1',2400,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_TWO,timeout=10) as interface:
     interface.write(bytes([speed+licht+funktion, lok]))   
print ("Komplette Runde gedreht!")
print ("Programm erfolgreich durchlaufen.")
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Zellsius: Also so grundsätzlich das was schon gesagt wurde. Wenn der Programmfluss den ``with``-Block verlässt wird dem/den Objekten die hinter dem ``with`` erstellt werden gesagt sie sollen bitte ”aufräumen”. Was `Serial`-Objekte dazu bringt sich zu schliessen. Du darfst also nicht für jeden Schreib- oder Lesevorgang neue `Serial`-Objekte erstellen, sondern für die beiden Schnittstellen jeweils nur eines und das dann durchgehend verwenden.

Sonstige Anmerkungen: Wird bei Gelegenheit mal einen Blick in den Style Guide for Python Code.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Da wiederholt sich einiges an Code das man in Funktionen heraus ziehen sollte.

Python hat keine spezielle Syntax für nachprüfende Schleifen, die drückt man deshalb idiomatisch als Endlosschleife aus, die nach einem entsprechenden Test mit ``break`` verlassen wird. Dann muss man keinen unnötigen Dummywert vor der Schleife definieren.

Byte-Objekte wandelt man nicht mit `str()` in Zeichenkettendarstellungen um um danach auf der *Darstellung* eines Byteobjekts mit Slicing zu operieren. Da hier eine Bytefolge in eine Zahl umgewandelt werden soll, braucht auch man gar keine Zeichenkette. Und `int()` kommt auch mit „Whitespace“-Bytewerten vor und nach den Ziffern klar:

Code: Alles auswählen

In [14]: int(b" 4711 \r\n")                                                     
Out[14]: 4711
Nur falls da etwas steht mit dem `int()` nicht zurecht kommt, müsste man das entfernen. Das geht dann aber auch auf dem `bytes`-Objekt.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import time

from serial import Serial, STOPBITS_TWO

LOK_NUMMER = 16
LOK_LICHT = 16
LOK_FUNKTION = 0


def set_speed(interface, value):
    interface.write(bytes([value + LOK_LICHT + LOK_FUNKTION, LOK_NUMMER]))


def drive_to_impulse_count(interface, wagen, speed, target_impulse_count):
    set_speed(interface, speed)
    _ = wagen.read()
    while True:
        impulse_count = int(wagen.readline())
        print(impulse_count)
        if impulse_count >= target_impulse_count:
            break


def main():
    with Serial("COM1", 2400, stopbits=STOPBITS_TWO, timeout=10) as interface:
        with Serial("COM6", timeout=10) as wagen:
            gesamtstrecke = 2680
            weg_bis_bruecke = 1320
            assert weg_bis_bruecke < gesamtstrecke

            drive_to_impulse_count(interface, wagen, 3, weg_bis_bruecke)

            set_speed(interface, 0)
            print("Brücke erreicht!")
            print("Warte 10 Sekunden......")
            time.sleep(2)  # Die ersten 2 von 10 Sekunden.

            drive_to_impulse_count(interface, wagen, 3, gesamtstrecke)

            print("Jetzt fahre ich zum Startpunkt")
            set_speed(interface, 0)
            print("Komplette Runde gedreht!")
            print("Programm erfolgreich durchlaufen.")


if __name__ == "__main__":
    main()
Ein bisschen komisch ist das überlesen eines Bytes von der `wagen`-Verbindung. Eventuell sollte man das nur nach dem öffnen der Verbindung machen‽
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Zellsius
User
Beiträge: 8
Registriert: Donnerstag 12. Mai 2022, 19:34

sparrow: Vielen Dank fuer den Hinweis, aber ich weiss leider nicht, wo ich diesen Button finde bzw. wo ich ihn suchen muss.
Als IDE verwende ich Thonny.

---------------------------------

blackjack:
Dein Code lief auf Anhieb :-)

Zu der Umwandlung bzw. dem Beschneiden des Strings:
Ja, es werden Zeichen empfangen, welche nicht INT-tauglich sind.
Das an COM6: angeschlossene Geraet sendet ein * zu Beginn des Strings und ein % an dessen Ende.
Ausserdem sendet das Geraet auch fuehrende Nullen bis zu einer siebenstelligen Zahl.
Ein typischer String koennte also sein: *0000456%
Das waeren dann 456 Impulse seit dem letzten Reset.

Ich schreibe dir das, weil ich beim Start des Programms sporadisch immer wieder einmal einen Fehler erhalte mit folgendem Wortlaut:

impulse = int(gekuerzter_String)
ValueError: invalid literal for int() with base 10: ''


Starte ich das Programm einfach erneut, taucht der Fehler ( meistens) nicht mehr auf.

Da ich deinen Code ein klein wenig an die Stringbeschneidung angepasst habe, ist dieser Fehler nun nicht wirklich passend.

Ich suche jetzt nach dem Button, den sparrow mir nannte und dann poste ich den aktuellen Code nochmals in einem separaten Beitrag.
Bitte also kurz abwarten, ich bleibe am Ball.
Zellsius
User
Beiträge: 8
Registriert: Donnerstag 12. Mai 2022, 19:34

Code: Alles auswählen

# BR03 faehrt aufs hintere Abstellgleis
import time
from serial import Serial, STOPBITS_TWO
LOK_NUMMER = 30
LOK_LICHT = 16
LOK_FUNKTION = 0
weg_bis_weiche = 2200
weg_bis_abstellgleis = 2500
weg_bis_endpunkt = 3272

def set_speed(interface, value):
    interface.write(bytes([value + LOK_LICHT + LOK_FUNKTION, LOK_NUMMER]))

def drive_to_impulse_count(interface, wagen, speed, target_impulse_count):
    set_speed(interface, speed)
    _ = wagen.read()
    while True:
         erhaltener_String = str(wagen.readline())
         gekuerzter_String = erhaltener_String[3 : -6]  
         impulse = int(gekuerzter_String)
         print(impulse)
         if impulse>= target_impulse_count:
           break        

def main():
    with Serial("COM1", 2400, stopbits=STOPBITS_TWO, timeout=10) as interface:
        with Serial("COM6", timeout=10) as wagen:
            drive_to_impulse_count(interface, wagen, 5, weg_bis_weiche)
            set_speed(interface, 0)
            print("Weiche erreicht!")
            print("Warte 5 Sekunden......")
            time.sleep(6)
            print("Weiche wird in zwei Sekunden geschaltet.")
            time.sleep(2)
            interface.write(bytes([33, 10]))
            time.sleep(2)
            print("Lok wird auf Abstellgleis gefahren.")
            time.sleep(2)
            drive_to_impulse_count(interface, wagen, 15, weg_bis_weiche) # Fahrtrichtung wechseln
            time.sleep(0.2)
            drive_to_impulse_count(interface, wagen, 2, weg_bis_abstellgleis) # langsam rueckwaerts fahren
            set_speed(interface, 0)
            print("Jetzt fahre ich zum Startpunkt")
            print("Warte 5 Sekunden......")
            time.sleep(6)
            drive_to_impulse_count(interface, wagen, 15, weg_bis_abstellgleis) # Fahrtrichtung wechseln
            time.sleep(0.2)
            drive_to_impulse_count(interface, wagen, 5, weg_bis_endpunkt)
            set_speed(interface, 0)
            time.sleep(0.2)
            interface.write(bytes([34, 10]))
            print("Komplette Runde gedreht!")
            print("Die Weiche wurde zurueckgestellt.")
            print("Programm erfolgreich durchlaufen.")

if __name__ == "__main__":
    main()
Zellsius
User
Beiträge: 8
Registriert: Donnerstag 12. Mai 2022, 19:34

sparrow: Das war ja total einfach :-)

Vielen Dank fuer diesen hilfreichen Hinweis.
Antworten