Seite 1 von 1

String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 13:28
von patrickk83
Hallo Leute!

Ich versuche gerade eine Waage mit einem Raspberry Pi auszuwerten. Die Waage verfügt über eine RS232 Schnittstelle und ist mit einem Kabel an einem USB zu RS232 Adapter angeschlossen. Um nun das aktuelle Gewicht auszugeben, muss man die Hardwarenummer (eingestellt 27 bzw. 0x1B) + das Zeichen "p" (0x70) über die serielle Schnittstelle senden. Das funktioniert auch problemlos. Der einfache Code dazu sieht so aus:

Code: Alles auswählen

import atexit
import serial
import time

ser = serial.Serial(port='/dev/ttyUSB0', baudrate=9600, timeout=0.1)
atexit.register(ser.close)

while True:
	ser.write(b'\x1B\x70')  # x1B -> 27    x70 -> p
	print(ser.readline())
	time.sleep(1)
Die Ausgabe sieht dabei wie folgt aus:

Code: Alles auswählen

b'   57.372 ct\r\n'
b'   57.372 ct\r\n'
b'   57.372 ct\r\n'
An der Ausgabe ist zu sehen, dass die Gewichtseinheit auf ct (Karat) eingestellt ist. Ziel ist es genau diese eingestellte Gewichtseinheit zu prüfen und auf die gewünschte Einheit GN (Grain) zu ändern. Um die Einheit zu wechseln muss wieder die Hardwarenummer (0x1B) sowie das Zeichen "s" (0x73) an die Waage gesendet werden. Funktioniert im Prinzip auch wenn ich es einzeln an die Waage sende.
Ich würde aber gerne eine Funktion einbauen um den übertragenen String auf die aktuell eingestellte Gewichtseinheit zu überprüfen und bei Abweichung solange den Code für das Ändern der Gewichtseinheit senden bis die gewünschte Einheit eingestellt ist.

Hier mal meine Überlegung:

Code: Alles auswählen

import atexit
import serial
import time

ser = serial.Serial(port='/dev/ttyUSB0', baudrate=9600, timeout=0.1)
atexit.register(ser.close)

def change_Units():
  ser.write(b'\x1B\x73')

def read_Units():
  ser.write(b'\x1B\x70')
  units = ser.readline()
  units = units[9:12]
  if units != " GN":
    change_Units()
    time.sleep(1)

  print(units)

if __name__ == "__main__":

  while True:

    read_Units()

Die Bedingung wird aber irgendwie nie erfüllt. Die Einheiten werden zwar geändert aber selbst wenn die gewünschten Grain eingestellt sind, läuft die Schleife weiter.

Hier noch ein kurzer Auszug aus dem Manual:

Bild
Bild

Wäre toll wenn mal jemand da drüber sehen würde.

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 14:43
von __blackjack__
@patrickk83: Die Bedingung wird nie erfüllt weil `bytes` niemals gleich Zeichenketten (`str`) sind:

Code: Alles auswählen

In [229]: "Hallo" == b"Hallo"
Out[229]: False

In [230]: b"Hallo" == b"Hallo"
Out[230]: True
Eingerückt wird vier Leerzeichen pro Ebene, nicht zwei.

Das mit dem `atexit()` ist etwas exotisch. `Serial`-Objekte sind Kontextmanager, das würde man also mit der ``with``-Anweisung lösen.

Du hast ein `timeout` eingestellt, der Code kümmert sich aber überhaupt gar nicht um den Fall wenn der Fall mal eintreten sollte.

Das „slicen“ mit den magischen Indexzahlen ist nicht so wirklich leicht zu lesen.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Die Funktion braucht das `Serial`-Objekt als Argument. Das darf nicht einfach so magisch aus der Umgebung kommen.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

`units` wird erst an die ganze Zeile gebunden und nicht nur an die Einheit (Einzahl) — das stimmt dann ja inhaltlich nicht. Ebenso wie `read_Units()` deutlich mehr macht als die Einheit zu lesen. Entweder ist dort der Name falsch, oder der Code ist falsch zwischen den Funktionen aufgeteilt.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import time

import serial


def change_unit(connection):
    connection.write(b"\x1Bs")


def read_unit(connection):
    connection.write(b"\x1Bp")
    _, _, unit = connection.readline().rstrip().rpartition(b" ")
    return unit


def main():
    with serial.Serial("/dev/ttyUSB0") as connection:
        while True:
            unit = read_unit(connection)
            if unit == b"GN":
                change_unit(connection)
                time.sleep(1)
            print(unit)


if __name__ == "__main__":
    main()

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 14:48
von Sirius3
Schreiben die hier wirklich bits, wenn sie bytes meinen??

Bei Dir gilt das übrigens auch, Du schreibst " GN" was ein String ist, Du möchtest aber Bytes b" GN".
Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 2. Funktionen und Variablen werden klein geschrieben, globale Variablen darf man nicht nutzen. Man sollte with statt atexit verwenden.

Code: Alles auswählen

import serial
import time

def change_unit(port):
    port.write(b'\x1B\x73')

def read_unit(port):
    port.write(b'\x1B\x70')
    line = port.readline()
    units = line[9:12]
    if units != b" GN":
        change_units(port)
    return unit

def main():
    with serial.Serial(port='/dev/ttyUSB0', baudrate=9600, timeout=0.1) as port:
        print(read_unit(port))
        time.sleep(1)

if __name__ == "__main__":
    main()

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 15:02
von patrickk83
Danke euch beiden für die raschen Antworten! Python ist komplettes Neuland für mich, ich komme aus der Ära C und dass ist jetzt auch schon ein paar Monde her ;-)
Werde eure sehr hilfreichen Tipps gleich mal austesten.

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 15:16
von patrickk83
Noch eine Verständnisfrage: unit = line[9:12] -> Beginnt man bei Python bei 0 zu zählen? Also wären das jetzt die Bytes 9 bis 12 oder 10 bis 13?
Der Programmcode von Sirius3 wird allerdings nur einmal durchlaufen dann wird das Programm beendet... Die Einheit wird gewechselt dann beendet das Programm obwohl noch nicht die korrekte Einheit "GN" eingestellt ist.

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 16:02
von Sirius3
Ja, Python beginnt bei 0. Wenn das Protokoll wirklich so ist, dass es fixe Byte-Positionen benutzt, kommt man um das Index-Auslesen fast nicht drumrum. Oder ein regulärer Ausdruck zum Parsen. Dazu müßte man aber noch Beispiele kennen, die negativ sind, oder mehr als 3 Vorkommastellen (oder 7 Zeichen) haben.
Und bei mir fehlt die while-Schleife.

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 16:16
von patrickk83
Ja, das Protokoll verwendet fixe Byte-Positionen. Ein Beispiel mit negativem Wert kann ich gleich nachliefern. Ich bin auch schon verzweifelt am Überlegen wo eine while-Schleife am idealsten wäre... Wäre echt super wenn ich noch weiter auf Unterstützung bitten dürfte.

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 16:24
von patrickk83
Für negative Werte sieht die Ausgabe so aus:

Code: Alles auswählen

b'-  176.47 GN\r\n'
Grundsätzlich möchte ich nur erreichen, dass eine Schleife solange den Code zum Einheitenwechseln sendet, bis diese drei Bytes " GN" enthalten (ich denke, vor dem GN steht noch ein Leerzeichen da einige Gewichtseinheiten dreistellig sein können).

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 22:03
von Sirius3
Dann würde ich den Text mit einem regulären Ausdruck parsen:

Code: Alles auswählen

sign, value, unit = re.fullmatch(rb'(-)?\s*(\d*\.\d*)\s*(\S*)\s*', line).groups()

Re: String auf bestimmte Zeichen prüfen

Verfasst: Montag 16. Januar 2023, 23:31
von patrickk83
Danke, ich sehe mir das morgen genauer an.

Re: String auf bestimmte Zeichen prüfen

Verfasst: Dienstag 17. Januar 2023, 19:37
von patrickk83
Folgender Code schaltet die Einheiten meiner Waage solange durch, bis die gewünschte Einheit erreicht ist.

Code: Alles auswählen

import atexit
import serial
import time

ser = serial.Serial(port='/dev/ttyUSB0', baudrate=9600, timeout=0.1)
atexit.register(ser.close)

def change_units():
    ser.write(b'\x1B\x73')

def read_units():
    ser.write(b'\x1B\x70')
    units = ser.readline().decode('utf-8')
    units = units[9:12]
    print(units)
    return units


if __name__ == "__main__":

    while read_units() != " GN":
        change_units()
        time.sleep(1)