cancel
Showing results for 
Search instead for 
Did you mean: 

STM32WB: Using "Service Changed" indication

NSAUG.1
Associate III

Hello,

Not a question but since I searched for this information and could not locate it anywhere, I post it here to maybe help others.

When calling aci_gatt_init(void), it is said that the Service Changed Characteristic is added automatically.

/**
 * @brief ACI_GATT_INIT
 * Initialize the GATT layer for server and client roles. It adds also the GATT
 * service with Service Changed Characteristic.
[...]

The "Service Changed" characteristic is thus always added with the very same handle (which is a requirement for it to be usable), but after that it is basically never mentioned again anywhere in the code or documentation. No info on what this handle is, nor what the payload should be to use it.

So here is how to use it from the Server side (to say that things changed), for those like me who want to experiment with variable lists of services in their BLE devices.

#define GENERIC_ATTRIBUTE_SERVICE_HANDLE   (0x0001)
#define SERVICE_CHANGED_CHARACTERISTIC_HANDLE   (0x0002)
 
tBleStatus ble_indicate_service_changed() {
  // In Bluetooth Core Specification v5.3 (Core_v5.3.pdf) [Vol 3, Part G, 7.1] Service Changed
  // It is explained that the value to indicate a service change is
  // the range of 16bits values of the handles that changed.
  // Full range: { 0x0001, 0xFFFF } according to https://www.oreilly.com/library/view/getting-started-with/9781491900550/ch04.html
 
  tBleStatus return_value = BLE_STATUS_FAILED;
 
  uint8_t payload[] = {0x00, 0x01, 0xFF, 0xFF}; // range of Handles to re-discover
 
  return_value = aci_gatt_update_char_value(
      GENERIC_ATTRIBUTE_SERVICE_HANDLE,
      SERVICE_CHANGED_CHARACTERISTIC_HANDLE,
      0, /* charValOffset */
      sizeof(payload),
      payload);
 
  if (return_value != BLE_STATUS_SUCCESS) {
      LOG_ERROR("  ble_indicate_service_changed failed: ret= %#X", return_value);
    } else {
      LOG_DEBUG("  Success sending: ble_indicate_service_changed");
    }
 
  return return_value;
}

(You need to change the logging functions LOG_ERROR and LOG_DEBUG to match your own implementation).

You could use a variable payload (here I only ask for a full update of all services) for more efficient use.

Service Changed is a characteristic with the property INDICATE, which is like NOTIFY (sent to the client whenever written to in the Server) but with the distinction that the Client should acknowledge the indication.

So if the Client received it and acknowledged it, you will get an event back, in pseudo-code from ble_app.c:

SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void *p_Pckt) {
	p_event_pckt = (hci_event_pckt*) ((hci_uart_pckt*) p_Pckt)->data;
	switch (p_event_pckt->evt) {
		case HCI_LE_META_EVT_CODE: {
			p_meta_evt = (evt_le_meta_event*) p_event_pckt->data;
			case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE:
				p_blecore_evt = (evt_blecore_aci*) p_event_pckt->data;
				switch (p_blecore_evt->ecode) {
					case ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE:
						// event received after Indication is acknowledged by Client, but not sure about the details here though...

Note that this is not the ONLY action that will yield that ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE, there might be a way to be certain (maybe info about what is confirmed by this event in the payload ?) but I did not go that far myself, sorry.

I think this feature should be cleaned up and added to

Limitations:

I verified that it worked with an Android 13 phone using nRFConnect.

I can attest that if the Client does things right (I can't tell you what those things are though, I only crossed my fingers that Android and nRFconnect would know...) then when my device writes to the Service Changed Characteristic, nRFconnect detects the need for re-discovering the services, and triggers a GattServicesDicovery() as expected.

However it did not work as I would have expected in multiple cases.

  • If I paired and bonded the device, sending the Service Changed Indication would not update the pairing information in-place. I would still need to delete bonding info then pair again (unless if the event happens with nRFConnect connected to the device). Not sure if that's normal or not, but disapointing :(

  • When I tried to use the device un-bonded with apps that actually consume the data my device produces, the "Service Changed" indication also did not make the new services magically appear to the app. The device would receive the indication and send back the ack event, but the app connected to my device would not see that change until it disconnected and re-connected. That's probably because said app only requests the service list at startup then runs with it. This kind of makes sense because support for dynamic services in most apps would be a needless complexity.

Hope that can help some.

Cheers

1 REPLY 1
DS.4
Senior II

As i was exploring the feature myself, your summary was very helpful!

can you tell me if you managed to remove this service /characteristic or know how?