ESP-IDF v3.0 蓝牙 gatt 服务器示例或帮助使用多个服务?

ESP-IDF v3.0 Bluetooth gatt server example or help using multiple services?

我目前正在开发一个固件项目,我需要设备广播多个蓝牙服务,例如设备信息服务、电池服务等...我梳理了 esp-idf GitHub 和 readthedocs 站点以及 GATT 服务器的所有示例似乎只有一个服务。截至目前,我的广告工作正常,一项服务功能齐全,但我完全不知道如何让第二项服务正常工作。如果有人有任何例子或建议,将不胜感激!

我这里的大部分内容都基于 ESP-IDF GATT 服务器服务 Table 示例: https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/gatt_server_service_table

我正在像这样初始化蓝牙库(在我的整个代码中,我删除了很多日志和错误处理以提高可读性...功能是相同的):

esp_err_t Bluetooth_Interface_Init(void)
{
    esp_err_t err;

    Bluetooth_Driver_Init(); //Takes care of bt hardware inits

    err = esp_ble_gatts_register_callback(gatts_event_handler);
    err = esp_ble_gap_register_callback(gap_event_handler);

    err = esp_ble_gatts_app_register(ESP_APP_ID);
    err = esp_ble_gatt_set_local_mtu(500);

    return err;
}

我的广告数据在这里:

static uint8_t 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,
};

/* The length of adv data must be less than 31 bytes */
static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp        = false,
    .include_name        = true,
    .include_txpower     = true,
    .min_interval        = 0x20,
    .max_interval        = 0x40,
    .appearance          = 0x00,
    .manufacturer_len    = 0,    //TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data = NULL, //test_manufacturer,
    .service_data_len    = 0,
    .p_service_data      = NULL,
    .service_uuid_len    = sizeof(service_uuid),
    .p_service_uuid      = service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

// scan response data
static esp_ble_adv_data_t scan_rsp_data = {
    .set_scan_rsp        = true,
    .include_name        = true,
    .include_txpower     = true,
    .min_interval        = 0x20,
    .max_interval        = 0x40,
    .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(service_uuid),
    .p_service_uuid      = service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

static esp_ble_adv_params_t adv_params = {
    .adv_int_min         = 0x20,
    .adv_int_max         = 0x40,
    .adv_type            = ADV_TYPE_IND,
    .own_addr_type       = BLE_ADDR_TYPE_PUBLIC,
    .channel_map         = ADV_CHNL_ALL,
    .adv_filter_policy   = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

我的 GAP 事件处理程序:

   static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
    {
        switch (event) {
            case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
                adv_config_done &= (~ADV_CONFIG_FLAG);
                if (adv_config_done == 0){
                    esp_ble_gap_start_advertising(&adv_params);
                }
                break;
            case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
                adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
                if (adv_config_done == 0){
                    esp_ble_gap_start_advertising(&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(TAG_IBLUETOOTH, "advertising start failed");
                }else{
                    ESP_LOGI(TAG_IBLUETOOTH, "advertising start successfully");
                }
                break;
            case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
                if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
                    ESP_LOGE(TAG_IBLUETOOTH, "Advertising stop failed");
                }
                else {
                    ESP_LOGI(TAG_IBLUETOOTH, "Stop adv successfully\n");
                }
                break;
            case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
                ESP_LOGI(TAG_IBLUETOOTH, "update connetion params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
                      param->update_conn_params.status,
                      param->update_conn_params.min_int,
                      param->update_conn_params.max_int,
                      param->update_conn_params.conn_int,
                      param->update_conn_params.latency,
                      param->update_conn_params.timeout);
                break;
            default:
                break;
        }
    }

我的 GATTS 事件处理程序:

static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
#ifdef IBLUETOOTH_DEBUG
    ESP_LOGI(TAG_IBLUETOOTH, "gatts event handler: %d", event);
#endif
    /* 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) {


            bt_profile_tab[INFO_IDX].gatts_if = gatts_if; //This is all that works... It registers the info profile 
            //bt_profile_tab[param->reg.app_id].gatts_if = gatts_if; //Nothing gets registered when doing this
            //bt_profile_tab[WIFI_IDX].gatts_if = gatts_if; //Doing this always fails to register both.. Info Service is all that gets registered
        } else {
            ESP_LOGE(TAG_IBLUETOOTH, "reg app failed, app_id %04x, status %d", param->reg.app_id, param->reg.status);
            return;
        }
    }
    do {
        int idx;
        for (idx = 0; idx < PROFILE_NUM; idx++) {
            /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
            if (gatts_if == ESP_GATT_IF_NONE || gatts_if == bt_profile_tab[idx].gatts_if) {
                if (bt_profile_tab[idx].gatts_cb) {
                    bt_profile_tab[idx].gatts_cb(event, gatts_if, param);
                }
            }
        }
    } while (0);
}

服务事件处理程序之一的示例:

static const uint16_t INFO_SERV_uuid                = ESP_GATT_UUID_DEVICE_INFO_SVC;  //0x180A
static const uint16_t INFO_SERV_char_sysid_uuid         = ...;
static const uint16_t INFO_SERV_char_model_uuid         = ...;
static const uint16_t INFO_SERV_char_serial_uuid        = ...;
static const uint16_t INFO_SERV_char_fw_uuid        = ...;
static const uint16_t INFO_SERV_char_protocol_uuid  = ...;

static const uint8_t INFO_SERV_char_sysid_descr[]       = ...;
static const uint8_t INFO_SERV_char_model_descr[]       = ...;
static const uint8_t INFO_SERV_char_serial_descr[]      = ...;
static const uint8_t INFO_SERV_char_fw_descr[]      = ...;
static const uint8_t INFO_SERV_char_protocol_descr[]        = ...;

static const esp_gatts_attr_db_t info_serv_gatt_db[INFO_NB] =
{
    [INFO_SERV] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *) &primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(INFO_SERV_uuid), (uint8_t *)&INFO_SERV_uuid}},
...
 };

static void info_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
    switch (event) {
        case ESP_GATTS_REG_EVT:{
            esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(DEVICE_NAME);

            //config adv data
            esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
            adv_config_done |= ADV_CONFIG_FLAG;

            //config scan response data
            ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
            adv_config_done |= SCAN_RSP_CONFIG_FLAG;

            esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(info_serv_gatt_db, gatts_if, INFO_NB, SVC_INST_ID);
        }
            break;
        case ESP_GATTS_READ_EVT:

... 案例ESP_GATTS_EXEC_WRITE_EVT: ...
案例ESP_GATTS_MTU_EVT: ESP_LOGI(TAG_IBLUETOOTH, "ESP_GATTS_MTU_EVT, MTU %d", 参数->mtu.mtu); 休息;

        case ESP_GATTS_CONF_EVT:
            ESP_LOGI(TAG_IBLUETOOTH, "ESP_GATTS_CONF_EVT, status = %d", param->conf.status);
            break;

        case ESP_GATTS_START_EVT:
            ESP_LOGI(TAG_IBLUETOOTH, "SERVICE_START_EVT, status %d, service_handle %d", param->start.status, param->start.service_handle);
            break;

        case ESP_GATTS_CONNECT_EVT:
            ESP_LOGI(TAG_IBLUETOOTH, "ESP_GATTS_CONNECT_EVT, conn_id = %d", param->connect.conn_id);
            esp_log_buffer_hex(TAG_IBLUETOOTH, param->connect.remote_bda, 6);
            esp_ble_conn_update_params_t conn_params = {0};
            memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
            /* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */
            conn_params.latency = 0;
            conn_params.max_int = 0x20;    // max_int = 0x20*1.25ms = 40ms
            conn_params.min_int = 0x10;    // min_int = 0x10*1.25ms = 20ms
            conn_params.timeout = 400;    // timeout = 400*10ms = 4000ms
            //start sent the update connection parameters to the peer device.
            esp_ble_gap_update_conn_params(&conn_params);
            break;
        case ESP_GATTS_DISCONNECT_EVT:
            ESP_LOGI(TAG_IBLUETOOTH, "ESP_GATTS_DISCONNECT_EVT, reason = %d", param->disconnect.reason);
            esp_ble_gap_start_advertising(&adv_params);
            break;

        case ESP_GATTS_CREAT_ATTR_TAB_EVT:
            if (param->add_attr_tab.status != ESP_GATT_OK){
                ESP_LOGE(TAG_IBLUETOOTH, "create attribute table failed, error code=0x%x", param->add_attr_tab.status);
            }
            else if (param->add_attr_tab.num_handle != INFO_NB){
                ESP_LOGE(TAG_IBLUETOOTH, "create attribute table abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)", param->add_attr_tab.num_handle, INFO_NB);
            }
            else {
                ESP_LOGI(TAG_IBLUETOOTH, "create attribute table successfully, the number handle = %d\n",param->add_attr_tab.num_handle);
                memcpy(info_handle_table, param->add_attr_tab.handles, sizeof(info_handle_table));
                esp_ble_gatts_start_service(info_handle_table[INFO_SERV]);
            }
            break;

        case ESP_GATTS_STOP_EVT:
        case ESP_GATTS_OPEN_EVT:
        case ESP_GATTS_CANCEL_OPEN_EVT:
        case ESP_GATTS_CLOSE_EVT:
        case ESP_GATTS_LISTEN_EVT:
        case ESP_GATTS_CONGEST_EVT:
        case ESP_GATTS_UNREG_EVT:
        case ESP_GATTS_DELETE_EVT:
        default:
            break;
    }
}

我觉得我做的事情非常明显错误...但是对于执行此过程的任何反馈、示例或建议将不胜感激!

对一直在寻找此问题答案的任何人表示抱歉。我设置了一个 GitHub 存储库,我在其中模块化了 ESP-IDF GATTS 服务 Table 示例。该项目将 ESP-IDF 蓝牙分解为接口、GAP、GATT 服务器和服务。它的好处是服务被分解成单独的文件,因此可以根据提供的模板快速轻松地添加新服务。它仍在进行中,但希望它能帮助任何对 esp-idf 蓝牙示例感到困惑的人。

https://github.com/eagi223/esp-idf_Bluetooth_Multi-Service