Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BTstackLib LE Peripheral Notify client on Characteristic Change #551

Open
savejeff opened this issue Nov 26, 2023 · 8 comments
Open

BTstackLib LE Peripheral Notify client on Characteristic Change #551

savejeff opened this issue Nov 26, 2023 · 8 comments

Comments

@savejeff
Copy link

savejeff commented Nov 26, 2023

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
Copy link
Member

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
Copy link
Author

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:

  • Advertising, (already implemented)
  • create services and characteristics with Read Write and Notify attributes (already implemented)
  • Reading from Write-Characteristics (already implemented via callback)
  • Writing to Read-Characteristics (not sure if implemented)
  • Trigger Notify with payload data (currently not implemented)

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
Copy link
Member

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
Copy link
Author

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
Copy link
Member

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
Copy link

mglazzari-qinmotion commented Jul 29, 2024

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.

  • Create a global variable in your Arduino program to hold the connection handle.
hci_con_handle_t con_handle;
  • You will obtain the handle and save it during the Connect event (callback):
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
  switch (status) {
    case BLE_STATUS_OK:
      con_handle = device->getHandle();
      Serial.println("Device connected!");
      break;
    default:
      break;
  }
}
  • When your characteristic changes, you push it to your Client (call this function manually). newCharValue is the new value of your characteristic (assuming String):
bool notify_client_characteristic_changed(String newCharValue){
  //att_server_request_can_send_now_event(con_handle);
  if (att_server_can_send_packet_now(con_handle)){
    uint8_t buffer[256];//create a buffer large enough
    newCharValue.toCharArray((char*)buffer, sizeof(buffer));//Convert the string to a char array and put it in the buffer
    att_server_notify(con_handle, characteristicHandle, buffer, newCharValue.length());//push the new value to the client
    Serial.println("Notification sent");
  return true;//success
  }else{
    Serial.println("Client busy. Could not send");
  }
  return false;//failed
}

The characteristicHandle above is the value returned by BTstack.addGATTCharacteristic when you register your characteristic.
Now, this may fail, for instance, if the Client is busy when we try to push the notification
.
The official way of avoiding this failure is by calling att_server_request_can_send_now_event(con_handle);, which will put a special message in the packet handler when the client is ready to receive the notification. When you receive that message, you call the function above and it should work. But the packet handler is inside the Arduino library, so doing a full implementation requires changes to it. The solution presented here is simple and should work most of the time.

@savejeff
Copy link
Author

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
Copy link

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants