Fehler beim trennen des BLE Gerätes " Could not get GATT characteristics for ..:"

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
Nietzsche
User
Beiträge: 20
Registriert: Dienstag 2. Mai 2023, 13:15

Hallo,

ich möchte die Verbindung zu einem ESP via bleak trennen. Einmal wenn ich den Sensor update. Hier wird zuerst eine Verbindung hergestellt, die Daten übermittelt und diese dann getrennt. Das klappt auch:

Code: Alles auswählen

# Klasse für das Updaten des ESP
class Worker(QThread):
    completed = pyqtSignal()
    received = pyqtSignal(str)

    def run(self):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        async def do_work():
            try:
                self.received.emit("Suche Gerät")  # Text "Suche Geräte" anzeigen
                print("Suche Gerät")
                einstellungen_window_ui.Update.setEnabled(False)


                client = bleak.BleakClient(mac_address)
                await self.connect_client(client)  # Verbindung herstellen

                update_progress(einstellungen_window_ui.progressBar)  # Aktualisieren des  Fortschrittsbalken im Einstellungsfenster

                # String erstellen
                str_parameters = f"{Variable1},{Variable2},{Variable3},{Variable4},{Variable5},{Variable6}"

                # Byte-Array erstellen
                byte_array = str_parameters.encode('ascii')

                print("Sende Werte")
                # Array senden
                await client.write_gatt_char(UUID3, byte_array)

                # Daten vom ESP32 ?ber BLE empfangen
                received = False  # Variable, um anzuzeigen, ob Daten empfangen wurden

                while not received and not self.isInterruptionRequested():
                    data = await client.read_gatt_char(UUID3)
                    if data:
                        value = data.decode('utf-8')
                        print(f"{value}")
                        received = True
                        self.received.emit(value)
                        einstellungen_window_ui.Update.setEnabled(True) #Schaltet den Update Button wieder ein

                await self.disconnect_client(client)  # Verbindung trennen
            except Exception as e:
                print("Nicht verbunden")
                einstellungen_window_ui.Update.setEnabled(True) #Schaltet den Update Button wieder ein

                print(e)
                self.received.emit("Gerät nicht gefunden")
                einstellungen_window_ui.Parameter_Update_Text.setStyleSheet("color: red;")
                Parameter_Update_Text = "Gerät nicht gefunden"


            self.completed.emit()

        loop.run_until_complete(do_work())

    async def connect_client(self, client):
        await client.connect()

    async def disconnect_client(self, client):
        await client.disconnect()
Das funktioniert nun auch. Aber ich möchte andererseits auch eine Verbindung herstellen und Daten aufzeichnen, auch das klappt, nur kann ich über Stopp keine Verbindung trennen. Hier mein Code:

Code: Alles auswählen

class StoppThread (QThread):
    clos_signal = pyqtSignal(str)
    def __init__(self):
        super().__init__()

    def run(self):
        print("Stopp Thread gestartet")

        async def disconnect_client_async():
            
            print("Führe Trennung Client aus")

            async with BleakClient(mac_address) as client:
                await client.disconnect()
                print("Verbindung zum ESP über BLE getrennt.")

        async def close_threads_async():
            await safe_to_excel.close_threads()

        async def stop_tasks():
            await disconnect_client_async()
            await close_threads_async()

        # Starte die asynchronen Aufgaben im aktuellen Event Loop
        loop.run_until_complete(stop_tasks())

        # Sende ein Signal, um den Abschluss des Threads anzuzeigen
        self.clos_signal.emit("Trennung abgeschlossen")
Hier bekomme ich den Fehler:

Code: Alles auswählen

Stopp Thread gestartet
Führe Trennung Client aus
Traceback (most recent call last):
  File "C:\Users\emanu\source\repos\PythonApplication3\PythonApplication3\PythonApplication3.py", line 236, in run
    loop.run_until_complete(stop_tasks())
  File "C:\Users\emanu\miniconda34\lib\asyncio\base_events.py", line 649, in run_until_complete
    return future.result()
  File "C:\Users\emanu\source\repos\PythonApplication3\PythonApplication3\PythonApplication3.py", line 232, in stop_tasks
    await disconnect_client_async()
  File "C:\Users\emanu\source\repos\PythonApplication3\PythonApplication3\PythonApplication3.py", line 224, in disconnect_client_async
    async with BleakClient(mac_address) as client:
  File "C:\Users\emanu\miniconda34\lib\site-packages\bleak\__init__.py", line 491, in __aenter__
    await self.connect()
  File "C:\Users\emanu\miniconda34\lib\site-packages\bleak\__init__.py", line 531, in connect
    return await self._backend.connect(**kwargs)
  File "C:\Users\emanu\miniconda34\lib\site-packages\bleak\backends\winrt\client.py", line 440, in connect
    self.services = await self.get_services(
  File "C:\Users\emanu\miniconda34\lib\site-packages\bleak\backends\winrt\client.py", line 706, in get_services
    characteristics: Sequence[GattCharacteristic] = _ensure_success(
  File "C:\Users\emanu\miniconda34\lib\site-packages\bleak\backends\winrt\client.py", line 120, in _ensure_success
    raise BleakError(f"{fail_msg}: Access Denied")
bleak.exc.BleakError: Could not get GATT characteristics for <_bleak_winrt_Windows_Devices_Bluetooth_GenericAttributeProfile.GattDeviceService object at 0x000001525B1D3D90>: Access Denied
starte update
Wie kann das sein?
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Nietzsche: `Worker` ist als Name zu generisch. Das sollte einen Namen haben der den Kommentar überflüssig macht wofür diese Klasse da ist.

Das `completed`-Signal ist überflüssig weil `QThread` selbst schon ein `finished`-Signal hat. Analog das `clos_signal`-Signal auf `StoppThread`.

Das `received`-Signal ist falsch benannt oder falsch benutzt, denn es übermittelt nicht nur die empfangenen Daten.

`einstellungen_window_ui` kommt einfach so aus dem Nichts, was nicht sein darf. Das hat auf Modulebene nichts zu suchen.

Und das ist GUI die in einem anderen Thread als dem in dem die GUI-Hauptschleife läuft, verändert wird. Das darf man ebenfalls nicht machen, weil die GUI-Zugriffe nicht thread-sicher sind. Das Übertragen der Einstellungen hat ja auch überhaupt nichts mit GUI zu tun, womit der Code da sowieso nichts zu suchen hat. Den Button würde man deaktivieren wenn er gedrückt wurde und beim `completed` bzw. `finished`-Signal dann wieder aktivieren.

Die ”Methoden” `connect_client()` und `disconnect_client()` sind keine echten Methoden und auch als Funktionen wären sie ziemlich überflüssig.

`mac_address` kommt aus dem Nichts. Könnte das eine Konstante sein? Dann sollte es entsprechend KOMPLETT_GROSS geschrieben werden.

`update_progress()` sollte ziemlich sicher keine Funktion sein und auch hier wieder: Nicht aus anderen Threads als dem Hauptthread die GUI verändern. Threads können Fortschritt über ein Signal melden das dann im Hauptthread entsprechend verarbeitet wird.

Das mit `Variable1` bis `Variable6` hatten wird doch ziemlich sicher auch schon mal als Thema. Die Namen sind nichtssagend, falsch geschrieben für Konstanten, und man nummeriert keine Namen. Hier wahrscheinlich ein Kandidat für eine Liste.

Das `received`-Flag für die Schleife kann man sich sparen wenn man die Schleife einfach mit ``break`` verlässt.

Was soll ``f"{value}"`` bei einem `value` das hier garantiert eine Zeichenkette ist?

Der lokale Name `Parameter_Update_Text` ist falsch geschrieben und wird nirgends verwendet.

Eine `__init__()` die einzig die `__init__()` der Basisklasse aufruft, macht keinen Sinn.

`safe_to_excel` und `loop` in `StoppThread` kommen aus dem Nichts und `loop` ist auch komisch, denn das ist ja hier ein eigener Thread, da sollte man nichts in einer EventLoop machen die in einem anderen Thread erstellt wurde. Man kann nicht auf der gleichen EventLoop von zwei verschiedenen Threads aus etwas ausführen und erwarten, dass das funktioniert.

Zwischenstand (ungetestet):

Code: Alles auswählen

UUID3 = ...
MAC_ADDRESS = ...
VARIABLEN = [...]  # TODO Besserer Name.


class ESPUpdateThread(QThread):
    received = pyqtSignal(str)  # TODO Falsch benannt.
    progess = pyqtSignal()

    def run(self):
        async def do_work():
            try:
                self.received.emit("Suche Gerät")
                print("Suche Gerät")

                client = bleak.BleakClient(MAC_ADDRESS)
                await client.connect()
                self.progess.emit()

                print("Sende Werte")
                await client.write_gatt_char(
                    UUID3, ",".join(map(str, VARIABLEN)).encode("ascii")
                )

                while not self.isInterruptionRequested():
                    data = await client.read_gatt_char(UUID3)
                    if data:
                        value = data.decode("utf-8")
                        print(value)
                        self.received.emit(value)
                        break

                await client.disconnect()

            except Exception as error:
                print("Nicht verbunden")
                print(error)
                self.received.emit("Gerät nicht gefunden")

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(do_work())


class StoppThread(QThread):
    def __init__(self, loop, safe_to_excel, parent=None):
        QThread.__init__(self, parent)
        self.loop = loop
        self.safe_to_excel = safe_to_excel

    def run(self):
        print("Stopp Thread gestartet")

        async def stop_tasks():
            print("Führe Trennung Client aus")
            async with bleak.BleakClient(MAC_ADDRESS) as client:
                await client.disconnect()
                print("Verbindung zum ESP über BLE getrennt.")

            await self.safe_to_excel.close_threads()

        self.loop.run_until_complete(stop_tasks())
Der Code dort macht nicht wirklich Sinn. Einen `BleakClient` erstellen und `disconnect()` aufrufen ohne das da vorher `connect()` aufgerufen wurde‽
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Nietzsche
User
Beiträge: 20
Registriert: Dienstag 2. Mai 2023, 13:15

Hallo,

ich komme leider erst jetzt dazu zu antworten. @blackjack Ja, es lag an der Stelle die du erwähnt hattest, ich hatte das kurz nach dem Abschicken auch gesehen und ausgebessert, der Fehler wurde dadurch behoben. Danke für die Anmerkungen, ich habe da begonnen diese aufzuarbeiten, einiges war mir noch nicht bewusst wie bspw das mit der GUI und den Threads. Das mit der Variable zB (bei der MAC) ist nur temporär eine Konstante, das wird in Kürze umgeschrieben.

LG
Antworten