"Can only concatenate str (not "bytes") to str"

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
fcutim
User
Beiträge: 12
Registriert: Dienstag 11. Oktober 2022, 16:03

Moin,

ich bin aktuell dabei, ein GSM Modul über Python anzusteuern. Nun aber bekomme ich eine Fehler beim ausführen des Programmes ausgespuckt. Das Programm soll an "COM7" den Befehl "AT" senden, und soll vorher noch ausgeben, ob der Port offen ist. Das Programm gibt noch an "COM7 ist offen", stürzt aber dann mit dem Fehler code "Can only concatenate str (not "bytes") to str" ab.

Hat jemand davon eine Ahnung wie ich das beheben kann?

Code:

Code: Alles auswählen


import serial, time

def serial_def():
    ser = serial.Serial()
    ser.port = "COM7"
    ser.baudrate = 9600
    ser.bytesize = serial.EIGHTBITS 
    ser.parity = serial.PARITY_NONE 
    ser.stopbits = serial.STOPBITS_ONE 
    ser.timeout = 2              
    ser.xonxoff = False     
    ser.rtscts = False     
    ser.dsrdtr = False       
    ser.writeTimeout = 2
    ser.open()

    if ser.isOpen():
         print(ser.name + ' is open...')

    ser.write(str.encode("AT"+"\r\n"))
    time.sleep(1)
    out=''
    while True:
        out += ser.read(1)
        print (out)        

    ser.close()

if __name__ == '__main__':
    serial_def()
    
    
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Meldung kann man auch googeln, dann findet man eine ganze Menge von Antworten. Aber eine mehr schadet ja auch nicht. Von der seriellen Schnittstelle bekommst du Bytes. Das sind eine Reihe von Werten von 0-255. 8Bit/Byte eben. Und jetzt willst du das mit einem String verkbinden, bei dem jedes Zeichen eines von zehn- oder hunderttausenden aus den Schriftzeichen, Sonderzeichen, Zahlen, Emojiis und was nicht der ganzen Welt sein kann. Das geht halt nicht. Die Bytes muessen erst einem encoding entsprechend in einen String gewandelt werden. Fuer dich reicht das encoding "ascii". Also

Code: Alles auswählen

ser.read(1).decode("ascii")
Ein paar weitere Anmerkungen: der Name "serial_def" ist komisch, def steht doch schon davor. Die ganzen Argumente an das Serial-Objekt kann man im Konstruktor uebergeben (oder auch sein lassen, weil alles zur Paritaet, Bitanzahl, Stopbits eh per default schon richtig sind, siehe https://pyserial.readthedocs.io/en/late ... ial.Serial - damit entfaellt dann auch das open. Das ist dann schon open. Und nach dem open kann das auch nicht nicht open sein. Das 'isOpen' ist also cargo-cultiger Quatsch. Das zusammenpropeln des AT-Kommandos aus zwei Strings ist auch ueberkompliziert, warum nicht "AT\r\n"?

Edit: narpfels Anmerkung eingebaut, decode benutzt.
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

Kleiner Flüchtigkeitsfehler bei __deets__: Von `bytes` nach `str` muss man natürlich dekodieren, nicht enkodieren:

Code: Alles auswählen

ser.read(1).decode("ascii")
Außerdem: Methoden ruft man normalerweise auf einem Objekt auf, also

Code: Alles auswählen

"AT\r\n".encode()
statt

Code: Alles auswählen

str.encode("AT\r\n")
Wobei man hier auch einfacher ein `bytes`-Literal benutzen kann:

Code: Alles auswählen

b"AT\r\n"
Edit: Name korrigiert, ich kann anscheinend nicht lesen. :shock:
Zuletzt geändert von narpfel am Dienstag 11. Oktober 2022, 17:31, insgesamt 1-mal geändert.
fcutim
User
Beiträge: 12
Registriert: Dienstag 11. Oktober 2022, 16:03

narpfel hat geschrieben: Dienstag 11. Oktober 2022, 17:04
Außerdem: Methoden ruft man normalerweise auf einem Objekt auf, also

Code: Alles auswählen

"AT\r\n".encode()
statt

Code: Alles auswählen

str.encode("AT\r\n")
Hab jetzt deine Variante ausprobiert, und es kommt immer noch die selbe Nachricht. Mach ich irgendwie etwas falsch oder liegts doch am Code?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bitte den jetztigen Code zeigen. Und die vollstaendige Fehlermeldung. Die zitierten Aenderungen sind im uebrigen "Kosmetik", wenn auch natuerlich richtig. Aber reichen nicht, um den Fehler zu beheben. Dazu muss schon dekodiert werden, wie ich (und narpfel) es zeigen.
fcutim
User
Beiträge: 12
Registriert: Dienstag 11. Oktober 2022, 16:03

Code: Alles auswählen


import serial, time
def serial_def():
    ser = serial.Serial()
    ser.port = "COM7"
    ser.baudrate = 9600
    ser.bytesize = serial.EIGHTBITS
    ser.parity = serial.PARITY_NONE
    ser.stopbits = serial.STOPBITS_ONE
    ser.timeout = 2
    ser.xonxoff = False
    ser.rtscts = False
    ser.dsrdtr = False
    ser.write_timeout = 2
    ser.open()

    if ser.isOpen():
        print(ser.name + ' is open')

    ser.write("AT"+"\r\n".encode())
    time.sleep(1)
    out='' 
    while True:
        out += ser.read(1).decode("ascii")
        print (out)

    ser.close()

if name == 'main':
    serial_def()

Fehler Meldung:

Code: Alles auswählen


Traceback (most recent call last):
  File "c:\Users\Tim\Desktop\GSM\main.py", line 29, in <module>
    serial_def()
  File "c:\Users\Tim\Desktop\GSM\main.py", line 19, in serial_def
    ser.write("AT"+"\r\n".encode())
TypeError: can only concatenate str (not "bytes") to str

Hoffe ich hab das so richtig verstanden, wie ihr das meintet.
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@fcutim: Naja, das ist jetzt der gleiche Fehler, der vorher in der Zeile mit dem `out += ...` aufgetreten ist, an einer anderen Stelle. Der Code, den du zeigst, passt übrigens nicht zur Fehlermeldung. Bitte achte darauf, den tatsächlichen Code zu zeigen, nicht irgendwas, was so ähnlich ist.
Zuletzt geändert von narpfel am Dienstag 11. Oktober 2022, 17:34, insgesamt 1-mal geändert.
fcutim
User
Beiträge: 12
Registriert: Dienstag 11. Oktober 2022, 16:03

Wärst du so lieb und schreibst mir das fix um? Ich denke mal das ich zu doof dazu bin :lol:
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Benutze keine kryptischen Abkürzungen. Was soll `def` bei `serial_def` denn bedeuten? `ser` ist auch komisch? Die ganzen Einstellungen kann man gleich bei der Initalisierung angeben, und muß sie nicht nachträglich setzen. Defaultwerte muß man nicht unbedingt setzen.
Der Aufruf von open ist damit überflüssig und es ist garantiert, dass der Port offen ist, ein isOpen-Aufruf ist damit genauso überflüssig, vor allem, weil auch im False-Fall Du so weiter machst, als ob der Port offen ist.
encode ruft man auf einem String-Objekt auf, und nicht über die Klasse. Wobei hier am besten gleich mit literalen Bytes gearbeitet wird.
Man benutzt with.

Code: Alles auswählen

import serial, time

def communicate():
    with serial.Serial(
        port="COM7",
        baudrate=9600,
        timeout=2,
        write_timeout=2
    )  as port:
        port.write(b"AT\r\n")
        time.sleep(1)
        out = b''
        while True:
            out += port.read(1)
            print(out)

if __name__ == '__main__':
    communicate()
fcutim
User
Beiträge: 12
Registriert: Dienstag 11. Oktober 2022, 16:03

Vielen Dank! Aber jetzt kommt ein kleines Problem...

Code: Alles auswählen

NameError: name 'name' is not defined
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Und schon wieder eine verstümmelte Fehlermeldung ohne den passenden Code dazu...
fcutim
User
Beiträge: 12
Registriert: Dienstag 11. Oktober 2022, 16:03

Du hast doch gerade eben selbst den Code reingeschickt... Dann brauch ich ihn doch nicht noch einmal reinschicken.

Edit: Habs jetzt selbst herausgefunden.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da in Sirius3 Code name nicht vorkommt, hast du offensichtlich was verbockt. Und darum solltest du den Code zeigen. Musst du aber natürlich nicht, kann dir halt dann keiner helfen.
fcutim
User
Beiträge: 12
Registriert: Dienstag 11. Oktober 2022, 16:03

Guck den Edit von meinem Kommentar auf die Antwort von Sirius3, dann weißt du Bescheid. 8)
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@fcutim: `out` ist ein komischer Name für Daten die *rein* kommen. Ich würde da aus Effizienzgründen auch eher ein `bytearray` verwenden statt `bytes`. Das kann nämlich tatsächlich um neue Daten erweitert werden.

Dann sollte entweder das `write_timeout` nicht sein, oder da gehört noch Code hin der auf diesen Fall auch sinnvoll reagiert.

Beim `timeout` bin ich mir nicht sicher ob Du das verhalten wirklich haben willst, oder warum Du das gesetzt hast‽ Die ist klar welchen Effekt das bei dem vorhanden Code hat, und das soll auch so sein?

Zwischenstand:

Code: Alles auswählen

def communicate():
    with serial.Serial(port="COM7", timeout=2) as port:
        port.write(b"AT\r\n")
        time.sleep(1)
        buffer = bytearray()
        while True:
            buffer += port.read(1)
            print(buffer)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten