# 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 . 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. 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. ### Implementing Standard GATT Services {#sec:GATTStandardServices} Implementation of a standard GATT Service consists of the following 4 steps: 1. Identify full Service Name 2. Use Service Name to fetch XML definition at Bluetooth SIG site and convert into generic .gatt file 3. Edit .gatt file to set constant values and exclude unwanted Characteristics 4. Implement Service server, e.g., battery_service_server.c Step 1: To facilitate the creation of .gatt files for standard profiles defined by the Bluetooth SIG, the *tool/convert_gatt_service.py* script can be used. When run without a parameter, it queries the Bluetooth SIG website and lists the available Services by their Specification Name, e.g., *org.bluetooth.service.battery_service*. $ tool/convert_gatt_service.py Fetching list of services from https://www.bluetooth.com/specifications/gatt/services Specification Type | Specification Name | UUID -------------------------------------------------------+-------------------------------+----------- org.bluetooth.service.alert_notification | Alert Notification Service | 0x1811 org.bluetooth.service.automation_io | Automation IO | 0x1815 org.bluetooth.service.battery_service | Battery Service | 0x180F ... org.bluetooth.service.weight_scale | Weight Scale | 0x181D To convert a service into a .gatt file template, please call the script again with the requested Specification Type and the output file name Usage: tool/convert_gatt_service.py SPECIFICATION_TYPE [service_name.gatt] Step 2: To convert service into .gatt file, call *tool/convert_gatt_service.py with the requested Specification Type and the output file name. $ tool/convert_gatt_service.py org.bluetooth.service.battery_service battery_service.gatt Fetching org.bluetooth.service.battery_service from https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.battery_service.xml Service Battery Service - Characteristic Battery Level - properties ['Read', 'Notify'] -- Descriptor Characteristic Presentation Format - TODO: Please set values -- Descriptor Client Characteristic Configuration Service successfully converted into battery_service.gatt Please check for TODOs in the .gatt file Step 3: In most cases, you will need to customize the .gatt file. Please pay attention to the tool output and have a look at the generated .gatt file. E.g. in the generated .gatt file for the Battery Service // Specification Type org.bluetooth.service.battery_service // https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.battery_service.xml // Battery Service 180F PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL, DYNAMIC | READ | NOTIFY, // TODO: Characteristic Presentation Format: please set values #TODO CHARACTERISTIC_FORMAT, READ, _format_, _exponent_, _unit_, _name_space_, _description_ CLIENT_CHARACTERISTIC_CONFIGURATION, READ | WRITE, you could delete the line regarding the CHARACTERISTIC_FORMAT, since it's not required if there is a single instance of the service. Please compare the .gatt file against the [Adopted Specifications](https://www.bluetooth.com/specifications/adopted-specifications). 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