Automatic pairing of ESP32 using BLE



  • Hey guys...I've been working on a project where I need to pair two ESP32's using their BLE just as how we do for Master slave configuration of two HC-05 Bluetooth modules.....Can anyone please help me out with the code part...!!...Thank you so much in advance



  • Hi @kowshik1729 ,

    Using Bluetooth Classic (like HC-05..etc ) is much simpler than Bluetooth Low Energy (like esp32, nRF52..etc).

    Understanding Bluetooth Low Energy (BLE)
    The Bluetooth Low Energy, as the name indicates consumes less power than classic Bluetooth. It is achieved by sending data as needed with pre-defined periodic updates. But unlike classic Bluetooth, it is not used to transfer Files or Music.

    GAP
    GAP is an acronym for the Generic Access Profile, and it controls connections and advertising in Bluetooth. GAP is what makes your device visible to the outside world, and determines how two devices can (or can't) interact with each other.

    Device Roles
    GAP defines various roles for devices, but the two key concepts to keep in mind are Central devices and Peripheral devices.

    • Peripheral devices are small, low power, resource-constrained devices that can connect to a much more powerful central device. Peripheral devices are things like a heart rate monitor, a BLE enabled proximity tag, etc.

    • Central devices are usually the mobile phone or tablet that you connect to with far more processing power and memory.

    Once you establish a connection between your peripheral and a central device, the advertising process will generally stop and you will typically no longer be able to send advertising packets out anymore, and you will use GATT services and characteristics to communicate in both directions.

    GATT
    GATT is an acronym for the Generic Attribute Profile, and it defines the way that two Bluetooth Low Energy devices transfer data back and forth using concepts called Services and Characteristics. It makes use of a generic data protocol called the Attribute Protocol (ATT), which is used to store Services.

    Note : GATT comes into play once a dedicated connection is established between two devices, meaning that you have already gone through the advertising process governed by GAP.

    A BLE peripheral can only be connected to one central device (a mobile phone, etc.) at a time! As soon as a peripheral connects to a central device, it will stop advertising itself and other devices will no longer be able to see it or connect to it until the existing connection is broken.

    GATT Transactions
    An important concept to understand with GATT is the server/client relationship.

    The peripheral is known as the GATT Server, which holds the ATT lookup data and service and characteristic definitions, and the GATT Client (the phone/tablet), which sends requests to this server.

    All transactions are started by the master device, the GATT Client, which receives a response from the slave device, the GATT Server.

    When establishing a connection, the peripheral will suggest a 'Connection Interval' to the central device, and the central device will try to reconnect every connection interval to see if any new data is available, etc. It's important to keep in mind that this connection interval is really just a suggestion, though! Your central device may not be able to honor the request because it's busy talking to another peripheral or the required system resources just aren't available.

    Services and Characteristics
    GATT transactions in BLE are based on high-level, nested objects called Profiles, Services and Characteristics.
    alt text

    Profiles
    A Profile doesn't actually exist on the BLE peripheral itself, it's simply a pre-defined collection of Services that has been compiled by either the Bluetooth SIG or by the peripheral designers. The Heart Rate Profile, for example, combines the Heart Rate Service and the Device Information Service. The complete list of officially adopted GATT-based profiles can be seen here: Profiles Overview.

    Services
    Services are used to break data up into logic entities and contain specific chunks of data called characteristics. A service can have one or more characteristics, and each service distinguishes itself from other services by means of a unique numeric ID called a UUID, which can be either 16-bit (for officially adopted BLE Services) or 128-bit (for custom services).

    Characteristics
    The lowest level concept in GATT transactions is the Characteristic, which encapsulates a single data point (though it may contain an array of related data, such as X/Y/Z values from a 3-axis accelerometer, etc.).

    conclusion: we can see that it was a little bit different from Bluetooth classic that we used before, and now you can use the service and Characteristics to communicate with the BLE devices, also there is example code provided with esp32 board manager.

    #define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
    #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
    
    // 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");
    

    Here we are defining the SERVICE_UUID and CHARACTERISTIC_UUID in the Server device then we are connecting from the Client device using the same SERVICE_UUID and CHARACTERISTIC_UUID .

    Resources :



  • @salmanfaris
    Hello Salman...Thankyou very much for the reply. I implemented your code and I got below error. doubt.PNG

    I simply tried to read the temperature data from ESP32 and send it to the client ESP32 whenever it is connected. What is my error? Please help me out..!!



  • @kowshik1729 Could please share the full error log? (on the bottom right corner click copy error message) ,



  • @salmanfaris

    The complete error statement is the same as shown in the above image. Nothing else is printed in the Error Log.



  • @kowshik1729 Ok, then could you please share the central and peripheral code? can't find anything from the error instead of the missing function!



  • @salmanfaris
    Sure...Here are my the codes that I have used.



  • @kowshik1729

    //                                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);
    
    }
    

    The following code is for the Client side.

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


  • @kowshik1729 You are trying to pass a string value (send_Data) to a pointer object(setValue), to solve that you should use std::string class which is an instantiation of the basic_string class template that uses char (i.e., bytes) as its character type.

    Comment line no.96 which doing convertion from Float value into String using the String constructor

    // String send_Data = String(mea);
    

    and add the following line to use the std:string this will help represent the sequence of characters as an object of the class .

    std::string send_Data((char*)&mea, 2);
    

    edited code

    //                                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>
    #include <BLECharacteristic.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);
       std::string send_Data((char*)&mea, 2);
       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);
    
    }
    

    Try it out.



  • @salmanfaris Thanks for correcting me back. I implemented this on my ESP's and the data that I acquired on the client end is very weird as shown in the below image and whereas my client-side code remained the same as shown in the previous post.

    doubt.PNG



Recent Posts

  • F

    @salmanfaris best if you could point me to an example that works as described: getting different values depending on hoe close the finger is without really touching. nothing i tried did work yet

    read more
  • F

    @salmanfaris I haven't found a good sensor module for this at all. I used a thing which was calles touch sensor and it returns only 1/0. Tried to use touchRead() but it is not sensitive apparently, only 40 and 1 more or less.

    #define touch_pin_numer 33

    const int VALUE_THRESHOLD = 30;
    int TOUCH_SENSOR_VALUE;
    void setup()
    {
    Serial.begin(115200);
    delay(1000);

    }
    void loop(){
    TOUCH_SENSOR_VALUE = touchRead(touch_pin_numer);
    Serial.println(TOUCH_SENSOR_VALUE);
    if(TOUCH_SENSOR_VALUE < VALUE_THRESHOLD)
    {
    Serial.println ("Touch ON");
    }
    else{
    Serial.println ("Touch OFF");
    }
    }

    should show something between 40 (current max) and 1 (current min) the closer the finger gets but it shows 40/41/39 and 1 - nothing inbetween. got this code from the web

    read more
  • @FlorianB Can you share the code, output window/error and details of the sensor module?

    read more
  • F

    Hi! I am trying to add an capacitive sensor to the ESP32, that will work through glass with 3mm thickness and an distance of 8 to 2 cm. I have bought sensors which only work on touch, i have looked into the touchRead() function but cant get a steady value increase/decrease with distance and only a real touch gets me enough difference to count it as a "touch". I have checked Arduino examples with foil where a resistor can set the sensitivity but nothing worked on my ESP32. i have spent hours looking for an example, can somebody point me to one please?

    read more
  • Capture.PNG

    Welcome to the Second edition of MakerGram Live, a newsletter where we pull together information about Interesting projects, Industry news, upcoming events, job opportunities and other interesting bits that you may find useful. 😉

    If you are a member of makergram, you already received a mail, please check your inbox or you can read from here: MakerGram Live | May 2020 Maker Update

    read more