/* * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ /**************************************************************************** * * This file is for Classic Bluetooth device and service discovery Demo. * ****************************************************************************/ #include #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, "%08"PRIx32, 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%"PRIx32, cod); break; case ESP_BT_GAP_DEV_PROP_RSSI: rssi = *(int8_t *)(p->val); ESP_LOGI(GAP_TAG, "--RSSI: %"PRId32, 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->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_gap_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); /* initialize 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) { char bda_str[18] = {0}; /* 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", __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", __func__, esp_err_to_name(ret)); return; } esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT(); if ((ret = esp_bluedroid_init_with_cfg(&bluedroid_cfg)) != ESP_OK) { ESP_LOGE(GAP_TAG, "%s initialize bluedroid failed: %s", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_bluedroid_enable()) != ESP_OK) { ESP_LOGE(GAP_TAG, "%s enable bluedroid failed: %s", __func__, esp_err_to_name(ret)); return; } ESP_LOGI(GAP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str))); bt_app_gap_start_up(); }