arduino-esp32 icon indicating copy to clipboard operation
arduino-esp32 copied to clipboard

ESP32 BLE client connect to multiple servers

Open UmangSuthar101 opened this issue 2 years ago • 12 comments

Board

ESP32 DevKitc V4

Device Description

  • Development Kit: ESP32 DevKitc V4(attached picture) ESP32-DEV-KIT-DevKitC-v4-pinout-mischianti

  • Module or chip used: ESP32-WROOM-32

  • Compiler version: Extensa-esp32-elf-GCC

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

  • Stable BLE connection
  • Can connect multiple BLE servers on this module and get the data simultaneously

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

  • [X] I confirm I have checked existing issues, online documentation and Troubleshooting guide.

UmangSuthar101 avatar Jun 30 '22 11:06 UmangSuthar101

@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.

SuGlider avatar Jun 30 '22 13:06 SuGlider

@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

UmangSuthar101 avatar Jul 02 '22 05:07 UmangSuthar101

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

SuGlider avatar Jul 04 '22 03:07 SuGlider

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 !!!

UmangSuthar101 avatar Jul 05 '22 05:07 UmangSuthar101

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.

chegewara avatar Jul 05 '22 07:07 chegewara

@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.

UmangSuthar101 avatar Jul 05 '22 08:07 UmangSuthar101

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.

chegewara avatar Jul 05 '22 08:07 chegewara

@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?

UmangSuthar101 avatar Jul 05 '22 08:07 UmangSuthar101

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

chegewara avatar Jul 05 '22 09:07 chegewara

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 avatar Jul 05 '22 12:07 SuGlider

@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.

SuGlider avatar Jul 05 '22 13:07 SuGlider

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

khalidnk83 avatar Jul 30 '22 04:07 khalidnk83

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

  • on starting and stopping scan...
  • on connection IMPORTANT: might not work with older version Arduino Version: 2.0.1-nightly-20221024 ESP32 2.0.5
/**
 * A BLE client example that is rich in capabilities.
 * There is a lot new capabilities implemented.
 * Copyright © 2022 Arduino SA
 * author unknown
 * updated by chegewara
 * modified for 3 connections by Sylvain Lareau
 *  usefull exemple https://www.youtube.com/watch?v=iCKIIMrphtg

 * IMPORTANT: might not work with older version
   Arduino Version: 2.0.1-nightly-20221024
   ESP32 2.0.5
The program is not final  and could easely be improve and simplified...
  
 */

#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;
    }
}

/**
 * Scan for BLE servers and find the one that advertises the service we are looking for.
 */
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 /**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");
    Serial.println(advertisedDevice.toString().c_str());

    // We have found a device, let us now see if it contains the service we are looking for.
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID0)) {
      //BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);  // not used?
      doConnect0 = true;
      doScan0 = true;
      Serial.print("Found serviceUUID0");
    } // Found our server

    // We have found a device, let us now see if it contains the service we are looking for.
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID1)) {
      //BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect1 = true;
      doScan1 = true;
      Serial.print("Found serviceUUID1");
    } // Found our server1

    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID2)) {
      //BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect2 = true;
      doScan2 = true;
      Serial.print("Found serviceUUID2");
    } // Found our server2

  } // onResult
}; // MyAdvertisedDeviceCallbacks

//*********************************
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

SL06 avatar Oct 27 '22 17:10 SL06

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 avatar Apr 03 '23 11:04 petrrpancon

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.

petrrpancon avatar Apr 03 '23 11:04 petrrpancon

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.

Melmac2 avatar Apr 30 '23 13:04 Melmac2

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

davidangelo64 avatar Oct 23 '23 20:10 davidangelo64

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.

petrrpancon avatar Oct 23 '23 21:10 petrrpancon