xref: /btstack/example/gatt_battery_query.c (revision f3d85318ba8ac4b5e15b53561f141894fecb510d)
1bcf00d8fSMatthias Ringwald /*
2bcf00d8fSMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
3bcf00d8fSMatthias Ringwald  *
4bcf00d8fSMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
5bcf00d8fSMatthias Ringwald  * modification, are permitted provided that the following conditions
6bcf00d8fSMatthias Ringwald  * are met:
7bcf00d8fSMatthias Ringwald  *
8bcf00d8fSMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
9bcf00d8fSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
10bcf00d8fSMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11bcf00d8fSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
12bcf00d8fSMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
13bcf00d8fSMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
14bcf00d8fSMatthias Ringwald  *    contributors may be used to endorse or promote products derived
15bcf00d8fSMatthias Ringwald  *    from this software without specific prior written permission.
16bcf00d8fSMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
17bcf00d8fSMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
18bcf00d8fSMatthias Ringwald  *    monetary gain.
19bcf00d8fSMatthias Ringwald  *
20bcf00d8fSMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21bcf00d8fSMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22bcf00d8fSMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23bcf00d8fSMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24bcf00d8fSMatthias Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25bcf00d8fSMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26bcf00d8fSMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27bcf00d8fSMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28bcf00d8fSMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29bcf00d8fSMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30bcf00d8fSMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31bcf00d8fSMatthias Ringwald  * SUCH DAMAGE.
32bcf00d8fSMatthias Ringwald  *
33bcf00d8fSMatthias Ringwald  * Please inquire about commercial licensing options at
34bcf00d8fSMatthias Ringwald  * [email protected]
35bcf00d8fSMatthias Ringwald  *
36bcf00d8fSMatthias Ringwald  */
37bcf00d8fSMatthias Ringwald 
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "gatt_battery_query.c"
39ab2c6ae4SMatthias Ringwald 
40bcf00d8fSMatthias Ringwald // *****************************************************************************
41ec8ae085SMilanka Ringwald /* EXAMPLE_START(gatt_battery_query): GATT Battery Client
42ec8ae085SMilanka Ringwald  *
43ec8ae085SMilanka Ringwald  */
44bcf00d8fSMatthias Ringwald 
45bcf00d8fSMatthias Ringwald #include <stdint.h>
46bcf00d8fSMatthias Ringwald #include <stdio.h>
47bcf00d8fSMatthias Ringwald #include <stdlib.h>
48bcf00d8fSMatthias Ringwald #include <string.h>
49bcf00d8fSMatthias Ringwald 
50bcf00d8fSMatthias Ringwald #include "btstack.h"
51a63a688aSMatthias Ringwald 
52a63a688aSMatthias Ringwald // gatt_battery_query.gatt contains the declaration of the provided GATT Services + Characteristics
53a63a688aSMatthias Ringwald // gatt_battery_query.h    contains the binary representation of gatt_battery_query.gatt
54a63a688aSMatthias Ringwald // it is generated by the build system by calling: $BTSTACK_ROOT/tool/compile_gatt.py gatt_battery_query.gatt gatt_battery_query.h
55a63a688aSMatthias Ringwald // it needs to be regenerated when the GATT Database declared in gatt_battery_query.gatt file is modified
56add9769eSMatthias Ringwald #include "gatt_battery_query.h"
57bcf00d8fSMatthias Ringwald 
58bcf00d8fSMatthias Ringwald typedef struct advertising_report {
59bcf00d8fSMatthias Ringwald     uint8_t   type;
60bcf00d8fSMatthias Ringwald     uint8_t   event_type;
61bcf00d8fSMatthias Ringwald     uint8_t   address_type;
62bcf00d8fSMatthias Ringwald     bd_addr_t address;
63bcf00d8fSMatthias Ringwald     uint8_t   rssi;
64bcf00d8fSMatthias Ringwald     uint8_t   length;
653ee82ab1SMilanka Ringwald     const uint8_t * data;
66bcf00d8fSMatthias Ringwald } advertising_report_t;
67bcf00d8fSMatthias Ringwald 
6883b6788cSMilanka Ringwald static enum {
6983b6788cSMilanka Ringwald     APP_STATE_IDLE,
7083b6788cSMilanka Ringwald     APP_STATE_W4_SCAN_RESULT,
7183b6788cSMilanka Ringwald     APP_STATE_W4_CONNECT,
7283b6788cSMilanka Ringwald     APP_STATE_CONNECTED
7383b6788cSMilanka Ringwald } app_state;
74bcf00d8fSMatthias Ringwald 
750f09bd96SMilanka Ringwald static int blacklist_index = 0;
760f09bd96SMilanka Ringwald static bd_addr_t blacklist[20];
770f09bd96SMilanka Ringwald static advertising_report_t report;
780f09bd96SMilanka Ringwald 
7983b6788cSMilanka Ringwald static hci_con_handle_t connection_handle;
8083b6788cSMilanka Ringwald static uint16_t battery_service_cid;
81bcf00d8fSMatthias Ringwald 
82c30af2ffSMatthias Ringwald static bd_addr_t cmdline_addr;
83bcf00d8fSMatthias Ringwald static int cmdline_addr_found = 0;
84bcf00d8fSMatthias Ringwald 
85bcf00d8fSMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration;
86bcf00d8fSMatthias Ringwald 
87287eecffSMilanka Ringwald static int blacklist_size(void){
880f09bd96SMilanka Ringwald     return sizeof(blacklist) / sizeof(bd_addr_t);
890f09bd96SMilanka Ringwald }
900f09bd96SMilanka Ringwald 
910f09bd96SMilanka Ringwald static int blacklist_contains(bd_addr_t addr){
920f09bd96SMilanka Ringwald     int i;
930f09bd96SMilanka Ringwald     for (i=0; i<blacklist_size(); i++){
940f09bd96SMilanka Ringwald         if (bd_addr_cmp(addr, blacklist[i]) == 0) return 1;
950f09bd96SMilanka Ringwald     }
960f09bd96SMilanka Ringwald     return 0;
970f09bd96SMilanka Ringwald }
980f09bd96SMilanka Ringwald 
990f09bd96SMilanka Ringwald static void add_to_blacklist(bd_addr_t addr){
10089b171cfSMilanka Ringwald     printf("%s added to blacklist (no battery service found).\n", bd_addr_to_str(addr));
1010f09bd96SMilanka Ringwald     bd_addr_copy(blacklist[blacklist_index], addr);
1020f09bd96SMilanka Ringwald     blacklist_index = (blacklist_index + 1) % blacklist_size();
1030f09bd96SMilanka Ringwald }
1040f09bd96SMilanka Ringwald 
10583b6788cSMilanka Ringwald static void dump_advertising_report(uint8_t *packet){
10683b6788cSMilanka Ringwald     bd_addr_t address;
10783b6788cSMilanka Ringwald     gap_event_advertising_report_get_address(packet, address);
10883b6788cSMilanka Ringwald 
10983b6788cSMilanka Ringwald     printf("    * adv. event: evt-type %u, addr-type %u, addr %s, rssi %u, length adv %u, data: ",
11083b6788cSMilanka Ringwald         gap_event_advertising_report_get_advertising_event_type(packet),
11183b6788cSMilanka Ringwald         gap_event_advertising_report_get_address_type(packet),
11283b6788cSMilanka Ringwald         bd_addr_to_str(address),
11383b6788cSMilanka Ringwald         gap_event_advertising_report_get_rssi(packet),
11483b6788cSMilanka Ringwald         gap_event_advertising_report_get_data_length(packet));
11583b6788cSMilanka Ringwald     printf_hexdump(gap_event_advertising_report_get_data(packet), gap_event_advertising_report_get_data_length(packet));
116bcf00d8fSMatthias Ringwald 
117bcf00d8fSMatthias Ringwald }
118bcf00d8fSMatthias Ringwald 
119bcf00d8fSMatthias Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1209ec2630cSMatthias Ringwald     UNUSED(packet_type);
1219ec2630cSMatthias Ringwald     UNUSED(channel);
1229ec2630cSMatthias Ringwald     UNUSED(size);
1239ec2630cSMatthias Ringwald 
124*f3d85318SMilanka Ringwald     uint8_t status;
125*f3d85318SMilanka Ringwald     uint8_t att_status;
126bcf00d8fSMatthias Ringwald 
127174a0c1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META){
128174a0c1cSMilanka Ringwald         return;
129174a0c1cSMilanka Ringwald     }
130174a0c1cSMilanka Ringwald 
131174a0c1cSMilanka Ringwald     switch (hci_event_gattservice_meta_get_subevent_code(packet)){
1327ec9a1bbSMilanka Ringwald         case GATTSERVICE_SUBEVENT_BATTERY_SERVICE_CONNECTED:
1337ec9a1bbSMilanka Ringwald             status = gattservice_subevent_battery_service_connected_get_status(packet);
134174a0c1cSMilanka Ringwald             switch (status){
135174a0c1cSMilanka Ringwald                 case ERROR_CODE_SUCCESS:
136*f3d85318SMilanka Ringwald                     printf("Battery service client connected, found %d services, poll bitmap 0x%02x\n",
137*f3d85318SMilanka Ringwald                         gattservice_subevent_battery_service_connected_get_num_instances(packet),
138*f3d85318SMilanka Ringwald                         gattservice_subevent_battery_service_connected_get_poll_bitmap(packet));
139bcf00d8fSMatthias Ringwald                     break;
140174a0c1cSMilanka Ringwald                 default:
14189b171cfSMilanka Ringwald                     printf("Battery service client connection failed, err 0x%02x.\n", status);
1420f09bd96SMilanka Ringwald                     add_to_blacklist(report.address);
1430f09bd96SMilanka Ringwald                     gap_disconnect(connection_handle);
144bcf00d8fSMatthias Ringwald                     break;
145bcf00d8fSMatthias Ringwald             }
146bcf00d8fSMatthias Ringwald             break;
147174a0c1cSMilanka Ringwald 
14883b6788cSMilanka Ringwald         case GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL:
149*f3d85318SMilanka Ringwald             att_status = gattservice_subevent_battery_service_level_get_att_status(packet);
150*f3d85318SMilanka Ringwald             if (att_status != ATT_ERROR_SUCCESS){
151*f3d85318SMilanka Ringwald                 printf("Battery level read failed, ATT Error 0x%02x\n", att_status);
152*f3d85318SMilanka Ringwald             } else {
15389b171cfSMilanka Ringwald                 printf("Service index: %d, Battery level: %d\n",
15483b6788cSMilanka Ringwald                     gattservice_subevent_battery_service_level_get_sevice_index(packet),
15583b6788cSMilanka Ringwald                     gattservice_subevent_battery_service_level_get_level(packet));
156*f3d85318SMilanka Ringwald 
157*f3d85318SMilanka Ringwald             }
1580f09bd96SMilanka Ringwald             break;
159bcf00d8fSMatthias Ringwald 
160bcf00d8fSMatthias Ringwald         default:
161bcf00d8fSMatthias Ringwald             break;
162bcf00d8fSMatthias Ringwald     }
163bcf00d8fSMatthias Ringwald }
164bcf00d8fSMatthias Ringwald 
165bcf00d8fSMatthias Ringwald static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1669ec2630cSMatthias Ringwald     UNUSED(channel);
1679ec2630cSMatthias Ringwald     UNUSED(size);
1689ec2630cSMatthias Ringwald 
169174a0c1cSMilanka Ringwald     uint8_t status;
17083b6788cSMilanka Ringwald     bd_addr_t address;
1710f09bd96SMilanka Ringwald 
17283b6788cSMilanka Ringwald     if (packet_type != HCI_EVENT_PACKET){
17383b6788cSMilanka Ringwald         return;
17483b6788cSMilanka Ringwald     }
17583b6788cSMilanka Ringwald 
17683b6788cSMilanka Ringwald     switch (hci_event_packet_get_type(packet)) {
177bcf00d8fSMatthias Ringwald         case BTSTACK_EVENT_STATE:
178bcf00d8fSMatthias Ringwald             // BTstack activated, get started
179cdc7d1abSMilanka Ringwald             if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break;
180bcf00d8fSMatthias Ringwald             if (cmdline_addr_found){
1810f09bd96SMilanka Ringwald                 printf("Connect to %s\n", bd_addr_to_str(cmdline_addr));
18283b6788cSMilanka Ringwald                 app_state = APP_STATE_W4_CONNECT;
183bcf00d8fSMatthias Ringwald                 gap_connect(cmdline_addr, 0);
184bcf00d8fSMatthias Ringwald                 break;
185bcf00d8fSMatthias Ringwald             }
1860f09bd96SMilanka Ringwald             printf("Start scanning!\n");
18783b6788cSMilanka Ringwald             app_state = APP_STATE_W4_SCAN_RESULT;
188bcf00d8fSMatthias Ringwald             gap_set_scan_parameters(0,0x0030, 0x0030);
189bcf00d8fSMatthias Ringwald             gap_start_scan();
190bcf00d8fSMatthias Ringwald             break;
1913ee82ab1SMilanka Ringwald 
19283b6788cSMilanka Ringwald         case GAP_EVENT_ADVERTISING_REPORT:
19383b6788cSMilanka Ringwald             if (app_state != APP_STATE_W4_SCAN_RESULT) return;
19483b6788cSMilanka Ringwald 
19583b6788cSMilanka Ringwald             gap_event_advertising_report_get_address(packet, address);
19683b6788cSMilanka Ringwald             if (blacklist_contains(address)) {
1970f09bd96SMilanka Ringwald                 break;
1980f09bd96SMilanka Ringwald             }
19983b6788cSMilanka Ringwald             dump_advertising_report(packet);
20083b6788cSMilanka Ringwald 
201bcf00d8fSMatthias Ringwald             // stop scanning, and connect to the device
20283b6788cSMilanka Ringwald             app_state = APP_STATE_W4_CONNECT;
203bcf00d8fSMatthias Ringwald             gap_stop_scan();
2040f09bd96SMilanka Ringwald             printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(report.address));
205bcf00d8fSMatthias Ringwald             gap_connect(report.address,report.address_type);
206bcf00d8fSMatthias Ringwald             break;
20783b6788cSMilanka Ringwald 
208bcf00d8fSMatthias Ringwald         case HCI_EVENT_LE_META:
209bcf00d8fSMatthias Ringwald             // wait for connection complete
21010cad102SMilanka Ringwald             if (hci_event_le_meta_get_subevent_code(packet) !=  HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
21183b6788cSMilanka Ringwald             if (app_state != APP_STATE_W4_CONNECT) return;
212a59bfbf7SMatthias Ringwald             connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
21383b6788cSMilanka Ringwald 
21449f15046SMilanka Ringwald             status = battery_service_client_connect(connection_handle, handle_gatt_client_event, 2000, &battery_service_cid);
21583b6788cSMilanka Ringwald             btstack_assert(status == ERROR_CODE_SUCCESS);
21683b6788cSMilanka Ringwald 
21783b6788cSMilanka Ringwald             app_state = APP_STATE_CONNECTED;
21883b6788cSMilanka Ringwald             printf("Battery service connected.\n");
219bcf00d8fSMatthias Ringwald             break;
22083b6788cSMilanka Ringwald 
221bcf00d8fSMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
22226687d14SMatthias Ringwald             connection_handle = HCI_CON_HANDLE_INVALID;
22383b6788cSMilanka Ringwald             // Disconnect battery service
22483b6788cSMilanka Ringwald             battery_service_client_disconnect(battery_service_cid);
2250f09bd96SMilanka Ringwald 
2260f09bd96SMilanka Ringwald             if (cmdline_addr_found){
22783b6788cSMilanka Ringwald                 printf("Disconnected %s\n", bd_addr_to_str(cmdline_addr));
228eb8fc740SMatthias Ringwald                 return;
2290f09bd96SMilanka Ringwald             }
2300f09bd96SMilanka Ringwald 
23183b6788cSMilanka Ringwald             printf("Disconnected %s\n", bd_addr_to_str(report.address));
2320f09bd96SMilanka Ringwald             printf("Restart scan.\n");
23383b6788cSMilanka Ringwald             app_state = APP_STATE_W4_SCAN_RESULT;
2340f09bd96SMilanka Ringwald             gap_start_scan();
235bcf00d8fSMatthias Ringwald             break;
236bcf00d8fSMatthias Ringwald         default:
237bcf00d8fSMatthias Ringwald             break;
238bcf00d8fSMatthias Ringwald     }
239bcf00d8fSMatthias Ringwald }
240bcf00d8fSMatthias Ringwald 
241bcf00d8fSMatthias Ringwald int btstack_main(int argc, const char * argv[]);
242bcf00d8fSMatthias Ringwald int btstack_main(int argc, const char * argv[]){
243bcf00d8fSMatthias Ringwald 
24483b6788cSMilanka Ringwald     // parse address if command line arguments are provided
245bcf00d8fSMatthias Ringwald     int arg = 1;
246bcf00d8fSMatthias Ringwald     cmdline_addr_found = 0;
247bcf00d8fSMatthias Ringwald 
248bcf00d8fSMatthias Ringwald     while (arg < argc) {
249bcf00d8fSMatthias Ringwald         if(!strcmp(argv[arg], "-a") || !strcmp(argv[arg], "--address")){
250bcf00d8fSMatthias Ringwald             arg++;
251a6efb919SMatthias Ringwald             cmdline_addr_found = sscanf_bd_addr(argv[arg], cmdline_addr);
252bcf00d8fSMatthias Ringwald             arg++;
2530f09bd96SMilanka Ringwald             if (!cmdline_addr_found) exit(1);
254bcf00d8fSMatthias Ringwald             continue;
255bcf00d8fSMatthias Ringwald         }
25683b6788cSMilanka Ringwald         fprintf(stderr, "\nUsage: %s [-a|--address aa:bb:cc:dd:ee:ff]\n", argv[0]);
25783b6788cSMilanka Ringwald         fprintf(stderr, "If no argument is provided, GATT browser will start scanning and connect to the first found device.\nTo connect to a specific device use argument [-a].\n\n");
258bcf00d8fSMatthias Ringwald         return 0;
259bcf00d8fSMatthias Ringwald     }
2606316c8edSMatthias Ringwald     (void)argv;
26183b6788cSMilanka Ringwald 
262bcf00d8fSMatthias Ringwald     l2cap_init();
263bcf00d8fSMatthias Ringwald 
264add9769eSMatthias Ringwald     // setup ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android phones
265add9769eSMatthias Ringwald     att_server_init(profile_data, NULL, NULL);
266add9769eSMatthias Ringwald 
267add9769eSMatthias Ringwald     // GATT Client setup
268bcf00d8fSMatthias Ringwald     gatt_client_init();
269174a0c1cSMilanka Ringwald     battery_service_client_init();
270bcf00d8fSMatthias Ringwald 
271bcf00d8fSMatthias Ringwald     sm_init();
272bcf00d8fSMatthias Ringwald     sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
273bcf00d8fSMatthias Ringwald 
274a4fe6467SMatthias Ringwald     hci_event_callback_registration.callback = &hci_event_handler;
275a4fe6467SMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
276a4fe6467SMatthias Ringwald 
27783b6788cSMilanka Ringwald     app_state = APP_STATE_IDLE;
278a4fe6467SMatthias Ringwald 
279bcf00d8fSMatthias Ringwald     // turn on!
280bcf00d8fSMatthias Ringwald     hci_power_control(HCI_POWER_ON);
281bcf00d8fSMatthias Ringwald 
282bcf00d8fSMatthias Ringwald     return 0;
283bcf00d8fSMatthias Ringwald }
284bcf00d8fSMatthias Ringwald 
285ec8ae085SMilanka Ringwald /* EXAMPLE_END */
286bcf00d8fSMatthias Ringwald 
287bcf00d8fSMatthias Ringwald 
288