Mobi Zaman
Mobi Zaman

Reputation: 645

Error in BLE communication between ESP32 and Python

I am running a BLE server on an ESP32 which is sending the value of time periodically. This code is being run on Arduino IDE. On the other hand, I have a Python code running on my PC which needs to receive that value and display it on the GUI.

When I run the Python code, it connects to the ESP32 successfully but throws an error while trying to subscribe to the notifications from the ESP32. The error is as follows:

Exception in thread Thread-1 (start_ble_loop):
Traceback (most recent call last):
  File "C:\Program Files\Python312\Lib\threading.py", line 1073, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python312\Lib\threading.py", line 1010, in run
    self._target(*self._args, **self._kwargs)
  File "E:\AC_Work\Task_9_Radar implementation\BLE GUI\gui_code.py", line 42, in start_ble_loop
    loop.run_until_complete(connect_and_receive())
  File "C:\Program Files\Python312\Lib\asyncio\base_events.py", line 684, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "E:\AC_Work\Task_9_Radar implementation\BLE GUI\gui_code.py", line 28, in connect_and_receive
    await client.start_notify(BLE_CHARACTERISTIC_UUID, notification_handler)
  File "D:\Users\26101179\AppData\Roaming\Python\Python312\site-packages\bleak\__init__.py", line 844, in start_notify
    await self._backend.start_notify(characteristic, wrapped_callback, **kwargs)
  File "D:\Users\26101179\AppData\Roaming\Python\Python312\site-packages\bleak\backends\winrt\client.py", line 981, in start_notify
    await winrt_char.write_client_characteristic_configuration_descriptor_async(
OSError: [WinError -2140864509] The attribute cannot be written

My Arduino code is as follows:

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include "esp_bt_device.h"

#define SERVICE_UUID        "12345678-1234-5678-1234-56789abcdef0"
#define CHARACTERISTIC_UUID "87654321-4321-6789-4321-67890abcdef0"

BLECharacteristic *pCharacteristic;
bool deviceConnected = false;

void printBLEMacAddress() {
    const uint8_t* mac = esp_bt_dev_get_address();
    
    Serial.print("ESP32 BLE MAC Address: ");
    for (int i = 0; i < 6; i++) {
        Serial.printf("%02X", mac[i]);
        if (i < 5) Serial.print(":");
    }
    Serial.println();
}

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
        deviceConnected = true;
    }

    void onDisconnect(BLEServer* pServer) {
        deviceConnected = false;
        pServer->getAdvertising()->start(); // Restart advertising
    }
};

void setup() {
    Serial.begin(115200);

    BLEDevice::init("ESP32_BLE");

    // Print BLE MAC Address
    printBLEMacAddress();

    BLEServer *pServer = BLEDevice::createServer();
    pServer->setCallbacks(new MyServerCallbacks());

    BLEService *pService = pServer->createService(SERVICE_UUID);
    pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ |
                      BLECharacteristic::PROPERTY_NOTIFY
                    );

    pService->start();
    pServer->getAdvertising()->start();
    Serial.println("BLE Server Started");
}

void loop() {
    if (deviceConnected) {
        String millisStr = String(millis());  // Get millis() value
        pCharacteristic->setValue(millisStr.c_str());
        pCharacteristic->notify();  // Send the value
        Serial.println("Sent: " + millisStr);
    }
    delay(1000);
}

And my Python code is as follows:

import tkinter as tk
from bleak import BleakClient, BleakError
import asyncio
import time
import threading

# Values determined through nrfConnect
ESP32_BLE_MAC = "74:4D:BD:61:D2:6D"
BLE_CHARACTERISTIC_UUID = "87654321-4321-6789-4321-67890abcdef0"

async def connect_and_receive():
    while True:  # Keep retrying connection
        try:
            print("Attempting to connect to ESP32 BLE...")
            label.config(text="Connecting to ESP32...")
            
            async with BleakClient(ESP32_BLE_MAC) as client:
                if client.is_connected:
                    print("Connected to ESP32 BLE")
                    label.config(text="Connected to ESP32")

                    def notification_handler(sender, data):
                        """Callback function when new data is received."""
                        millis_value = int.from_bytes(data, byteorder="little")
                        label.config(text=f"Millis: {millis_value}")

                    # Subscribe to notifications
                    await client.start_notify(BLE_CHARACTERISTIC_UUID, notification_handler)

                    while True:
                        await asyncio.sleep(1)  # Keep listening for data
            
        except BleakError as e:
            print(f"Connection failed: {e}")
            label.config(text="ESP32 not found! Retrying...")
            time.sleep(5)  # Wait before retrying

# Function to run asyncio loop in a separate thread
def start_ble_loop():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(connect_and_receive())

# Tkinter GUI Setup
root = tk.Tk()
root.title("ESP32 BLE Monitor")

label = tk.Label(root, text="Waiting for connection...", font=("Arial", 16))
label.pack(pady=20)

# Start BLE communication in a separate thread
threading.Thread(target=start_ble_loop, daemon=True).start()

# Run the GUI
root.mainloop()

Any ideas what could be causing the problem?

Upvotes: 0

Views: 39

Answers (1)

MJepbarov
MJepbarov

Reputation: 120

The data sent by ESP32 is actually a string, not a raw integer, but Python code is probably interpreting it as raw bytes and trying to convert into an integer. This approach might be better:

def notification_handler(sender, data):
    millis_value = data.decode("utf-8")  # decoding to read as string
    label.config(text=f"Millis: {millis_value}")

According to Arduino code, there are recommendations to add BLE2902.h (it is a requirement for Windows OS)

in setup()

pCharacteristic->addDescriptor(new BLE2902());

Upvotes: 0

Related Questions