In the following, we explain how the various Bluetooth profiles are used in BTstack. ## 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 addition Client Configuration Characteristic. Property | Meaning ------------------------|----------------------------------------------- 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 | Meaning ------------------------|----------------------------------------------- AUTHENTICATION_REQUIRED | Read and Write operatsions 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] 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_can_send_now* to receive a ATT_EVENT_CAN_SEND_NOW event. 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 neccessary 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_can_send_now_callback()*. 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*.