Serielle Kommunikation

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
mochtend

Hey Leute,

ich habe eine Frage zum Thema Serielle Kommunikation, habe mich mit der Suche bemüht allerdings habe ich kein Thema gefunden was meinem Problem ähnelt.
Erstmal zu meinem Vorhaben:
Ich möchte per serieller Kommunikation Istwerte und Sollwerte auslesen.
Ich bekomme zuerst meinen Sollwert und ein paar Sekunden später meinen Istwert gesendet.

Ich habe ein kleines Programm welches zum auslesen der Daten dient und auch funktioniert:

Code: Alles auswählen

import serial
import time

s = serial.Serial(port = "/dev/ttyS0",
                  baudrate = 19200,
                  parity = serial.PARITY_NONE,
                  stopbits = serial.STOPBITS_ONE,
                  bytesize = 8,
                  timeout = 1)
                  
istwert = 0
sollwert = 0

def get_serial():
    while True:
        daten = s.readline()
        time.sleep(0.03)
        daten_left = s.inWaiting()
        daten += s.read(daten_left)
        daten_str = str(daten)
        print(daten_str)
        if daten_str[2] == 6:
            sollwert = daten_str
            print("Empfangener Sollwert =", sollwert)
            return sollwert
        if daten_str[2] == 5:
            istwert = daten_str
            print("Empfangener Istwert =", istwert)
            return istwert
    
print(istwert)
print(sollwert)
Meine Ist- und Sollwerte sind mit zwei verschiedenen Zahlen zur Unterscheidung gekennzeichnet 6 = Sollwert, 5 = Istwert.

Mein Problem hierbei ist, dass ich in der while-Schleife feststecke und dauerhaft Daten bekomme, auch wenn ich meinen Istwert schon bekommen habe.
Beim Ausführen des Codes erhalte ich dann:
  • Ausgabe:
    b' '
    b' '
    b'Istwert'
    b' '
    b' '
Hat da jemand eine Idee woran das liegen kann?
Einen Error bekomme ich nicht

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

Variabennamen sollten aussagekräftig sein, `s` ist das nicht. Du rufst get_serial gar nicht auf, da kann also auch nichts passieren. Benutze keine globalen Variablen.
Auch bei Serieller Kommunikation muß man darauf achten, dass man nur komplette Nachrichten verarbeitet. Dein Code ignoriert das bisher. Heißt, lese nur ganze Zeilen, das mit dem sleep und inWaiting, etc ist viel zu kompliziert. Du hast einen Timeout definiert, von daher ist es logisch, dass Du leere Antworten bekommst. Lass einfach den Timeout weg. Das `return` ist wenig sinnvoll, so weißt Du ja gar nicht, ob ein Sollwert oder ein Ist-Wert zurückgegeben wird.

Code: Alles auswählen

import serial
import time

def get_serial(connection):
    for daten in connection:
        if len(daten) > 2:
            # nicht robust, Du mußt ein eindeutiges Kennzeichen
            # dafür haben, dass die Nachricht komplett ist.
            if daten[2] == 6:
                print("Empfangener Sollwert =", daten)
                return "sollwert", daten
            elif daten[2] == 5:
                print("Empfangener Istwert =", daten)
                return "istwert", daten
        # else: ignore data
        print("ignoriert:", daten)

def main():
    connection = serial.Serial(port = "/dev/ttyS0",
                  baudrate = 19200,
                  parity = serial.PARITY_NONE,
                  stopbits = serial.STOPBITS_ONE,
                  bytesize = 8)
    while True:
        typ, wert = get_value(connection)
        print(typ, wert)

if __name__ == '__main__':
    main()
mochtend

Ja da hast du recht ich rufe es im Programm direkt nicht auf aber per Kommandozeile, weil ich es erstmal testen wollte, habe ich vergessen zu erwähnen sorry!!

Danke für die schnelle Antwort :)
mochtend

Oh das mit dem Timeout war mir nicht klar, danke für die Info.
Warum genau ist mein return denn wenig sinnvoll?
Ich gebe doch die Daten nur zurück wenn ich vorher einen 5 für istwert oder eine 6 für sollwert erkannt habe oder nicht?

Habe deinen Code mal fix getestet habe einen Fehler rausbekommen:
Traceback (most recent call last):
File "/home/pi/Desktop/Testprogramme/Serial Read.py", line 32, in <module>
main()
File "/home/pi/Desktop/Testprogramme/Serial Read.py", line 28, in main
typ, wert = get_value(connection)
NameError: name 'get_value' is not defined

Meinst du statt get_value an der Stelle eher get_serial?
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Du bekommst einen Wert, aber weißt nicht, ob das nun der Sollwert oder der Istwert ist.
mochtend

Hey wollte das Thema nochmal aufgreifen.

Die Serielle Übertragung läuft soweit, ich bekomme aber dennoch einen kleinen Fehler.

Code:

Code: Alles auswählen

connection_soll = serial.Serial(port="/dev/ttyS0",
                           baudrate=19200,
                           parity=serial.PARITY_NONE,
                           stopbits=serial.STOPBITS_ONE,
                           bytesize=8,
                           timeout=1)

def get_serial_ist():
    while True:
        data = connection_ist.readline()
        print(len(data))
        # print(data)
        data_str = str(data)
        # print(data_str[2])
        if len(data) == 86 and data_str[2] == "5":
            istwert = data_str 
            # print("Empfangen(Ist-Wert) =", istwert)
            return istwert
            
def get_serial_soll():
    while True:
        data = connection_soll.readline()
        print(len(data))
        # print(data)
        data_str = str(data)
        # print(data_str[2])
        if len(data) == 86 and data_str[2] == "6":
            sollwert = data_str
            # print("Empfangen(Soll-Wert) =", sollwert)
            return sollwert
            
def vergleich():
    if para_ist == para_soll:
        print("Parameter identisch")
        return True
    else:
        print("Parameter abweichend")
        return False
       
       
"""MAIN"""       
# Soll-Wert Abfrage
print("Sollwert Empfangen: ")
sollwert = get_serial_soll()

# Ist-Wert Abfrage
print("Istwert Empfangen: ")
istwert = get_serial_ist()

vergleichsabfrage = vergleich()


dann kommt gut was an Code dazwischen, bevor ich meinen Istwert wieder zum erneuten vergleich abrufe...

Code: Alles auswählen

# Ist-Wert Abfrage
print("Istwert Empfangen: ")
istwert = get_serial_ist()

vergleichsabfrage = vergleich()

Was hier jetzt genau nicht Funktioniert ist die spätere Abfrage welche im 2. Codeabschnitt steht.
Normalerweise sollte ich dort ein 86 Zeichen langes Protokoll erhalten, aber ich erhalte dort nur 3 Zahlen wie z.B.: 172 oder 344 oder 275.

Wenn ich mir allerdings manuell also mit Handbedienung einen Parameter Eingebe erhalte ich ein Protokoll welches wieder 86 Zeichen lang ist und auch richtig ist.

Das was mir jetzt Fragen aufwirft ist, warum ich da diese nicht gewollten Zahlen bekomme...
Hat da jemand eine Idee warum das so ist?
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Die while-Schleifen sind eigentlich for-Schleifen, wie ich in meinem letzten Post schon gezeigt hatte. Da die beiden get_-Funktionen quasi identisch sind, sollten sie nur einmal existieren.
Die Stringrepräsentation eines Bytes-Objekts ist nicht für die Weiterverarbeitung gedacht. Was möchtest Du denn mit den empfangenen Zeilen weiter machen?
Um Deine konkrete Frage beantworten zu können, müßte man die ganzen empfangenen Daten auch kennen.
Eine String """MAIN""" ersetzt keine main-Funktion, zusätzlich fehlen allen Funktionen die nötigen Parameter, weil globale Variablen zu benutzen ist schlecht.
mochtend

Code: Alles auswählen

def get_serial(connection):
    for data in connection
        data = connection.readline()
        print(len(data))
        # print(data)
        data_str = str(data)
        # print(data_str[2])
        if len(data) == 86 and data_str[2] == "5":
            istwert = data_str     # Übergabe des Datenstring
            # print("Empfangen(Ist-Wert) =", istwert)
            return istwert
        if len(data) == 86 and data_str[2] == "6":
            sollwert = data_str
            # print("Empfangen(Soll-Wert) =", sollwert)
            return sollwert
So ist es also eher der Norm entsprechend?

Das """MAIN""" soll auch keine main Funktion ersetzen, das ist einfach ein Überpunkt für mich um zu sehen wo mein Hauptprogramm beginnt.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja genau.
Solche "Überpunkte" macht man nicht, weil der saubere Weg das Definieren von Funktionen ist.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mochtend: Jetzt sind da spezifische Codeteile für beide Fälle ich der Funktion, die ja im Grunde immer noch aus Code-Wiederholung(en) bestehen. Der Wert an zweiter Stelle in den Daten ist ein Parameter.

Ausserdem ist da jetzt durch ``for`` *und* `readline()` ein Fehler drin → Du überliest jede zweite Zeile einfach.

Ungetestet:

Code: Alles auswählen

from functools import partial


def read_value(this_arguments_needs_a_better_name, connection):
    for data in connection:
        if len(data) == 86 and data[1] == this_arguments_needs_a_better_name:
            return data
    
    raise IOError("unexpected end of data")


read_ist_wert = partial(read_value, b"5")
read_soll_wert = partial(read_value, b"6")
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
mochtend

@Sirius3
Okay danke, gut zu wissen.

Habe das jetzt mal so im Programm untergebracht.

Allerdings ergibt sich dadurch folgender Fehler...

Und zwar, wenn ich einen Soll Wert erwarte und aus irgendeinem Grund ein zufälliges Ist Wert Protokoll erhalte wird mir ein Ist Wert Protokoll als Soll Wert Protokoll geschickt was ja nicht meine Absicht, da dies zu Fehlern in meiner Automatisierung führt, naja nicht direkt Fehler... eher ungewollte Umwege.
Also kann ich in diesem Fall nicht beide get_serial_... in eine schreiben.
mochtend

@Sirius3

Du hattest noch gefragt was ich überhaupt mit den Daten die ich empfange machen möchte.
Nachdem ich die Daten Empfange mache ich daraus erstmal einen String, aus diesem String hole ich mir dann meine Parameter, welche ich benötige raus und wandle diese wieder in Integer um.
Dann habe ich meine Istwerte in einem Array und in dem anderen meine Sollwerte, diese werden dann verglichen.
Anhand der Übereinstimmung oder auch nicht werden Parameter an meinem "Zielgerät" angepasst. Das funktioniert auch alles ohne Probleme.

Nur meine spätere istwert abfrage, welche nach der Anpassung zum nochmaligem vergleich von Ist und Soll aufgerufen wird, will nicht das richtige Protokoll empfangen, stattdessen empfängt es nur Zahlen (bis jetzt 3 stellig wie z.B. 172)

Das ist das einzige Problem was ich noch an meinem Programm habe danach wäre es komplett.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mochtend: Das ist ja gerade der Fehler der durch das zusätzliche Argument behoben wird das entscheidet *was* gelesen werden soll.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie man die beiden get-Funktionen zusammenfasst, hat ja __blackjack__ nun gezeigt.
Zu Deinem Problem, versteh ich immer noch nicht, was das Problem ist. Kannst Du die Zeilen posten, die Du bekommst und den Code, der diese dann verarbeitet.
mochtend

__blackjack__ hat geschrieben: Montag 1. Februar 2021, 15:30 @mochtend: Das ist ja gerade der Fehler der durch das zusätzliche Argument behoben wird das entscheidet *was* gelesen werden soll.
Ich nehme an, dass dies auf meinen Post bezogen ist,wo ich gesagt habe, dass man dann ist und soll nicht voneinander unterscheiden kann?
mochtend

@Sirius3 da meine Künste im erläutern von Problemstellungen anscheinend nicht so gut sind führe ich es hier nochmal etwas mehr aus:

Das ist ein Dataframe was ich normalerweise empfange:

Sollwert Empfangen:
Raw_Dataframe(SOLL) = b'60-01.00.0015:49:0801.02.2021 31-------- 4 50050---- 0300--- 0-----------------60\r'
Dataframe nach Transformation(SOLL) = ('60', ' 3', '1', ' 4', ' 500', '50', '----', '300', ' 0', '---', ' 0', '----', '---', '---')
Parameter(SOLL) = (4, 500, 50, 300, 0, 0)


Istwert Empfangen(S):
Raw_Dataframe(IST) = b'50A01.00.0015:49:4101.02.2021 311 400100 4 50085---- 0300--- 0-----------------66\r'
Dataframe nach Transformation(IST) = ('50', ' 3', '1', ' 4', ' 500', '85', '----', '300', ' 0', '---', ' 0', '----', '---', '---')
Parameter(IST) = (4, 500, 85, 300, 0, 0)

Die Frames sind natürlich gleich von der Struktur.


Danach vergleiche ich die beiden Parameter bzw. bilde die Differenz der Werte mit:

Code: Alles auswählen

def diff_soll_ist(soll, ist):
    diff = list(map(int.__sub__, soll, ist))
    print("Ist =", ist)
    print("Soll =", soll)
    print("Diff =", diff)
    return diff
    
Im Fall der Soll und Ist Werte welche oben stehen erhalte ich dann:

Differenz = (0, 0, -35, 0, 0 ,0)

Danach erfolgt dann meine Automatische Werteanpassung welche die Werte, welche ungleich 0 sind anpasst.


im Anschluss frage ich dann meinen neuen Istwert ab (an dieser stelle ist dann der Fehler):

Code: Alles auswählen

istwert = get_serial()
hier erhalte ich dann aber kein Protokoll mit 86 Zeichen sondern nur drei Werte:
Was ich da erhalte habe ich mit einer kleinen Modifikation meines alten Codes anzeigen lassen:

Code: Alles auswählen

def get_serial():
    while True:
        data = connection_ist.readline()
        # print(len(data))
        # print(data)
        data_str = str(data)
        # print(data_str[2])
        if len(data) == 86 and data_str[2] == "5":
            istwert = data_str     # Übergabe des Datenstring
            # print("Empfangen(Ist-Wert) =", istwert)
            return istwert
        elif len(data) >= 2:
            empfangen = data_str
            print("Empfangen(Ist-Wert) =", empfangen)
und hier erhält meine Automatisierung dann kein Protokoll sondern bleibt dann "hängen".


@Sirius3 ich hoffe ich konnte mit dieser kleinen Ausführung deutlicher rüberbringen was genau jetzt der fehler bei mir ist.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Es scheint also so zu sein, dass das Protokoll beim Ändern von Werten von dem abweicht, was Du erwartest.
Konsequenz daraus ist, dass Du Dein Programm anpassen mußt, dass es mit diesem Fakt auch umgehen kann. Es sei denn, Du machst beim Ansprechen deines Geräts einen Fehler. Da Du weder das eine noch das andere hier gezeigt hast, kann man Dir da nicht weiter helfen.
Aber entweder das eine oder das andere sollte doch in der Dokumentation der Schnittstelle stehen und Schnittstellen muß man exakt so wie vorgesehen bedienen, jede kleinste Abweichung kann zu undefiniertem Verhalten führen.
Antworten