Multiple Client data handling with ESP32 BLE server

MCUism
Posts: 52
Joined: Mon Jul 15, 2019 9:25 am

Multiple Client data handling with ESP32 BLE server

Postby MCUism » Mon Sep 16, 2019 4:05 pm

Hello team,
I've been working on an application where I need to make one ESP32 as server and it must connect to 2 clients. One of those clients is my mobile phone and another client is my ESP32. So, my goal is to send the data strings from mobile phone to client ESP32 via the server. How can I do this?? Please help me out.

I've used the following codes in order to make automatic connection between ESP32 server and the client.

Code: Select all

//                                Server side code
/*
  A connect hander associated with the server starts a background task that performs notification
  every couple of seconds.
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>


#ifdef __cplusplus
extern "C" {
#endif
uint8_t temprature_sens_read();
#ifdef __cplusplus
}
#endif
uint8_t temprature_sens_read();



BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
float mea = 0;
int LED13 = 5;   //  The on-board Arduion LED

// https://www.uuidgenerator.net/

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"


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

   void onDisconnect(BLEServer* pServer) {
     deviceConnected = false;
   }
};




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

 // Create the BLE Device
 BLEDevice::init("ESP32");

 // Create the BLE Server
 pServer = BLEDevice::createServer();
 pServer->setCallbacks(new MyServerCallbacks());

 // Create the BLE Service
 BLEService *pService = pServer->createService(SERVICE_UUID);

 // Create a BLE Characteristic
 pCharacteristic = pService->createCharacteristic(
                     CHARACTERISTIC_UUID,
                     BLECharacteristic::PROPERTY_READ   |
                     BLECharacteristic::PROPERTY_WRITE  |
                     BLECharacteristic::PROPERTY_NOTIFY 
                   );

 // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
 // Create a BLE Descriptor
 pCharacteristic->addDescriptor(new BLE2902());

 // Start the service
 pService->start();

 // Start advertising
 BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
 pAdvertising->addServiceUUID(SERVICE_UUID);
 pAdvertising->setScanResponse(false);
 pAdvertising->setMinPreferred(0x0);  // set value to 0x00 to not advertise this parameter
 BLEDevice::startAdvertising();
 pinMode(LED13, OUTPUT);
 Serial.println("Waiting a client connection to notify...");

}

void loop() {
 // notify changed value
  // Read the PulseSensor's value.
 // Assign this value to the "Signal" variable.
 if (deviceConnected) 
 {
   mea = (temprature_sens_read()-32)/1.8;
   String send_Data = String(mea);
   pCharacteristic->setValue(send_Data);
   pCharacteristic->notify();
   
   // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
 }
 else 
 {
   digitalWrite(LED13, LOW);               //  Else, the sigal must be below "550", so "turn-off" this LED.
 }

 delay(10);

}



and for the client side, I've used the following code.

Code: Select all

//                                             Client Side code

#include "BLEDevice.h"
#include "BLEScan.h"

// The remote service we wish to connect to.
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");

static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;

static void notifyCallback(
  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);
}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
  }

  void onDisconnect(BLEClient* pclient) {
    connected = false;
    Serial.println("onDisconnect");
  }
};

bool connectToServer() {
    Serial.print("Forming a connection to ");
    Serial.println(myDevice->getAddress().toString().c_str());
    
    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(" - Created client");

    pClient->setClientCallbacks(new MyClientCallback());

    // 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.println(" - Connected to server");

    // 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: ");
      Serial.println(serviceUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    Serial.println(" - Found our service");


    // 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(charUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    Serial.println(" - Found our characteristic");

    // 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(pRemoteCharacteristic->canNotify())
      pRemoteCharacteristic->registerForNotify(notifyCallback);

    connected = true;
}
/**
 * Scan for BLE servers and find the first 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(serviceUUID)) {

      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
      doScan = true;

    } // Found our server
  } // onResult
}; // MyAdvertisedDeviceCallbacks


void setup() {
  Serial.begin(115200);
  Serial.println("Starting Arduino BLE Client application...");
  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.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);
} // 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.
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
    } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
    }
    doConnect = false;
  }

  delay(1000); // Delay a second between loops.
} // End of loop
But I'm not able to send the data to server ESP32 and receive the data correctly. Please help me out, Thanks in advance..!!

bonadio
Posts: 30
Joined: Mon Jul 29, 2019 3:31 pm

Re: Multiple Client data handling with ESP32 BLE server

Postby bonadio » Wed Sep 18, 2019 11:53 pm

Try to solve one problem at time, make your server code and use an app "BLE Scanner" on your smartphone to make sure you can connect and see the data you are trying to send, after your server code is working with the BLE Scanner app you can try your client code. Trying to solve both sides at same time is very hard.

[]s

MCUism
Posts: 52
Joined: Mon Jul 15, 2019 9:25 am

Re: Multiple Client data handling with ESP32 BLE server

Postby MCUism » Thu Sep 19, 2019 12:00 pm

Hey Bonadio, Thanks for the reply. As you said at the intial stages of my work I have debugged everything using nRF Connect App. I do things in a very modularizing way. So, I've tested the things carefully and now I'm in this stage of handling multiple clients with ESP32 as server. Please suggest me ways how can I acheive this.

bonadio
Posts: 30
Joined: Mon Jul 29, 2019 3:31 pm

Re: Multiple Client data handling with ESP32 BLE server

Postby bonadio » Thu Sep 19, 2019 1:50 pm

Hi MCUism

Ok, so what exactly is not working?
Can you successfully connect from the nRF app to your server and read the data?
Can you do it from esp client too?

[]s

MCUism
Posts: 52
Joined: Mon Jul 15, 2019 9:25 am

Re: Multiple Client data handling with ESP32 BLE server

Postby MCUism » Fri Sep 20, 2019 12:09 pm

Hey Bonadio. Yes, I am able to connect both my application and client esp32 to the server and successfully read the data. It is being updated simultaneously without any problems. Now, I am trying to send data from my mobile phone which is connected to server ESP32. This data must be sent to the client via server ESP32.

For instance, If I send a string like UTC,34,567!,311, now this data must be sent to client via server esp32. I have attached a simple architecture diagram for your perusal. Please let me know whether I can provide any further information.

Image

bonadio
Posts: 30
Joined: Mon Jul 29, 2019 3:31 pm

Re: Multiple Client data handling with ESP32 BLE server

Postby bonadio » Fri Sep 20, 2019 3:32 pm

Ok, now I understand what you want

Looking at your server code you dont have a callback to receive the Characteristcs Write, so you wont be able to write to your server

I included in your code the callback so with that you will be able to receive the value of the characteristic, once received on the server just put it on another variable and you can start notifying tho the ESPclient

Code: Select all

//                                Server side code
/*
  A connect hander associated with the server starts a background task that performs notification
  every couple of seconds.
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>


#ifdef __cplusplus
extern "C" {
#endif
uint8_t temprature_sens_read();
#ifdef __cplusplus
}
#endif
uint8_t temprature_sens_read();



BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
float mea = 0;
int LED13 = 5;   //  The on-board Arduion LED

// https://www.uuidgenerator.net/

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"


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

   void onDisconnect(BLEServer* pServer) {
     deviceConnected = false;
   }
};


/* ###############################################################  CALL back to receive data from Phone */
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"

class MyCallbacks: public BLECharacteristicCallbacks {

    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue();
      Serial.println(rxValue[0]);
 
      if (rxValue.length() > 0) {
        Serial.println("*********");
        Serial.print("Received Value: ");
 
        for (int i = 0; i < rxValue.length(); i++) {
          Serial.print(rxValue[i]);
        }
        Serial.println();
        Serial.println("*********");
      }
 
    }
};
/* ############################################################### */


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

 // Create the BLE Device
 BLEDevice::init("ESP32");

 // Create the BLE Server
 pServer = BLEDevice::createServer();
 pServer->setCallbacks(new MyServerCallbacks());

 // Create the BLE Service
 BLEService *pService = pServer->createService(SERVICE_UUID);

 // Create a BLE Characteristic
 pCharacteristic = pService->createCharacteristic(
                     CHARACTERISTIC_UUID,
                     BLECharacteristic::PROPERTY_READ   |
                     BLECharacteristic::PROPERTY_WRITE  |
                     BLECharacteristic::PROPERTY_NOTIFY 
                   );

 // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
 // Create a BLE Descriptor
 pCharacteristic->addDescriptor(new BLE2902());


/* ###############################################################  define callback */
  BLECharacteristic *pWriteCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID_RX,
                                         BLECharacteristic::PROPERTY_WRITE
                                       );
 
  pWriteCharacteristic->setCallbacks(new MyCallbacks());
/* ############################################################### */

 // Start the service
 pService->start();

 // Start advertising
 BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
 pAdvertising->addServiceUUID(SERVICE_UUID);
 pAdvertising->setScanResponse(false);
 pAdvertising->setMinPreferred(0x0);  // set value to 0x00 to not advertise this parameter
 BLEDevice::startAdvertising();
 pinMode(LED13, OUTPUT);
 Serial.println("Waiting a client connection to notify...");

}

void loop() {
 // notify changed value
  // Read the PulseSensor's value.
 // Assign this value to the "Signal" variable.
 if (deviceConnected) 
 {
   mea = (temprature_sens_read()-32)/1.8;
   String send_Data = String(mea);
   pCharacteristic->setValue(send_Data);
   pCharacteristic->notify();
   
   // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
 }
 else 
 {
   digitalWrite(LED13, LOW);               //  Else, the sigal must be below "550", so "turn-off" this LED.
 }

 delay(10);

}

Hope that help
[]s

MCUism
Posts: 52
Joined: Mon Jul 15, 2019 9:25 am

Re: Multiple Client data handling with ESP32 BLE server

Postby MCUism » Fri Sep 20, 2019 8:50 pm

Hello, Thank you so much for the reply and coorection. I implemented your code but I am not able to read longer strings. I have attached the output images here. what could be the reason? Is there any overflow of the data?


The data sent through nRF Connect App.
Image

The output data that I got on the serial monitor.

Image

MCUism
Posts: 52
Joined: Mon Jul 15, 2019 9:25 am

Re: Multiple Client data handling with ESP32 BLE server

Postby MCUism » Fri Sep 20, 2019 9:32 pm

I've made the following changes to the code that you've given to me.

Code: Select all

class MyCallbacks: public BLECharacteristicCallbacks {

    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue();
      //Serial.println(rxValue[0]);
 
      if (rxValue.length() > 0) {
        Serial.println("*********");
        Serial.print("Received Value: ");
 
        for (int i = 0; i < rxValue.length(); i++) 
        {
          item+=String(rxValue[i]);
        }
        /*Part that I've Changed to notify the data to ESP32 Client*/
        pCharacteristic->setValue(item.c_str());
        pCharacteristic->notify();
        Serial.println("*********");
      }
 
    }
};
And I remained the client side code as I've posted in the first thread of this discussion. But, after implementing this codes, I am not able to get the data over the client side. I'm sure that i am messing up with the Charecterstic UUID definition on the client-side. please correct me Thanks in advance..!!

bonadio
Posts: 30
Joined: Mon Jul 29, 2019 3:31 pm

Re: Multiple Client data handling with ESP32 BLE server

Postby bonadio » Fri Sep 20, 2019 10:33 pm

Yes there is a limit of 20 characteres for notify()

I think you should not call notify() from within the write callback, not sure about that but I would write to a global var and notify from that var

Good luck
[]s

MCUism
Posts: 52
Joined: Mon Jul 15, 2019 9:25 am

Re: Multiple Client data handling with ESP32 BLE server

Postby MCUism » Sat Sep 21, 2019 7:16 pm

Hello,
Thanks for the info. I am facing a problem in reading the data from the server on the ESP32 client. The data from the phone to server is beign sent properly, but the client is not receiving it correctly. The client side code remained the same as I shared in the previous thread of this post. Am I doing anything wrong sir?

Who is online

Users browsing this forum: No registered users and 97 guests