/* This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ /**************************************************************************** * * This file is for Classic Bluetooth device and service discovery Demo. * ****************************************************************************/ #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "nvs.h" #include "nvs_flash.h" #include "esp_system.h" #include "esp_log.h" #include "esp_bt.h" #include "esp_bt_main.h" #include "esp_bt_device.h" #include "esp_gap_bt_api.h" #define GAP_TAG "GAP" typedef enum { APP_GAP_STATE_IDLE = 0, APP_GAP_STATE_DEVICE_DISCOVERING, APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE, APP_GAP_STATE_SERVICE_DISCOVERING, APP_GAP_STATE_SERVICE_DISCOVER_COMPLETE, } app_gap_state_t; typedef struct { bool dev_found; uint8_t bdname_len; uint8_t eir_len; uint8_t rssi; uint32_t cod; uint8_t eir[ESP_BT_GAP_EIR_DATA_LEN]; uint8_t bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; esp_bd_addr_t bda; app_gap_state_t state; } app_gap_cb_t; static app_gap_cb_t m_dev_info; static char *bda2str(esp_bd_addr_t bda, char *str, size_t size) { if (bda == NULL || str == NULL || size < 18) { return NULL; } uint8_t *p = bda; sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], p[3], p[4], p[5]); return str; } static char *uuid2str(esp_bt_uuid_t *uuid, char *str, size_t size) { if (uuid == NULL || str == NULL) { return NULL; } if (uuid->len == 2 && size >= 5) { sprintf(str, "%04x", uuid->uuid.uuid16); } else if (uuid->len == 4 && size >= 9) { sprintf(str, "%08x", uuid->uuid.uuid32); } else if (uuid->len == 16 && size >= 37) { uint8_t *p = uuid->uuid.uuid128; sprintf(str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", p[15], p[14], p[13], p[12], p[11], p[10], p[9], p[8], p[7], p[6], p[5], p[4], p[3], p[2], p[1], p[0]); } else { return NULL; } return str; } static bool get_name_from_eir(uint8_t *eir, uint8_t *bdname, uint8_t *bdname_len) { uint8_t *rmt_bdname = NULL; uint8_t rmt_bdname_len = 0; if (!eir) { return false; } rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len); if (!rmt_bdname) { rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len); } if (rmt_bdname) { if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) { rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN; } if (bdname) { memcpy(bdname, rmt_bdname, rmt_bdname_len); bdname[rmt_bdname_len] = '\0'; } if (bdname_len) { *bdname_len = rmt_bdname_len; } return true; } return false; } static void update_device_info(esp_bt_gap_cb_param_t *param) { char bda_str[18]; uint32_t cod = 0; int32_t rssi = -129; /* invalid value */ uint8_t *bdname = NULL; uint8_t bdname_len = 0; uint8_t *eir = NULL; uint8_t eir_len = 0; esp_bt_gap_dev_prop_t *p; ESP_LOGI(GAP_TAG, "Device found: %s", bda2str(param->disc_res.bda, bda_str, 18)); for (int i = 0; i < param->disc_res.num_prop; i++) { p = param->disc_res.prop + i; switch (p->type) { case ESP_BT_GAP_DEV_PROP_COD: cod = *(uint32_t *)(p->val); ESP_LOGI(GAP_TAG, "--Class of Device: 0x%x", cod); break; case ESP_BT_GAP_DEV_PROP_RSSI: rssi = *(int8_t *)(p->val); ESP_LOGI(GAP_TAG, "--RSSI: %d", rssi); break; case ESP_BT_GAP_DEV_PROP_BDNAME: bdname_len = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN : (uint8_t)p->len; bdname = (uint8_t *)(p->val); break; case ESP_BT_GAP_DEV_PROP_EIR: { eir_len = p->len; eir = (uint8_t *)(p->val); break; } default: break; } } /* search for device with Major device type "PHONE" or "Audio/Video" in COD */ app_gap_cb_t *p_dev = &m_dev_info; if (p_dev->dev_found) { return; } if (!esp_bt_gap_is_valid_cod(cod) || (!(esp_bt_gap_get_cod_major_dev(cod) == ESP_BT_COD_MAJOR_DEV_PHONE) && !(esp_bt_gap_get_cod_major_dev(cod) == ESP_BT_COD_MAJOR_DEV_AV))) { return; } memcpy(p_dev->bda, param->disc_res.bda, ESP_BD_ADDR_LEN); p_dev->dev_found = true; p_dev->cod = cod; p_dev->rssi = rssi; if (bdname_len > 0) { memcpy(p_dev->bdname, bdname, bdname_len); p_dev->bdname[bdname_len] = '\0'; p_dev->bdname_len = bdname_len; } if (eir_len > 0) { memcpy(p_dev->eir, eir, eir_len); p_dev->eir_len = eir_len; } if (p_dev->eir && p_dev->bdname_len == 0) { get_name_from_eir(p_dev->eir, p_dev->bdname, &p_dev->bdname_len); } ESP_LOGI(GAP_TAG, "Found a target device, address %s, name %s", bda_str, p_dev->bdname); p_dev->state = APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE; ESP_LOGI(GAP_TAG, "Cancel device discovery ..."); esp_bt_gap_cancel_discovery(); } static void bt_app_gap_init(void) { app_gap_cb_t *p_dev = &m_dev_info; memset(p_dev, 0, sizeof(app_gap_cb_t)); p_dev->state = APP_GAP_STATE_IDLE; } static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) { app_gap_cb_t *p_dev = &m_dev_info; char bda_str[18]; char uuid_str[37]; switch (event) { case ESP_BT_GAP_DISC_RES_EVT: { update_device_info(param); break; } case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: { if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) { ESP_LOGI(GAP_TAG, "Device discovery stopped."); if ( (p_dev->state == APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE || p_dev->state == APP_GAP_STATE_DEVICE_DISCOVERING) && p_dev->dev_found) { p_dev->state = APP_GAP_STATE_SERVICE_DISCOVERING; ESP_LOGI(GAP_TAG, "Discover services ..."); esp_bt_gap_get_remote_services(p_dev->bda); } } else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) { ESP_LOGI(GAP_TAG, "Discovery started."); } break; } case ESP_BT_GAP_RMT_SRVCS_EVT: { if (memcmp(param->rmt_srvcs.bda, p_dev->bda, ESP_BD_ADDR_LEN) == 0 && p_dev->state == APP_GAP_STATE_SERVICE_DISCOVERING) { p_dev->state = APP_GAP_STATE_SERVICE_DISCOVER_COMPLETE; if (param->rmt_srvcs.stat == ESP_BT_STATUS_SUCCESS) { ESP_LOGI(GAP_TAG, "Services for device %s found", bda2str(p_dev->bda, bda_str, 18)); for (int i = 0; i < param->rmt_srvcs.num_uuids; i++) { esp_bt_uuid_t *u = param->rmt_srvcs.uuid_list + i; ESP_LOGI(GAP_TAG, "--%s", uuid2str(u, uuid_str, 37)); } } else { ESP_LOGI(GAP_TAG, "Services for device %s not found", bda2str(p_dev->bda, bda_str, 18)); } } break; } case ESP_BT_GAP_RMT_SRVC_REC_EVT: default: { ESP_LOGI(GAP_TAG, "event: %d", event); break; } } return; } static void bt_app_gap_start_up(void) { /* register GAP callback function */ esp_bt_gap_register_callback(bt_app_gap_cb); char *dev_name = "ESP_GAP_INQRUIY"; esp_bt_dev_set_device_name(dev_name); /* set discoverable and connectable mode, wait to be connected */ esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); /* inititialize device information and status */ bt_app_gap_init(); /* start to discover nearby Bluetooth devices */ app_gap_cb_t *p_dev = &m_dev_info; p_dev->state = APP_GAP_STATE_DEVICE_DISCOVERING; esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); } void app_main(void) { /* Initialize NVS — it is used to store PHY calibration data and save key-value pairs in flash memory*/ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { ESP_LOGE(GAP_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { ESP_LOGE(GAP_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_bluedroid_init()) != ESP_OK) { ESP_LOGE(GAP_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_bluedroid_enable()) != ESP_OK) { ESP_LOGE(GAP_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); return; } bt_app_gap_start_up(); }