Serielles Interface lässt sich unter Windows oft nicht aufrufen

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
Crix1990
User
Beiträge: 5
Registriert: Sonntag 19. Dezember 2021, 15:39

Hallo,

ich hoffe, ihr könnt mir hier weiterhelfen.
Ich steuere Über Python Skripte meine Stereo Anlage (via Seriellem Interface).
Nachdem ich mir einen neuen Computer gekauft habe, habe ich aber leider regelmäßig Probleme damit.
Das USB-Serial Interface ist das gleiche geblieben. USB Anschlüsse habe ich mehrere durchprobiert.

Hier mal ein Biespiel für die Skripte:

Code: Alles auswählen

import serial
import sys
import threading
import re
import time

try:
    ser = serial.Serial( port="COM7",
                         baudrate=9600,
                         bytesize=serial.EIGHTBITS,
                         parity=serial.PARITY_NONE,
                         stopbits=serial.STOPBITS_ONE,
                         timeout=20000 )

    print ("Serial port is open")
except Exception as e:
    print ("error open serial port: " + str(e))
    exit()

try:
    s = "Test"
    print (s)
    ser.write(b'\r')
    ser.write(b'#1,01,0\r')
    s = ser.read(6)
    if s == (b'#11,01'):
        print ("Was already off.")
        ser.write(b'#1,01,1\r')

    print (s)
except Exception as e:
    print ("error communicating...: " + str(e))
Als Fehlermeldung kriege ich dieses zurück:

Code: Alles auswählen

Python 3.10.1 (tags/v3.10.1:2cd268a, Dec  6 2021, 19:10:37) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.

= RESTART: C:\Program Files (x86)\Corsair\CORSAIR iCUE Software\Macros\Python\Power_Toggel.pyw
error open serial port: could not open port 'COM7': PermissionError(13, 'Zugriff verweigert', None, 5)
w
Ich hatte auf meinem alten Rechner Python 3.7 und PySerial 3.4.
Jetzt habe ich 3.10 und 3.5.
Ein Versuch mit den alten Versionen ist aber erfolglos geblieben.

Im Moment leuft es nur so in 10% der Fälle.
Wenn es nicht funktioniert hilft nur PC Neustart.
Kein Anderes Programm nutzt (bewusst) dieses Interface.
Neustart des Interfaces über die Systemsteuerung hilft nur ganz selten.
Komplette Neuinstallation der Treiber hat leider auch nichts gebracht.

Ich hoffe ihr könnt mir helfen...

Grüße,
Felix
Benutzeravatar
sparrow
User
Beiträge: 4540
Registriert: Freitag 17. April 2009, 10:28

Irgend etwas benutzt den Port bereits, bevor du ihn öffnest.
Möglicherweise du selbst, denn du schließt den Port nirgends. Deshalb öffnet man, wie in der Dokumentation beschrieben mit dem with-Statement, um sicherzustellen, dass die Schnittstelle korrekt wieder geschlossen wird.
Benutzeravatar
darktrym
User
Beiträge: 785
Registriert: Freitag 24. April 2009, 09:26

Oder einfach die Verbindung explizit schließen.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
Benutzeravatar
sparrow
User
Beiträge: 4540
Registriert: Freitag 17. April 2009, 10:28

Das finde ich keine gute Idee, denn da tun sich Fallstricke auf, die man umgehen kann, wenn man einfach das with-Statement verwendet. Ich würde fast darauf wetten, dass es sonst nicht korrekt verwendet wird.
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

`sys`, `threading`, `re` und `time` werden importiert, aber gar nicht gebraucht.
Das Exceptionhandling ist ziemlich unsinnig, weil Du nur informative Tracebacks durch weniger aussagekräftige Meldungen ersetzt, die können also weg.
Der serielle Port muß auch wieder geschlossen werden. Ist das Zeilenende wirklich nur \r? Das ist sehr ungewöhnlich. Dass Du nur 6 Bytes liest und nicht eine ganze Zeile, sieht genauso falsch aus.

Besser sähe das wohl so aus:

Code: Alles auswählen

import serial

with serial.Serial(
        port="COM7",
        baudrate=9600,
        bytesize=serial.EIGHTBITS,
        parity=serial.PARITY_NONE,
        stopbits=serial.STOPBITS_ONE,
        timeout=20000) as port:
    port.write(b'\r\n')
    port.write(b'#1,01,0\r\n')
    data = port.readline()
    if data.startswith(b'#11,01'):
        print("Was already off.")
        port.write(b'#1,01,1\r\n')
    print(data)
Du hast ein Konsolenprogramm, warum heißt das dann Power_Toggel.pyw, mit w am Ende?
Crix1990
User
Beiträge: 5
Registriert: Sonntag 19. Dezember 2021, 15:39

Danke schon mal für die Rückmeldungen!

Ich werde versuchen mir das morgen mit dem sauberen Schließen anzulesen.

Um schon mal auf die anderen Fragen einzugehen:
Das /r ist hier ein Enterersatz, wird der Befehl damit nicht geschlossen, wird er vom Verstärker nicht ausgeführt (simple RS-232 Schnittstelle). Das vorausgehende /r soll quasi die Zeile frei machen, falls da noch "Reste drin sind".
Dass ich nur 6 Bytes auswerte liegt daran, dass der Verstärker maximal 6 Bytes zurück gibt. Auf die meisten Befehle gibt es gar keine Rückmeldung.
.pyw nutze ich, damit das Skript fensterlos im Hintergrund aufgerufen wird (ich habe die Skripte auf Tasten meiner Tastatur gelegt).

Wiegesagt, das ganze hat die letzten 6 Jahre problemlos funktioniert.
Erst mit dem neuen PC gibts Probleme...

Ich hab deinen Vorschlag mal ausprobiert, fliege aber auf massig Fehlermeldungen:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\Felix\Desktop\Power_Toggel.pyw", line 8, in <module>
    with serial.Serial(
  File "C:\Program Files\Python39\lib\site-packages\pyserial-3.5-py3.10.egg\serial\serialwin32.py", line 33, in __init__
    super(Serial, self).__init__(*args, **kwargs)
  File "C:\Program Files\Python39\lib\site-packages\pyserial-3.5-py3.10.egg\serial\serialutil.py", line 244, in __init__
    self.open()
  File "C:\Program Files\Python39\lib\site-packages\pyserial-3.5-py3.10.egg\serial\serialwin32.py", line 64, in open
    raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
serial.serialutil.SerialException: could not open port 'COM7': FileNotFoundError(2, 'Das System kann die angegebene Datei nicht finden.', None, 2)
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Crix1990: Das weniger als 6 Bytes von der Gegenseite kommen kann eigentlich nicht sein, weil ``read(6)`` wartet/blockiert bis 6 Bytes gelesen wurde. Ausser die Zeitüberschreitung von 20.000 Sekunden wird überschritten. Das heisst wenn weniger als 6 Bytes kommen, dann blockiert Dein Programm etwas mehr als 5½ Stunden die serielle Schnittstelle. Womit wir vielleicht dem ursprünglichen Problem auf der Spur wären‽ Kann es sein, dass ein alter/vorheriger Prozess Deines Programms den Port blockiert und verhindert, das ein erneuter Programmstart den benutzen kann‽
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Crix1990
User
Beiträge: 5
Registriert: Sonntag 19. Dezember 2021, 15:39

So, um positive Nachrichten zu bringen:
Das grundsätzliche Problem war, dass der Port nicht geschlossen wurde (in jedem Skript).
Ich schließe ihn jetzt nach jedem Skript explizit mit ser.close() und habe den Timeout auf 2 Sekunden reduziert.

Um sauberer zu laufen habe ich das Auslesen der Antworten auf readline() gesetzt (wobei es nach den vorherigen Änderungen auch mit ser.read(6) geklappt hat.

Ich habe dann auch mal die Imports ausgedünnt, wobei time mit Absicht drin war, da es in der Hälfte der Skripte genutzt wird.

Wahrscheinlich hatte ich mit dem alten PC (warum auch immer) nur Glück, dass die Ports kein Problem mit dem schließen hatten.

Danke euch nachmals und schöne Weihnachten!

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

@Crix1990: Statt den manuell mit `close()` zu schliessen solltest Du ``with`` verwenden. Dann ist egal *warum* der Block verlassen wird, es wird immer geschlossen. Zum Beispiel auch wenn das Programm wegen einer Ausnahme abbricht während der Port offen ist.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Crix1990
User
Beiträge: 5
Registriert: Sonntag 19. Dezember 2021, 15:39

Kannst du mir dafür ein Beispiel geben?
Ich habe mir meine Python Kentnisse quasi nur an diesen Skripten beigebracht und hab sonst keine Ahnung...^^
Benutzeravatar
sparrow
User
Beiträge: 4540
Registriert: Freitag 17. April 2009, 10:28

Dehalb habe ich dir in meiner ersten Antwort sogar einen Link zur Dokumentation gepackt.
Crix1990
User
Beiträge: 5
Registriert: Sonntag 19. Dezember 2021, 15:39

Danke, das war für mich am Anfang nicht klar, wie da die Syntax aufgebaut wird, habs jetzt aber mit Probieren raus bekommen.

Nur um sicher zu sein, so ists sauber?

Code: Alles auswählen

import serial


with serial.Serial( port="COM3",
                         baudrate=9600,
                         bytesize=serial.EIGHTBITS,
                         parity=serial.PARITY_NONE,
                         stopbits=serial.STOPBITS_ONE,
                         timeout=2 ) as ser:






    ser.write(b'\r')
    ser.write(b'#1,02\r')
    print("write data: Volume Up")
Antworten