From 4c517414ddb85700d40bb91c4010208117fc5030 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Fri, 5 Mar 2021 18:29:49 +0100 Subject: [PATCH] example: improve documentation for gatt_battery_query and gatt_device_information_query --- example/gatt_battery_query.c | 247 +++++++++++++--------- example/gatt_device_information_query.c | 261 +++++++++++++++--------- 2 files changed, 325 insertions(+), 183 deletions(-) diff --git a/example/gatt_battery_query.c b/example/gatt_battery_query.c index 2e9f33a69..c4f53f28c 100644 --- a/example/gatt_battery_query.c +++ b/example/gatt_battery_query.c @@ -38,9 +38,15 @@ #define BTSTACK_FILE__ "gatt_battery_query.c" // ***************************************************************************** -/* EXAMPLE_START(gatt_battery_query): GATT Battery Client +/* EXAMPLE_START(gatt_battery_query): GATT Battery Service Client * + * @text This example demonstrates how to use the GATT Battery Service client to + * receive battery level information. The client supports querying of multiple + * battery services instances of on the remote device. + * The example scans for remote devices and connects to the first found device + * and starts the battery service client. */ +// ***************************************************************************** #include #include @@ -84,6 +90,48 @@ static int cmdline_addr_found = 0; static btstack_packet_callback_registration_t hci_event_callback_registration; +/* @section Main Application Setup + * + * @text The Listing MainConfiguration shows how to setup Battery Service client. + * Besides calling init() method for each service, you'll also need to register HCI packet handler + * to handle advertisements, as well as connect and disconect events. + * + * @text Handling of GATT Battery Service events will be later delegated to a sepparate packet + * handler, i.e. gatt_client_event_handler. + * + * @note There are two additional files associated with this client to allow a remote device to query out GATT database: + * - gatt_battary_query.gatt - contains the declaration of the provided GATT Services and Characteristics. + * - gatt_battary_query.h - contains the binary representation of gatt_battary_query.gatt. + * + * gatt_battary_query.h is generated by the build system by calling: + * $BTSTACK_ROOT/tool/compile_gatt.py gatt_battary_query.gatt gatt_battary_query.h + * This file needs to be regenerated when the GATT Database declared in gatt_battary_query.gatt file is modified. + */ + +/* LISTING_START(MainConfiguration): Setup Device Battery Client service */ +static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void gatt_client_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +static void battery_service_client_setup(void){ + // Init L2CAP + l2cap_init(); + + // Setup ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android phones + att_server_init(profile_data, NULL, NULL); + + // GATT Client setup + gatt_client_init(); + // Device Information Service Client setup + battery_service_client_init(); + + sm_init(); + sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); + + hci_event_callback_registration.callback = &hci_event_handler; + hci_add_event_handler(&hci_event_callback_registration); +} +/* LISTING_END */ + static int blacklist_size(void){ return sizeof(blacklist) / sizeof(bd_addr_t); } @@ -116,11 +164,114 @@ static void dump_advertising_report(uint8_t *packet){ } -static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ +/* LISTING_START(packetHandler): Packet Handler */ +static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + /* LISTING_PAUSE */ + UNUSED(channel); + UNUSED(size); + + /* LISTING_RESUME */ + uint8_t status; + bd_addr_t address; + + if (packet_type != HCI_EVENT_PACKET){ + return; + } + + switch (hci_event_packet_get_type(packet)) { + /* LISTING_PAUSE */ + + case BTSTACK_EVENT_STATE: + // BTstack activated, get started + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break; + if (cmdline_addr_found){ + printf("Connect to %s\n", bd_addr_to_str(cmdline_addr)); + app_state = APP_STATE_W4_CONNECT; + gap_connect(cmdline_addr, 0); + break; + } + printf("Start scanning!\n"); + app_state = APP_STATE_W4_SCAN_RESULT; + gap_set_scan_parameters(0,0x0030, 0x0030); + gap_start_scan(); + break; + + case GAP_EVENT_ADVERTISING_REPORT: + if (app_state != APP_STATE_W4_SCAN_RESULT) return; + + gap_event_advertising_report_get_address(packet, address); + if (blacklist_contains(address)) { + break; + } + dump_advertising_report(packet); + + // stop scanning, and connect to the device + app_state = APP_STATE_W4_CONNECT; + gap_stop_scan(); + printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(report.address)); + gap_connect(report.address,report.address_type); + break; + + /* LISTING_RESUME */ + case HCI_EVENT_LE_META: + // Wait for connection complete + if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; + + /* LISTING_PAUSE */ + if (app_state != APP_STATE_W4_CONNECT) return; + + /* LISTING_RESUME */ + // Get connection handle from event + connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); + + // Connect to remote Battery Service. + // On succesful connection, the client tries to register for notifications. If notifications + // are not supported by remote Battery Service, the client will automatically poll the battery level - here every 2 seconds. + // If poll_interval_ms is 0, polling is disabled, and only notifications will be received (for manual polling, + // see battery_service_client.h). + // All GATT Battery Service events are handled by the gatt_client_event_handler. + status = battery_service_client_connect(connection_handle, gatt_client_event_handler, 2000, &battery_service_cid); + btstack_assert(status == ERROR_CODE_SUCCESS); + + app_state = APP_STATE_CONNECTED; + printf("Battery service connected.\n"); + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + connection_handle = HCI_CON_HANDLE_INVALID; + // Disconnect battery service + battery_service_client_disconnect(battery_service_cid); + + /* LISTING_PAUSE */ + if (cmdline_addr_found){ + printf("Disconnected %s\n", bd_addr_to_str(cmdline_addr)); + return; + } + + /* LISTING_RESUME */ + printf("Disconnected %s\n", bd_addr_to_str(report.address)); + printf("Restart scan.\n"); + app_state = APP_STATE_W4_SCAN_RESULT; + gap_start_scan(); + break; + default: + break; + } +} +/* LISTING_END */ + +/* LISTING_START(gatt_client_event_handler): GATT Client Event Handler */ +// The gatt_client_event_handler receives following events from remote device: +// - GATTSERVICE_SUBEVENT_BATTERY_SERVICE_CONNECTED +// - GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL +// +static void gatt_client_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + /* LISTING_PAUSE */ UNUSED(packet_type); UNUSED(channel); UNUSED(size); + /* LISTING_RESUME */ uint8_t status; uint8_t att_status; @@ -161,82 +312,7 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint break; } } - -static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - UNUSED(channel); - UNUSED(size); - - uint8_t status; - bd_addr_t address; - - if (packet_type != HCI_EVENT_PACKET){ - return; - } - - switch (hci_event_packet_get_type(packet)) { - case BTSTACK_EVENT_STATE: - // BTstack activated, get started - if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break; - if (cmdline_addr_found){ - printf("Connect to %s\n", bd_addr_to_str(cmdline_addr)); - app_state = APP_STATE_W4_CONNECT; - gap_connect(cmdline_addr, 0); - break; - } - printf("Start scanning!\n"); - app_state = APP_STATE_W4_SCAN_RESULT; - gap_set_scan_parameters(0,0x0030, 0x0030); - gap_start_scan(); - break; - - case GAP_EVENT_ADVERTISING_REPORT: - if (app_state != APP_STATE_W4_SCAN_RESULT) return; - - gap_event_advertising_report_get_address(packet, address); - if (blacklist_contains(address)) { - break; - } - dump_advertising_report(packet); - - // stop scanning, and connect to the device - app_state = APP_STATE_W4_CONNECT; - gap_stop_scan(); - printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(report.address)); - gap_connect(report.address,report.address_type); - break; - - case HCI_EVENT_LE_META: - // wait for connection complete - if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; - if (app_state != APP_STATE_W4_CONNECT) return; - connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); - - status = battery_service_client_connect(connection_handle, handle_gatt_client_event, 2000, &battery_service_cid); - btstack_assert(status == ERROR_CODE_SUCCESS); - - app_state = APP_STATE_CONNECTED; - printf("Battery service connected.\n"); - break; - - case HCI_EVENT_DISCONNECTION_COMPLETE: - connection_handle = HCI_CON_HANDLE_INVALID; - // Disconnect battery service - battery_service_client_disconnect(battery_service_cid); - - if (cmdline_addr_found){ - printf("Disconnected %s\n", bd_addr_to_str(cmdline_addr)); - return; - } - - printf("Disconnected %s\n", bd_addr_to_str(report.address)); - printf("Restart scan.\n"); - app_state = APP_STATE_W4_SCAN_RESULT; - gap_start_scan(); - break; - default: - break; - } -} + /* LISTING_END */ int btstack_main(int argc, const char * argv[]); int btstack_main(int argc, const char * argv[]){ @@ -259,20 +335,7 @@ int btstack_main(int argc, const char * argv[]){ } (void)argv; - l2cap_init(); - - // setup ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android phones - att_server_init(profile_data, NULL, NULL); - - // GATT Client setup - gatt_client_init(); - battery_service_client_init(); - - sm_init(); - sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); - - hci_event_callback_registration.callback = &hci_event_handler; - hci_add_event_handler(&hci_event_callback_registration); + battery_service_client_setup(); app_state = APP_STATE_IDLE; diff --git a/example/gatt_device_information_query.c b/example/gatt_device_information_query.c index e98f6db7f..3b66b2ecf 100644 --- a/example/gatt_device_information_query.c +++ b/example/gatt_device_information_query.c @@ -38,9 +38,15 @@ #define BTSTACK_FILE__ "gatt_device_information_query.c" // ***************************************************************************** -/* EXAMPLE_START(gatt_device_information_query): GATT Device Information Client +/* EXAMPLE_START(gatt_device_information_query): GATT Device Information Service Client * + * @text This example demonstrates how to use the GATT Device Information Service client to + * receive device information such as various IDs and revisions. The example scans + * for remote devices and connects to the first found device. If the remote device provides a Device + * Information Service, the information is collected and printed in console output, otherwise, + * the device will be blacklisted and the scan restarted. */ +// ***************************************************************************** #include #include @@ -83,6 +89,48 @@ static int cmdline_addr_found = 0; static btstack_packet_callback_registration_t hci_event_callback_registration; +/* @section Main Application Setup + * + * @text The Listing MainConfiguration shows how to setup Device Information Service client. + * Besides calling init() method for each service, you'll also need to register HCI packet handler + * to handle advertisements, as well as connect and disconect events. + * + * @text Handling of GATT Device Information Service events will be later delegated to a sepparate packet + * handler, i.e. gatt_client_event_handler. + * + * @note There are two additional files associated with this client to allow a remote device to query out GATT database: + * - gatt_device_information_query.gatt - contains the declaration of the provided GATT Services and Characteristics. + * - gatt_device_information_query.h - contains the binary representation of gatt_device_information_query.gatt. + * + * gatt_device_information_query.h is generated by the build system by calling: + * $BTSTACK_ROOT/tool/compile_gatt.py gatt_device_information_query.gatt gatt_device_information_query.h + * This file needs to be regenerated when the GATT Database declared in gatt_device_information_query.gatt file is modified. + */ + +/* LISTING_START(MainConfiguration): Setup Device Information Service Client service */ +static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void gatt_client_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +static void device_information_service_client_setup(void){ + // Init L2CAP + l2cap_init(); + + // Setup ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android phones + att_server_init(profile_data, NULL, NULL); + + // GATT Client setup + gatt_client_init(); + // Device Information Service Client setup + device_information_service_client_init(); + + sm_init(); + sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); + + hci_event_callback_registration.callback = &hci_packet_handler; + hci_add_event_handler(&hci_event_callback_registration); +} +/* LISTING_END */ + static int blacklist_size(void){ return sizeof(blacklist) / sizeof(bd_addr_t); } @@ -115,11 +163,109 @@ static void dump_advertising_report(uint8_t *packet){ } -static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ +/* LISTING_START(packetHandler): Packet Handler */ +static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + /* LISTING_PAUSE */ + UNUSED(channel); + UNUSED(size); + + uint8_t status; + bd_addr_t address; + + if (packet_type != HCI_EVENT_PACKET){ + return; + } + + switch (hci_event_packet_get_type(packet)) { + case BTSTACK_EVENT_STATE: + // BTstack activated, get started + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break; + if (cmdline_addr_found){ + printf("Connect to %s\n", bd_addr_to_str(cmdline_addr)); + app_state = APP_STATE_W4_CONNECT; + gap_connect(cmdline_addr, 0); + break; + } + printf("Start scanning!\n"); + app_state = APP_STATE_W4_SCAN_RESULT; + gap_set_scan_parameters(0,0x0030, 0x0030); + gap_start_scan(); + break; + + case GAP_EVENT_ADVERTISING_REPORT: + if (app_state != APP_STATE_W4_SCAN_RESULT) return; + + gap_event_advertising_report_get_address(packet, address); + if (blacklist_contains(address)) { + break; + } + dump_advertising_report(packet); + + // stop scanning, and connect to the device + app_state = APP_STATE_W4_CONNECT; + gap_stop_scan(); + printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(report.address)); + gap_connect(report.address,report.address_type); + break; + + /* LISTING_RESUME */ + case HCI_EVENT_LE_META: + // wait for connection complete + if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; + + /* LISTING_PAUSE */ + if (app_state != APP_STATE_W4_CONNECT) return; + + /* LISTING_RESUME */ + // get connection handle from event + connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); + + // Connect to remote Device Information Service. The client will query the remote service and emit events, + // that will be passed on to gatt_client_event_handler. + status = device_information_service_client_query(connection_handle, gatt_client_event_handler); + btstack_assert(status == ERROR_CODE_SUCCESS); + + printf("Device Information connected.\n"); + + app_state = APP_STATE_CONNECTED; + break; + /* LISTING_PAUSE */ + + case HCI_EVENT_DISCONNECTION_COMPLETE: + connection_handle = HCI_CON_HANDLE_INVALID; + + if (cmdline_addr_found){ + printf("Disconnected %s\n", bd_addr_to_str(cmdline_addr)); + return; + } + + printf("Disconnected %s\n", bd_addr_to_str(report.address)); + printf("Restart scan.\n"); + app_state = APP_STATE_W4_SCAN_RESULT; + gap_start_scan(); + break; + default: + break; + } +} +/* LISTING_END */ + + +/* LISTING_START(gatt_client_event_handler): GATT Client Event Handler */ +// The gatt_client_event_handler receives following events from remote device: +// - GATTSERVICE_SUBEVENT_BATTERY_SERVICE_CONNECTED +// - GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL +// +// Event GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_DONE is received when all queries are done, +// of if service was not found. The status field of this event indicated ATT errors (see bluetooth.h). + +static void gatt_client_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + /* LISTING_PAUSE */ UNUSED(packet_type); UNUSED(channel); UNUSED(size); + /* LISTING_RESUME */ uint8_t att_status = 0; if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META){ @@ -135,7 +281,10 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint printf("Manufacturer Name: %s\n", gattservice_subevent_device_information_manufacturer_name_get_value(packet)); } break; - + + // ... + /* LISTING_PAUSE */ + case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_MODEL_NUMBER: att_status = gattservice_subevent_device_information_model_number_get_att_status(packet); if (att_status != ATT_ERROR_SUCCESS){ @@ -216,92 +365,35 @@ static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint } break; + /* LISTING_RESUME */ case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_DONE: att_status = gattservice_subevent_device_information_serial_number_get_att_status(packet); + switch (att_status){ + case ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE: + printf("Device Information service client not found.\n"); + add_to_blacklist(report.address); + gap_disconnect(connection_handle); + break; + case ATT_ERROR_SUCCESS: + printf("Query done\n"); + break; + default: + printf("Query failed, ATT Error 0x%02x\n", att_status); + break; + + } if (att_status != ATT_ERROR_SUCCESS){ + if (att_status == ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE) printf("Query failed, ATT Error 0x%02x\n", att_status); } else { printf("Query done\n"); } - - default: - break; - } -} - -static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - UNUSED(channel); - UNUSED(size); - - uint8_t status; - bd_addr_t address; - - if (packet_type != HCI_EVENT_PACKET){ - return; - } - - switch (hci_event_packet_get_type(packet)) { - case BTSTACK_EVENT_STATE: - // BTstack activated, get started - if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break; - if (cmdline_addr_found){ - printf("Connect to %s\n", bd_addr_to_str(cmdline_addr)); - app_state = APP_STATE_W4_CONNECT; - gap_connect(cmdline_addr, 0); - break; - } - printf("Start scanning!\n"); - app_state = APP_STATE_W4_SCAN_RESULT; - gap_set_scan_parameters(0,0x0030, 0x0030); - gap_start_scan(); - break; - - case GAP_EVENT_ADVERTISING_REPORT: - if (app_state != APP_STATE_W4_SCAN_RESULT) return; - - gap_event_advertising_report_get_address(packet, address); - if (blacklist_contains(address)) { - break; - } - dump_advertising_report(packet); - - // stop scanning, and connect to the device - app_state = APP_STATE_W4_CONNECT; - gap_stop_scan(); - printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(report.address)); - gap_connect(report.address,report.address_type); - break; - - case HCI_EVENT_LE_META: - // wait for connection complete - if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; - if (app_state != APP_STATE_W4_CONNECT) return; - connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); - - status = device_information_service_client_query(connection_handle, handle_gatt_client_event); - btstack_assert(status == ERROR_CODE_SUCCESS); - - app_state = APP_STATE_CONNECTED; - printf("Device Information connected.\n"); - break; - - case HCI_EVENT_DISCONNECTION_COMPLETE: - connection_handle = HCI_CON_HANDLE_INVALID; - - if (cmdline_addr_found){ - printf("Disconnected %s\n", bd_addr_to_str(cmdline_addr)); - return; - } - - printf("Disconnected %s\n", bd_addr_to_str(report.address)); - printf("Restart scan.\n"); - app_state = APP_STATE_W4_SCAN_RESULT; - gap_start_scan(); break; default: break; } } + /* LISTING_END */ int btstack_main(int argc, const char * argv[]); int btstack_main(int argc, const char * argv[]){ @@ -324,20 +416,7 @@ int btstack_main(int argc, const char * argv[]){ } (void)argv; - l2cap_init(); - - // setup ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android phones - att_server_init(profile_data, NULL, NULL); - - // GATT Client setup - gatt_client_init(); - device_information_service_client_init(); - - sm_init(); - sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); - - hci_event_callback_registration.callback = &hci_event_handler; - hci_add_event_handler(&hci_event_callback_registration); + device_information_service_client_setup(); app_state = APP_STATE_IDLE;