espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.3k stars 7.35k forks source link

ESP32 BLE client connect to multiple servers #6926

Open UmangSuthar101 opened 2 years ago

UmangSuthar101 commented 2 years ago

Board

ESP32 DevKitc V4

Device Description

Hardware Configuration

GPIO(34) connected to the resistor n/w for getting analog value of the battery!!

Version

v1.0.6

IDE Name

Arduino IDE

Operating System

Windows 10

Flash frequency

80 MHz

PSRAM enabled

no

Upload speed

921600

Description

I upload the code for the BLE multi-client in this module.

After some time such logs appear in the console: .... lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 2) .... lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)

BLE disconnects happen too often and show me these types of logs. Capture

Expected Behavior

Sketch

bool connectToServer() {

  BLEClient*  pClient  = BLEDevice::createClient();
  pClient->setClientCallbacks(new MyClientCallback());
  pClient->connect(myDevice);
  Serial.println(" - Connected to Spo2");

  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    pClient->disconnect();
    return false;
  }
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    pClient->disconnect();
    return false;
  }
  if (pRemoteCharacteristic->canNotify())
    pRemoteCharacteristic->registerForNotify(notifyCallback);
  return true;
}

//This upper code will write multiple times as per servers //numbers

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      // We have found a device, let us now see if it contains the service we are looking for.
      if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
        BLEDevice::getScan()->stop();
        myDevice = new BLEAdvertisedDevice(advertisedDevice);
        doConnect = true;
        doScan = true;
      }
    }
};

void setup() {
  Serial.begin(115200);
  ble();
  ble1();
  ble2();
  ble3();
}

void ble() {
  BLEDevice::init("abc");

  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(1);
}

// multiple ble's loop functions

Debug Message

....
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 2)
....
lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)

Note: I already attached serial output for the perticular error.

Other Steps to Reproduce

I go thruogh the espriff forum and also check github for HCI error but nothing get any solution.

I have checked existing issues, online documentation and the Troubleshooting Guide

SuGlider commented 2 years ago

@UmangSuthar101 The issue header says you are using Arduino Core 1.0.6. It is based on an old IDF version for BLE. Could you please update your Arduino Core to the latest version in the Arduino IDE. Current Core version is 2.0.3.

Let me know is the issue still occurs. Thanks.

UmangSuthar101 commented 2 years ago

@SuGlider , as you mentioned I update the esp-arduino to latest version 2.0.3 and again I got the following log check this:

Pulse: 84 SPO2: 99 Disconnect to Spo2 Device Connect to Temp. Device Connected to Temp. We are now connected to the Temp. BLE Server. Temp. : 98.26 lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1) Connect to BP Device Connected to BP lld_pdu_get_tx_flush_nb HCI packet count mismatch (1, 2) Disconnect to BP Device We have failed to connect to the BP server; there is nothin more we will do. Disconnect to Temp. Device

image

SuGlider commented 2 years ago

@chegewara - Could you please help @UmangSuthar101 on this issue? Thanks!

UmangSuthar101 commented 2 years ago

Greetings of the day,

@SuGlider & @chegewara could you plz help me with this issue, its very difficult for me to get multiple ble server data to ESP-32 ble client !!!

chegewara commented 2 years ago

This is low level error/warning log. I can only guessing it may be related to low memory, but this should be asked on esp-idf.

UmangSuthar101 commented 2 years ago

@chegewara I asked espriff team and they suggested me asking on esp-Arduino.

And this is not a warning log because after this line I cann't able to connect any BLE servers.

I also try this code on 8MB module but the same error show me, whenever I connect multiple BLE servers.

Is there any proper example that connects multiple BLE servers to ESP-32 BLE client?? Suggests me.

chegewara commented 2 years ago

Again, lld_pdu_* are low level library messages, and we dont even have access to source code to ungerstand what it means. If espressif is not willing to help you then maybe you should switch to nordic.

This is not problem with flash, so 4MB or 8MB makes no difference, but you should try to switch to NimBLE library. Its easy, and ive read its working better, has better support and most important its using less RAM.

UmangSuthar101 commented 2 years ago

@chegewara ok, get it!! I'll check NimBLE.

But is there any example or reference link(with ArduinoBLE library) which already connects multiple servers to the ESP BLE client, if that one gives me success then I will go through it?

chegewara commented 2 years ago

Im pretty sure you can find some, but nothing i am aware of.

SuGlider commented 2 years ago

Is there any proper example that connects multiple BLE servers to ESP-32 BLE client?? Suggests me.

There is an IDF example of GATTC connecting to 3 BLE Servers: https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/ble/gattc_multi_connect

Tutorial and explanation: https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/bluedroid/ble/gattc_multi_connect/tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md

SuGlider commented 2 years ago

@UmangSuthar101 I believe that it's necessary to instantiate one BLEClient object per Server connection in BLE Arduino Layer in order to make it work the way you need it.

khalidnk83 commented 2 years ago

The best and more stable BLE stack is the "NimbleBLE".... try using it from the link: https://github.com/h2zero/NimBLE-Arduino.git

SL06 commented 1 year ago

Hi, this program is sort of working but need further improvement:

include "BLEDevice.h"

int LED_BUILTIN = 2; bool toggle;

// The remote service we wish to connect to

//static BLEUUID0 serviceUUID("0000180d-0000-1000-8000-00805f9b34fb"); //0x180D //BLEUUID become a type, like int or boolean static BLEUUID serviceUUID0(BLEUUID((uint16_t)0x180D )); //Heart Rate static BLEUUID charUUID0(BLEUUID((uint16_t)0x2A37));

static BLEUUID serviceUUID1(BLEUUID((uint16_t)0x1818 )); // Cycling Power static BLEUUID charUUID1(BLEUUID((uint16_t)0x2A63));

static BLEUUID serviceUUID2(BLEUUID((uint16_t)0xFEE0 )); // SPO2 static BLEUUID charUUID2(BLEUUID((uint16_t)0xFEE1));

static boolean doConnect0 = false; static boolean connected0 = false;

static boolean doConnect1 = false; static boolean connected1 = false;

static boolean doConnect2 = false; static boolean connected2 = false;

static boolean doScan0 = false; static boolean doScan1 = false; static boolean doScan2 = false;

static boolean notification0 = false; static boolean notification1 = false; static boolean notification2 = false;

static BLERemoteCharacteristic pRemoteCharacteristic; static BLEAdvertisedDevice myDevice;

static void notifyCallback0( BLERemoteCharacteristic pBLERemoteCharacteristic, uint8_t pData, size_t length, bool isNotify) { //Serial.print("Notify callback for characteristic "); // Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); // Serial.print(" of data length "); // Serial.println(length); // Serial.print("data: "); //Serial.println((char*)pData);

Serial.print("HR_sensor value: ");
Serial.print(pData[1], DEC);

}

static void notifyCallback1( BLERemoteCharacteristic pBLERemoteCharacteristic, uint8_t pData, size_t length, bool isNotify) {

int  CPower =  pData[2] +  256 * pData[3];

Serial.print("Power value: ");
Serial.println(CPower);

} static void notifyCallback2( BLERemoteCharacteristic pBLERemoteCharacteristic, uint8_t pData, size_t length, bool isNotify) {

if (length == 4) {
  int SPO2 = pData[2];
  Serial.print("SPO2: "); Serial.println(SPO2);
  int PR =  pData[3];  // pulse rate
  Serial.print("PI: "); Serial.println(PR);
}

}

class MyClientCallback0 : public BLEClientCallbacks { void onConnect(BLEClient pclient) { } void onDisconnect(BLEClient pclient) { connected0 = false; notification0 = false; Serial.println("onDisconnect 0 "); } };

class MyClientCallback1 : public BLEClientCallbacks { void onConnect(BLEClient pclient) { } void onDisconnect(BLEClient pclient) { connected1 = false; notification1 = false; Serial.println("onDisconnect 1 "); } };

class MyClientCallback2 : public BLEClientCallbacks { void onConnect(BLEClient pclient) { } void onDisconnect(BLEClient pclient) { connected2 = false; notification2 = false; Serial.println("onDisconnect 2"); } };

bool connectToServer(int sensor, BLEUUID serviceUUID, BLEUUID charUUID) {// BLEUUID = type like integer Serial.print("Forming a connection to "); Serial.println(myDevice->getAddress().toString().c_str());

BLEClient*  pClient  = BLEDevice::createClient();
Serial.println(" - Created client");

     if (sensor == 0) pClient->setClientCallbacks(new MyClientCallback0());
else if (sensor == 1) pClient->setClientCallbacks(new MyClientCallback1());
else if (sensor == 2) pClient->setClientCallbacks(new MyClientCallback2());

// Connect to the remove BLE Server.
pClient->connect(myDevice);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.print(" - Connected to server ");
Serial.println(sensor);

pClient->setMTU(517); //set client to request maximum MTU from server (default is 23 otherwise)

// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
  Serial.print("Failed to find our service UUID sensor ");
  Serial.print (sensor);
  Serial.print (" : ");
  Serial.println(serviceUUID0.toString().c_str());
  pClient->disconnect();
  return false;
}
Serial.print(" - Found our service ");
Serial.println (sensor);

// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
  Serial.print("Failed to find our characteristic UUID: ");
  Serial.println(charUUID0.toString().c_str());
  pClient->disconnect();
  return false;
}

Serial.print(" - Found our characteristic ");
Serial.println (sensor);

// Read the value of the characteristic.
if(pRemoteCharacteristic->canRead()) {
  std::string value = pRemoteCharacteristic->readValue();
  Serial.print("The characteristic value was: ");
  Serial.println(value.c_str());
}

if (sensor == 0) {
  if(pRemoteCharacteristic->canNotify())
    pRemoteCharacteristic->registerForNotify(notifyCallback0);
  connected0 = true;
return true;
}
else  if (sensor == 1) {
  if(pRemoteCharacteristic->canNotify())
    pRemoteCharacteristic->registerForNotify(notifyCallback1);
  connected1 = true;
return true;
}
else  if (sensor == 2) {
  if(pRemoteCharacteristic->canNotify())
  pRemoteCharacteristic->registerForNotify(notifyCallback2);
  connected2 = true;
return true;
}

}

/**

//***** void setup() { Serial.begin(1000000); Serial.println("Starting Arduino BLE Client application...");

pinMode (LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW);

BLEDevice::init("");

// Retrieve a Scanner and set the callback we want to use to be informed when we // have detected a new device. Specify that we want active scanning and start the // scan to run for 5 seconds.

// I have played with those value, but lack good documentation... Help needed BLEScan* pBLEScan = BLEDevice::getScan(); pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setInterval(10000); // millisec , will affect the program main loop frequency 10000 = 10sec pBLEScan->setWindow(400); // millisec pBLEScan->setActiveScan(true); pBLEScan->start(5, false); // sec //pBLEScan->start(0); } // End of setup.

// This is the Arduino main loop function. void loop() {

// If the flag "doConnect" is true then we have scanned for and found the desired // BLE Server with which we wish to connect. Now we connect to it. Once we are // connected we set the connected flag to be true.

/Serial.print("doConnect0 "); Serial.println(doConnect0); Serial.print("doConnect1 "); Serial.println(doConnect1); Serial.print("doConnect2 "); Serial.println(doConnect2);/

if (toggle) digitalWrite(LED_BUILTIN, HIGH); else digitalWrite(LED_BUILTIN, LOW); toggle = !toggle;

if (doConnect0 == true) { if (connectToServer(0, serviceUUID0, charUUID0)) { Serial.println("We are now connected to the BLE Server 0."); } else { Serial.println("We have failed to connect to the server 0; there is nothin more we will do."); } doConnect0 = false; }

if (doConnect1 == true) { if (connectToServer(1, serviceUUID1, charUUID1)) { Serial.println("We are now connected to the BLE Server 1."); } else { Serial.println("We have failed to connect to the server 1; there is nothin more we will do."); } doConnect1 = false; }

if (doConnect2 == true) { if (connectToServer(2, serviceUUID2, charUUID2)) { Serial.println("We are now connected to the BLE Server 2."); } else { Serial.println("We have failed to connect to the server 2 ; there is nothin more we will do."); } doConnect2 = false; } / Serial.print("connected0 "); Serial.println(connected0); Serial.print("connected1 ");Serial.println(connected1); Serial.print("connected2 ");Serial.println(connected2); Serial.print("notification0 ");Serial.println(notification0); Serial.print("notification1 ");Serial.println(notification1); Serial.print("notification2 ");Serial.println(notification2); /

/Serial.print("doScan0 "); Serial.println(doScan0); Serial.print("doScan1 "); Serial.println(doScan1); Serial.print("doScan1 "); Serial.println(doScan1);/

// Need to repeat getScan()->start or it wont connect to the second+ server: if (connected0 && connected1 && connected2) BLEDevice::getScan()->stop(); else BLEDevice::getScan()->start(5, false);

// Turn notification on if (connected0) { if (notification0 == false) { Serial.println(F("Turning Notification On for sensor 0")); // F is an optimisation thing... const uint8_t onPacket[] = {0x1, 0x0}; // not shure about these number but it works pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)onPacket, 2, true); notification0 = true; } } else if(doScan0){ // try to reconnect // BLEDevice::getScan()->start(0);
BLEDevice::getScan()->start(5, false);
}

if (connected1) { if (notification1 == false) { Serial.println(F("Turning Notification On for sensor 1")); const uint8_t onPacket[] = {0x1, 0x0}; // not shure about these number but it works pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)onPacket, 2, true); notification1 = true; } } else if(doScan1){ BLEDevice::getScan()->start(5, false); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino

}

if (connected2) { if (notification2 == false) { Serial.println(F("Turning Notification On for sensor 2")); const uint8_t onPacket[] = {0x1, 0x0}; // not shure about these number but it works pRemoteCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)onPacket, 2, true); notification2 = true; } } else if(doScan2){ BLEDevice::getScan()->start(5, false); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino }

delay(1000); // Delay a second between loops. } // End of loop

petrrpancon commented 1 year ago

Is there any proper example that connects multiple BLE servers to ESP-32 BLE client?? Suggests me.

There is an IDF example of GATTC connecting to 3 BLE Servers: https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/ble/gattc_multi_connect

Tutorial and explanation: https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/bluedroid/ble/gattc_multi_connect/tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md

I think UmangSuthar101 was interested in using Arduino API not IDF. I have the same question and have not found any good example so far.

petrrpancon commented 1 year ago

SL06 BLEDevice::createClient(); overwrites a pointer in BLEdevice so making second client will likely break some callback events to 1st one going through that pointer and create memory leaks. The BLEDevice does not support multiple instances either. There is no reference to the client instance in call backs so I do not see how the connection is managed. The lack of any example lets me to conclusion that was not written to support multiple server connections properly.
I have tried to create the instance using "new BLEClient()" and it does work so far. but it needs a wrapper to all callbacks to be aware to what client is the message from/to. I can post the code here when finished if anyone is interested.

Melmac2 commented 1 year ago

petrrpancon - I would be interested in the code when finished. I'm trying to connect a single ESP32 client to multiple identical BLE motion sensors (with different addresses) at the same time and read their notification data; Arduino IDE.

davidangelo64 commented 10 months ago

@petrrpancon could share the code please, I'm trying to connect more than two clients and get their rssi continously

petrrpancon commented 10 months ago

Hi, the code is part of a project and I can not easily extract it, but let me show what i did. getScan is singleton so it will give you the same instance every time. I would run it only from 1 thread anyway. I call this once at start:

{
    BLEDevice::init("");
    BLEScan *scn = BLEDevice::getScan();
    scn->setAdvertisedDeviceCallbacks(&scan_cb);
    scn->setActiveScan(false);
}

then I have 2 clients as example:
ClientExt1 client1;
ClientExt2 client2;

then call this in loop task
for(;;)
{
      if (should_scan_for_connection(client)) // what ever condition to start the search
      {
            BLEScan *scn = BLEDevice::getScan();
            scn->start(2, false);
            if (check_for_found_device(client1)) // BLEAdvertisedDeviceCallbacks was called
            {  
                 here you can use 
                client1.connect(BLEAddress address, esp_ble_addr_type_t type) 
                with data from the event advertisedDevice.getAddress()
            }      
            if (check_for_found_device(client2)) // BLEAdvertisedDeviceCallbacks was called
            {  
                 same here for the other client
            }      
      }
     else
     {
          block the thread until something changes
     }
}

scan_cb is the instance class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks

for event when device is found and you need to check if it is something compatible in this method:
void MyAdvertisedDeviceCallbacks::onResult(BLEAdvertisedDevice advertisedDevice)
{
    check uuid or some other stuff matching your device here
    if you find the device you can stop the scan here immediately and store the address.
}

the advertisedDevice contains address to make connection advertisedDevice.getAddress()
so you can use that to connect to client.

you can create multiple instance of BLEClient, even extend this class with you own data. It woks as global instance or on heap with "new BLEClient". However managing dynamic data is harder to do right. Just do not use createClient. If you make more instances of the same ClientExt1 you check the client pointer inside any share callback so it still works for multiples.

class ClientExt1 : public BLEClient
{
    // data specific to your client
}

After the connection i run each client in its thread. It seem to work fine but don't expect a production quality from the arduino code. especially with threads. Esp32 had limit of 4 connections so be aware of that too.