bluekitchen / btstack

Dual-mode Bluetooth stack, with small memory footprint.
http://bluekitchen-gmbh.com
Other
1.73k stars 611 forks source link

BTstackLib LE Peripheral Notify client on Characteristic Change #551

Open savejeff opened 11 months ago

savejeff commented 11 months ago

Hi, I'm trying to get the Arduino Wrapper for BTstack working to send data to a connected client (for example Android Smartphone) I want to get the quasi-standard UART Service working to send data bi-directional. I first asked this here on the alternative Arduino RP2040 Core, but i was told this Wrapper was also part of the btstack project. Is that the case? i have a hard time finding this code this repo.

anyway here the original problem I'm working on.

for this i have these two Characteristics:

BTstack.addGATTService(new UUID(SERVICE_UUID_UART));

// client writes to this charac. to send data to RP2040
pCharacteristic_UART_RX = BTstack.addGATTCharacteristicDynamic(new UUID(CHARACTERISTIC_UUID_UART_RX), ATT_PROPERTY_WRITE, 0); 

// RP2040 writes to this charac. to trigger a notify message to the connected client to send data to remote device
pCharacteristic_UART_TX = BTstack.addGATTCharacteristicDynamic(new UUID(CHARACTERISTIC_UUID_UART_TX), ATT_PROPERTY_NOTIFY, 0); 

The problem is, that i cant find functionality to write to the pCharacteristic_UART_TX (which should then notify the connected client that the characteristic has changed)

In general, how can i write to created characteristics created with addGATTCharacteristicDynamic?

I know the library is still under development, i looked at this around half a year ago, and there seemed to be some changes, but i still can't find this functionality. Am i overlooking something? I feel like this functionality of having a device with a readable characteristic that is updated with sensor data is pretty much the basic use case for BLE GATT.

I also looked at the underlying btstack library but its far too complicated and i would rather wait for the arduino wrapper to get the functionality i need.


here equivalent code written for the ESP32 as a reference


    // Create the BLE Service
    pService_UART = pServer->createService(SERVICE_UUID_UART);

    // Create a BLE Characteristic
    pCharacteristic_UART_TX = pService_UART->createCharacteristic(
        CHARACTERISTIC_UUID_UART_TX,
        BLECharacteristic::PROPERTY_NOTIFY
    );

    pCharacteristic_UART_TX->addDescriptor(new BLE2902());

    pCharacteristic_UART_RX = pService_UART->createCharacteristic(
        CHARACTERISTIC_UUID_UART_RX,
        BLECharacteristic::PROPERTY_WRITE
    );

    pCharacteristic_UART_RX->setCallbacks(new BLE_Callback_UART_RX());

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

[...]

    // set Characteristics and notify client
    pCharacteristic_UART_TX->setValue((uint8_t*)buff, len);
    pCharacteristic_UART_TX->notify();
mringwal commented 11 months ago

Hi there. I did a quick check and it looks like we didn't add support for sending notifications to the Arduino Wrapper.

Is there a chance BTstack could be used directly without this additional wrapper in the Arduino world? As a quick hack, I guess you could call att_server_notify directly although you might get a "cannot send right now". If that works, you should call att_server_request_to_send_notification() to register a callback that is called when it's safe to call att_server_notify().

On the Pico W, you can directly use BTstack as part of the C/C++ SDK and all examples are ready to use, too.

savejeff commented 11 months ago

Hi, thanks for the quick response.

I have previously looked into btstack itself but if found it quite complicated to get what I want. the gatt config file etc is IMHO not as elegant as the ESP32 BLE wrapper. I think it would be very beneficial for the wider Arduino community to offer a Wrapper for BLE functionality similar to the ESP32 one. I'm personally quite experienced with embedded development, but I still prefer to use the Arduino libraries, because they make it easy to get started and that is I think the most important thing for more people to get into embedded development.

As there is already the mentioned wrapper for BTstack I would be very grateful if it would be extended to have the utmost basic GATT functionality for the embedded device action as a BLE "Server" a Remote device can connect to. I should include these features:

I have also looked through the BTstack example but there is not really a basic example that shows how to use the above-listed functionality. Especially for the notify feature i had a hard time finding an example code

mringwal commented 11 months ago

Hi @savejeff.

BTstack did initially only support GATT DB configuration via the .gatt file as it requires no RAM at run time. We later added the option to build the GATT DB in memory programmatically.

If you want to extend the existing Arduino Wrapper, you're welcome to do so.

A Characteristic can support Read, Write and/or Notify. UART simulation is usually done using ATT Write without Notify and sending ATT Notifications. You get the writes via the existing callback. To send, you'll need to call att_server_request_to_send_notification() with a btstack_context_callback_registration_t object to store your request. When you get the callback, you can then call att_server_notify().

The GATT Server is documented here: https://bluekitchen-gmbh.com/btstack/#profiles/#gatt-server The gatt_streamer_server example shows how to send as fast as possible when notifications get enabled.

savejeff commented 11 months ago

with the new generation of embedded processors like the ESP32 and RP2040, the ram is less of a problem. I prefer clean easy to read and well maintainable code over memory or flash optimization. I often have projects where i have different variants of the base code that is reconfigured through #ifdefs and "setting"-variables. the programmatic approach is very helpful here

A Characteristic can support Read, Write and/or Notify. UART simulation is usually done using ATT Write without Notify and sending ATT Notifications. You get the writes via the existing callback. To send, you'll need to call att_server_request_to_send_notification() with a btstack_context_callback_registration_t object to store your request. When you get the callback, you can then call att_server_notify().

Before putting in a weekend of getting into btstack and trying to implement it myself, would i be possible for a dev already familiar with the code to implement it. it should like quite a straightforward implementation if one knows how to do it.

Im mainly using ESP32 for my projects/products, but i don't like that i am that dependent on a single manufacturer (the stm32 users learned why this i a bad idea during the chip shortage 😅). And thus I tried to add RP2040 support to my firmware, and currently, I'm only missing the BLE implementation.

I it would be helpful i could define the interface and test code for the Wrapper, then only the btstack background functionality would need to be implemented. I would really love to have something very close to how the ESP32 arduino core has implemented it.

mringwal commented 11 months ago

Talking about vendor lock-in, BTstack is all about being portable, as you can run the same code on POSIX & Windows desktops, microcontrollers, esp. ESP32 and Pico W without any changes.

Yes, an interface for the Notify would be nice. Please keep in mind that a) we need to call att_server_request_to_send_notification() to let the stack know that the app wants send a notification, b) it should be malloc free, at least I don't want to allocate memory for data from the application.

mglazzari-qinmotion commented 3 months ago

I hit the same problem and landed here looking for a solution. Trying to follow mringwal advice, I found a simple way of doing this without modifying the Arduino library, although it won't work 100% of the time.

void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
  switch (status) {
    case BLE_STATUS_OK:
      con_handle = device->getHandle();
      Serial.println("Device connected!");
      break;
    default:
      break;
  }
}
savejeff commented 3 months ago

To me sure, does you code implement the equivalent of this:


// set Characteristics and notify client
    pCharacteristic_UART_TX->setValue((uint8_t*)buff, len);
    pCharacteristic_UART_TX->notify();
mglazzari-qinmotion commented 3 months ago

It looks very similar, with the exception that your code seems to be storing the new data in the characteristic and then pushing a notification to the client. If the characteristic is defined as Read / Notify, the client would have an opportunity to read it later, if required. In the code that I showed, the characteristic value is not stored, so if the intention is to allow the client to read it at a later time, it has to be stored somewhere (this is trivial to do, but my code does not include it). And like I mentioned, the "notify" part of my code is not very robust (it may fail)