nkolban / esp32-snippets

Sample ESP32 snippets and code fragments
https://leanpub.com/kolban-ESP32
Apache License 2.0
2.37k stars 710 forks source link

Cannot read BLE characteristic value after the 3rd scan #1037

Open hientv1999 opened 3 years ago

hientv1999 commented 3 years ago

My plan is to make a bunch of esp32 with the almost same code that both advertises and scans for each other. However, each device will have a different characteristic value to specify each device. Below is the code of my main.ino


unsigned int DeviceOrder = 1;
#define ORDER "1"
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include "BLEAddress.h"                                   
#include "esp_bt_main.h"
#include "esp_bt_device.h"       
BLECharacteristic *pCharacteristic2;
#define SERVICE_UUID            "b596e525-c3c6-45b3-a8bb-b8cddcc62a61"              
static BLEUUID serviceUUID("b596e525-c3c6-45b3-a8bb-b8cddcc62a61");
#define CHARACTERISTIC_UUID     "f364c9cb-2d2c-4eb6-92d3-28db399a09ee"  
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks                
{
    void onResult(BLEAdvertisedDevice advertisedDevice){}
};

class MyClientCallback : public BLEClientCallbacks {                           
  void onConnect(BLEClient* pclient) {}
  void onDisconnect(BLEClient* pclient) {
      pclient->disconnect();
  }
};

int ConnectToESP32(BLEAdvertisedDevice device){
    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(" - Created client");

    // Connect to the remove BLE Server.
    pClient->connect(&device);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)

    // Obtain a reference to the service we are after in the remote BLE server.

    if (pClient->getService(SERVICE_UUID) == nullptr) {
        pClient->disconnect();
        return 0;
    }
    Serial.println(" - Found our service");
    BLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
    // Obtain a reference to the characteristic in the service of the remote BLE server.
    BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
    if (pRemoteCharacteristic == nullptr) {
        pClient->disconnect();
        return 0;
    }
    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());
        return atoi(value.c_str());
        pClient->connect(&device);      
    }
    return 0;
}
long long int scanTimer;
const int CUTOFF = -74;    

void setup()
 {
    Serial.begin(115200);
    BLEDevice::init("MyESP");                                                    
    BLEServer *pServer = BLEDevice::createServer();                                 
    BLEService *pService = pServer->createService(SERVICE_UUID);
    BLECharacteristic *Sending_Characteristic = pService->createCharacteristic(            
                                     CHARACTERISTIC_UUID,
                                     BLECharacteristic::PROPERTY_READ |
                                     BLECharacteristic::PROPERTY_WRITE
    );
    Sending_Characteristic ->setValue(ORDER);
    pService->start();
    pServer->getAdvertising()->start();
    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    pAdvertising->setScanResponse(true);
    pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
    pAdvertising->setMinPreferred(0x12);
    BLEDevice::startAdvertising();
    scanTimer = millis();
 }

 void loop(){
    if (scanTimer + 5000 < millis()){
            BLEScan* pBLEScan = BLEDevice::getScan();                                   //create new scan
            BLEScanResults scanResults = pBLEScan->start(3, false);                     //scan for 3 seconds, not continuous
            Serial.print("Number of devices found this loop:");
            Serial.println(scanResults.getCount());
            Serial.println("List of devices found: ");                                                                    
            for (int i=0 ; i < scanResults.getCount(); i++){                                        //check each new BluetoothID found
                BLEAdvertisedDevice device = scanResults.getDevice(i); 
                Serial.println(device.toString().c_str());                                                  
                if (device.getRSSI() > CUTOFF && device.haveServiceUUID () && device.getServiceUUID().equals(serviceUUID)){                  //check the Bluetooth signal is strong enough and have correct ServiceUUID  
                    unsigned int ToDevice = ConnectToESP32(device);
                    if (ToDevice != 0){
                    }
                } 
            }
            BLEDevice::getScan()->clearResults();                                    
            scanTimer = millis();
    }
 }

Here is the output on Serial Monitor:

Number of devices found this loop:5
List of devices found: 
Name: , Address: 00:5d:7c:47:cd:44, manufacturer data: 0600010920026a9eac80fa2de399675162b04c6378dd71fe7aada7dc2d
Name: , Address: 0b:73:5d:2f:c8:e3, manufacturer data: 060001092002e25e7e97a9ee68d85091d77df30c75d4ec37385b253860
Name: , Address: 39:4c:e7:cb:40:7d, serviceUUID: fd6f-0000-1000-8000-00805f9b34fb
Name: , Address: 45:1f:c6:61:80:9b, manufacturer data: 4c001005411c4f19b5, txPower: 12
Name: , Address: 65:21:a6:08:c6:d1, manufacturer data: 4c001005361c27568f, txPower: 7
Number of devices found this loop:6
List of devices found: 
Name: , Address: 00:5d:7c:47:cd:44, manufacturer data: 0600010920026a9eac80fa2de399675162b04c6378dd71fe7aada7dc2d
Name: , Address: 0b:73:5d:2f:c8:e3, manufacturer data: 060001092002e25e7e97a9ee68d85091d77df30c75d4ec37385b253860
Name: , Address: 24:62:ab:e1:ad:62, serviceUUID: b596e525-c3c6-45b3-a8bb-b8cddcc62a
 - Created client
 - Found our service
 - Found our characteristic
The characteristic value was: 2
Name: , Address: 39:4c:e7:cb:40:7d, serviceUUID: fd6f-0000-1000-8000-00805f9b34fb
Name: , Address: 45:1f:c6:61:80:9b, manufacturer data: 4c001005411c4f19b5, txPower: 12
Name: , Address: 65:21:a6:08:c6:d1, manufacturer data: 4c001005361c27568f, txPower: 7
Number of devices found this loop:6
List of devices found: 
Name: , Address: 00:5d:7c:47:cd:44, manufacturer data: 0600010920026a9eac80fa2de399675162b04c6378dd71fe7aada7dc2d
Name: , Address: 0b:73:5d:2f:c8:e3, manufacturer data: 060001092002e25e7e97a9ee68d85091d77df30c75d4ec37385b253860
Name: , Address: 24:62:ab:e1:ad:62, serviceUUID: b596e525-c3c6-45b3-a8bb-b8cddcc62a
 - Created client
 - Found our service
 - Found our characteristic
The characteristic value was: 2
Name: , Address: 39:4c:e7:cb:40:7d, serviceUUID: fd6f-0000-1000-8000-00805f9b34fb
Name: , Address: 45:1f:c6:61:80:9b, manufacturer data: 4c001005411c4f19b5, txPower: 12
Name: , Address: 65:21:a6:08:c6:d1, manufacturer data: 4c001005361c27568f, txPower: 7
Number of devices found this loop:6
List of devices found: 
Name: , Address: 00:5d:7c:47:cd:44, manufacturer data: 0600010920026a9eac80fa2de399675162b04c6378dd71fe7aada7dc2d
Name: , Address: 0b:73:5d:2f:c8:e3, manufacturer data: 060001092002e25e7e97a9ee68d85091d77df30c75d4ec37385b253860
Name: , Address: 24:62:ab:e1:ad:62, serviceUUID: b596e525-c3c6-45b3-a8bb-b8cddcc62a
 - Created client
 - Found our service
 - Found our characteristic
The characteristic value was: 2
Name: , Address: 39:4c:e7:cb:40:7d, serviceUUID: fd6f-0000-1000-8000-00805f9b34fb
Name: , Address: 45:1f:c6:61:80:9b, manufacturer data: 4c001005411c4f19b5, txPower: 12
Name: , Address: 65:21:a6:08:c6:d1, manufacturer data: 4c001005361c27568f, txPower: 7
Number of devices found this loop:6
List of devices found: 
Name: , Address: 00:5d:7c:47:cd:44, manufacturer data: 0600010920026a9eac80fa2de399675162b04c6378dd71fe7aada7dc2d
Name: , Address: 0b:73:5d:2f:c8:e3, manufacturer data: 060001092002e25e7e97a9ee68d85091d77df30c75d4ec37385b253860
Name: , Address: 24:62:ab:e1:ad:62, serviceUUID: b596e525-c3c6-45b3-a8bb-b8cddcc62a
 - Created client
Name: , Address: 39:4c:e7:cb:40:7d, serviceUUID: fd6f-0000-1000-8000-00805f9b34fb
Name: , Address: 45:1f:c6:61:80:9b, manufacturer data: 4c001005411c4f19b5, txPower: 12
Name: , Address: 65:21:a6:08:c6:d1, manufacturer data: 4c001005361c27568f, txPower: 7
Number of devices found this loop:5
List of devices found: 
Name: , Address: 00:5d:7c:47:cd:44, manufacturer data: 0600010920026a9eac80fa2de399675162b04c6378dd71fe7aada7dc2d
Name: , Address: 24:62:ab:e1:ad:62, serviceUUID: b596e525-c3c6-45b3-a8bb-b8cddcc62a
 - Created client
Name: , Address: 39:4c:e7:cb:40:7d, serviceUUID: fd6f-0000-1000-8000-00805f9b34fb
Name: , Address: 45:1f:c6:61:80:9b, manufacturer data: 4c001005411c4f19b5, txPower: 12
Name: , Address: 65:21:a6:08:c6:d1, manufacturer data: 4c001005361c27568f, txPower: 7

I have tested them more than 20 times and the longest it can hold is 3 scans. Sometimes, it can only do 1 or 2 scans. It will never find the device again until I reboot the device it cannot find. I suspect it could be the esp32 saves the paired devices and ignore it after a few cycles. Is there any way I can overcome this issue. There will be at least 100 esp32 working in my configuration. I just tested it with 2 esp32 already. *I have changed the waiting time for client to connect to server down to 3 seconds (the default setting is 40-50 days I believe)

chegewara commented 3 years ago

From your description i am suggesting to use nimble. It wont solve that issue, but will help you with future problems related to heap shortage.

hientv1999 commented 3 years ago

From your description i am suggesting to use nimble. It wont solve that issue, but will help you with future problems related to heap shortage.

Thank you for your great recommendation. I never heard about NimBLE before but I just read it and it should be an amazing alternative for traditional BLE.

hientv1999 commented 3 years ago

I tried NimBLE but the issue still exists and it even causes a new issue. The serviceUUIDs of other server ESP32 are not shown by the client ESP32.

Below is the code I changed into NimBLE way.

unsigned int DeviceOrder = 1;
#define ORDER "1"
#include <NimBLEDevice.h>                              
#include "esp_bt_main.h"
#include "esp_bt_device.h"       
BLECharacteristic *pCharacteristic2;
#define SERVICE_UUID            "b596e525-c3c6-45b3-a8bb-b8cddcc62a61"              
static NimBLEUUID serviceUUID("b596e525-c3c6-45b3-a8bb-b8cddcc62a61");
#define CHARACTERISTIC_UUID     "f364c9cb-2d2c-4eb6-92d3-28db399a09ee"  
class MyAdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks                
{
    void onResult(NimBLEAdvertisedDevice advertisedDevice){}
};

class MyClientCallback : public NimBLEClientCallbacks {                           
  void onConnect(NimBLEClient* pclient) {}
  void onDisconnect(NimBLEClient* pclient) {
      pclient->disconnect();
  }
};

int ConnectToESP32(NimBLEAdvertisedDevice device){
    BLEClient*  pClient  = NimBLEDevice::createClient();
    Serial.println(" - Created client");

    // Connect to the remove BLE Server.
    pClient->connect(&device);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)

    // Obtain a reference to the service we are after in the remote BLE server.

    if (pClient->getService(SERVICE_UUID) == nullptr) {
        pClient->disconnect();
        return 0;
    }
    Serial.println(" - Found our service");
    NimBLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
    // Obtain a reference to the characteristic in the service of the remote BLE server.
    NimBLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
    if (pRemoteCharacteristic == nullptr) {
        pClient->disconnect();
        return 0;
    }
    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());
        return atoi(value.c_str());
        pClient->connect(&device);      
    }
    return 0;
}
long long int scanTimer;
const int CUTOFF = -74;    

void setup()
 {
    Serial.begin(115200);
    NimBLEDevice::init("MyESP");                                                    //initialzie the local BLE environment as COVID_Alert_Device client device
    NimBLEServer *pServer = NimBLEDevice::createServer();                                 //create server
    NimBLEService *pService = pServer->createService(SERVICE_UUID);
    NimBLECharacteristic *Sending_Characteristic = pService->createCharacteristic(            //characteristic for sending data
                                     CHARACTERISTIC_UUID,
                                     NIMBLE_PROPERTY::READ |
                                     NIMBLE_PROPERTY::WRITE
    );
    Sending_Characteristic ->setValue(ORDER);
    pService->start();
    pServer->getAdvertising()->start();
    NimBLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    pAdvertising->setScanResponse(true);
    NimBLEDevice::startAdvertising();
    scanTimer = millis();
 }

 void loop(){
    if (scanTimer + 5000 < millis()){
            BLEScan* pBLEScan = NimBLEDevice::getScan();                                   //create new scan
            BLEScanResults scanResults = pBLEScan->start(3, false);                     //scan for 3 seconds, not continuous
            Serial.print("Number of devices found this loop:");
            Serial.println(scanResults.getCount());
            Serial.println("List of devices found: ");                                                                    
            for (int i=0 ; i < scanResults.getCount(); i++){                                        //check each new BluetoothID found
                BLEAdvertisedDevice device = scanResults.getDevice(i); 
                Serial.println(device.toString().c_str());                                                  
                if (device.getRSSI() > CUTOFF && device.haveServiceUUID () && device.getServiceUUID().equals(serviceUUID)){                  //check the Bluetooth signal is strong enough and have correct ServiceUUID  
                    unsigned int ToDevice = ConnectToESP32(device);
                    if (ToDevice != 0){
                    }
                } 
            }
            NimBLEDevice::getScan()->clearResults();                                    
            scanTimer = millis();
    }
 }

And here is the output:

List of devices found: 
Name: , Address: 65:c2:ae:c8:2f:08, manufacturer data: 4c000c0e00d43ee5e4e9693ac51ac17ae4da, txPower: 12
Name: , Address: 1a:1f:08:5e:bb:a7, manufacturer data: 06000109200220d68373afcb336ccabcc8c9d056214c53e5ed973245a1
Name: , Address: 24:93:0d:4c:2f:4a, manufacturer data: 060001092002c97d4abd0b1be7882721186b459fa7a1c7a5cbe3c974e2
Name: , Address: 40:cc:ba:63:87:a8, manufacturer data: 4c001005381c8be8bd, txPower: 7
Name: MyESP, Address: 24:0a:c4:61:35:8a
Name: , Address: 58:51:e0:ff:15:48, manufacturer data: 4c001006471d1f9cfe08, txPower: 12
Name: , Address: 41:6c:fb:7f:ed:3b, manufacturer data: 4c001005481c344d27, txPower: 12
Name: , Address: 03:99:bf:f4:3c:36, manufacturer data: 4c0005120000000000000000013de44f1d655ac9de00
Name: , Address: 23:e2:da:20:c9:49, serviceUUID: 0xfd6f
Service Data:
UUID: 0xfd6f, Data: x^⸮⸮2T⸮0
$
Name: , Address: 4d:03:d1:ee:9a:c4, manufacturer data: 4c0010055118263eb7, txPower: 12
Number of devices found this loop:10

Did I miss anything here or did I make any mistake here?

chegewara commented 3 years ago

Try this: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEScan.cpp#L173

hientv1999 commented 3 years ago

I'm not sure if this is a dumb question but for example, if I develop my program in a way that it will have several switch cases and some of them do not want to use BLE at all, and if I implement the callback function in a switch case for scanning, does it interrupt the program whenever an advertiser is found? Since there should be some points in the program I don't want either the callback or BLE work to be invoked.

hientv1999 commented 3 years ago

I have changed the program into the callback way but only the txPower appears, the serviceUUID still disappears.

Here is my program:

unsigned int DeviceOrder = 1;
#define ORDER "1"
#include <NimBLEDevice.h>                              
#include "esp_bt_main.h"
#include "esp_bt_device.h"       
BLECharacteristic *pCharacteristic2;
#define SERVICE_UUID            "b596e525-c3c6-45b3-a8bb-b8cddcc62a61"              
static NimBLEUUID serviceUUID("b596e525-c3c6-45b3-a8bb-b8cddcc62a61");
#define CHARACTERISTIC_UUID     "f364c9cb-2d2c-4eb6-92d3-28db399a09ee"  

int ConnectToESP32(NimBLEAdvertisedDevice device){
    BLEClient*  pClient  = NimBLEDevice::createClient();
    Serial.println(" - Created client");

    // Connect to the remove BLE Server.
    pClient->connect(&device);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)

    // Obtain a reference to the service we are after in the remote BLE server.

    if (pClient->getService(SERVICE_UUID) == nullptr) {
        pClient->disconnect();
        return 0;
    }
    Serial.println(" - Found our service");
    NimBLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
    // Obtain a reference to the characteristic in the service of the remote BLE server.
    NimBLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
    if (pRemoteCharacteristic == nullptr) {
        pClient->disconnect();
        return 0;
    }
    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());
        return atoi(value.c_str());
        pClient->connect(&device);      
    }
    return 0;
}
long long int scanTimer;
const int CUTOFF = -74;    

/** Define a class to handle the callbacks when advertisments are received */
class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {

    void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
        Serial.print("Advertised Device found: ");
        Serial.println(advertisedDevice->toString().c_str());
        if (advertisedDevice->haveServiceUUID() && advertisedDevice->getServiceUUID().equals(serviceUUID)){
            unsigned int ToDevice = ConnectToESP32(*advertisedDevice);
            Serial.print("I got the characteristic value: ");
            Serial.println(ToDevice);
        }
    };
};

/** Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
    Serial.println("Scan Ended");
}

void setup()
 {
    Serial.begin(115200);
    NimBLEDevice::init("MyESP");                                                    //initialzie the local BLE environment as COVID_Alert_Device client device
    NimBLEServer *pServer = NimBLEDevice::createServer();                                 //create server
    NimBLEService *pService = pServer->createService(SERVICE_UUID);
    NimBLECharacteristic *Sending_Characteristic = pService->createCharacteristic(            //characteristic for sending data
                                     CHARACTERISTIC_UUID,
                                     NIMBLE_PROPERTY::READ |
                                     NIMBLE_PROPERTY::WRITE
    );
    Sending_Characteristic ->setValue(ORDER);
    pService->start();
    pServer->getAdvertising()->start();
    NimBLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    pAdvertising->setScanResponse(true);
    NimBLEDevice::startAdvertising();
    scanTimer = millis();
 }

 void loop(){
    if (scanTimer + 5000 < millis()){
      /** create new scan */  
      NimBLEScan* pScan = NimBLEDevice::getScan(); 

      /** create a callback that gets called when advertisers are found */
      pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());

      /** Set scan interval (how often) and window (how long) in milliseconds */
      pScan->setInterval(45);
      pScan->setWindow(15);

      /** Active scan will gather scan response data from advertisers
      *  but will use more energy from both devices
      */
     pScan->setActiveScan(true);
      /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
      *  Optional callback for when scanning stops. 
      */
      pScan->start(3, scanEndedCB);
      scanTimer = millis();
    }
 } 

and here is the output of Serial Monitor:

Advertised Device found: Name: MyESP, Address: 24:0a:c4:61:35:8a, txPower: 3
Advertised Device found: Name: , Address: 19:4b:e1:01:dc:ab, manufacturer data: 0600010920021d14837e67876156c9700bd826284fac33b6d455e261c4
Advertised Device found: Name: , Address: 6a:2a:ee:7f:be:4c, manufacturer data: 4c0010053e1c09ecd7, txPower: 7
Advertised Device found: Name: , Address: 16:f3:09:e5:9e:f1, serviceUUID: 0xfd6f
Service Data:
UUID: 0xfd6f, Data: Uc  J
⸮⸮*⸮R⸮⸮
⸮⸮⸮_⸮?
Scan Ended
chegewara commented 3 years ago
  pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());

you didnt set second parameter to true (wants duplicates), which by default is false.