xref: /btstack/example/gatt_battery_query.c (revision 83b6788cc176b84e4053592dc57941e20a8b19d5)
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 
68*83b6788cSMilanka Ringwald static enum {
69*83b6788cSMilanka Ringwald     APP_STATE_IDLE,
70*83b6788cSMilanka Ringwald     APP_STATE_W4_SCAN_RESULT,
71*83b6788cSMilanka Ringwald     APP_STATE_W4_CONNECT,
72*83b6788cSMilanka Ringwald     APP_STATE_CONNECTED
73*83b6788cSMilanka 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 
79*83b6788cSMilanka Ringwald static hci_con_handle_t connection_handle;
80*83b6788cSMilanka 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){
1000f09bd96SMilanka 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 
105*83b6788cSMilanka Ringwald static void dump_advertising_report(uint8_t *packet){
106*83b6788cSMilanka Ringwald     bd_addr_t address;
107*83b6788cSMilanka Ringwald     gap_event_advertising_report_get_address(packet, address);
108*83b6788cSMilanka Ringwald 
109*83b6788cSMilanka Ringwald     printf("    * adv. event: evt-type %u, addr-type %u, addr %s, rssi %u, length adv %u, data: ",
110*83b6788cSMilanka Ringwald         gap_event_advertising_report_get_advertising_event_type(packet),
111*83b6788cSMilanka Ringwald         gap_event_advertising_report_get_address_type(packet),
112*83b6788cSMilanka Ringwald         bd_addr_to_str(address),
113*83b6788cSMilanka Ringwald         gap_event_advertising_report_get_rssi(packet),
114*83b6788cSMilanka Ringwald         gap_event_advertising_report_get_data_length(packet));
115*83b6788cSMilanka 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 
1240f09bd96SMilanka Ringwald     int status;
125bcf00d8fSMatthias Ringwald 
126174a0c1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META){
127174a0c1cSMilanka Ringwald         return;
128174a0c1cSMilanka Ringwald     }
129174a0c1cSMilanka Ringwald 
130174a0c1cSMilanka Ringwald     switch (hci_event_gattservice_meta_get_subevent_code(packet)){
1317ec9a1bbSMilanka Ringwald         case GATTSERVICE_SUBEVENT_BATTERY_SERVICE_CONNECTED:
1327ec9a1bbSMilanka Ringwald             status = gattservice_subevent_battery_service_connected_get_status(packet);
133174a0c1cSMilanka Ringwald             switch (status){
134174a0c1cSMilanka Ringwald                 case ERROR_CODE_SUCCESS:
1357ec9a1bbSMilanka Ringwald                     printf("Battery service client connected - found %d services",
1367ec9a1bbSMilanka Ringwald                         gattservice_subevent_battery_service_connected_get_num_instances(packet));
137bcf00d8fSMatthias Ringwald                     break;
138174a0c1cSMilanka Ringwald                 default:
1397ec9a1bbSMilanka Ringwald                     printf("Battery service client connection failed - Error status 0x%02x.\n", status);
1400f09bd96SMilanka Ringwald                     add_to_blacklist(report.address);
1410f09bd96SMilanka Ringwald                     gap_disconnect(connection_handle);
142bcf00d8fSMatthias Ringwald                     break;
143bcf00d8fSMatthias Ringwald             }
144bcf00d8fSMatthias Ringwald             break;
145174a0c1cSMilanka Ringwald 
146*83b6788cSMilanka Ringwald         case GATTSERVICE_SUBEVENT_BATTERY_SERVICE_LEVEL:
147*83b6788cSMilanka Ringwald             printf("Service index: %d, Battery level %d \n",
148*83b6788cSMilanka Ringwald                 gattservice_subevent_battery_service_level_get_sevice_index(packet),
149*83b6788cSMilanka Ringwald                 gattservice_subevent_battery_service_level_get_level(packet));
1500f09bd96SMilanka Ringwald             break;
151bcf00d8fSMatthias Ringwald 
152bcf00d8fSMatthias Ringwald         default:
153bcf00d8fSMatthias Ringwald             break;
154bcf00d8fSMatthias Ringwald     }
155bcf00d8fSMatthias Ringwald }
156bcf00d8fSMatthias Ringwald 
157bcf00d8fSMatthias Ringwald static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1589ec2630cSMatthias Ringwald     UNUSED(channel);
1599ec2630cSMatthias Ringwald     UNUSED(size);
1609ec2630cSMatthias Ringwald 
161174a0c1cSMilanka Ringwald     uint8_t status;
162*83b6788cSMilanka Ringwald     bd_addr_t address;
1630f09bd96SMilanka Ringwald 
164*83b6788cSMilanka Ringwald     if (packet_type != HCI_EVENT_PACKET){
165*83b6788cSMilanka Ringwald         return;
166*83b6788cSMilanka Ringwald     }
167*83b6788cSMilanka Ringwald 
168*83b6788cSMilanka Ringwald     switch (hci_event_packet_get_type(packet)) {
169bcf00d8fSMatthias Ringwald         case BTSTACK_EVENT_STATE:
170bcf00d8fSMatthias Ringwald             // BTstack activated, get started
171cdc7d1abSMilanka Ringwald             if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break;
172bcf00d8fSMatthias Ringwald             if (cmdline_addr_found){
1730f09bd96SMilanka Ringwald                 printf("Connect to %s\n", bd_addr_to_str(cmdline_addr));
174*83b6788cSMilanka Ringwald                 app_state = APP_STATE_W4_CONNECT;
175bcf00d8fSMatthias Ringwald                 gap_connect(cmdline_addr, 0);
176bcf00d8fSMatthias Ringwald                 break;
177bcf00d8fSMatthias Ringwald             }
1780f09bd96SMilanka Ringwald             printf("Start scanning!\n");
179*83b6788cSMilanka Ringwald             app_state = APP_STATE_W4_SCAN_RESULT;
180bcf00d8fSMatthias Ringwald             gap_set_scan_parameters(0,0x0030, 0x0030);
181bcf00d8fSMatthias Ringwald             gap_start_scan();
182bcf00d8fSMatthias Ringwald             break;
1833ee82ab1SMilanka Ringwald 
184*83b6788cSMilanka Ringwald         case GAP_EVENT_ADVERTISING_REPORT:
185*83b6788cSMilanka Ringwald             if (app_state != APP_STATE_W4_SCAN_RESULT) return;
186*83b6788cSMilanka Ringwald 
187*83b6788cSMilanka Ringwald             gap_event_advertising_report_get_address(packet, address);
188*83b6788cSMilanka Ringwald             if (blacklist_contains(address)) {
1890f09bd96SMilanka Ringwald                 break;
1900f09bd96SMilanka Ringwald             }
191*83b6788cSMilanka Ringwald             dump_advertising_report(packet);
192*83b6788cSMilanka Ringwald 
193bcf00d8fSMatthias Ringwald             // stop scanning, and connect to the device
194*83b6788cSMilanka Ringwald             app_state = APP_STATE_W4_CONNECT;
195bcf00d8fSMatthias Ringwald             gap_stop_scan();
1960f09bd96SMilanka Ringwald             printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(report.address));
197bcf00d8fSMatthias Ringwald             gap_connect(report.address,report.address_type);
198bcf00d8fSMatthias Ringwald             break;
199*83b6788cSMilanka Ringwald 
200bcf00d8fSMatthias Ringwald         case HCI_EVENT_LE_META:
201bcf00d8fSMatthias Ringwald             // wait for connection complete
20210cad102SMilanka Ringwald             if (hci_event_le_meta_get_subevent_code(packet) !=  HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
203*83b6788cSMilanka Ringwald             if (app_state != APP_STATE_W4_CONNECT) return;
204a59bfbf7SMatthias Ringwald             connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
205*83b6788cSMilanka Ringwald 
206174a0c1cSMilanka Ringwald             status = battery_service_client_connect(connection_handle, handle_gatt_client_event, &battery_service_cid);
207*83b6788cSMilanka Ringwald             btstack_assert(status == ERROR_CODE_SUCCESS);
208*83b6788cSMilanka Ringwald 
209*83b6788cSMilanka Ringwald             app_state = APP_STATE_CONNECTED;
210*83b6788cSMilanka Ringwald             printf("Battery service connected.\n");
211bcf00d8fSMatthias Ringwald             break;
212*83b6788cSMilanka Ringwald 
213bcf00d8fSMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
21426687d14SMatthias Ringwald             connection_handle = HCI_CON_HANDLE_INVALID;
215*83b6788cSMilanka Ringwald             // Disconnect battery service
216*83b6788cSMilanka Ringwald             battery_service_client_disconnect(battery_service_cid);
2170f09bd96SMilanka Ringwald 
2180f09bd96SMilanka Ringwald             if (cmdline_addr_found){
219*83b6788cSMilanka Ringwald                 printf("Disconnected %s\n", bd_addr_to_str(cmdline_addr));
220eb8fc740SMatthias Ringwald                 return;
2210f09bd96SMilanka Ringwald             }
2220f09bd96SMilanka Ringwald 
223*83b6788cSMilanka Ringwald             printf("Disconnected %s\n", bd_addr_to_str(report.address));
2240f09bd96SMilanka Ringwald             printf("Restart scan.\n");
225*83b6788cSMilanka Ringwald             app_state = APP_STATE_W4_SCAN_RESULT;
2260f09bd96SMilanka Ringwald             gap_start_scan();
227bcf00d8fSMatthias Ringwald             break;
228bcf00d8fSMatthias Ringwald         default:
229bcf00d8fSMatthias Ringwald             break;
230bcf00d8fSMatthias Ringwald     }
231bcf00d8fSMatthias Ringwald }
232bcf00d8fSMatthias Ringwald 
233bcf00d8fSMatthias Ringwald int btstack_main(int argc, const char * argv[]);
234bcf00d8fSMatthias Ringwald int btstack_main(int argc, const char * argv[]){
235bcf00d8fSMatthias Ringwald 
236*83b6788cSMilanka Ringwald     // parse address if command line arguments are provided
237bcf00d8fSMatthias Ringwald     int arg = 1;
238bcf00d8fSMatthias Ringwald     cmdline_addr_found = 0;
239bcf00d8fSMatthias Ringwald 
240bcf00d8fSMatthias Ringwald     while (arg < argc) {
241bcf00d8fSMatthias Ringwald         if(!strcmp(argv[arg], "-a") || !strcmp(argv[arg], "--address")){
242bcf00d8fSMatthias Ringwald             arg++;
243a6efb919SMatthias Ringwald             cmdline_addr_found = sscanf_bd_addr(argv[arg], cmdline_addr);
244bcf00d8fSMatthias Ringwald             arg++;
2450f09bd96SMilanka Ringwald             if (!cmdline_addr_found) exit(1);
246bcf00d8fSMatthias Ringwald             continue;
247bcf00d8fSMatthias Ringwald         }
248*83b6788cSMilanka Ringwald         fprintf(stderr, "\nUsage: %s [-a|--address aa:bb:cc:dd:ee:ff]\n", argv[0]);
249*83b6788cSMilanka 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");
250bcf00d8fSMatthias Ringwald         return 0;
251bcf00d8fSMatthias Ringwald     }
2526316c8edSMatthias Ringwald     (void)argv;
253*83b6788cSMilanka Ringwald 
254bcf00d8fSMatthias Ringwald     l2cap_init();
255bcf00d8fSMatthias Ringwald 
256add9769eSMatthias Ringwald     // setup ATT server - only needed if LE Peripheral does ATT queries on its own, e.g. Android phones
257add9769eSMatthias Ringwald     att_server_init(profile_data, NULL, NULL);
258add9769eSMatthias Ringwald 
259add9769eSMatthias Ringwald     // GATT Client setup
260bcf00d8fSMatthias Ringwald     gatt_client_init();
261174a0c1cSMilanka Ringwald     battery_service_client_init();
262bcf00d8fSMatthias Ringwald 
263bcf00d8fSMatthias Ringwald     sm_init();
264bcf00d8fSMatthias Ringwald     sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
265bcf00d8fSMatthias Ringwald 
266a4fe6467SMatthias Ringwald     hci_event_callback_registration.callback = &hci_event_handler;
267a4fe6467SMatthias Ringwald     hci_add_event_handler(&hci_event_callback_registration);
268a4fe6467SMatthias Ringwald 
269*83b6788cSMilanka Ringwald     app_state = APP_STATE_IDLE;
270a4fe6467SMatthias Ringwald 
271bcf00d8fSMatthias Ringwald     // turn on!
272bcf00d8fSMatthias Ringwald     hci_power_control(HCI_POWER_ON);
273bcf00d8fSMatthias Ringwald 
274bcf00d8fSMatthias Ringwald     return 0;
275bcf00d8fSMatthias Ringwald }
276bcf00d8fSMatthias Ringwald 
277ec8ae085SMilanka Ringwald /* EXAMPLE_END */
278bcf00d8fSMatthias Ringwald 
279bcf00d8fSMatthias Ringwald 
280