1*bcf00d8fSMatthias Ringwald /* 2*bcf00d8fSMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 3*bcf00d8fSMatthias Ringwald * 4*bcf00d8fSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5*bcf00d8fSMatthias Ringwald * modification, are permitted provided that the following conditions 6*bcf00d8fSMatthias Ringwald * are met: 7*bcf00d8fSMatthias Ringwald * 8*bcf00d8fSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9*bcf00d8fSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10*bcf00d8fSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11*bcf00d8fSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12*bcf00d8fSMatthias Ringwald * documentation and/or other materials provided with the distribution. 13*bcf00d8fSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14*bcf00d8fSMatthias Ringwald * contributors may be used to endorse or promote products derived 15*bcf00d8fSMatthias Ringwald * from this software without specific prior written permission. 16*bcf00d8fSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17*bcf00d8fSMatthias Ringwald * personal benefit and not for any commercial purpose or for 18*bcf00d8fSMatthias Ringwald * monetary gain. 19*bcf00d8fSMatthias Ringwald * 20*bcf00d8fSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21*bcf00d8fSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*bcf00d8fSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23*bcf00d8fSMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24*bcf00d8fSMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25*bcf00d8fSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26*bcf00d8fSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27*bcf00d8fSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28*bcf00d8fSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29*bcf00d8fSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30*bcf00d8fSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*bcf00d8fSMatthias Ringwald * SUCH DAMAGE. 32*bcf00d8fSMatthias Ringwald * 33*bcf00d8fSMatthias Ringwald * Please inquire about commercial licensing options at 34*bcf00d8fSMatthias Ringwald * [email protected] 35*bcf00d8fSMatthias Ringwald * 36*bcf00d8fSMatthias Ringwald */ 37*bcf00d8fSMatthias Ringwald 38*bcf00d8fSMatthias Ringwald // ***************************************************************************** 39*bcf00d8fSMatthias Ringwald // 40*bcf00d8fSMatthias Ringwald // BLE Client 41*bcf00d8fSMatthias Ringwald // 42*bcf00d8fSMatthias Ringwald // ***************************************************************************** 43*bcf00d8fSMatthias Ringwald 44*bcf00d8fSMatthias Ringwald #include <stdint.h> 45*bcf00d8fSMatthias Ringwald #include <stdio.h> 46*bcf00d8fSMatthias Ringwald #include <stdlib.h> 47*bcf00d8fSMatthias Ringwald #include <string.h> 48*bcf00d8fSMatthias Ringwald 49*bcf00d8fSMatthias Ringwald #include "btstack.h" 50*bcf00d8fSMatthias Ringwald 51*bcf00d8fSMatthias Ringwald typedef struct advertising_report { 52*bcf00d8fSMatthias Ringwald uint8_t type; 53*bcf00d8fSMatthias Ringwald uint8_t event_type; 54*bcf00d8fSMatthias Ringwald uint8_t address_type; 55*bcf00d8fSMatthias Ringwald bd_addr_t address; 56*bcf00d8fSMatthias Ringwald uint8_t rssi; 57*bcf00d8fSMatthias Ringwald uint8_t length; 58*bcf00d8fSMatthias Ringwald uint8_t * data; 59*bcf00d8fSMatthias Ringwald } advertising_report_t; 60*bcf00d8fSMatthias Ringwald 61*bcf00d8fSMatthias Ringwald 62*bcf00d8fSMatthias Ringwald typedef enum { 63*bcf00d8fSMatthias Ringwald TC_IDLE, 64*bcf00d8fSMatthias Ringwald TC_W4_SCAN_RESULT, 65*bcf00d8fSMatthias Ringwald TC_W4_CONNECT, 66*bcf00d8fSMatthias Ringwald TC_W4_SERVICE_RESULT, 67*bcf00d8fSMatthias Ringwald TC_W4_CHARACTERISTIC_RESULT, 68*bcf00d8fSMatthias Ringwald TC_W4_BATTERY_DATA 69*bcf00d8fSMatthias Ringwald } gc_state_t; 70*bcf00d8fSMatthias Ringwald 71*bcf00d8fSMatthias Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 72*bcf00d8fSMatthias Ringwald 73*bcf00d8fSMatthias Ringwald static bd_addr_t cmdline_addr = { }; 74*bcf00d8fSMatthias Ringwald static int cmdline_addr_found = 0; 75*bcf00d8fSMatthias Ringwald 76*bcf00d8fSMatthias Ringwald uint16_t gc_handle; 77*bcf00d8fSMatthias Ringwald static uint16_t battery_service_uuid = 0x180F; 78*bcf00d8fSMatthias Ringwald static uint16_t battery_level_characteristic_uuid = 0x2a19; 79*bcf00d8fSMatthias Ringwald static gatt_client_service_t battery_service; 80*bcf00d8fSMatthias Ringwald static gatt_client_characteristic_t config_characteristic; 81*bcf00d8fSMatthias Ringwald 82*bcf00d8fSMatthias Ringwald static gc_state_t state = TC_IDLE; 83*bcf00d8fSMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 84*bcf00d8fSMatthias Ringwald 85*bcf00d8fSMatthias Ringwald static void printUUID(uint8_t * uuid128, uint16_t uuid16){ 86*bcf00d8fSMatthias Ringwald if (uuid16){ 87*bcf00d8fSMatthias Ringwald printf("%04x",uuid16); 88*bcf00d8fSMatthias Ringwald } else { 89*bcf00d8fSMatthias Ringwald printf("%s", uuid128_to_str(uuid128)); 90*bcf00d8fSMatthias Ringwald } 91*bcf00d8fSMatthias Ringwald } 92*bcf00d8fSMatthias Ringwald 93*bcf00d8fSMatthias Ringwald static void dump_advertising_report(advertising_report_t * e){ 94*bcf00d8fSMatthias Ringwald printf(" * adv. event: evt-type %u, addr-type %u, addr %s, rssi %u, length adv %u, data: ", e->event_type, 95*bcf00d8fSMatthias Ringwald e->address_type, bd_addr_to_str(e->address), e->rssi, e->length); 96*bcf00d8fSMatthias Ringwald printf_hexdump(e->data, e->length); 97*bcf00d8fSMatthias Ringwald 98*bcf00d8fSMatthias Ringwald } 99*bcf00d8fSMatthias Ringwald 100*bcf00d8fSMatthias Ringwald static void dump_characteristic_value(uint8_t * blob, uint16_t blob_length){ 101*bcf00d8fSMatthias Ringwald printf(" * characteristic value of length %d *** ", blob_length ); 102*bcf00d8fSMatthias Ringwald printf_hexdump(blob , blob_length); 103*bcf00d8fSMatthias Ringwald printf("\n"); 104*bcf00d8fSMatthias Ringwald } 105*bcf00d8fSMatthias Ringwald 106*bcf00d8fSMatthias Ringwald static void dump_characteristic(gatt_client_characteristic_t * characteristic){ 107*bcf00d8fSMatthias Ringwald printf(" * characteristic: [0x%04x-0x%04x-0x%04x], properties 0x%02x, uuid ", 108*bcf00d8fSMatthias Ringwald characteristic->start_handle, characteristic->value_handle, characteristic->end_handle, characteristic->properties); 109*bcf00d8fSMatthias Ringwald printUUID(characteristic->uuid128, characteristic->uuid16); 110*bcf00d8fSMatthias Ringwald printf("\n"); 111*bcf00d8fSMatthias Ringwald } 112*bcf00d8fSMatthias Ringwald 113*bcf00d8fSMatthias Ringwald static void dump_service(gatt_client_service_t * service){ 114*bcf00d8fSMatthias Ringwald printf(" * service: [0x%04x-0x%04x], uuid ", service->start_group_handle, service->end_group_handle); 115*bcf00d8fSMatthias Ringwald printUUID(service->uuid128, service->uuid16); 116*bcf00d8fSMatthias Ringwald printf("\n"); 117*bcf00d8fSMatthias Ringwald } 118*bcf00d8fSMatthias Ringwald 119*bcf00d8fSMatthias Ringwald static void error_code(int status){ 120*bcf00d8fSMatthias Ringwald switch(status){ 121*bcf00d8fSMatthias Ringwald case ATT_ERROR_INSUFFICIENT_AUTHENTICATION: 122*bcf00d8fSMatthias Ringwald printf("ATT_ERROR_INSUFFICIENT_AUTHENTICATION. TODO: security manager is not complete yet.\n"); 123*bcf00d8fSMatthias Ringwald break; 124*bcf00d8fSMatthias Ringwald default: 125*bcf00d8fSMatthias Ringwald printf(" status 0x%02x\n", status); 126*bcf00d8fSMatthias Ringwald break; 127*bcf00d8fSMatthias Ringwald } 128*bcf00d8fSMatthias Ringwald } 129*bcf00d8fSMatthias Ringwald 130*bcf00d8fSMatthias Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 131*bcf00d8fSMatthias Ringwald 132*bcf00d8fSMatthias Ringwald switch(state){ 133*bcf00d8fSMatthias Ringwald case TC_W4_SERVICE_RESULT: 134*bcf00d8fSMatthias Ringwald switch(packet[0]){ 135*bcf00d8fSMatthias Ringwald case GATT_EVENT_SERVICE_QUERY_RESULT: 136*bcf00d8fSMatthias Ringwald gatt_event_service_query_result_get_service(packet, &battery_service); 137*bcf00d8fSMatthias Ringwald printf("Battery service found:\n"); 138*bcf00d8fSMatthias Ringwald dump_service(&battery_service); 139*bcf00d8fSMatthias Ringwald break; 140*bcf00d8fSMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE: 141*bcf00d8fSMatthias Ringwald if (!packet[4]){ 142*bcf00d8fSMatthias Ringwald printf("Battery service not found. Restart scan.\n"); 143*bcf00d8fSMatthias Ringwald state = TC_W4_SCAN_RESULT; 144*bcf00d8fSMatthias Ringwald gap_start_scan(); 145*bcf00d8fSMatthias Ringwald break; 146*bcf00d8fSMatthias Ringwald } 147*bcf00d8fSMatthias Ringwald state = TC_W4_CHARACTERISTIC_RESULT; 148*bcf00d8fSMatthias Ringwald printf("\nSearch for battery level characteristic in battery service. "); 149*bcf00d8fSMatthias Ringwald gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, gc_handle, &battery_service, battery_level_characteristic_uuid); 150*bcf00d8fSMatthias Ringwald break; 151*bcf00d8fSMatthias Ringwald default: 152*bcf00d8fSMatthias Ringwald break; 153*bcf00d8fSMatthias Ringwald } 154*bcf00d8fSMatthias Ringwald break; 155*bcf00d8fSMatthias Ringwald 156*bcf00d8fSMatthias Ringwald case TC_W4_CHARACTERISTIC_RESULT: 157*bcf00d8fSMatthias Ringwald switch(packet[0]){ 158*bcf00d8fSMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 159*bcf00d8fSMatthias Ringwald printf("Battery level characteristic found:\n"); 160*bcf00d8fSMatthias Ringwald gatt_event_characteristic_query_result_get_characteristic(packet, &config_characteristic); 161*bcf00d8fSMatthias Ringwald dump_characteristic(&config_characteristic); 162*bcf00d8fSMatthias Ringwald break; 163*bcf00d8fSMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE: 164*bcf00d8fSMatthias Ringwald state = TC_W4_BATTERY_DATA; 165*bcf00d8fSMatthias Ringwald printf("\nConfigure battery level characteristic for notify."); 166*bcf00d8fSMatthias Ringwald gatt_client_write_client_characteristic_configuration(handle_gatt_client_event, gc_handle, &config_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 167*bcf00d8fSMatthias Ringwald break; 168*bcf00d8fSMatthias Ringwald default: 169*bcf00d8fSMatthias Ringwald break; 170*bcf00d8fSMatthias Ringwald } 171*bcf00d8fSMatthias Ringwald break; 172*bcf00d8fSMatthias Ringwald case TC_W4_BATTERY_DATA: 173*bcf00d8fSMatthias Ringwald if (packet[0] == GATT_EVENT_QUERY_COMPLETE){ 174*bcf00d8fSMatthias Ringwald if (packet[4] != 0){ 175*bcf00d8fSMatthias Ringwald printf("\nNotification is not possible: "); 176*bcf00d8fSMatthias Ringwald error_code(packet[4]); 177*bcf00d8fSMatthias Ringwald } 178*bcf00d8fSMatthias Ringwald break; 179*bcf00d8fSMatthias Ringwald } 180*bcf00d8fSMatthias Ringwald if (packet[0] != GATT_EVENT_NOTIFICATION) break; 181*bcf00d8fSMatthias Ringwald printf("\nBattery Data:\n"); 182*bcf00d8fSMatthias Ringwald dump_characteristic_value(&packet[8], little_endian_read_16(packet, 6)); 183*bcf00d8fSMatthias Ringwald break; 184*bcf00d8fSMatthias Ringwald 185*bcf00d8fSMatthias Ringwald default: 186*bcf00d8fSMatthias Ringwald printf("error\n"); 187*bcf00d8fSMatthias Ringwald break; 188*bcf00d8fSMatthias Ringwald } 189*bcf00d8fSMatthias Ringwald 190*bcf00d8fSMatthias Ringwald } 191*bcf00d8fSMatthias Ringwald 192*bcf00d8fSMatthias Ringwald static void fill_advertising_report_from_packet(advertising_report_t * report, uint8_t *packet){ 193*bcf00d8fSMatthias Ringwald int pos = 2; 194*bcf00d8fSMatthias Ringwald report->event_type = packet[pos++]; 195*bcf00d8fSMatthias Ringwald report->address_type = packet[pos++]; 196*bcf00d8fSMatthias Ringwald reverse_bd_addr(&packet[pos], report->address); 197*bcf00d8fSMatthias Ringwald pos += 6; 198*bcf00d8fSMatthias Ringwald report->rssi = packet[pos++]; 199*bcf00d8fSMatthias Ringwald report->length = packet[pos++]; 200*bcf00d8fSMatthias Ringwald report->data = &packet[pos]; 201*bcf00d8fSMatthias Ringwald pos += report->length; 202*bcf00d8fSMatthias Ringwald dump_advertising_report(report); 203*bcf00d8fSMatthias Ringwald } 204*bcf00d8fSMatthias Ringwald 205*bcf00d8fSMatthias Ringwald static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 206*bcf00d8fSMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 207*bcf00d8fSMatthias Ringwald advertising_report_t report; 208*bcf00d8fSMatthias Ringwald uint8_t event = packet[0]; 209*bcf00d8fSMatthias Ringwald switch (event) { 210*bcf00d8fSMatthias Ringwald case BTSTACK_EVENT_STATE: 211*bcf00d8fSMatthias Ringwald // BTstack activated, get started 212*bcf00d8fSMatthias Ringwald if (packet[2] != HCI_STATE_WORKING) break; 213*bcf00d8fSMatthias Ringwald if (cmdline_addr_found){ 214*bcf00d8fSMatthias Ringwald printf("Start connect to %s\n", bd_addr_to_str(cmdline_addr)); 215*bcf00d8fSMatthias Ringwald state = TC_W4_CONNECT; 216*bcf00d8fSMatthias Ringwald gap_connect(cmdline_addr, 0); 217*bcf00d8fSMatthias Ringwald break; 218*bcf00d8fSMatthias Ringwald } 219*bcf00d8fSMatthias Ringwald printf("BTstack activated, start scanning!\n"); 220*bcf00d8fSMatthias Ringwald state = TC_W4_SCAN_RESULT; 221*bcf00d8fSMatthias Ringwald gap_set_scan_parameters(0,0x0030, 0x0030); 222*bcf00d8fSMatthias Ringwald gap_start_scan(); 223*bcf00d8fSMatthias Ringwald break; 224*bcf00d8fSMatthias Ringwald case GAP_LE_EVENT_ADVERTISING_REPORT: 225*bcf00d8fSMatthias Ringwald if (state != TC_W4_SCAN_RESULT) return; 226*bcf00d8fSMatthias Ringwald fill_advertising_report_from_packet(&report, packet); 227*bcf00d8fSMatthias Ringwald // stop scanning, and connect to the device 228*bcf00d8fSMatthias Ringwald state = TC_W4_CONNECT; 229*bcf00d8fSMatthias Ringwald gap_stop_scan(); 230*bcf00d8fSMatthias Ringwald printf("Stop scan. Start connect to device with addr %s.\n", bd_addr_to_str(report.address)); 231*bcf00d8fSMatthias Ringwald gap_connect(report.address,report.address_type); 232*bcf00d8fSMatthias Ringwald break; 233*bcf00d8fSMatthias Ringwald case HCI_EVENT_LE_META: 234*bcf00d8fSMatthias Ringwald // wait for connection complete 235*bcf00d8fSMatthias Ringwald if (packet[2] != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; 236*bcf00d8fSMatthias Ringwald if (state != TC_W4_CONNECT) return; 237*bcf00d8fSMatthias Ringwald gc_handle = little_endian_read_16(packet, 4); 238*bcf00d8fSMatthias Ringwald // initialize gatt client context with handle, and add it to the list of active clients 239*bcf00d8fSMatthias Ringwald // query primary services 240*bcf00d8fSMatthias Ringwald printf("\nSearch for battery service. "); 241*bcf00d8fSMatthias Ringwald state = TC_W4_SERVICE_RESULT; 242*bcf00d8fSMatthias Ringwald gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, gc_handle, battery_service_uuid); 243*bcf00d8fSMatthias Ringwald break; 244*bcf00d8fSMatthias Ringwald case HCI_EVENT_DISCONNECTION_COMPLETE: 245*bcf00d8fSMatthias Ringwald printf("\nDISCONNECTED\n"); 246*bcf00d8fSMatthias Ringwald exit(0); 247*bcf00d8fSMatthias Ringwald break; 248*bcf00d8fSMatthias Ringwald default: 249*bcf00d8fSMatthias Ringwald break; 250*bcf00d8fSMatthias Ringwald } 251*bcf00d8fSMatthias Ringwald } 252*bcf00d8fSMatthias Ringwald 253*bcf00d8fSMatthias Ringwald static void usage(const char *name){ 254*bcf00d8fSMatthias Ringwald fprintf(stderr, "\nUsage: %s [-a|--address aa:bb:cc:dd:ee:ff]\n", name); 255*bcf00d8fSMatthias 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"); 256*bcf00d8fSMatthias Ringwald } 257*bcf00d8fSMatthias Ringwald 258*bcf00d8fSMatthias Ringwald int btstack_main(int argc, const char * argv[]); 259*bcf00d8fSMatthias Ringwald int btstack_main(int argc, const char * argv[]){ 260*bcf00d8fSMatthias Ringwald 261*bcf00d8fSMatthias Ringwald int arg = 1; 262*bcf00d8fSMatthias Ringwald cmdline_addr_found = 0; 263*bcf00d8fSMatthias Ringwald 264*bcf00d8fSMatthias Ringwald while (arg < argc) { 265*bcf00d8fSMatthias Ringwald if(!strcmp(argv[arg], "-a") || !strcmp(argv[arg], "--address")){ 266*bcf00d8fSMatthias Ringwald arg++; 267*bcf00d8fSMatthias Ringwald cmdline_addr_found = sscanf_bd_addr((uint8_t *)argv[arg], cmdline_addr); 268*bcf00d8fSMatthias Ringwald arg++; 269*bcf00d8fSMatthias Ringwald continue; 270*bcf00d8fSMatthias Ringwald } 271*bcf00d8fSMatthias Ringwald usage(argv[0]); 272*bcf00d8fSMatthias Ringwald return 0; 273*bcf00d8fSMatthias Ringwald } 274*bcf00d8fSMatthias Ringwald 275*bcf00d8fSMatthias Ringwald hci_event_callback_registration.callback = &hci_event_handler; 276*bcf00d8fSMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration); 277*bcf00d8fSMatthias Ringwald 278*bcf00d8fSMatthias Ringwald l2cap_init(); 279*bcf00d8fSMatthias Ringwald 280*bcf00d8fSMatthias Ringwald gatt_client_init(); 281*bcf00d8fSMatthias Ringwald 282*bcf00d8fSMatthias Ringwald sm_init(); 283*bcf00d8fSMatthias Ringwald sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); 284*bcf00d8fSMatthias Ringwald 285*bcf00d8fSMatthias Ringwald // turn on! 286*bcf00d8fSMatthias Ringwald hci_power_control(HCI_POWER_ON); 287*bcf00d8fSMatthias Ringwald 288*bcf00d8fSMatthias Ringwald return 0; 289*bcf00d8fSMatthias Ringwald } 290*bcf00d8fSMatthias Ringwald 291*bcf00d8fSMatthias Ringwald 292*bcf00d8fSMatthias Ringwald 293*bcf00d8fSMatthias Ringwald 294