espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.71k stars 7.3k forks source link

Connect to ESP32 device via standard Bluetooth dialogue using gatt server example (IDFGH-12208) #13261

Closed Talha-Babar closed 8 months ago

Talha-Babar commented 8 months ago

Answers checklist.

General issue report

Description: I am currently facing an issue where I am unable to connect to an ESP32 device via the standard Bluetooth dialogue on both Android and iOS devices. I have been using the GATT server example provided by the ESP-IDF framework to implement Bluetooth Low Energy (BLE) communication between the ESP32 device and mobile devices.

Here are the specific details of the problem:

Current State: I have successfully implemented the GATT server example: https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/ble/gatt_server I am able to read and write data using third-party apps on both Android and iOS devices. However, when attempting to connect to the ESP32 device via the standard Bluetooth dialogue on Android or iOS, I encounter difficulties.

Issue: The ESP32 device is not discoverable or connectable through the standard Bluetooth dialogue on Android or iOS devices. Instead, the devices show a message indicating that "this device requires special software to operate". This makes it challenging to establish a connection between the ESP32 device and the mobile devices in a user-friendly manner.

Desired Outcome: I want to verify if it is possible to connect to the ESP32 device via the standard Bluetooth dialogue on Android and iOS devices. If it is indeed possible, I would like guidance on what changes need to be made to the ESP32 codebase to facilitate this connection. My ultimate goal is to enable seamless and user-friendly communication between the ESP32 device and mobile devices, utilizing third-party apps for data transmission once the connection is established.

Any insights, suggestions, or guidance on how to resolve this issue and make the ESP32 device discoverable and connectable via the standard Bluetooth dialogue would be greatly appreciated. Thank you for your assistance.

esp-zhp commented 8 months ago

The ESP32 device is not discoverable or connectable through the standard Bluetooth dialogue on Android or iOS devices.

This is because Android or iOS devices do not include the desired service in their broadcast packets(adv data). If there is a corresponding service, it can be detected in the broadcast.

esp-zhp commented 8 months ago

Additionally, some devices may initiate encryption, which requires the ESP32 to support pairing. In the esp-idf/examples/bluetooth/bluedroid/ble/gatt_server example, pairing functionality is not supported. Instead, you can refer to the esp-idf/examples/bluetooth/bluedroid/ble/gatt_security_server example, which supports pairing functionality.

esp-zhp commented 8 months ago

For instance, advertising data in the following can be discoverable or connectable through the standard Bluetooth interface on Android or iOS devices.

static uint8_t raw_adv_data[] = {
    0x02, 0x01, 0x06, 
    0x03, 0x19, 0xC0, 0x03, 0x04, 
    0x09, 0x48, 0x49, 0x44, 0x02, 0x0A, 0x09, 0x03, 0x03, 0x12, 0x18, 
    0x05, 0x12, 0x06, 0x00, 0x10, 0x00,
};
static uint8_t raw_scan_rsp_data[] =
{
    0x02, 0x01, 0x06, 
    0x03, 0x19, 0xC0, 0x03, 0x04, 
    0x09, 0x48, 0x49, 0x44, 0x02, 0x0A, 0x09, 0x03, 0x03, 0x12, 0x18, 
    0x05, 0x12, 0x06, 0x00, 0x10, 0x00,
};
Talha-Babar commented 8 months ago

Dear @zhp0406 ,

Thank you very much for your assistance. I tried implementing the advertising data you provided in my GATT server example, and it indeed made the ESP32 discoverable and connectable via the standard Bluetooth dialogue on iOS devices. However, I encountered a couple of issues that I hope you could shed some light on.

Firstly, while the ESP32 device is now discoverable and connectable on iOS, I am still facing difficulties with Android devices. Despite using the same advertising data, Android devices are unable to detect or connect to the ESP32 device via the standard Bluetooth dialogue. Do you have any insights on why this might be the case or any suggestions for resolving this issue?

Secondly, on iOS devices, I noticed that while the BLE name I set in the advertising data appears correctly in the standard Bluetooth dialogue, it appears as "HID" within the app itself. This discrepancy is causing confusion for users. Could you please provide some clarification on why this might be happening and any potential solutions to ensure consistency in the displayed BLE name?

Once again, thank you for your assistance, and I look forward to your insights on these matters.

esp-zhp commented 8 months ago

You can set the broadcast content yourself. One of the items in the broadcast content is setting the Complete Local Name, which I have set to HID. You can change it to any other name you prefer. This code represents static advertising data used for Bluetooth Low Energy (BLE) devices. Here's the breakdown of each byte:

0x02: Indicates that the following data length is 2 bytes.

0x01: Indicates the type of advertising data, in this case, Flags, which describe the Bluetooth device type and status.

0x06: Represents the value of the Flags, indicating that the device is in LE General Discoverable Mode and BR/EDR Not Supported.

0x03: Indicates that the following data length is 3 bytes.

0x19: Represents the type of advertising data, specifically, Appearance, which describes the device's appearance category.

0xC0, 0x03: Represents the HID (Human Interface Device) Keyboard appearance.

0x04: Indicates that the following data length is 4 bytes.

0x09: Represents the type of advertising data, which is the Complete Local Name, indicating the local name of the device.

0x48, 0x49, 0x44: Represents the local name of the device as "HID".

0x02: Indicates that the following data length is 2 bytes.

0x0A: Represents the type of advertising data, which is the TX Power Level, indicating the transmit power level.

0x09: Represents the transmit power level as -9 dBm.

0x03: Indicates that the following data length is 3 bytes.

0x03: Represents the type of advertising data, which is Service UUIDs, describing the UUIDs of services provided by the device.

0x12, 0x18: Represents the UUID of the first service.

0x05: Indicates that the following data length is 5 bytes.

0x12: Represents the type of advertising data, which is the Slave Connection Interval Range, specifying the range of slave connection intervals.

0x06, 0x00: Represents the minimum (6 units, each unit 1.25 milliseconds) and maximum (10 units) slave connection interval range.

Please note that we also have an API available for setting the device name, namely esp_bt_dev_set_device_name. This API does not alter the broadcast data but instead changes the content of the Device Name characteristic. It's advisable to keep the content of the Device Name characteristic consistent with the Complete Local Name set in the broadcast data for better coherence during usage.

ref:BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 3, Part C page 1356 image

by the way, the content of the broadcast packet I referred to is based on the HID example. The broadcast from this example can be scanned by iOS, Android, Windows, and macOS devices. You can find the example code at the following link: HID example

esp-zhp commented 8 months ago

Have you tried with another Android device? I tested it here, and the results show that Android devices can detect the ESP32 device.

Talha-Babar commented 8 months ago

Yes, i have tried it to different Android devices and it detects the ESP32 device but not able to connect.

esp-zhp commented 8 months ago

What hints does the ESP32 log provide when the connection is not established? I believe this situation shouldn't occur. could you tested the HID discovery and connection using the HID example(HID example) with an Android device?

esp-zhp commented 8 months ago

here is my code(base on example esp-idf/examples/bluetooth/bluedroid/ble/gatt_security_server) and log,It works well in my device,

/*
 * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "example_ble_sec_gatts_demo.h"

#define GATTS_TABLE_TAG "SEC_GATTS_DEMO"

#define HEART_PROFILE_NUM                         1
#define HEART_PROFILE_APP_IDX                     0
#define ESP_HEART_RATE_APP_ID                     0x55
#define EXAMPLE_DEVICE_NAME                       "HID"
#define HEART_RATE_SVC_INST_ID                    0

#define GATTS_DEMO_CHAR_VAL_LEN_MAX               0x40

#define ADV_CONFIG_FLAG                           (1 << 0)
#define SCAN_RSP_CONFIG_FLAG                      (1 << 1)

static uint8_t adv_config_done = 0;

static uint16_t heart_rate_handle_table[HRS_IDX_NB];

static uint8_t test_manufacturer[3]={'E', 'S', 'P'};

static uint8_t sec_service_uuid[16] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    //first uuid, 16bit, [12],[13] is the value
    0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x18, 0x0D, 0x00, 0x00,
};

// config adv data
static esp_ble_adv_data_t heart_rate_adv_config = {
    .set_scan_rsp = false,
    .include_txpower = true,
    .min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
    .max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
    .appearance = 0x00,
    .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data =  NULL, //&test_manufacturer[0],
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = sizeof(sec_service_uuid),
    .p_service_uuid = sec_service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
// config scan response data
static esp_ble_adv_data_t heart_rate_scan_rsp_config = {
    .set_scan_rsp = true,
    .include_name = true,
    .manufacturer_len = sizeof(test_manufacturer),
    .p_manufacturer_data = test_manufacturer,
};

static esp_ble_adv_params_t heart_rate_adv_params = {
    .adv_int_min        = 0x100,
    .adv_int_max        = 0x100,
    .adv_type           = ADV_TYPE_IND,
    .own_addr_type      = BLE_ADDR_TYPE_RPA_PUBLIC,
    .channel_map        = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

static uint8_t raw_adv_data[] = {
    0x02, 0x01, 0x06, 
    0x03, 0x19, 0xC0, 0x03, 0x04, 
    0x09, 0x48, 0x49, 0x44, 0x02, 0x0A, 0x09, 0x03, 0x03, 0x12, 0x18, 
    0x05, 0x12, 0x06, 0x00, 0x10, 0x00,
};
static uint8_t raw_scan_rsp_data[] =
{
    0x02, 0x01, 0x06, 
    0x03, 0x19, 0xC0, 0x03, 0x04, 
    0x09, 0x48, 0x49, 0x44, 0x02, 0x0A, 0x09, 0x03, 0x03, 0x12, 0x18, 
    0x05, 0x12, 0x06, 0x00, 0x10, 0x00,
};

struct gatts_profile_inst {
    esp_gatts_cb_t gatts_cb;
    uint16_t gatts_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_handle;
    esp_gatt_srvc_id_t service_id;
    uint16_t char_handle;
    esp_bt_uuid_t char_uuid;
    esp_gatt_perm_t perm;
    esp_gatt_char_prop_t property;
    uint16_t descr_handle;
    esp_bt_uuid_t descr_uuid;
};

static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
                                        esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
static struct gatts_profile_inst heart_rate_profile_tab[HEART_PROFILE_NUM] = {
    [HEART_PROFILE_APP_IDX] = {
        .gatts_cb = gatts_profile_event_handler,
        .gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
    },

};

/*
 *  Heart Rate PROFILE ATTRIBUTES
 ****************************************************************************************
 */

/// Heart Rate Sensor Service
static const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC;

#define CHAR_DECLARATION_SIZE   (sizeof(uint8_t))
static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY;
static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ;
static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_READ;

/// Heart Rate Sensor Service - Heart Rate Measurement Characteristic, notify
static const uint16_t heart_rate_meas_uuid = ESP_GATT_HEART_RATE_MEAS;
static const uint8_t heart_measurement_ccc[2] ={ 0x00, 0x00};

/// Heart Rate Sensor Service -Body Sensor Location characteristic, read
static const uint16_t body_sensor_location_uuid = ESP_GATT_BODY_SENSOR_LOCATION;
static const uint8_t body_sensor_loc_val[1] = {0x00};

/// Heart Rate Sensor Service - Heart Rate Control Point characteristic, write&read
static const uint16_t heart_rate_ctrl_point = ESP_GATT_HEART_RATE_CNTL_POINT;
static const uint8_t heart_ctrl_point[1] = {0x00};

/// Full HRS Database Description - Used to add attributes into the database
static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] =
{
    // Heart Rate Service Declaration
    [HRS_IDX_SVC]                    =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
      sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}},

    // Heart Rate Measurement Characteristic Declaration
    [HRS_IDX_HR_MEAS_CHAR]            =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
      CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}},

    // Heart Rate Measurement Characteristic Value
    [HRS_IDX_HR_MEAS_VAL]             =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ,
      HRPS_HT_MEAS_MAX_LEN,0, NULL}},

    // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor
    [HRS_IDX_HR_MEAS_NTF_CFG]        =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
      sizeof(uint16_t),sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}},

    // Body Sensor Location Characteristic Declaration
    [HRS_IDX_BOBY_SENSOR_LOC_CHAR]  =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
      CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read}},

    // Body Sensor Location Characteristic Value
    [HRS_IDX_BOBY_SENSOR_LOC_VAL]   =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&body_sensor_location_uuid, ESP_GATT_PERM_READ_ENCRYPTED,
      sizeof(uint8_t), sizeof(body_sensor_loc_val), (uint8_t *)body_sensor_loc_val}},

    // Heart Rate Control Point Characteristic Declaration
    [HRS_IDX_HR_CTNL_PT_CHAR]          =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
      CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}},

    // Heart Rate Control Point Characteristic Value
    [HRS_IDX_HR_CTNL_PT_VAL]             =
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_ctrl_point, ESP_GATT_PERM_WRITE_ENCRYPTED|ESP_GATT_PERM_READ_ENCRYPTED,
      sizeof(uint8_t), sizeof(heart_ctrl_point), (uint8_t *)heart_ctrl_point}},
};

static char *esp_key_type_to_str(esp_ble_key_type_t key_type)
{
   char *key_str = NULL;
   switch(key_type) {
    case ESP_LE_KEY_NONE:
        key_str = "ESP_LE_KEY_NONE";
        break;
    case ESP_LE_KEY_PENC:
        key_str = "ESP_LE_KEY_PENC";
        break;
    case ESP_LE_KEY_PID:
        key_str = "ESP_LE_KEY_PID";
        break;
    case ESP_LE_KEY_PCSRK:
        key_str = "ESP_LE_KEY_PCSRK";
        break;
    case ESP_LE_KEY_PLK:
        key_str = "ESP_LE_KEY_PLK";
        break;
    case ESP_LE_KEY_LLK:
        key_str = "ESP_LE_KEY_LLK";
        break;
    case ESP_LE_KEY_LENC:
        key_str = "ESP_LE_KEY_LENC";
        break;
    case ESP_LE_KEY_LID:
        key_str = "ESP_LE_KEY_LID";
        break;
    case ESP_LE_KEY_LCSRK:
        key_str = "ESP_LE_KEY_LCSRK";
        break;
    default:
        key_str = "INVALID BLE KEY TYPE";
        break;

   }

   return key_str;
}

static char *esp_auth_req_to_str(esp_ble_auth_req_t auth_req)
{
   char *auth_str = NULL;
   switch(auth_req) {
    case ESP_LE_AUTH_NO_BOND:
        auth_str = "ESP_LE_AUTH_NO_BOND";
        break;
    case ESP_LE_AUTH_BOND:
        auth_str = "ESP_LE_AUTH_BOND";
        break;
    case ESP_LE_AUTH_REQ_MITM:
        auth_str = "ESP_LE_AUTH_REQ_MITM";
        break;
    case ESP_LE_AUTH_REQ_BOND_MITM:
        auth_str = "ESP_LE_AUTH_REQ_BOND_MITM";
        break;
    case ESP_LE_AUTH_REQ_SC_ONLY:
        auth_str = "ESP_LE_AUTH_REQ_SC_ONLY";
        break;
    case ESP_LE_AUTH_REQ_SC_BOND:
        auth_str = "ESP_LE_AUTH_REQ_SC_BOND";
        break;
    case ESP_LE_AUTH_REQ_SC_MITM:
        auth_str = "ESP_LE_AUTH_REQ_SC_MITM";
        break;
    case ESP_LE_AUTH_REQ_SC_MITM_BOND:
        auth_str = "ESP_LE_AUTH_REQ_SC_MITM_BOND";
        break;
    default:
        auth_str = "INVALID BLE AUTH REQ";
        break;
   }

   return auth_str;
}

static void show_bonded_devices(void)
{
    int dev_num = esp_ble_get_bond_device_num();
    if (dev_num == 0) {
        ESP_LOGI(GATTS_TABLE_TAG, "Bonded devices number zero\n");
        return;
    }

    esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
    if (!dev_list) {
        ESP_LOGI(GATTS_TABLE_TAG, "malloc failed, return\n");
        return;
    }
    esp_ble_get_bond_device_list(&dev_num, dev_list);
    ESP_LOGI(GATTS_TABLE_TAG, "Bonded devices number : %d", dev_num);

    ESP_LOGI(GATTS_TABLE_TAG, "Bonded devices list : %d", dev_num);
    for (int i = 0; i < dev_num; i++) {
        esp_log_buffer_hex(GATTS_TABLE_TAG, (void *)dev_list[i].bd_addr, sizeof(esp_bd_addr_t));
    }

    free(dev_list);
}

static void __attribute__((unused)) remove_all_bonded_devices(void)
{
    int dev_num = esp_ble_get_bond_device_num();
    if (dev_num == 0) {
        ESP_LOGI(GATTS_TABLE_TAG, "Bonded devices number zero\n");
        return;
    }

    esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
    if (!dev_list) {
        ESP_LOGI(GATTS_TABLE_TAG, "malloc failed, return\n");
        return;
    }
    esp_ble_get_bond_device_list(&dev_num, dev_list);
    for (int i = 0; i < dev_num; i++) {
        esp_ble_remove_bond_device(dev_list[i].bd_addr);
    }

    free(dev_list);
}

static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    ESP_LOGV(GATTS_TABLE_TAG, "GAP_EVT, event %d", event);

    switch (event) {
    case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
        adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
        if (adv_config_done == 0){
            esp_ble_gap_start_advertising(&heart_rate_adv_params);
        }
        break;
    case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
        adv_config_done &= (~ADV_CONFIG_FLAG);
        if (adv_config_done == 0){
            esp_ble_gap_start_advertising(&heart_rate_adv_params);
        }
        break;
    case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
        //advertising start complete event to indicate advertising start successfully or failed
        if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
            ESP_LOGE(GATTS_TABLE_TAG, "advertising start failed, error status = %x", param->adv_start_cmpl.status);
            break;
        }
        ESP_LOGI(GATTS_TABLE_TAG, "advertising start success");
        break;
    case ESP_GAP_BLE_PASSKEY_REQ_EVT:                           /* passkey request event */
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT");
        /* Call the following function to input the passkey which is displayed on the remote device */
        //esp_ble_passkey_reply(heart_rate_profile_tab[HEART_PROFILE_APP_IDX].remote_bda, true, 0x00);
        break;
    case ESP_GAP_BLE_OOB_REQ_EVT: {
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_OOB_REQ_EVT");
        uint8_t tk[16] = {1}; //If you paired with OOB, both devices need to use the same tk
        esp_ble_oob_req_reply(param->ble_security.ble_req.bd_addr, tk, sizeof(tk));
        break;
    }
    case ESP_GAP_BLE_LOCAL_IR_EVT:                               /* BLE local IR event */
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT");
        break;
    case ESP_GAP_BLE_LOCAL_ER_EVT:                               /* BLE local ER event */
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT");
        break;
    case ESP_GAP_BLE_NC_REQ_EVT:
        /* The app will receive this evt when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability.
        show the passkey number to the user to confirm it with the number displayed by peer device. */
        esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, true);
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_NC_REQ_EVT, the passkey Notify number:%" PRIu32, param->ble_security.key_notif.passkey);
        break;
    case ESP_GAP_BLE_SEC_REQ_EVT:
        /* send the positive(true) security response to the peer device to accept the security request.
        If not accept the security request, should send the security response with negative(false) accept value*/
        esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
        break;
    case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:  ///the app will receive this evt when the IO  has Output capability and the peer device IO has Input capability.
        ///show the passkey number to the user to input it in the peer device.
        ESP_LOGI(GATTS_TABLE_TAG, "The passkey Notify number:%06" PRIu32, param->ble_security.key_notif.passkey);
        break;
    case ESP_GAP_BLE_KEY_EVT:
        //shows the ble key info share with peer device to the user.
        ESP_LOGI(GATTS_TABLE_TAG, "key type = %s", esp_key_type_to_str(param->ble_security.ble_key.key_type));
        break;
    case ESP_GAP_BLE_AUTH_CMPL_EVT: {
        esp_bd_addr_t bd_addr;
        memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
        ESP_LOGI(GATTS_TABLE_TAG, "remote BD_ADDR: %08x%04x",\
                (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
                (bd_addr[4] << 8) + bd_addr[5]);
        ESP_LOGI(GATTS_TABLE_TAG, "address type = %d", param->ble_security.auth_cmpl.addr_type);
        ESP_LOGI(GATTS_TABLE_TAG, "pair status = %s",param->ble_security.auth_cmpl.success ? "success" : "fail");
        if(!param->ble_security.auth_cmpl.success) {
            ESP_LOGI(GATTS_TABLE_TAG, "fail reason = 0x%x",param->ble_security.auth_cmpl.fail_reason);
        } else {
            ESP_LOGI(GATTS_TABLE_TAG, "auth mode = %s",esp_auth_req_to_str(param->ble_security.auth_cmpl.auth_mode));
        }
        show_bonded_devices();
        break;
    }
    case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: {
        ESP_LOGD(GATTS_TABLE_TAG, "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT status = %d", param->remove_bond_dev_cmpl.status);
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_REMOVE_BOND_DEV");
        ESP_LOGI(GATTS_TABLE_TAG, "-----ESP_GAP_BLE_REMOVE_BOND_DEV----");
        esp_log_buffer_hex(GATTS_TABLE_TAG, (void *)param->remove_bond_dev_cmpl.bd_addr, sizeof(esp_bd_addr_t));
        ESP_LOGI(GATTS_TABLE_TAG, "------------------------------------");
        break;
    }
    case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT:
        if (param->local_privacy_cmpl.status != ESP_BT_STATUS_SUCCESS){
            ESP_LOGE(GATTS_TABLE_TAG, "config local privacy failed, error status = %x", param->local_privacy_cmpl.status);
            break;
        }

        esp_err_t ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data));
        if (ret){
            ESP_LOGE(GATTS_TABLE_TAG, "config adv data failed, error code = %x", ret);
        }else{
            adv_config_done |= ADV_CONFIG_FLAG;
        }

        ret = esp_ble_gap_config_scan_rsp_data_raw(raw_scan_rsp_data, sizeof(raw_scan_rsp_data));
        if (ret){
            ESP_LOGE(GATTS_TABLE_TAG, "config adv data failed, error code = %x", ret);
        }else{
            adv_config_done |= SCAN_RSP_CONFIG_FLAG;
        }

        break;
    default:
        break;
    }
}

static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
                                        esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
    ESP_LOGV(GATTS_TABLE_TAG, "event = %x",event);
    switch (event) {
        case ESP_GATTS_REG_EVT:
            esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME);
            //generate a resolvable random address
            esp_ble_gap_config_local_privacy(true);
            esp_ble_gatts_create_attr_tab(heart_rate_gatt_db, gatts_if,
                                      HRS_IDX_NB, HEART_RATE_SVC_INST_ID);
            break;
        case ESP_GATTS_READ_EVT:
            break;
        case ESP_GATTS_WRITE_EVT:
            ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_WRITE_EVT, write value:");
            esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);
            break;
        case ESP_GATTS_EXEC_WRITE_EVT:
            break;
        case ESP_GATTS_MTU_EVT:
            break;
        case ESP_GATTS_CONF_EVT:
            break;
        case ESP_GATTS_UNREG_EVT:
            break;
        case ESP_GATTS_DELETE_EVT:
            break;
        case ESP_GATTS_START_EVT:
            break;
        case ESP_GATTS_STOP_EVT:
            break;
        case ESP_GATTS_CONNECT_EVT:
            ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_CONNECT_EVT");
            /* start security connect with peer device when receive the connect event sent by the master */
            esp_ble_set_encryption(param->connect.remote_bda, ESP_BLE_SEC_ENCRYPT_MITM);
            break;
        case ESP_GATTS_DISCONNECT_EVT:
            ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_DISCONNECT_EVT, disconnect reason 0x%x", param->disconnect.reason);
            /* start advertising again when missing the connect */
            esp_ble_gap_start_advertising(&heart_rate_adv_params);
            break;
        case ESP_GATTS_OPEN_EVT:
            break;
        case ESP_GATTS_CANCEL_OPEN_EVT:
            break;
        case ESP_GATTS_CLOSE_EVT:
            break;
        case ESP_GATTS_LISTEN_EVT:
            break;
        case ESP_GATTS_CONGEST_EVT:
            break;
        case ESP_GATTS_CREAT_ATTR_TAB_EVT: {
            ESP_LOGI(GATTS_TABLE_TAG, "The number handle = %x",param->add_attr_tab.num_handle);
            if (param->create.status == ESP_GATT_OK){
                if(param->add_attr_tab.num_handle == HRS_IDX_NB) {
                    memcpy(heart_rate_handle_table, param->add_attr_tab.handles,
                    sizeof(heart_rate_handle_table));
                    esp_ble_gatts_start_service(heart_rate_handle_table[HRS_IDX_SVC]);
                }else{
                    ESP_LOGE(GATTS_TABLE_TAG, "Create attribute table abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)",
                         param->add_attr_tab.num_handle, HRS_IDX_NB);
                }
            }else{
                ESP_LOGE(GATTS_TABLE_TAG, " Create attribute table failed, error code = %x", param->create.status);
            }
        break;
    }

        default:
           break;
    }
}

static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
                                esp_ble_gatts_cb_param_t *param)
{
    /* If event is register event, store the gatts_if for each profile */
    if (event == ESP_GATTS_REG_EVT) {
        if (param->reg.status == ESP_GATT_OK) {
            heart_rate_profile_tab[HEART_PROFILE_APP_IDX].gatts_if = gatts_if;
        } else {
            ESP_LOGI(GATTS_TABLE_TAG, "Reg app failed, app_id %04x, status %d",
                    param->reg.app_id,
                    param->reg.status);
            return;
        }
    }

    do {
        int idx;
        for (idx = 0; idx < HEART_PROFILE_NUM; idx++) {
            if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
                    gatts_if == heart_rate_profile_tab[idx].gatts_if) {
                if (heart_rate_profile_tab[idx].gatts_cb) {
                    heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param);
                }
            }
        }
    } while (0);
}

void app_main(void)
{
    esp_err_t ret;

    // Initialize NVS.
    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_CLASSIC_BT));

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s init controller failed: %s", __func__, esp_err_to_name(ret));
        return;
    }
    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    ESP_LOGI(GATTS_TABLE_TAG, "%s init bluetooth", __func__);
    esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
    ret = esp_bluedroid_init_with_cfg(&bluedroid_cfg);
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));
        return;
    }
    ret = esp_bluedroid_enable();
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_ble_gatts_register_callback(gatts_event_handler);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gatts register error, error code = %x", ret);
        return;
    }
    ret = esp_ble_gap_register_callback(gap_event_handler);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gap register error, error code = %x", ret);
        return;
    }
    ret = esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gatts app register error, error code = %x", ret);
        return;
    }

    /* set the security iocap & auth_req & key size & init key response key parameters to the stack*/
    esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;     //bonding with peer device after authentication
    esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;           //set the IO capability to No output No input
    uint8_t key_size = 16;      //the key size should be 7~16 bytes
    uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
    uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
    //set static passkey
    uint32_t passkey = 123456;
    uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE;
    uint8_t oob_support = ESP_BLE_OOB_DISABLE;
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t));
    /* If your BLE device acts as a Slave, the init_key means you hope which types of key of the master should distribute to you,
    and the response key means which key you can distribute to the master;
    If your BLE device acts as a master, the response key means you hope which types of key of the slave should distribute to you,
    and the init key means which key you can distribute to the slave. */
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));

    /* Just show how to clear all the bonded devices
     * Delay 30s, clear all the bonded devices
     *
     * vTaskDelay(30000 / portTICK_PERIOD_MS);
     * remove_all_bonded_devices();
     */
}

log

I (29) boot: ESP-IDF v5.3-dev-2123-g21490894cd-dirty 2nd stage bootloader
I (29) boot: compile time Feb 28 2024 11:38:45
I (32) boot: Multicore bootloader
I (36) boot: chip revision: v3.1
I (40) boot.esp32: SPI Speed      : 40MHz
I (44) boot.esp32: SPI Mode       : DIO
I (49) boot.esp32: SPI Flash Size : 2MB
I (53) boot: Enabling RNG early entropy source...
I (59) boot: Partition Table:
I (62) boot: ## Label            Usage          Type ST Offset   Length
I (70) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (77) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (84) boot:  2 factory          factory app      00 00 00010000 00100000
I (92) boot: End of partition table
I (96) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=234fch (144636) map
I (154) esp_image: segment 1: paddr=00033524 vaddr=3ffbdb60 size=04ad4h ( 19156) load
I (162) esp_image: segment 2: paddr=00038000 vaddr=40080000 size=08018h ( 32792) load
I (175) esp_image: segment 3: paddr=00040020 vaddr=400d0020 size=8116ch (528748) map
I (356) esp_image: segment 4: paddr=000c1194 vaddr=40088018 size=110cch ( 69836) load
I (396) boot: Loaded app from partition at offset 0x10000
I (396) boot: Disabling RNG early entropy source...
I (408) cpu_start: Multicore app
I (417) cpu_start: Pro cpu start user code
I (417) cpu_start: cpu freq: 160000000 Hz
I (417) app_init: Application information:
I (420) app_init: Project name:     sec_gatts_demo
I (425) app_init: App version:      v5.3-dev-2123-g21490894cd-dirty
I (432) app_init: Compile time:     Feb 28 2024 11:38:40
I (438) app_init: ELF file SHA256:  b03360538...
I (443) app_init: ESP-IDF:          v5.3-dev-2123-g21490894cd-dirty
I (450) app_init: Min chip rev:     v0.0
I (455) app_init: Max chip rev:     v3.99 
I (460) app_init: Chip rev:         v3.1
I (465) heap_init: Initializing. RAM available for dynamic allocation:
I (472) heap_init: At 3FFAFF10 len 000000F0 (0 KiB): DRAM
I (478) heap_init: At 3FFB6388 len 00001C78 (7 KiB): DRAM
I (484) heap_init: At 3FFB9A20 len 00004108 (16 KiB): DRAM
I (490) heap_init: At 3FFC7210 len 00018DF0 (99 KiB): DRAM
I (496) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (502) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (509) heap_init: At 400990E4 len 00006F1C (27 KiB): IRAM
I (517) spi_flash: detected chip: gd
I (519) spi_flash: flash io: dio
W (523) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (537) coexist: coex firmware version: 77cd7f8
I (542) main_task: Started on CPU0
I (552) main_task: Calling app_main()
I (572) BTDM_INIT: BT controller compile version [0f0c5a2]
I (572) BTDM_INIT: Bluetooth MAC: 08:b6:1f:ed:4e:42
I (582) phy_init: phy_version 4791,2c4672b,Dec 20 2023,16:06:06
I (892) SEC_GATTS_DEMO: app_main init bluetooth
I (992) SEC_GATTS_DEMO: The number handle = 8
I (1002) main_task: Returned from app_main()
I (1022) SEC_GATTS_DEMO: advertising start success
I (25852) SEC_GATTS_DEMO: ESP_GATTS_CONNECT_EVT
W (28862) BT_SMP: FOR LE SC LTK IS USED INSTEAD OF STK
I (29092) SEC_GATTS_DEMO: key type = ESP_LE_KEY_LENC
I (29092) SEC_GATTS_DEMO: key type = ESP_LE_KEY_PENC
I (29092) SEC_GATTS_DEMO: key type = ESP_LE_KEY_LID
I (29542) SEC_GATTS_DEMO: key type = ESP_LE_KEY_PID
I (29602) SEC_GATTS_DEMO: remote BD_ADDR: 54f8fe672588
I (29602) SEC_GATTS_DEMO: address type = 1
I (29612) SEC_GATTS_DEMO: pair status = success
I (29612) SEC_GATTS_DEMO: auth mode = ESP_LE_AUTH_REQ_SC_BOND
I (29622) SEC_GATTS_DEMO: Bonded devices number : 1
I (29622) SEC_GATTS_DEMO: Bonded devices list : 1
I (29632) SEC_GATTS_DEMO: 54 f8 fe 67 25 88 
W (34712) BT_HCI: hcif disc complete: hdl 0x0, rsn 0x13
I (34712) SEC_GATTS_DEMO: ESP_GATTS_DISCONNECT_EVT, disconnect reason 0x13
I (34732) SEC_GATTS_DEMO: advertising start success
Talha-Babar commented 8 months ago

Would you kindly share your complete project, one of the header files is not included in the HID example and I am facing compiling issues

esp-zhp commented 8 months ago

please replace code in "esp-idf/examples/bluetooth/bluedroid/ble/gatt_security_server/main/example_ble_sec_gatts_demo.c" note: I used latest master

esp-zhp commented 8 months ago

@Talha-Babar Have you solved the problem?

Talha-Babar commented 8 months ago

Hello @zhp0406,

Thank you for following up. I've tested your code and successfully established a connection to the ESP32 using the standard Bluetooth dialogue on both Android and iOS. However, I've encountered an issue where I am unable to send data from a third-party app after connecting via Bluetooth. The third-party app fails to recognize any connected Bluetooth devices, necessitating a reconnection each time data needs to be sent.

I'm unsure whether this issue stems from the third-party app itself or if it's related to the ESP32 code.

esp-zhp commented 8 months ago

@Talha-Babar It looks like this issue has been resolved, if there are other issues, please start another github issue and give related log,thanks.

Alvin1Zhang commented 8 months ago

Thanks for reporting and sharing the updates, feel free to reopen.

Talha-Babar commented 7 months ago

Hello @zhp0406,

I apologize for the delay as I had temporarily paused the project, but I have resumed work on it now. The code is functioning correctly, and I can successfully establish a connection through the standard Bluetooth dialog. However, I am encountering an issue with sending and receiving serial data. For testing purposes, I am using the Serial Bluetooth Terminal app. Unfortunately, the app is unable to maintain a stable connection and reports that no serial profile is found. Consequently, I am unable to send or receive data through the app.

Thank you for your understanding.

Best regards,