h2zero / esp-nimble-cpp

C++ library for the esp32 NimBLE stack based on and mostly compatible with @nkolban cpp_utils BLE library.
https://h2zero.github.io/esp-nimble-cpp/
Apache License 2.0
182 stars 62 forks source link

Issue with Switching Services #163

Open michaelboeding opened 6 months ago

michaelboeding commented 6 months ago

I'm running into an issue where I start provisioning my device using esps prov api, which uses its own services. When I go out of that later in the application I create my services, but it seems like IOS is caching these values until I reset my network settings on the device. Which isn't a great solution for customers, is there anyway to make the client not read from the cache on a new connection with the server? Or some other solution to this?

To add a little more context what I'm trying to accomplish is trigger something like the below for IOS

"Once you remove and re-add the service, that will modify the GATT database and trigger a notification to the central. The Bluetooth-standard way to handle this is using the Service Changed characteristic (0x2a05), but it's possible Apple does this in a proprietary way if you're talking between Apple devices. If these are both iOS devices, you should expect the central's CBPeripheralDelegate to receive peripheral(_:didModifyServices:) (possibly twice, once for removing, and once for adding)"

 func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
        print("Sevices have been updated called for this item")
        peripheral.discoverServices(scanningServiceArray)
    }

And then what I currently have is something like the below

//methoud used to setup the bluetooth as a server void BluetoothLayer::setupBluetoothServer(){ //check to make sure the bluetooth is not already setup if (this->server != nullptr){ return; } //setup the bluetooth server NimBLEDevice::init(DEVICE_ID); // Create the BLE Server this->server = NimBLEDevice::createServer(); this->server->setCallbacks(this); //setup the services this->setupGATTService(); this->setupGenericAccessService(); this->setupDeviceInfoService(); this->setupDeviceBatteryService(); this->setupCustomService(); this->startAdvertising();

this->mtuSize = NimBLEDevice::getMTU();
printf("ALL SERVICES SETUP FOR BLE\n");

}

Basically I am creating all the services I want to use and setting up the characteristics but this is where I also get some weird behavior as the Generic Access Service, and the GATT Service never seem to show up in my ble sniffer apps or they show up on my MAC sniffer app but it says "There aren't any characteristics for this service" but im adding the SERVICE_CHANGED_CHARACTERISTIC_UUID as specified by the BLE SIG. Is there something happening under the hood for this? Or something that I don't understand?

I've also tried to enable dynamic services in the nimble config.

Also here is the link to the esp ble prov api I'm using https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/provisioning/wifi_provisioning.html?highlight=wifi%20prov#_CPPv420wifi_prov_cb_event_t

h2zero commented 6 months ago

The basic required services are automatically created by the stack, no need to define them yourself.

michaelboeding commented 6 months ago

I never see those services when I run a BLE sniffer though? Should they automatically appear as services?

h2zero commented 6 months ago

They show in nrfConnect on android but not iOS. iOS automatically subscribes to the service changed characteristic.

michaelboeding commented 6 months ago

Got it - the weird thing is I never get the callbacks for services changed...has it ever been tested?

// This method is called when the peripheral's services are modified
func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
    print("Services modified: \(invalidatedServices)")

    // Handle the modified services
    for service in invalidatedServices {
        print("Service \(service.uuid) was invalidated")

        // Rediscover the services if necessary
        peripheral.discoverServices([service.uuid])
    }
}
h2zero commented 6 months ago

That is all handled by the stack at a lower level, not presented to the app code. With full debug logging turned on you can see iOS subscribe to it. If you make a change to the services the indication will be sent by the stack as well.

michaelboeding commented 6 months ago

I'll try to run it in full debug - I'm never receiving it currently in CoreBluetooths callbacks. I'm also running the latest 5.2.1 of the IDF but I can't confirm that it worked previously either.

h2zero commented 5 months ago

You won't see anything for the most part, all you'll see is a log message from the NimBLE core that shows iOS subscribed to a handle. As far as changing services goes there needs to be no active connections or advertising/scanning active when they are changed, then when a bonded device connects they receive the service changed notification.