2021-03-05 09:13:19 +01:00
/*
* Copyright ( C ) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
* 4. Any redistribution , use , or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain .
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2021-11-02 09:31:00 +01:00
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
2021-03-05 09:13:19 +01:00
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS
* OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
*
* Please inquire about commercial licensing options at
* contact @ bluekitchen - gmbh . com
*
*/
# define BTSTACK_FILE__ "gatt_device_information_query.c"
// *****************************************************************************
2021-03-05 18:29:49 +01:00
/* EXAMPLE_START(gatt_device_information_query): GATT Device Information Service Client
2021-03-05 09:13:19 +01:00
*
2021-03-05 18:29:49 +01:00
* @ 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 .
2021-03-05 09:13:19 +01:00
*/
2021-03-05 18:29:49 +01:00
// *****************************************************************************
2021-03-05 09:13:19 +01:00
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include "btstack.h"
// gatt_device_information_query.gatt contains the declaration of the provided GATT Services + Characteristics
// gatt_device_information_query.h contains the binary representation of gatt_device_information_query.gatt
// it is generated by the build system by calling: $BTSTACK_ROOT/tool/compile_gatt.py gatt_device_information_query.gatt gatt_device_information_query.h
// it needs to be regenerated when the GATT Database declared in gatt_device_information_query.gatt file is modified
# include "gatt_device_information_query.h"
typedef struct advertising_report {
uint8_t type ;
uint8_t event_type ;
uint8_t address_type ;
bd_addr_t address ;
uint8_t rssi ;
uint8_t length ;
const uint8_t * data ;
} advertising_report_t ;
static enum {
APP_STATE_IDLE ,
APP_STATE_W4_SCAN_RESULT ,
APP_STATE_W4_CONNECT ,
APP_STATE_CONNECTED
} app_state ;
static int blacklist_index = 0 ;
static bd_addr_t blacklist [ 20 ] ;
static advertising_report_t report ;
static hci_con_handle_t connection_handle ;
static bd_addr_t cmdline_addr ;
static int cmdline_addr_found = 0 ;
static btstack_packet_callback_registration_t hci_event_callback_registration ;
2021-03-05 18:29:49 +01:00
/* @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 */
2021-03-05 09:13:19 +01:00
static int blacklist_size ( void ) {
return sizeof ( blacklist ) / sizeof ( bd_addr_t ) ;
}
static int blacklist_contains ( bd_addr_t addr ) {
int i ;
for ( i = 0 ; i < blacklist_size ( ) ; i + + ) {
if ( bd_addr_cmp ( addr , blacklist [ i ] ) = = 0 ) return 1 ;
}
return 0 ;
}
static void add_to_blacklist ( bd_addr_t addr ) {
printf ( " %s added to blacklist (no device information service found). \n " , bd_addr_to_str ( addr ) ) ;
bd_addr_copy ( blacklist [ blacklist_index ] , addr ) ;
blacklist_index = ( blacklist_index + 1 ) % blacklist_size ( ) ;
}
static void dump_advertising_report ( uint8_t * packet ) {
bd_addr_t address ;
gap_event_advertising_report_get_address ( packet , address ) ;
printf ( " * adv. event: evt-type %u, addr-type %u, addr %s, rssi %u, length adv %u, data: " ,
gap_event_advertising_report_get_advertising_event_type ( packet ) ,
gap_event_advertising_report_get_address_type ( packet ) ,
bd_addr_to_str ( address ) ,
gap_event_advertising_report_get_rssi ( packet ) ,
gap_event_advertising_report_get_data_length ( packet ) ) ;
printf_hexdump ( gap_event_advertising_report_get_data ( packet ) , gap_event_advertising_report_get_data_length ( packet ) ) ;
}
2021-03-05 18:29:49 +01:00
/* 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 */
2021-03-05 09:13:19 +01:00
UNUSED ( packet_type ) ;
UNUSED ( channel ) ;
UNUSED ( size ) ;
2021-03-05 18:29:49 +01:00
/* LISTING_RESUME */
2021-03-05 15:10:45 +01:00
uint8_t att_status = 0 ;
2021-03-05 09:13:19 +01:00
if ( hci_event_packet_get_type ( packet ) ! = HCI_EVENT_GATTSERVICE_META ) {
return ;
}
switch ( hci_event_gattservice_meta_get_subevent_code ( packet ) ) {
2021-03-05 15:10:45 +01:00
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_MANUFACTURER_NAME :
att_status = gattservice_subevent_device_information_manufacturer_name_get_att_status ( packet ) ;
if ( att_status ! = ATT_ERROR_SUCCESS ) {
printf ( " Manufacturer Name read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " Manufacturer Name: %s \n " , gattservice_subevent_device_information_manufacturer_name_get_value ( packet ) ) ;
}
break ;
2021-03-05 18:29:49 +01:00
// ...
/* LISTING_PAUSE */
2021-03-05 15:10:45 +01:00
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 ) {
printf ( " Model Number read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " Model Number: %s \n " , gattservice_subevent_device_information_model_number_get_value ( packet ) ) ;
}
break ;
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_SERIAL_NUMBER :
att_status = gattservice_subevent_device_information_serial_number_get_att_status ( packet ) ;
if ( att_status ! = ATT_ERROR_SUCCESS ) {
printf ( " Serial Number read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " Serial Number: %s \n " , gattservice_subevent_device_information_serial_number_get_value ( packet ) ) ;
}
break ;
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_HARDWARE_REVISION :
att_status = gattservice_subevent_device_information_hardware_revision_get_att_status ( packet ) ;
if ( att_status ! = ATT_ERROR_SUCCESS ) {
printf ( " Hardware Revision read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " Hardware Revision: %s \n " , gattservice_subevent_device_information_hardware_revision_get_value ( packet ) ) ;
}
break ;
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_FIRMWARE_REVISION :
att_status = gattservice_subevent_device_information_firmware_revision_get_att_status ( packet ) ;
if ( att_status ! = ATT_ERROR_SUCCESS ) {
printf ( " Firmware Revision read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " Firmware Revision: %s \n " , gattservice_subevent_device_information_firmware_revision_get_value ( packet ) ) ;
}
break ;
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_SOFTWARE_REVISION :
att_status = gattservice_subevent_device_information_software_revision_get_att_status ( packet ) ;
if ( att_status ! = ATT_ERROR_SUCCESS ) {
printf ( " Software Revision read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " Software Revision: %s \n " , gattservice_subevent_device_information_software_revision_get_value ( packet ) ) ;
}
break ;
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_SYSTEM_ID :
att_status = gattservice_subevent_device_information_system_id_get_att_status ( packet ) ;
if ( att_status ! = ATT_ERROR_SUCCESS ) {
printf ( " System ID read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
uint32_t manufacturer_identifier_low = gattservice_subevent_device_information_system_id_get_manufacturer_id_low ( packet ) ;
uint8_t manufacturer_identifier_high = gattservice_subevent_device_information_system_id_get_manufacturer_id_high ( packet ) ;
printf ( " Manufacturer ID: 0x%02x%08x \n " , manufacturer_identifier_high , manufacturer_identifier_low ) ;
printf ( " Organizationally Unique ID: 0x%06x \n " , gattservice_subevent_device_information_system_id_get_organizationally_unique_id ( packet ) ) ;
}
break ;
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_IEEE_REGULATORY_CERTIFICATION :
att_status = gattservice_subevent_device_information_ieee_regulatory_certification_get_att_status ( packet ) ;
if ( att_status ! = ATT_ERROR_SUCCESS ) {
printf ( " IEEE Regulatory Certification read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " value_a: 0x%04x \n " , gattservice_subevent_device_information_ieee_regulatory_certification_get_value_a ( packet ) ) ;
printf ( " value_b: 0x%04x \n " , gattservice_subevent_device_information_ieee_regulatory_certification_get_value_b ( packet ) ) ;
}
break ;
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_PNP_ID :
att_status = gattservice_subevent_device_information_pnp_id_get_att_status ( packet ) ;
if ( att_status ! = ATT_ERROR_SUCCESS ) {
printf ( " PNP ID read failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " Vendor Source ID: 0x%02x \n " , gattservice_subevent_device_information_pnp_id_get_vendor_source_id ( packet ) ) ;
printf ( " Vendor ID: 0x%04x \n " , gattservice_subevent_device_information_pnp_id_get_vendor_id ( packet ) ) ;
printf ( " Product ID: 0x%04x \n " , gattservice_subevent_device_information_pnp_id_get_product_id ( packet ) ) ;
printf ( " Product Version: 0x%04x \n " , gattservice_subevent_device_information_pnp_id_get_product_version ( packet ) ) ;
}
break ;
2021-03-05 18:29:49 +01:00
/* LISTING_RESUME */
2021-03-05 15:10:45 +01:00
case GATTSERVICE_SUBEVENT_DEVICE_INFORMATION_DONE :
att_status = gattservice_subevent_device_information_serial_number_get_att_status ( packet ) ;
2021-03-05 18:29:49 +01:00
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 ;
}
2021-03-05 15:10:45 +01:00
if ( att_status ! = ATT_ERROR_SUCCESS ) {
2021-03-05 18:29:49 +01:00
if ( att_status = = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE )
2021-03-05 15:10:45 +01:00
printf ( " Query failed, ATT Error 0x%02x \n " , att_status ) ;
} else {
printf ( " Query done \n " ) ;
}
2021-03-05 09:13:19 +01:00
break ;
default :
break ;
}
}
2021-03-05 18:29:49 +01:00
/* LISTING_END */
2021-03-05 09:13:19 +01:00
int btstack_main ( int argc , const char * argv [ ] ) ;
int btstack_main ( int argc , const char * argv [ ] ) {
// parse address if command line arguments are provided
int arg = 1 ;
cmdline_addr_found = 0 ;
while ( arg < argc ) {
if ( ! strcmp ( argv [ arg ] , " -a " ) | | ! strcmp ( argv [ arg ] , " --address " ) ) {
arg + + ;
cmdline_addr_found = sscanf_bd_addr ( argv [ arg ] , cmdline_addr ) ;
arg + + ;
if ( ! cmdline_addr_found ) exit ( 1 ) ;
continue ;
}
fprintf ( stderr , " \n Usage: %s [-a|--address aa:bb:cc:dd:ee:ff] \n " , argv [ 0 ] ) ;
fprintf ( stderr , " If no argument is provided, GATT browser will start scanning and connect to the first found device. \n To connect to a specific device use argument [-a]. \n \n " ) ;
return 0 ;
}
( void ) argv ;
2021-03-05 18:29:49 +01:00
device_information_service_client_setup ( ) ;
2021-03-05 09:13:19 +01:00
app_state = APP_STATE_IDLE ;
// turn on!
hci_power_control ( HCI_POWER_ON ) ;
return 0 ;
}
/* EXAMPLE_END */