# In the following, we explain how the various Bluetooth profiles are used in BTstack. ## A2DP - Advanced Audio Distribution The A2DP profile defines how to stream audio over a Bluetooth connection from one device, such as a mobile phone, to another device such as a headset. A device that acts as source of audio stream implements the A2DP Source role. Similarly, a device that receives an audio stream implements the A2DP Sink role. As such, the A2DP service allows uni-directional transfer of an audio stream, from single channel mono, up to two channel stereo. Our implementation includes mandatory support for the low-complexity SBC codec. Signaling for optional codes (FDK AAC, LDAC, APTX) is supported as well, by you need to provide your own codec library. ## AVRCP - Audio/Video Remote Control Profile The AVRCP profile defines how audio playback on a remote device (e.g. a music app on a smartphone) can be controlled as well as how to state changes such as volume, information on currently played media, battery, etc. can be received from a remote device (e.g. a speaker). Usually, each device implements two roles: - The Controller role allows to query information on currently played media, such are title, artist and album, as well as to control the playback, i.e. to play, stop, repeat, etc. - The Target role responds to commands, e.g. playback control, and queries, e.g. playback status, media info, from the Controller currently played media. ## GAP - Generic Access Profile: Classic The GAP profile defines how devices find each other and establish a secure connection for other profiles. As mentioned before, the GAP functionality is split between and `hci.h` and `gap.h`. Please check both. ### Become discoverable A remote unconnected Bluetooth device must be set as “discoverable” in order to be seen by a device performing the inquiry scan. To become discoverable, an application can call *gap_discoverable_control* with input parameter 1. If you want to provide a helpful name for your device, the application can set its local name by calling *gap_set_local_name*. To save energy, you may set the device as undiscoverable again, once a connection is established. See Listing [below](#lst:Discoverable) for an example. ~~~~ {#lst:Discoverable .c caption="{Setting discoverable mode.}"} int main(void){ ... // make discoverable gap_discoverable_control(1); btstack_run_loop_execute(); return 0; } void packet_handler (uint8_t packet_type, uint8_t *packet, uint16_t size){ ... switch(state){ case W4_CHANNEL_COMPLETE: // if connection is successful, make device undiscoverable gap_discoverable_control(0); ... } } ~~~~ ### Discover remote devices {#sec:GAPdiscoverRemoteDevices} To scan for remote devices, the *hci_inquiry* command is used. Found remote devices are reported as a part of: - HCI_EVENT_INQUIRY_RESULT, - HCI_EVENT-_INQUIRY_RESULT_WITH_RSSI, or - HCI_EVENT_EXTENDED_INQUIRY_RESPONSE events. Each response contains at least the Bluetooth address, the class of device, the page scan repetition mode, and the clock offset of found device. The latter events add information about the received signal strength or provide the Extended Inquiry Result (EIR). A code snippet is shown in Listing [below](#lst:DiscoverDevices). ~~~~ {#lst:DiscoverDevices .c caption="{Discover remote devices.}"} void print_inquiry_results(uint8_t *packet){ int event = packet[0]; int numResponses = hci_event_inquiry_result_get_num_responses(packet); uint16_t classOfDevice, clockOffset; uint8_t rssi, pageScanRepetitionMode; for (i=0; i PRIMARY_SERVICE, {SERVICE_UUID} CHARACTERISTIC, {ATTRIBUTE_TYPE_UUID}, {PROPERTIES}, {VALUE} CHARACTERISTIC, {ATTRIBUTE_TYPE_UUID}, {PROPERTIES}, {VALUE} ... PRIMARY_SERVICE, {SERVICE_UUID} CHARACTERISTIC, {ATTRIBUTE_TYPE_UUID}, {PROPERTIES}, {VALUE} ... ~~~~ UUIDs are either 16 bit (1800) or 128 bit (00001234-0000-1000-8000-00805F9B34FB). Value can either be a string (“this is a string”), or, a sequence of hex bytes (e.g. 01 02 03). Properties can be a list of properties combined using '|' Reads/writes to a Characteristic that is defined with the DYNAMIC flag, are forwarded to the application via callback. Otherwise, the Characteristics cannot be written and it will return the specified constant value. Adding NOTIFY and/or INDICATE automatically creates an additional Client Configuration Characteristic. Property | Description ------------------------|----------------------------------------------- READ | Characteristic can be read WRITE | Characteristic can be written using Write Request WRITE_WITHOUT_RESPONSE | Characteristic can be written using Write Command NOTIFY | Characteristic allows notifications by server INDICATE | Characteristic allows indication by server DYNAMIC | Read or writes to Characteristic are handled by application To require encryption or authentication before a Characteristic can be accessed, you can add one or more of the following properties: Property | Description ------------------------|----------------------------------------------- AUTHENTICATION_REQUIRED | Read and Write operations require Authentication READ_ENCRYPTED | Read operations require Encryption READ_AUTHENTICATED | Read operations require Authentication WRITE_ENCRYPTED | Write operations require Encryption WRITE_AUTHENTICATED | Write operations require Authentication ENCRYPTION_KEY_SIZE_X | Require encryption size >= X, with W in [7..16] For example, Volume State Characteristic (Voice Control Service) requires: - Mandatory Properties: Read, Notify - Security Permissions: Encryption Required In addition, its read is handled by application. We can model this Characteristic as follows: ~~~~ CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_VOLUME_STATE, DYNAMIC | READ | NOTIFY | ENCRYPTION_KEY_SIZE_16 ~~~~ To use already implemented GATT Services, you can import it using the *#import * command. See [list of provided services](gatt_services.md). BTstack only provides an ATT Server, while the GATT Server logic is mainly provided by the GATT compiler. While GATT identifies Characteristics by UUIDs, ATT uses Handles (16 bit values). To allow to identify a Characteristic without hard-coding the attribute ID, the GATT compiler creates a list of defines in the generated \*.h file. Similar to other protocols, it might be not possible to send any time. To send a Notification, you can call *att_server_request_to_send_notification* to request a callback, when yuo can send the Notification. ### Deferred Handling of ATT Read / Write Requests If your application cannot handle an ATT Read Request in the *att_read_callback* in some situations, you can enable support for this by adding ENABLE_ATT_DELAYED_RESPONSE to *btstack_config.h*. Now, you can store the requested attribute handle and return *ATT_READ_RESPONSE_PENDING* instead of the length of the provided data when you don't have the data ready. For ATT operations that read more than one attribute, your *att_read_callback* might get called multiple times as well. To let you know that all necessary attribute handles have been 'requested' by the *att_server*, you'll get a final *att_read_callback* with the attribute handle of *ATT_READ_RESPONSE_PENDING*. When you've got the data for all requested attributes ready, you can call *att_server_response_ready*, which will trigger processing of the current request. Please keep in mind that there is only one active ATT operation and that it has a 30 second timeout after which the ATT server is considered defunct by the GATT Client. Similarly, you can return ATT_ERROR_WRITE_RESPONSE_PENDING in the *att_write_callback*. The ATT Server will not respond to the ATT Client in this case and wait for your code to call *att_server_response_ready*, which then triggers the *att_write_callback* again. Please have a look at the [ATT Delayed Response example](../examples/examples/#sec:attdelayedresponseExample). ### Implementing Standard GATT Services {#sec:GATTStandardServices} Implementation of a standard GATT Service consists of the following 4 steps: 1. Get the Service specification from Bluetooth SIG 2. Find the Service Characteristics table and their properties 3. Create .gatt file from Service Characteristics table 4. Implement Service server, e.g., battery_service_server.c Step 1: All GATT Service specifications can be found [here](https://www.bluetooth.com/specifications/specs/). Step 2: The Service Characteristics table is usually in chapter "Service Characteristics". Let's have a look at an actual example, the [Battery Service Specification v1.0](https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=245138). In it, we find this: Characteristic | Ref | Mandatory/Optional ---------------|-----|------------------- Battery Level | 3.1 | M So, the Battery Service has a single mandatory Characteristic. Characteristic | Broadcast | Read | Write without Response | Write | Notify | Indicate | Signed Write | Reliable Write | Writable Auxiliaries ---------------|-----------|------|------------------------|-------|--------|----------|--------------|----------------|--------- Battery Level | x | M | x | x | O | x | x | x | x The Battery Level Characteristic must supports Read and optionally allows for Notifications. Step 3: Following the Battery Service v1.0 example, let's create `battery_service.gatt`. BTstack has a list of most GATT Service and Characteristics UUIDs in `src/bluetooth_gatt.h`, which can be used in .gatt files. Missing UUIDs can be found in Bluetooth SIG Bitbucket repo: - [Service UUIDs](https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/uuids/service_uuids.yaml) - [Characteristic UUIDs](https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/uuids/characteristic_uuids.yaml) First we add the Primary Service definition: ``` // Battery Service v1.0 PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE ``` Next, we add all Characteristics and map their properties into the format of the .gatt file. In this example, the Battery Level is dynamic and supports Read and Notification. ``` CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL, DYNAMIC | READ | NOTIFY, ``` Feel free to take a look at already implemented GATT Service .gatt files in `src/ble/gatt-service/`. Step 4: As described [above](#sec:GATTServerProfiles) all read/write requests are handled by the application. To implement the new services as a reusable module, it's necessary to get access to all read/write requests related to this service. For this, the ATT DB allows to register read/write callbacks for a specific handle range with *att_server_register_service_handler()*. Since the handle range depends on the application's .gatt file, the handle range for Primary and Secondary Services can be queried with *gatt_server_get_get_handle_range_for_service_with_uuid16*. Similarly, you will need to know the attribute handle for particular Characteristics to handle Characteristic read/writes requests. You can get the attribute value handle for a Characteristics *gatt_server_get_value_handle_for_characteristic_with_uuid16()*. In addition to the attribute value handle, the handle for the Client Characteristic Configuration is needed to support Indications/Notifications. You can get this attribute handle with *gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16()* Finally, in order to send Notifications and Indications independently from the main application, *att_server_register_can_send_now_callback* can be used to request a callback when it's possible to send a Notification or Indication. To see how this works together, please check out the Battery Service Server in *src/ble/battery_service_server.c*. ### GATT Database Hash When a GATT Client connects to a GATT Server, it cannot know if the GATT Database has changed and has to discover the provided GATT Services and Characteristics after each connect. To speed this up, the Bluetooth specification defines a GATT Service Changed Characteristic, with the idea that a GATT Server would notify a bonded GATT Client if its database changed. However, this is quite fragile and it is not clear how it can be implemented in a robust way. The Bluetooth Core Spec 5.1 introduced the GATT Database Hash Characteristic, which allows for a simple robust mechanism to cache a remote GATT Database. The GATT Database Hash is a 16-byte value that is calculated over the list of Services and Characteristics. If there is any change to the database, the hash will change as well. To support this on the GATT Server, you only need to add a GATT Service with the GATT Database Characteristic to your .gatt file. The hash value is then calculated by the GATT compiler. PRIMARY_SERVICE, GATT_SERVICE CHARACTERISTIC, GATT_DATABASE_HASH, READ, Note: make sure to install the PyCryptodome python package as the hash is calculated using AES-CMAC, e.g. with: pip install pycryptodomex