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

Setting advertisement type only of BLEAdvertising type object alone does not change advertisement type #589

Open MehmetCagriK opened 6 years ago

MehmetCagriK commented 6 years ago

I was trying to create a low power temperature broadcaster BLE device. Naturally, you don' t want it to be connectable and you would share data only with advertisements anyways.

So I realized libraries did not allow me to modify m_advParams.adv_type of BLEAdvertising object. So, I added necessary function and its signature both to BLEAdvertising.h and BLEAdvertising.cpp. Source code changes are added below;

// ADD THIS INTO BLEAdvertising.cpp
/**
 * @brief Set Advertisement Type
 * @param [in] advType Type of the advertisement
 * @description
 * ADV_TYPE_IND = 0x00
 * ADV_TYPE_DIRECT_IND_HIGH = 0x01
 * ADV_TYPE_SCAN_IND = 0x02
 * ADV_TYPE_NONCONN_IND = 0x03
 * ADV_TYPE_DIRECT_IND_LOW = 0x04
 */
void BLEAdvertising::setAdvType(esp_ble_adv_type_t advType) {
    if(advType >= 0x00 && advType <= 0x04)
    m_advParams.adv_type = advType;
} // setAdvType
// ADD THIS INTO BLEAdvertising.h into class BLEAdvertising definition
void setAdvType(esp_ble_adv_type_t advType);

I prepared a sample code to test it and I saw setting advertisement type only does not change advertisement behavior. When I was setting it to ADV_TYPE_NONCONN_IND, I could connect to the device with both NRFConnect and LightBlue apps.

However, while tinkering with other parameters, I realized setting min and max advertisement intervals with setting advertisement type was giving me what I want, the device is no longer connectable. Thus, it fulfills broadcaster role.

Here is the smallest working piece of code;

#include <BLEDevice.h>

void setup() {
  BLEDevice::init("BLE Device");
  BLEServer *pServer = BLEDevice::createServer();

  BLEAdvertising *pAdvertising = pServer->getAdvertising();
// Comment out the code for non-connectable advertisements
//  pAdvertising->setMinInterval(500);
//  pAdvertising->setMaxInterval(1000);
  pAdvertising->setAdvType(ADV_TYPE_NONCONN_IND);
  pAdvertising->start();

  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void loop() {

}

I am reminding you that default minimum adv. interval is 0x20 and maximum adv. interval is 0x40, given in initialization. I do not know correlation between advertisement intervals and connectability behavior but if we solve this abd test it throughly, we may add nice feature.

MehmetCagriK commented 6 years ago

Second method of making a device kinda not connectable is setting advertisement filter policy to prevent non-whitelisted devices from connecting to the advertiser. To achieve it, simply add the code below before starting advertisement;

// Passing second parameter as true prevents connection from devices out of whitelist
BLEAdvertising *pAdvertising;
pAdvertising->setScanFilter(false, true);

But the behavior is different with this method.

  1. In method given in the first post of this issue, we are advertising the device as non-connectable. The devices receiving the advertisement CAN NOT send connection request.
  2. In the second method, given in this post, we are setting advertiser device to reject connect requests coming from non-whitelisted devices. So, advertisement type is still connectable, so devices hearing advertisement CAN send connection request but this time, advertiser rejects it.

Both method, in the end, prevents connection to advertiser devices but only the first method is true non-connectable method and if you are trying to create low power broadcaster/advertiser device that only wants to transmit(not to handle energy loss from rejecting-dealing connection requests), you better pick first method.

vuanh-minh commented 6 years ago

Thank you for your methods @MehmetCagriK, it helps me a lot for my project !

But i still have a problem with whitelist : when i set setScanFilter(true,true), to only advertise and accept connection to the whitelist, even my whitelisted device can't find the server. And when i set the filter to ( false,true ), my whitelisted device can see the server, but the advertiser reject the connection request. Do you know where that problem may come from ?

For the moment, i let the the scanfilter to ( true, false ) so only the device who knows the server address can connect.

chegewara commented 6 years ago

Looks like your whitelisted device is not whitelisted or we have bug in whitelist options (this library or esp-idf). If you have some code i could test this and you can share would be nice.

MehmetCagriK commented 6 years ago

@vuanh-minh Glad i could help. Can you send me smallest subset of code that still provides same faults, so I can try myself. If both server and client devices are esp32, send me simplified codes both. Because some smartphone bluetooth apps do not have enough configuration, they might not be correct tools for debugging ble servers.

vuanh-minh commented 6 years ago

I think i have found where the problem came from : In my program, the ESP32 is both server and client, and the device too.

ESP32 side : when a non-paired device connects to the ESP32 Server, the ESP32 scan for specific device UUID, and create a Task MyClient which connects back to the device's server.

class MyClient: public Task {
    void run(void* data) {
//If device is not paired yet, pDeviceAddress = the address's device which just connected, then we store the address in nvs, and add to the whitelist

        if (!isPaired){
        pDeviceAddress = (BLEAddress*)data;
         esp_err_t err = nvs_set_str(my_handle,myKey,pDeviceAddress->toString().c_str());
        ESP_LOGI(TAG,"Device's address '%s' stored in NVS for key %s",            pDeviceAddress->toString().c_str(),myKey );

        BLEDevice::whiteListAdd(*pDeviceAddress);
    ESP_LOGI(TAG,"Device's address '%s' stored in whitelist", pDeviceAddress->toString().c_str() );
        }

        BLEClient*  pClient  = BLEDevice::createClient();

        // Connect to the remote BLE Server.
        pClient->connect(*pDeviceAddress); 
        AppConnected=true;

        ESP_LOGI(TAG, "ESP32 Client is now connected to application Server");
        ESP_LOGI(TAG, "Application address is : %s",pDeviceAddress->toString().c_str());

        // Obtain a reference to the service we are after in the remote BLE server.
        BLERemoteService* pRemoteService = pClient->getService(app_serviceUUID);
        if (pRemoteService == nullptr) {
            ESP_LOGI(TAG, "Failed to find our service UUID: %s", app_serviceUUID.toString().c_str());
            return;
        }

        // Obtain a reference to the characteristic in the service of the remote BLE server.
        pRemoteCharacteristic = pRemoteService->getCharacteristic(app_charUUID);
        if (pRemoteCharacteristic == nullptr) {
            ESP_LOGI(TAG, "Failed to find our characteristic UUID: %s", app_charUUID.toString().c_str());
            return;
        }
    } // run
}; // MyClientTask

When i put *BLEDevice::whiteListAdd(pDeviceAddress);** in that task, the server doesn't seems to add the client into the whitelist, and so, the client can't connect even if there is no errors message and the log "Device's address '%s' stored in whitelist" appears correctly.

So i tried to move this line in the BLE Server configuration task :

`class MainBLEServer: public Task { void run(void *data) { BLEDevice::init("ESP32");

        if (isPaired){
            BLEDevice::whiteListAdd(*pDeviceAddress);
            scanRequestWhitelistOnly = true;
            connectWhitelistOnly = true;
        }
        else {
            scanRequestWhitelistOnly = false;
            connectWhitelistOnly = false;
        }

        BLEServer* pServer = BLEDevice::createServer();
        pServer->setCallbacks(new MyServerCallbacks());

......Service & Characteristics configuration....

...

        pService->start();` 

And now, i test it and it seems to work for the whitelist, and the client can reconnect to the server after initial bonding. The only thing is that the ESP32 add the address to the whitelist at every boot, is that ok ?

chegewara commented 6 years ago

Its hard to say. There is limited space for whitelisted devices and you need to ask espressif devs how devices are added (duplicates stored or not). I dont see too much of your code but for some reason it seems to look odd (or maybe im just tired).

chegewara commented 6 years ago

@MehmetCagriK I think there is some bug in esp-idf ble stack because im getting very strange results: https://github.com/nkolban/esp32-snippets/issues/613#issuecomment-412311395