mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Compare commits
37 Commits
871d0c8c9a
...
c5eb3eaef5
Author | SHA1 | Date | |
---|---|---|---|
|
c5eb3eaef5 | ||
|
2d02e60d7d | ||
|
b2dae87114 | ||
|
f8a3ba1400 | ||
|
c01e90daa8 | ||
|
d067a99a6c | ||
|
d07c700af8 | ||
|
645dc8addf | ||
|
8fc60de3d0 | ||
|
563641f948 | ||
|
39878b39ce | ||
|
a27a2ddfad | ||
|
856bc4bede | ||
|
0b8553067c | ||
|
f1917f4db3 | ||
|
ea9136226f | ||
|
4c993aeb48 | ||
|
fc754b2033 | ||
|
5e62d99961 | ||
|
bfd1ea82bd | ||
|
a29b207ffd | ||
|
a9d04549ce | ||
|
5f172ab251 | ||
|
d81180b0ec | ||
|
cbd12f5ce5 | ||
|
3da3748e61 | ||
|
f9effecd04 | ||
|
21aa38dcee | ||
|
9cd7e32a5b | ||
|
5e52ea47d1 | ||
|
2d018c5023 | ||
|
e58b10b1f6 | ||
|
4ffd741232 | ||
|
9a3ae21aa7 | ||
|
3e6e68628d | ||
|
fe46bf48ce | ||
|
32545d7edb |
@ -434,14 +434,6 @@ build_examples_cmake_esp32c3:
|
|||||||
IDF_TARGET: esp32c3
|
IDF_TARGET: esp32c3
|
||||||
TEST_DIR: examples
|
TEST_DIR: examples
|
||||||
|
|
||||||
build_examples_cmake_esp32h2:
|
|
||||||
extends:
|
|
||||||
- .build_cmake_template
|
|
||||||
- .rules:build:example_test-esp32h2
|
|
||||||
variables:
|
|
||||||
IDF_TARGET: esp32h2
|
|
||||||
TEST_DIR: examples
|
|
||||||
|
|
||||||
build_test_apps_esp32:
|
build_test_apps_esp32:
|
||||||
extends:
|
extends:
|
||||||
- .build_cmake_template
|
- .build_cmake_template
|
||||||
|
@ -225,8 +225,8 @@ static bt_status_t btc_task_post(btc_msg_t *msg, uint32_t timeout)
|
|||||||
/**
|
/**
|
||||||
* transfer an message to another module in the different task.
|
* transfer an message to another module in the different task.
|
||||||
* @param msg message
|
* @param msg message
|
||||||
* @param arg paramter
|
* @param arg parameter
|
||||||
* @param arg_len length of paramter
|
* @param arg_len length of parameter
|
||||||
* @param copy_func deep copy function
|
* @param copy_func deep copy function
|
||||||
* @param free_func deep free function
|
* @param free_func deep free function
|
||||||
* @return BT_STATUS_SUCCESS: success
|
* @return BT_STATUS_SUCCESS: success
|
||||||
@ -272,7 +272,7 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* transfer an message to another module in tha same task.
|
* transfer an message to another module in the same task.
|
||||||
* @param msg message
|
* @param msg message
|
||||||
* @return BT_STATUS_SUCCESS: success
|
* @return BT_STATUS_SUCCESS: success
|
||||||
* others: fail
|
* others: fail
|
||||||
|
@ -200,8 +200,8 @@ choice BT_CTRL_DFT_TX_POWER_LEVEL
|
|||||||
bool "+15dBm"
|
bool "+15dBm"
|
||||||
config BT_CTRL_DFT_TX_POWER_LEVEL_P18
|
config BT_CTRL_DFT_TX_POWER_LEVEL_P18
|
||||||
bool "+18dBm"
|
bool "+18dBm"
|
||||||
config BT_CTRL_DFT_TX_POWER_LEVEL_P21
|
config BT_CTRL_DFT_TX_POWER_LEVEL_P20
|
||||||
bool "+21dBm"
|
bool "+20dBm"
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
config BT_CTRL_DFT_TX_POWER_LEVEL_EFF
|
config BT_CTRL_DFT_TX_POWER_LEVEL_EFF
|
||||||
@ -221,7 +221,7 @@ config BT_CTRL_DFT_TX_POWER_LEVEL_EFF
|
|||||||
default 12 if BT_CTRL_DFT_TX_POWER_LEVEL_P12
|
default 12 if BT_CTRL_DFT_TX_POWER_LEVEL_P12
|
||||||
default 13 if BT_CTRL_DFT_TX_POWER_LEVEL_P15
|
default 13 if BT_CTRL_DFT_TX_POWER_LEVEL_P15
|
||||||
default 14 if BT_CTRL_DFT_TX_POWER_LEVEL_P18
|
default 14 if BT_CTRL_DFT_TX_POWER_LEVEL_P18
|
||||||
default 15 if BT_CTRL_DFT_TX_POWER_LEVEL_P21
|
default 15 if BT_CTRL_DFT_TX_POWER_LEVEL_P20
|
||||||
default 0
|
default 0
|
||||||
|
|
||||||
config BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP
|
config BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit e652624750341aca124e9f850e261b0c1ac63529
|
Subproject commit e5c7ef0bf701d02c2203e26081fdd348d45935e5
|
@ -1 +1 @@
|
|||||||
Subproject commit d874f55e1132416fe18293ae1aa9ac73c40b3261
|
Subproject commit b8ef2c474d392a88ea7e6626f89acf1fa5f30e4a
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -15,6 +15,24 @@
|
|||||||
|
|
||||||
#if (defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE)
|
#if (defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE)
|
||||||
|
|
||||||
|
static bool esp_sdp_record_integrity_check(esp_bluetooth_sdp_record_t *record)
|
||||||
|
{
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
if (record != NULL) {
|
||||||
|
if (record->hdr.service_name_length > ESP_SDP_SERVER_NAME_MAX ||
|
||||||
|
strlen(record->hdr.service_name) + 1 != record->hdr.service_name_length) {
|
||||||
|
LOG_ERROR("Invalid server name!\n");
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("record is NULL!\n");
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t esp_sdp_register_callback(esp_sdp_cb_t callback)
|
esp_err_t esp_sdp_register_callback(esp_sdp_cb_t callback)
|
||||||
{
|
{
|
||||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||||
@ -85,9 +103,7 @@ esp_err_t esp_sdp_create_record(esp_bluetooth_sdp_record_t *record)
|
|||||||
{
|
{
|
||||||
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
|
||||||
|
|
||||||
if (record == NULL || record->hdr.service_name_length > ESP_SDP_SERVER_NAME_MAX
|
if (!esp_sdp_record_integrity_check(record)) {
|
||||||
|| strlen(record->hdr.service_name)+1 != record->hdr.service_name_length) {
|
|
||||||
LOG_ERROR("Invalid server name!\n");
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +116,7 @@ esp_err_t esp_sdp_create_record(esp_bluetooth_sdp_record_t *record)
|
|||||||
msg.act = BTC_SDP_ACT_CREATE_RECORD;
|
msg.act = BTC_SDP_ACT_CREATE_RECORD;
|
||||||
|
|
||||||
memset(&arg, 0, sizeof(btc_sdp_args_t));
|
memset(&arg, 0, sizeof(btc_sdp_args_t));
|
||||||
arg.creat_record.record = (bluetooth_sdp_record *)record;
|
arg.create_record.record = (bluetooth_sdp_record *)record;
|
||||||
|
|
||||||
/* Switch to BTC context */
|
/* Switch to BTC context */
|
||||||
stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t),
|
stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t),
|
||||||
|
@ -242,6 +242,7 @@ typedef union {
|
|||||||
*/
|
*/
|
||||||
struct gatts_rsp_evt_param {
|
struct gatts_rsp_evt_param {
|
||||||
esp_gatt_status_t status; /*!< Operation status */
|
esp_gatt_status_t status; /*!< Operation status */
|
||||||
|
uint16_t conn_id; /*!< Connection id */
|
||||||
uint16_t handle; /*!< Attribute handle which send response */
|
uint16_t handle; /*!< Attribute handle which send response */
|
||||||
} rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */
|
} rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -17,6 +17,17 @@ extern "C" {
|
|||||||
#define ESP_SDP_SERVER_NAME_MAX 32 /*!< Service name max length */
|
#define ESP_SDP_SERVER_NAME_MAX 32 /*!< Service name max length */
|
||||||
#define SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH 15 /*!< OPP supported format list maximum length */
|
#define SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH 15 /*!< OPP supported format list maximum length */
|
||||||
|
|
||||||
|
#define ESP_SDP_UUID_MAP_MAS 0x1132 /*!< Message Access Service UUID */
|
||||||
|
#define ESP_SDP_UUID_MAP_MNS 0x1133 /*!< Message Notification Service UUID */
|
||||||
|
#define ESP_SDP_UUID_PBAP_PSE 0x112F /*!< Phone Book Server Equipment UUID */
|
||||||
|
#define ESP_SDP_UUID_PBAP_PCE 0x112E /*!< Phone Book Client Equipment UUID */
|
||||||
|
#define ESP_SDP_UUID_OPP 0x1105 /*!< Object Push Profile UUID */
|
||||||
|
#define ESP_SDP_UUID_SAP 0x112D /*!< SIM Access Profile UUID */
|
||||||
|
#define ESP_SDP_UUID_DIP 0x1200 /*!< Device Identification Profile UUID */
|
||||||
|
|
||||||
|
#define ESP_SDP_BUILD_BT_UUID16(uuid16_val) \
|
||||||
|
(esp_bt_uuid_t) { .len = ESP_UUID_LEN_16, .uuid = {.uuid16 = (uint16_t)(uuid16_val),}, }
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ESP_SDP_SUCCESS = 0, /*!< Successful operation. */
|
ESP_SDP_SUCCESS = 0, /*!< Successful operation. */
|
||||||
ESP_SDP_FAILURE, /*!< Generic failure. */
|
ESP_SDP_FAILURE, /*!< Generic failure. */
|
||||||
@ -31,7 +42,7 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ESP_SDP_INIT_EVT = 0, /*!< When SDP is initialized, the event comes */
|
ESP_SDP_INIT_EVT = 0, /*!< When SDP is initialized, the event comes */
|
||||||
ESP_SDP_DEINIT_EVT = 1, /*!< When SDP is deinitialized, the event comes */
|
ESP_SDP_DEINIT_EVT = 1, /*!< When SDP is de-initialized, the event comes */
|
||||||
ESP_SDP_SEARCH_COMP_EVT = 2, /*!< When SDP search complete, the event comes */
|
ESP_SDP_SEARCH_COMP_EVT = 2, /*!< When SDP search complete, the event comes */
|
||||||
ESP_SDP_CREATE_RECORD_COMP_EVT = 3, /*!< When create SDP records complete, the event comes */
|
ESP_SDP_CREATE_RECORD_COMP_EVT = 3, /*!< When create SDP records complete, the event comes */
|
||||||
ESP_SDP_REMOVE_RECORD_COMP_EVT = 4, /*!< When remove a SDP record complete, the event comes */
|
ESP_SDP_REMOVE_RECORD_COMP_EVT = 4, /*!< When remove a SDP record complete, the event comes */
|
||||||
@ -47,27 +58,24 @@ typedef enum {
|
|||||||
ESP_SDP_TYPE_PBAP_PSE, /*!< Phone Book Profile - Server */
|
ESP_SDP_TYPE_PBAP_PSE, /*!< Phone Book Profile - Server */
|
||||||
ESP_SDP_TYPE_PBAP_PCE, /*!< Phone Book Profile - Client */
|
ESP_SDP_TYPE_PBAP_PCE, /*!< Phone Book Profile - Client */
|
||||||
ESP_SDP_TYPE_OPP_SERVER, /*!< Object Push Profile */
|
ESP_SDP_TYPE_OPP_SERVER, /*!< Object Push Profile */
|
||||||
ESP_SDP_TYPE_SAP_SERVER /*!< SIM Access Profile */
|
ESP_SDP_TYPE_SAP_SERVER, /*!< SIM Access Profile */
|
||||||
} esp_bluetooth_sdp_types_t;
|
} esp_bluetooth_sdp_types_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Some signals need additional pointers, hence we introduce a
|
* @brief SDP header structure
|
||||||
* generic way to handle these pointers.
|
|
||||||
*/
|
*/
|
||||||
typedef struct bluetooth_sdp_hdr_overlay {
|
typedef struct bluetooth_sdp_hdr_overlay {
|
||||||
esp_bluetooth_sdp_types_t type; /*!< SDP type */
|
esp_bluetooth_sdp_types_t type; /*!< SDP type */
|
||||||
esp_bt_uuid_t uuid; /*!< UUID type, include uuid and uuid length */
|
esp_bt_uuid_t uuid; /*!< UUID type, include uuid and uuid length, only needed to be set for RAW record creation */
|
||||||
uint32_t service_name_length; /*!< Service name length */
|
uint32_t service_name_length; /*!< Service name length */
|
||||||
char *service_name; /*!< service name */
|
char *service_name; /*!< Service name */
|
||||||
int32_t rfcomm_channel_number; /*!< rfcomm channel number, if not used set to -1*/
|
int32_t rfcomm_channel_number; /*!< RFCOMM channel number, if not used set to -1*/
|
||||||
int32_t l2cap_psm; /*!< l2cap psm, if not used set to -1 */
|
int32_t l2cap_psm; /*!< L2CAP psm, if not used set to -1 */
|
||||||
int32_t profile_version; /*!< profile version */
|
int32_t profile_version; /*!< Profile version */
|
||||||
|
int user1_ptr_len; /*!< User data1 length, only used for searching RAW record */
|
||||||
// User pointers, only used for some signals - see esp_bluetooth_sdp_ops_record_t
|
uint8_t *user1_ptr; /*!< User data1 pointer to the raw SDP response data, only used for searching RAW record */
|
||||||
int user1_ptr_len; /*!< see esp_bluetooth_sdp_ops_record_t */
|
int user2_ptr_len; /*!< User data2 length, only used for searching RAW record */
|
||||||
uint8_t *user1_ptr; /*!< see esp_bluetooth_sdp_ops_record_t */
|
uint8_t *user2_ptr; /*!< User data2 pointer, only used for searching RAW record */
|
||||||
int user2_ptr_len; /*!< see esp_bluetooth_sdp_ops_record_t */
|
|
||||||
uint8_t *user2_ptr; /*!< see esp_bluetooth_sdp_ops_record_t */
|
|
||||||
} esp_bluetooth_sdp_hdr_overlay_t;
|
} esp_bluetooth_sdp_hdr_overlay_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,7 +101,7 @@ typedef struct bluetooth_sdp_mns_record {
|
|||||||
*/
|
*/
|
||||||
typedef struct bluetooth_sdp_pse_record {
|
typedef struct bluetooth_sdp_pse_record {
|
||||||
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
|
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
|
||||||
uint32_t supported_features; /*!< Pbap Supported Features */
|
uint32_t supported_features; /*!< PBAP Supported Features */
|
||||||
uint32_t supported_repositories; /*!< Supported Repositories */
|
uint32_t supported_repositories; /*!< Supported Repositories */
|
||||||
} esp_bluetooth_sdp_pse_record_t;
|
} esp_bluetooth_sdp_pse_record_t;
|
||||||
|
|
||||||
@ -141,32 +149,32 @@ typedef union {
|
|||||||
* @brief ESP_SDP_INIT_EVT
|
* @brief ESP_SDP_INIT_EVT
|
||||||
*/
|
*/
|
||||||
struct sdp_init_evt_param {
|
struct sdp_init_evt_param {
|
||||||
esp_sdp_status_t status; /*!< status */
|
esp_sdp_status_t status; /*!< Status */
|
||||||
} init; /*!< SDP callback param of ESP_SDP_INIT_EVT */
|
} init; /*!< SDP callback param of ESP_SDP_INIT_EVT */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ESP_SDP_DEINIT_EVT
|
* @brief ESP_SDP_DEINIT_EVT
|
||||||
*/
|
*/
|
||||||
struct sdp_deinit_evt_param {
|
struct sdp_deinit_evt_param {
|
||||||
esp_sdp_status_t status; /*!< status */
|
esp_sdp_status_t status; /*!< Status */
|
||||||
} deinit; /*!< SDP callback param of ESP_SDP_DEINIT_EVT */
|
} deinit; /*!< SDP callback param of ESP_SDP_DEINIT_EVT */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ESP_SDP_SEARCH_COMP_EVT
|
* @brief ESP_SDP_SEARCH_COMP_EVT
|
||||||
*/
|
*/
|
||||||
struct sdp_search_evt_param {
|
struct sdp_search_evt_param {
|
||||||
esp_sdp_status_t status; /*!< status */
|
esp_sdp_status_t status; /*!< Status */
|
||||||
esp_bd_addr_t remote_addr; /*!< remote device address */
|
esp_bd_addr_t remote_addr; /*!< Remote device address */
|
||||||
esp_bt_uuid_t sdp_uuid; /*!< service uuid */
|
esp_bt_uuid_t sdp_uuid; /*!< Service uuid */
|
||||||
int record_count; /*!< Number of SDP records */
|
int record_count; /*!< Number of SDP records */
|
||||||
esp_bluetooth_sdp_record_t *records;/*!< SDP records */
|
esp_bluetooth_sdp_record_t *records; /*!< SDP records */
|
||||||
} search; /*!< SDP callback param of ESP_SDP_SEARCH_COMP_EVT */
|
} search; /*!< SDP callback param of ESP_SDP_SEARCH_COMP_EVT */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ESP_SDP_CREATE_RECORD_COMP_EVT
|
* @brief ESP_SDP_CREATE_RECORD_COMP_EVT
|
||||||
*/
|
*/
|
||||||
struct sdp_crate_record_evt_param {
|
struct sdp_create_record_evt_param {
|
||||||
esp_sdp_status_t status; /*!< status */
|
esp_sdp_status_t status; /*!< Status */
|
||||||
int record_handle; /*!< SDP record handle */
|
int record_handle; /*!< SDP record handle */
|
||||||
} create_record; /*!< SDP callback param of ESP_SDP_CREATE_RECORD_COMP_EVT */
|
} create_record; /*!< SDP callback param of ESP_SDP_CREATE_RECORD_COMP_EVT */
|
||||||
|
|
||||||
@ -174,11 +182,10 @@ typedef union {
|
|||||||
* @brief ESP_SDP_REMOVE_RECORD_COMP_EVT
|
* @brief ESP_SDP_REMOVE_RECORD_COMP_EVT
|
||||||
*/
|
*/
|
||||||
struct sdp_remove_record_evt_param {
|
struct sdp_remove_record_evt_param {
|
||||||
esp_sdp_status_t status; /*!< status */
|
esp_sdp_status_t status; /*!< Status */
|
||||||
} remove_record; /*!< SDP callback param of ESP_SDP_REMOVE_RECORD_COMP_EVT */
|
} remove_record; /*!< SDP callback param of ESP_SDP_REMOVE_RECORD_COMP_EVT */
|
||||||
|
|
||||||
} esp_sdp_cb_param_t; /*!< SDP callback parameter union type */
|
} esp_sdp_cb_param_t;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief SDP callback function type.
|
* @brief SDP callback function type.
|
||||||
|
@ -127,9 +127,6 @@ void bta_av_del_rc(tBTA_AV_RCB *p_rcb)
|
|||||||
}
|
}
|
||||||
/* else ACP && connected. do not clear the handle yet */
|
/* else ACP && connected. do not clear the handle yet */
|
||||||
AVRC_Close(rc_handle);
|
AVRC_Close(rc_handle);
|
||||||
if (rc_handle == bta_av_cb.rc_acp_handle) {
|
|
||||||
bta_av_cb.rc_acp_handle = BTA_AV_RC_HANDLE_NONE;
|
|
||||||
}
|
|
||||||
APPL_TRACE_EVENT("end del_rc handle: %d status=0x%x, rc_acp_handle:%d, lidx:%d",
|
APPL_TRACE_EVENT("end del_rc handle: %d status=0x%x, rc_acp_handle:%d, lidx:%d",
|
||||||
p_rcb->handle, p_rcb->status, bta_av_cb.rc_acp_handle, p_rcb->lidx);
|
p_rcb->handle, p_rcb->status, bta_av_cb.rc_acp_handle, p_rcb->lidx);
|
||||||
}
|
}
|
||||||
@ -302,7 +299,7 @@ UINT8 bta_av_rc_create(tBTA_AV_CB *p_cb, UINT8 role, UINT8 shdl, UINT8 lidx)
|
|||||||
bda = p_scb->peer_addr;
|
bda = p_scb->peer_addr;
|
||||||
status = BTA_AV_RC_ROLE_INT;
|
status = BTA_AV_RC_ROLE_INT;
|
||||||
} else {
|
} else {
|
||||||
if ((p_rcb = bta_av_get_rcb_by_shdl(shdl)) != NULL ) {
|
if (shdl != 0 && ((p_rcb = bta_av_get_rcb_by_shdl(shdl)) != NULL)) {
|
||||||
APPL_TRACE_ERROR("bta_av_rc_create ACP handle exist for shdl:%d", shdl);
|
APPL_TRACE_ERROR("bta_av_rc_create ACP handle exist for shdl:%d", shdl);
|
||||||
return p_rcb->handle;
|
return p_rcb->handle;
|
||||||
}
|
}
|
||||||
@ -1143,7 +1140,7 @@ void bta_av_conn_chg(tBTA_AV_DATA *p_data)
|
|||||||
p_data->conn_chg.peer_addr[5]);
|
p_data->conn_chg.peer_addr[5]);
|
||||||
if (p_lcb_rc->conn_msk && bdcmp(p_lcb_rc->addr, p_data->conn_chg.peer_addr) == 0) {
|
if (p_lcb_rc->conn_msk && bdcmp(p_lcb_rc->addr, p_data->conn_chg.peer_addr) == 0) {
|
||||||
/* AVRCP is already connected.
|
/* AVRCP is already connected.
|
||||||
* need to update the association betwen SCB and RCB */
|
* need to update the association between SCB and RCB */
|
||||||
p_lcb_rc->conn_msk = 0; /* indicate RC ONLY is not connected */
|
p_lcb_rc->conn_msk = 0; /* indicate RC ONLY is not connected */
|
||||||
p_lcb_rc->lidx = 0;
|
p_lcb_rc->lidx = 0;
|
||||||
p_scb->rc_handle = p_cb->rc_acp_handle;
|
p_scb->rc_handle = p_cb->rc_acp_handle;
|
||||||
@ -1636,6 +1633,8 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p_cb->rcb[rc_handle].peer_features = peer_features;
|
p_cb->rcb[rc_handle].peer_features = peer_features;
|
||||||
|
p_cb->rcb[rc_handle].peer_ct_features = peer_ct_features;
|
||||||
|
p_cb->rcb[rc_handle].peer_tg_features = peer_tg_features;
|
||||||
rc_feat.rc_handle = rc_handle;
|
rc_feat.rc_handle = rc_handle;
|
||||||
rc_feat.peer_features = peer_features;
|
rc_feat.peer_features = peer_features;
|
||||||
rc_feat.peer_ct_features = peer_ct_features;
|
rc_feat.peer_ct_features = peer_ct_features;
|
||||||
@ -1711,7 +1710,8 @@ void bta_av_rc_closed(tBTA_AV_DATA *p_data)
|
|||||||
bta_av_del_rc(p_rcb);
|
bta_av_del_rc(p_rcb);
|
||||||
|
|
||||||
/* if the AVRCP is no longer listening, create the listening channel */
|
/* if the AVRCP is no longer listening, create the listening channel */
|
||||||
if (bta_av_cb.rc_acp_handle == BTA_AV_RC_HANDLE_NONE && bta_av_cb.features & BTA_AV_FEAT_RCTG) {
|
if (bta_av_cb.rc_acp_handle == p_msg->handle && bta_av_cb.features & BTA_AV_FEAT_RCTG) {
|
||||||
|
bta_av_cb.rc_acp_handle = BTA_AV_RC_HANDLE_NONE;
|
||||||
bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);
|
bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "stack/btm_api.h"
|
#include "stack/btm_api.h"
|
||||||
#include "btm_int.h"
|
#include "btm_int.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
#include "bta/utl.h"
|
#include "bta/utl.h"
|
||||||
#include "osi/allocator.h"
|
#include "osi/allocator.h"
|
||||||
|
|
||||||
@ -996,17 +997,62 @@ tBTA_STATUS BTA_DmSetLocalDiRecord( tBTA_DI_RECORD *p_device_info,
|
|||||||
if (bta_dm_di_cb.di_num < BTA_DI_NUM_MAX) {
|
if (bta_dm_di_cb.di_num < BTA_DI_NUM_MAX) {
|
||||||
if (SDP_SetLocalDiRecord((tSDP_DI_RECORD *)p_device_info, p_handle) == SDP_SUCCESS) {
|
if (SDP_SetLocalDiRecord((tSDP_DI_RECORD *)p_device_info, p_handle) == SDP_SUCCESS) {
|
||||||
if (!p_device_info->primary_record) {
|
if (!p_device_info->primary_record) {
|
||||||
bta_dm_di_cb.di_handle[bta_dm_di_cb.di_num] = *p_handle;
|
for (uint8_t i = 1; i < BTA_DI_NUM_MAX; i++) {
|
||||||
bta_dm_di_cb.di_num ++;
|
if (!bta_dm_di_cb.di_handle[i]) {
|
||||||
|
bta_dm_di_cb.di_handle[i] = *p_handle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bta_dm_di_cb.di_num++;
|
||||||
|
} else if (!bta_dm_di_cb.di_handle[0]) {
|
||||||
|
bta_dm_di_cb.di_handle[0] = *p_handle;
|
||||||
|
bta_dm_di_cb.di_num++;
|
||||||
|
} else {
|
||||||
|
assert(bta_dm_di_cb.di_handle[0] == (*p_handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!bta_dm_di_cb.uuid_added) {
|
||||||
bta_sys_add_uuid(UUID_SERVCLASS_PNP_INFORMATION);
|
bta_sys_add_uuid(UUID_SERVCLASS_PNP_INFORMATION);
|
||||||
|
bta_dm_di_cb.uuid_added = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
status = BTA_SUCCESS;
|
status = BTA_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
** Function BTA_DmRemoveLocalDiRecord
|
||||||
|
**
|
||||||
|
** Description This function removes a DI record from the local SDP database.
|
||||||
|
**
|
||||||
|
** Returns BTA_SUCCESS if record is removed successfully, otherwise error code.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
tBTA_STATUS BTA_DmRemoveLocalDiRecord(UINT32 handle)
|
||||||
|
{
|
||||||
|
tBTA_STATUS status = BTA_FAILURE;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < BTA_DI_NUM_MAX; i++) {
|
||||||
|
if (bta_dm_di_cb.di_handle[i] == handle) {
|
||||||
|
if (SDP_DeleteRecord(handle)) {
|
||||||
|
bta_dm_di_cb.di_handle[i] = 0;
|
||||||
|
bta_dm_di_cb.di_num--;
|
||||||
|
status = BTA_SUCCESS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bta_dm_di_cb.di_num == 0 && bta_dm_di_cb.uuid_added) {
|
||||||
|
bta_sys_remove_uuid(UUID_SERVCLASS_PNP_INFORMATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
#endif ///SDP_INCLUDED == TRUE
|
#endif ///SDP_INCLUDED == TRUE
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
|
@ -1541,6 +1541,7 @@ typedef struct {
|
|||||||
#if (SDP_INCLUDED == TRUE)
|
#if (SDP_INCLUDED == TRUE)
|
||||||
tSDP_DISCOVERY_DB *p_di_db; /* pointer to the DI discovery database */
|
tSDP_DISCOVERY_DB *p_di_db; /* pointer to the DI discovery database */
|
||||||
#endif ///SDP_INCLUDED == TRUE
|
#endif ///SDP_INCLUDED == TRUE
|
||||||
|
BOOLEAN uuid_added;
|
||||||
UINT8 di_num; /* total local DI record number */
|
UINT8 di_num; /* total local DI record number */
|
||||||
UINT32 di_handle[BTA_DI_NUM_MAX]; /* local DI record handle, the first one is primary record */
|
UINT32 di_handle[BTA_DI_NUM_MAX]; /* local DI record handle, the first one is primary record */
|
||||||
} tBTA_DM_DI_CB;
|
} tBTA_DM_DI_CB;
|
||||||
|
@ -2125,6 +2125,17 @@ extern UINT16 BTA_DmGetConnectionState( BD_ADDR bd_addr );
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
extern tBTA_STATUS BTA_DmSetLocalDiRecord( tBTA_DI_RECORD *p_device_info,
|
extern tBTA_STATUS BTA_DmSetLocalDiRecord( tBTA_DI_RECORD *p_device_info,
|
||||||
UINT32 *p_handle );
|
UINT32 *p_handle );
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
** Function BTA_DmRemoveLocalDiRecord
|
||||||
|
**
|
||||||
|
** Description This function removes a DI record from the local SDP database.
|
||||||
|
**
|
||||||
|
** Returns BTA_SUCCESS if record is removed successfully, otherwise error code.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
extern tBTA_STATUS BTA_DmRemoveLocalDiRecord(UINT32 handle);
|
||||||
#endif ///SDP_INCLUDED == TRUE
|
#endif ///SDP_INCLUDED == TRUE
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
|
@ -41,7 +41,7 @@ typedef UINT8 tBTA_SDP_STATUS;
|
|||||||
/* SDP I/F callback events */
|
/* SDP I/F callback events */
|
||||||
/* events received by tBTA_SDP_DM_CBACK */
|
/* events received by tBTA_SDP_DM_CBACK */
|
||||||
#define BTA_SDP_ENABLE_EVT 0 /* SDP service enabled */
|
#define BTA_SDP_ENABLE_EVT 0 /* SDP service enabled */
|
||||||
#define BTA_SDP_DISENABLE_EVT 1 /* SDP service disenabled */
|
#define BTA_SDP_DISABLE_EVT 1 /* SDP service disenabled */
|
||||||
#define BTA_SDP_SEARCH_EVT 2 /* SDP search started */
|
#define BTA_SDP_SEARCH_EVT 2 /* SDP search started */
|
||||||
#define BTA_SDP_SEARCH_COMP_EVT 3 /* SDP search complete */
|
#define BTA_SDP_SEARCH_COMP_EVT 3 /* SDP search complete */
|
||||||
#define BTA_SDP_CREATE_RECORD_USER_EVT 4 /* SDP create record complete */
|
#define BTA_SDP_CREATE_RECORD_USER_EVT 4 /* SDP create record complete */
|
||||||
@ -67,10 +67,17 @@ typedef struct {
|
|||||||
int handle;
|
int handle;
|
||||||
} tBTA_SDP_CREATE_RECORD_USER;
|
} tBTA_SDP_CREATE_RECORD_USER;
|
||||||
|
|
||||||
|
/* data associated with BTA_SDP_REMOVE_RECORD_USER_EVT */
|
||||||
|
typedef struct {
|
||||||
|
tBTA_SDP_STATUS status;
|
||||||
|
int handle;
|
||||||
|
} tBTA_SDP_REMOVE_RECORD_USER;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
tBTA_SDP_STATUS status; /* BTA_SDP_SEARCH_EVT */
|
tBTA_SDP_STATUS status; /* BTA_SDP_SEARCH_EVT */
|
||||||
tBTA_SDP_SEARCH_COMP sdp_search_comp; /* BTA_SDP_SEARCH_COMP_EVT */
|
tBTA_SDP_SEARCH_COMP sdp_search_comp; /* BTA_SDP_SEARCH_COMP_EVT */
|
||||||
tBTA_SDP_CREATE_RECORD_USER sdp_create_record; /* BTA_SDP_CREATE_RECORD_USER_EVT */
|
tBTA_SDP_CREATE_RECORD_USER sdp_create_record; /* BTA_SDP_CREATE_RECORD_USER_EVT */
|
||||||
|
tBTA_SDP_REMOVE_RECORD_USER sdp_remove_record; /* BTA_SDP_REMOVE_RECORD_USER_EVT */
|
||||||
} tBTA_SDP;
|
} tBTA_SDP;
|
||||||
|
|
||||||
/* SDP DM Interface callback */
|
/* SDP DM Interface callback */
|
||||||
@ -78,8 +85,10 @@ typedef void (tBTA_SDP_DM_CBACK)(tBTA_SDP_EVT event, tBTA_SDP *p_data, void *use
|
|||||||
|
|
||||||
/* MCE configuration structure */
|
/* MCE configuration structure */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
UINT16 sdp_raw_size; /* The size of p_sdp_raw_data */
|
||||||
UINT16 sdp_db_size; /* The size of p_sdp_db */
|
UINT16 sdp_db_size; /* The size of p_sdp_db */
|
||||||
#if (SDP_INCLUDED == TRUE)
|
#if (SDP_INCLUDED == TRUE)
|
||||||
|
UINT8 *p_sdp_raw_data; /* The data buffer to keep raw data */
|
||||||
tSDP_DISCOVERY_DB *p_sdp_db; /* The data buffer to keep SDP database */
|
tSDP_DISCOVERY_DB *p_sdp_db; /* The data buffer to keep SDP database */
|
||||||
#endif ///SDP_INCLUDED == TRUE
|
#endif ///SDP_INCLUDED == TRUE
|
||||||
} tBTA_SDP_CFG;
|
} tBTA_SDP_CFG;
|
||||||
@ -108,14 +117,28 @@ extern tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback);
|
|||||||
**
|
**
|
||||||
** Function BTA_SdpDisable
|
** Function BTA_SdpDisable
|
||||||
**
|
**
|
||||||
** Description Disable the SDP search I/F service.
|
** Description This function is used to request a callback to perform disable
|
||||||
|
** operation. The registered callback will be called with event
|
||||||
|
** BTA_SDP_DISABLE_EVT.
|
||||||
|
**
|
||||||
|
** Returns BTA_SDP_SUCCESS, if the request is being processed.
|
||||||
|
** BTA_SDP_FAILURE, otherwise.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
extern tBTA_SDP_STATUS BTA_SdpDisable(void);
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
** Function BTA_SdpCleanup
|
||||||
|
**
|
||||||
|
** Description Cleanup the SDP search I/F service.
|
||||||
** Free buffer for SDP configuration structure.
|
** Free buffer for SDP configuration structure.
|
||||||
**
|
**
|
||||||
** Returns BTA_SDP_SUCCESS if successful.
|
** Returns BTA_SDP_SUCCESS if successful.
|
||||||
** BTA_SDP_FAIL if internal failure.
|
** BTA_SDP_FAIL if internal failure.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
extern tBTA_SDP_STATUS BTA_SdpDisable(void);
|
extern tBTA_SDP_STATUS BTA_SdpCleanup(void);
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
|
@ -53,6 +53,7 @@ const tBTA_SDP_ACTION bta_sdp_action[] = {
|
|||||||
bta_sdp_search, /* BTA_SDP_API_SEARCH_EVT */
|
bta_sdp_search, /* BTA_SDP_API_SEARCH_EVT */
|
||||||
bta_sdp_create_record, /* BTA_SDP_API_CREATE_RECORD_USER_EVT */
|
bta_sdp_create_record, /* BTA_SDP_API_CREATE_RECORD_USER_EVT */
|
||||||
bta_sdp_remove_record, /* BTA_SDP_API_REMOVE_RECORD_USER_EVT */
|
bta_sdp_remove_record, /* BTA_SDP_API_REMOVE_RECORD_USER_EVT */
|
||||||
|
bta_sdp_disable, /* BTA_SDP_API_DISABLE_EVT */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
@ -363,8 +363,8 @@ static void bta_create_raw_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_RE
|
|||||||
|
|
||||||
/* Try to extract a service name */
|
/* Try to extract a service name */
|
||||||
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) {
|
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL) {
|
||||||
record->pse.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
|
record->hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
|
||||||
record->pse.hdr.service_name = (char *)p_attr->attr_value.v.array;
|
record->hdr.service_name = (char *)p_attr->attr_value.v.array;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) {
|
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) {
|
||||||
@ -373,9 +373,9 @@ static void bta_create_raw_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_RE
|
|||||||
|
|
||||||
/* Try to extract an RFCOMM channel */
|
/* Try to extract an RFCOMM channel */
|
||||||
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
|
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
|
||||||
record->pse.hdr.rfcomm_channel_number = pe.params[0];
|
record->hdr.rfcomm_channel_number = pe.params[0];
|
||||||
}
|
}
|
||||||
record->hdr.user1_ptr_len = p_bta_sdp_cfg->p_sdp_db->raw_size;
|
record->hdr.user1_ptr_len = p_bta_sdp_cfg->p_sdp_db->raw_used;
|
||||||
record->hdr.user1_ptr = p_bta_sdp_cfg->p_sdp_db->raw_data;
|
record->hdr.user1_ptr = p_bta_sdp_cfg->p_sdp_db->raw_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,6 +526,10 @@ void bta_sdp_search(tBTA_SDP_MSG *p_data)
|
|||||||
SDP_InitDiscoveryDb (p_bta_sdp_cfg->p_sdp_db, p_bta_sdp_cfg->sdp_db_size, 1,
|
SDP_InitDiscoveryDb (p_bta_sdp_cfg->p_sdp_db, p_bta_sdp_cfg->sdp_db_size, 1,
|
||||||
bta_sdp_search_uuid, 0, NULL);
|
bta_sdp_search_uuid, 0, NULL);
|
||||||
|
|
||||||
|
/* tell SDP to keep the raw data */
|
||||||
|
p_bta_sdp_cfg->p_sdp_db->raw_size = p_bta_sdp_cfg->sdp_raw_size;
|
||||||
|
p_bta_sdp_cfg->p_sdp_db->raw_data = p_bta_sdp_cfg->p_sdp_raw_data;
|
||||||
|
|
||||||
if (!SDP_ServiceSearchAttributeRequest2(p_data->get_search.bd_addr, p_bta_sdp_cfg->p_sdp_db,
|
if (!SDP_ServiceSearchAttributeRequest2(p_data->get_search.bd_addr, p_bta_sdp_cfg->p_sdp_db,
|
||||||
bta_sdp_search_cback, (void *)bta_sdp_search_uuid)) {
|
bta_sdp_search_cback, (void *)bta_sdp_search_uuid)) {
|
||||||
bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_NONE;
|
bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_NONE;
|
||||||
@ -558,7 +562,7 @@ void bta_sdp_create_record(tBTA_SDP_MSG *p_data)
|
|||||||
APPL_TRACE_DEBUG("%s() event: %d\n", __func__, p_data->record.hdr.event);
|
APPL_TRACE_DEBUG("%s() event: %d\n", __func__, p_data->record.hdr.event);
|
||||||
tBTA_SDP_CREATE_RECORD_USER bta_sdp = {0};
|
tBTA_SDP_CREATE_RECORD_USER bta_sdp = {0};
|
||||||
bta_sdp.status = BTA_SDP_SUCCESS;
|
bta_sdp.status = BTA_SDP_SUCCESS;
|
||||||
bta_sdp.handle = (int)p_data->record.user_data;
|
bta_sdp.handle = -1;
|
||||||
if (bta_sdp_cb.p_dm_cback) {
|
if (bta_sdp_cb.p_dm_cback) {
|
||||||
bta_sdp_cb.p_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT, (tBTA_SDP *)&bta_sdp, p_data->record.user_data);
|
bta_sdp_cb.p_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT, (tBTA_SDP *)&bta_sdp, p_data->record.user_data);
|
||||||
}
|
}
|
||||||
@ -576,10 +580,30 @@ void bta_sdp_create_record(tBTA_SDP_MSG *p_data)
|
|||||||
void bta_sdp_remove_record(tBTA_SDP_MSG *p_data)
|
void bta_sdp_remove_record(tBTA_SDP_MSG *p_data)
|
||||||
{
|
{
|
||||||
APPL_TRACE_DEBUG("%s() event: %d\n", __func__, p_data->record.hdr.event);
|
APPL_TRACE_DEBUG("%s() event: %d\n", __func__, p_data->record.hdr.event);
|
||||||
|
tBTA_SDP_REMOVE_RECORD_USER bta_sdp;
|
||||||
|
bta_sdp.status = BTA_SDP_SUCCESS;
|
||||||
|
bta_sdp.handle = -1;
|
||||||
|
if (bta_sdp_cb.p_dm_cback) {
|
||||||
|
bta_sdp_cb.p_dm_cback(BTA_SDP_REMOVE_RECORD_USER_EVT, (tBTA_SDP *)&bta_sdp, p_data->record.user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
** Function bta_sdp_disable
|
||||||
|
**
|
||||||
|
** Description Removes an SDP record
|
||||||
|
**
|
||||||
|
** Returns void
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
void bta_sdp_disable(tBTA_SDP_MSG *p_data)
|
||||||
|
{
|
||||||
|
APPL_TRACE_DEBUG("%s()\n", __func__);
|
||||||
tBTA_SDP bta_sdp;
|
tBTA_SDP bta_sdp;
|
||||||
bta_sdp.status = BTA_SDP_SUCCESS;
|
bta_sdp.status = BTA_SDP_SUCCESS;
|
||||||
if (bta_sdp_cb.p_dm_cback) {
|
if (bta_sdp_cb.p_dm_cback) {
|
||||||
bta_sdp_cb.p_dm_cback(BTA_SDP_REMOVE_RECORD_USER_EVT, &bta_sdp, p_data->record.user_data);
|
bta_sdp_cb.p_dm_cback(BTA_SDP_DISABLE_EVT, &bta_sdp, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,9 @@ tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback)
|
|||||||
#if BTA_DYNAMIC_MEMORY == TRUE
|
#if BTA_DYNAMIC_MEMORY == TRUE
|
||||||
/* Malloc buffer for SDP configuration structure */
|
/* Malloc buffer for SDP configuration structure */
|
||||||
p_bta_sdp_cfg->p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(p_bta_sdp_cfg->sdp_db_size);
|
p_bta_sdp_cfg->p_sdp_db = (tSDP_DISCOVERY_DB *)osi_malloc(p_bta_sdp_cfg->sdp_db_size);
|
||||||
if (p_bta_sdp_cfg->p_sdp_db == NULL) {
|
p_bta_sdp_cfg->p_sdp_raw_data = (UINT8 *)osi_malloc(p_bta_sdp_cfg->sdp_raw_size);
|
||||||
|
if (p_bta_sdp_cfg->p_sdp_db == NULL || p_bta_sdp_cfg->p_sdp_raw_data == NULL) {
|
||||||
|
BTA_SdpCleanup();
|
||||||
return BTA_SDP_FAILURE;
|
return BTA_SDP_FAILURE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -101,15 +103,34 @@ tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback)
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
tBTA_SDP_STATUS BTA_SdpDisable(void)
|
tBTA_SDP_STATUS BTA_SdpDisable(void)
|
||||||
{
|
{
|
||||||
|
BT_HDR *p_buf = NULL;
|
||||||
tBTA_SDP_STATUS status = BTA_SDP_SUCCESS;
|
tBTA_SDP_STATUS status = BTA_SDP_SUCCESS;
|
||||||
|
|
||||||
|
if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) {
|
||||||
|
p_buf->event = BTA_SDP_API_DISABLE_EVT;
|
||||||
|
bta_sys_sendmsg(p_buf);
|
||||||
|
status = BTA_SDP_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
tBTA_SDP_STATUS BTA_SdpCleanup(void)
|
||||||
|
{
|
||||||
bta_sys_deregister(BTA_ID_SDP);
|
bta_sys_deregister(BTA_ID_SDP);
|
||||||
#if BTA_DYNAMIC_MEMORY == TRUE
|
#if BTA_DYNAMIC_MEMORY == TRUE
|
||||||
/* Free buffer for SDP configuration structure */
|
/* Free buffer for SDP configuration structure */
|
||||||
|
if (p_bta_sdp_cfg->p_sdp_db) {
|
||||||
osi_free(p_bta_sdp_cfg->p_sdp_db);
|
osi_free(p_bta_sdp_cfg->p_sdp_db);
|
||||||
p_bta_sdp_cfg->p_sdp_db = NULL;
|
p_bta_sdp_cfg->p_sdp_db = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_bta_sdp_cfg->p_sdp_raw_data) {
|
||||||
|
osi_free(p_bta_sdp_cfg->p_sdp_raw_data);
|
||||||
|
p_bta_sdp_cfg->p_sdp_raw_data = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return (status);
|
return BTA_SDP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
@ -30,16 +30,24 @@
|
|||||||
#define BTA_SDP_DB_SIZE 1500
|
#define BTA_SDP_DB_SIZE 1500
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef BTA_SDP_RAW_DATA_SIZE
|
||||||
|
#define BTA_SDP_RAW_DATA_SIZE 1024
|
||||||
|
#endif
|
||||||
|
|
||||||
#if BTA_DYNAMIC_MEMORY == FALSE
|
#if BTA_DYNAMIC_MEMORY == FALSE
|
||||||
|
static UINT8 bta_sdp_raw_data[BTA_SDP_RAW_DATA_SIZE];
|
||||||
static UINT8 __attribute__ ((aligned(4))) bta_sdp_db_data[BTA_SDP_DB_SIZE];
|
static UINT8 __attribute__ ((aligned(4))) bta_sdp_db_data[BTA_SDP_DB_SIZE];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SDP configuration structure */
|
/* SDP configuration structure */
|
||||||
tBTA_SDP_CFG bta_sdp_cfg = {
|
tBTA_SDP_CFG bta_sdp_cfg = {
|
||||||
|
BTA_SDP_RAW_DATA_SIZE,
|
||||||
BTA_SDP_DB_SIZE,
|
BTA_SDP_DB_SIZE,
|
||||||
#if BTA_DYNAMIC_MEMORY == FALSE
|
#if BTA_DYNAMIC_MEMORY == FALSE
|
||||||
|
bta_sdp_raw_data,
|
||||||
(tSDP_DISCOVERY_DB *)bta_sdp_db_data /* The data buffer to keep SDP database */
|
(tSDP_DISCOVERY_DB *)bta_sdp_db_data /* The data buffer to keep SDP database */
|
||||||
#else
|
#else
|
||||||
|
NULL,
|
||||||
NULL
|
NULL
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -42,6 +42,7 @@ enum {
|
|||||||
BTA_SDP_API_SEARCH_EVT,
|
BTA_SDP_API_SEARCH_EVT,
|
||||||
BTA_SDP_API_CREATE_RECORD_USER_EVT,
|
BTA_SDP_API_CREATE_RECORD_USER_EVT,
|
||||||
BTA_SDP_API_REMOVE_RECORD_USER_EVT,
|
BTA_SDP_API_REMOVE_RECORD_USER_EVT,
|
||||||
|
BTA_SDP_API_DISABLE_EVT,
|
||||||
BTA_SDP_MAX_INT_EVT
|
BTA_SDP_MAX_INT_EVT
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -105,6 +106,7 @@ extern void bta_sdp_enable (tBTA_SDP_MSG *p_data);
|
|||||||
extern void bta_sdp_search (tBTA_SDP_MSG *p_data);
|
extern void bta_sdp_search (tBTA_SDP_MSG *p_data);
|
||||||
extern void bta_sdp_create_record(tBTA_SDP_MSG *p_data);
|
extern void bta_sdp_create_record(tBTA_SDP_MSG *p_data);
|
||||||
extern void bta_sdp_remove_record(tBTA_SDP_MSG *p_data);
|
extern void bta_sdp_remove_record(tBTA_SDP_MSG *p_data);
|
||||||
|
extern void bta_sdp_disable(tBTA_SDP_MSG *p_data);
|
||||||
|
|
||||||
#endif ///SDP_INCLUDED == TRUE
|
#endif ///SDP_INCLUDED == TRUE
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -40,7 +40,7 @@ static void btc_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 c
|
|||||||
** Static variables
|
** Static variables
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
/* flag indicating wheter TG/CT is initialized */
|
/* flag indicating whether TG/CT is initialized */
|
||||||
static uint32_t s_rc_ct_init;
|
static uint32_t s_rc_ct_init;
|
||||||
static uint32_t s_rc_tg_init;
|
static uint32_t s_rc_tg_init;
|
||||||
|
|
||||||
@ -751,7 +751,7 @@ static void btc_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 c
|
|||||||
|
|
||||||
btc_rc_cb.rc_ntf[event_id - 1].registered = TRUE;
|
btc_rc_cb.rc_ntf[event_id - 1].registered = TRUE;
|
||||||
btc_rc_cb.rc_ntf[event_id - 1].label = label;
|
btc_rc_cb.rc_ntf[event_id - 1].label = label;
|
||||||
BTC_TRACE_EVENT("%s: New registerd notification: event_id:0x%x, label:0x%x",
|
BTC_TRACE_EVENT("%s: New register notification: event_id:0x%x, label:0x%x",
|
||||||
__FUNCTION__, event_id, label);
|
__FUNCTION__, event_id, label);
|
||||||
|
|
||||||
// set up callback
|
// set up callback
|
||||||
@ -947,7 +947,7 @@ void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data)
|
|||||||
memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t));
|
memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t));
|
||||||
param.conn_stat.connected = true;
|
param.conn_stat.connected = true;
|
||||||
memcpy(param.conn_stat.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t));
|
memcpy(param.conn_stat.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t));
|
||||||
btc_avrc_tg_cb_to_app(ESP_AVRC_CT_CONNECTION_STATE_EVT, ¶m);
|
btc_avrc_tg_cb_to_app(ESP_AVRC_TG_CONNECTION_STATE_EVT, ¶m);
|
||||||
}
|
}
|
||||||
} while (0);
|
} while (0);
|
||||||
btc_rc_cb.rc_features = p_data->rc_feat.peer_features;
|
btc_rc_cb.rc_features = p_data->rc_feat.peer_features;
|
||||||
@ -1041,7 +1041,7 @@ static void btc_avrc_ct_deinit(void)
|
|||||||
BTC_TRACE_API("## %s ##", __FUNCTION__);
|
BTC_TRACE_API("## %s ##", __FUNCTION__);
|
||||||
|
|
||||||
if (g_a2dp_on_deinit) {
|
if (g_a2dp_on_deinit) {
|
||||||
BTC_TRACE_WARNING("A2DP already deinit, AVRC CT shuold deinit in advance of A2DP !!!");
|
BTC_TRACE_WARNING("A2DP already deinit, AVRC CT should deinit in advance of A2DP !!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s_rc_ct_init != BTC_RC_CT_INIT_MAGIC) {
|
if (s_rc_ct_init != BTC_RC_CT_INIT_MAGIC) {
|
||||||
@ -1255,7 +1255,7 @@ static bt_status_t btc_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code
|
|||||||
BTA_AvRemoteCmd(btc_rc_cb.rc_handle, tl,
|
BTA_AvRemoteCmd(btc_rc_cb.rc_handle, tl,
|
||||||
(tBTA_AV_RC)key_code, (tBTA_AV_STATE)key_state);
|
(tBTA_AV_RC)key_code, (tBTA_AV_STATE)key_state);
|
||||||
status = BT_STATUS_SUCCESS;
|
status = BT_STATUS_SUCCESS;
|
||||||
BTC_TRACE_API("%s: succesfully sent passthrough command to BTA", __FUNCTION__);
|
BTC_TRACE_API("%s: successfully sent passthrough command to BTA", __FUNCTION__);
|
||||||
} else {
|
} else {
|
||||||
status = BT_STATUS_FAIL;
|
status = BT_STATUS_FAIL;
|
||||||
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
|
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
|
||||||
@ -1298,7 +1298,7 @@ static void btc_avrc_tg_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (g_a2dp_on_init) {
|
if (g_a2dp_on_init) {
|
||||||
BTC_TRACE_WARNING("AVRC Taget is expected to be initialized in advance of A2DP !!!");
|
BTC_TRACE_WARNING("AVRC Target is expected to be initialized in advance of A2DP !!!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1320,7 +1320,7 @@ static void btc_avrc_tg_deinit(void)
|
|||||||
BTC_TRACE_API("## %s ##", __FUNCTION__);
|
BTC_TRACE_API("## %s ##", __FUNCTION__);
|
||||||
|
|
||||||
if (g_a2dp_on_deinit) {
|
if (g_a2dp_on_deinit) {
|
||||||
BTC_TRACE_WARNING("A2DP already deinit, AVRC TG shuold deinit in advance of A2DP !!!");
|
BTC_TRACE_WARNING("A2DP already deinit, AVRC TG should deinit in advance of A2DP !!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) {
|
if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -469,7 +469,7 @@ static esp_gatt_status_t btc_gatts_check_valid_attr_tab(esp_gatts_attr_db_t *gat
|
|||||||
if(gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_16 &&
|
if(gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_16 &&
|
||||||
gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_32 &&
|
gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_32 &&
|
||||||
gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_128) {
|
gatts_attr_db[i+1].att_desc.uuid_length != ESP_UUID_LEN_128) {
|
||||||
BTC_TRACE_ERROR("%s, The Charateristic uuid length = %d is invalid", __func__,\
|
BTC_TRACE_ERROR("%s, The Characteristic uuid length = %d is invalid", __func__,\
|
||||||
gatts_attr_db[i+1].att_desc.uuid_length);
|
gatts_attr_db[i+1].att_desc.uuid_length);
|
||||||
return ESP_GATT_INVALID_ATTR_LEN;
|
return ESP_GATT_INVALID_ATTR_LEN;
|
||||||
}
|
}
|
||||||
@ -481,7 +481,7 @@ static esp_gatt_status_t btc_gatts_check_valid_attr_tab(esp_gatts_attr_db_t *gat
|
|||||||
uuid == ESP_GATT_UUID_CHAR_SRVR_CONFIG || uuid == ESP_GATT_UUID_CHAR_PRESENT_FORMAT ||
|
uuid == ESP_GATT_UUID_CHAR_SRVR_CONFIG || uuid == ESP_GATT_UUID_CHAR_PRESENT_FORMAT ||
|
||||||
uuid == ESP_GATT_UUID_CHAR_AGG_FORMAT || uuid == ESP_GATT_UUID_CHAR_VALID_RANGE ||
|
uuid == ESP_GATT_UUID_CHAR_AGG_FORMAT || uuid == ESP_GATT_UUID_CHAR_VALID_RANGE ||
|
||||||
uuid == ESP_GATT_UUID_EXT_RPT_REF_DESCR || uuid == ESP_GATT_UUID_RPT_REF_DESCR) {
|
uuid == ESP_GATT_UUID_EXT_RPT_REF_DESCR || uuid == ESP_GATT_UUID_RPT_REF_DESCR) {
|
||||||
BTC_TRACE_ERROR("%s, The charateristic value uuid = %d is invalid", __func__, uuid);
|
BTC_TRACE_ERROR("%s, The characteristic value uuid = %d is invalid", __func__, uuid);
|
||||||
return ESP_GATT_INVALID_PDU;
|
return ESP_GATT_INVALID_PDU;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,6 +694,7 @@ void btc_gatts_call_handler(btc_msg_t *msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
param.rsp.status = 0;
|
param.rsp.status = 0;
|
||||||
|
param.rsp.conn_id = BTC_GATT_GET_CONN_ID(arg->send_rsp.conn_id);
|
||||||
btc_gatts_cb_to_app(ESP_GATTS_RESPONSE_EVT, BTC_GATT_GET_GATT_IF(arg->send_rsp.conn_id), ¶m);
|
btc_gatts_cb_to_app(ESP_GATTS_RESPONSE_EVT, BTC_GATT_GET_GATT_IF(arg->send_rsp.conn_id), ¶m);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -34,37 +34,25 @@ typedef enum {
|
|||||||
SDP_TYPE_PBAP_PSE, // Phone Book Profile - Server
|
SDP_TYPE_PBAP_PSE, // Phone Book Profile - Server
|
||||||
SDP_TYPE_PBAP_PCE, // Phone Book Profile - Client
|
SDP_TYPE_PBAP_PCE, // Phone Book Profile - Client
|
||||||
SDP_TYPE_OPP_SERVER, // Object Push Profile
|
SDP_TYPE_OPP_SERVER, // Object Push Profile
|
||||||
SDP_TYPE_SAP_SERVER // SIM Access Profile
|
SDP_TYPE_SAP_SERVER, // SIM Access Profile
|
||||||
} bluetooth_sdp_types;
|
} bluetooth_sdp_types;
|
||||||
|
|
||||||
typedef struct _bluetooth_sdp_hdr {
|
|
||||||
bluetooth_sdp_types type;
|
|
||||||
esp_bt_uuid_t uuid;
|
|
||||||
uint32_t service_name_length;
|
|
||||||
char *service_name;
|
|
||||||
int32_t rfcomm_channel_number;
|
|
||||||
int32_t l2cap_psm;
|
|
||||||
int32_t profile_version;
|
|
||||||
} bluetooth_sdp_hdr;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some signals need additional pointers, hence we introduce a
|
* Some signals need additional pointers, hence we introduce a
|
||||||
* generic way to handle these pointers.
|
* generic way to handle these pointers.
|
||||||
*/
|
*/
|
||||||
typedef struct _bluetooth_sdp_hdr_overlay {
|
typedef struct _bluetooth_sdp_hdr_overlay {
|
||||||
bluetooth_sdp_types type;
|
bluetooth_sdp_types type;
|
||||||
esp_bt_uuid_t bt_uuid;
|
esp_bt_uuid_t uuid;
|
||||||
uint32_t service_name_length;
|
uint32_t service_name_length;
|
||||||
char *service_name;
|
char *service_name;
|
||||||
int32_t rfcomm_channel_number;
|
int32_t rfcomm_channel_number;
|
||||||
int32_t l2cap_psm;
|
int32_t l2cap_psm;
|
||||||
int32_t profile_version;
|
int32_t profile_version;
|
||||||
|
|
||||||
// User pointers, only used for some signals - see bluetooth_sdp_ops_record
|
|
||||||
int user1_ptr_len;
|
int user1_ptr_len;
|
||||||
uint8_t *user1_ptr;
|
uint8_t *user1_ptr;
|
||||||
int user2_ptr_len;
|
int user2_ptr_len; // not used
|
||||||
uint8_t *user2_ptr;
|
uint8_t *user2_ptr; // not used
|
||||||
} bluetooth_sdp_hdr_overlay;
|
} bluetooth_sdp_hdr_overlay;
|
||||||
|
|
||||||
typedef struct _bluetooth_sdp_mas_record {
|
typedef struct _bluetooth_sdp_mas_record {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -32,9 +32,9 @@ typedef union {
|
|||||||
} search;
|
} search;
|
||||||
|
|
||||||
//BTC_SDP_ACT_CREATE_RECORD
|
//BTC_SDP_ACT_CREATE_RECORD
|
||||||
struct creat_record_arg {
|
struct create_record_arg {
|
||||||
bluetooth_sdp_record *record;
|
bluetooth_sdp_record *record;
|
||||||
} creat_record;
|
} create_record;
|
||||||
|
|
||||||
//BTC_SDP_ACT_REMOVE_RECORD
|
//BTC_SDP_ACT_REMOVE_RECORD
|
||||||
struct remove_record_arg {
|
struct remove_record_arg {
|
||||||
|
@ -25,12 +25,14 @@ typedef enum {
|
|||||||
} sdp_state_t;
|
} sdp_state_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
sdp_state_t state;
|
uint8_t state;
|
||||||
int sdp_handle;
|
int sdp_handle;
|
||||||
bluetooth_sdp_record* record_data;
|
esp_bt_uuid_t uuid;
|
||||||
|
void* record_data;
|
||||||
} sdp_slot_t;
|
} sdp_slot_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
bool search_allowed;
|
||||||
sdp_slot_t *sdp_slots[SDP_MAX_RECORDS];
|
sdp_slot_t *sdp_slots[SDP_MAX_RECORDS];
|
||||||
osi_mutex_t sdp_slot_mutex;
|
osi_mutex_t sdp_slot_mutex;
|
||||||
} sdp_local_param_t;
|
} sdp_local_param_t;
|
||||||
@ -48,6 +50,21 @@ static sdp_local_param_t *sdp_local_param_ptr;
|
|||||||
#define is_sdp_init() (&sdp_local_param != NULL && sdp_local_param.sdp_slot_mutex != NULL)
|
#define is_sdp_init() (&sdp_local_param != NULL && sdp_local_param.sdp_slot_mutex != NULL)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void btc_sdp_cleanup(void)
|
||||||
|
{
|
||||||
|
#if SDP_DYNAMIC_MEMORY == TRUE
|
||||||
|
if (sdp_local_param_ptr) {
|
||||||
|
#endif
|
||||||
|
if (sdp_local_param.sdp_slot_mutex) {
|
||||||
|
osi_mutex_free(&sdp_local_param.sdp_slot_mutex);
|
||||||
|
sdp_local_param.sdp_slot_mutex = NULL;
|
||||||
|
}
|
||||||
|
#if SDP_DYNAMIC_MEMORY == TRUE
|
||||||
|
osi_free(sdp_local_param_ptr);
|
||||||
|
sdp_local_param_ptr = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static inline void btc_sdp_cb_to_app(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param)
|
static inline void btc_sdp_cb_to_app(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param)
|
||||||
{
|
{
|
||||||
@ -57,46 +74,25 @@ static inline void btc_sdp_cb_to_app(esp_sdp_cb_event_t event, esp_sdp_cb_param_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdp_disable_handler(void)
|
static int get_sdp_record_size(bluetooth_sdp_record* in_record)
|
||||||
{
|
{
|
||||||
btc_msg_t msg;
|
bluetooth_sdp_record *record = in_record;
|
||||||
bt_status_t status;
|
|
||||||
|
|
||||||
msg.sig = BTC_SIG_API_CB;
|
|
||||||
msg.pid = BTC_PID_SDP;
|
|
||||||
msg.act = BTA_SDP_DISENABLE_EVT;
|
|
||||||
|
|
||||||
status = btc_transfer_context(&msg, NULL, 0, NULL, NULL);
|
|
||||||
|
|
||||||
if (status != BT_STATUS_SUCCESS) {
|
|
||||||
BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_sdp_records_size(bluetooth_sdp_record* in_record, int count)
|
|
||||||
{
|
|
||||||
bluetooth_sdp_record* record = in_record;
|
|
||||||
int records_size = 0;
|
int records_size = 0;
|
||||||
|
|
||||||
for(int i = 0; i < count; i++) {
|
|
||||||
record = &in_record[i];
|
|
||||||
records_size += sizeof(bluetooth_sdp_record);
|
records_size += sizeof(bluetooth_sdp_record);
|
||||||
records_size += record->hdr.service_name_length;
|
records_size += record->hdr.service_name_length;
|
||||||
if(record->hdr.service_name_length > 0){
|
if (record->hdr.service_name_length > 0) {
|
||||||
records_size++; /* + '\0' termination of string */
|
records_size++; /* + '\0' termination of string */
|
||||||
}
|
}
|
||||||
records_size += record->hdr.user1_ptr_len;
|
|
||||||
records_size += record->hdr.user2_ptr_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
return records_size;
|
return records_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_sdp_handle(int id, int handle)
|
static void set_sdp_slot_info(int id, int sdp_handle, esp_bt_uuid_t *uuid)
|
||||||
{
|
{
|
||||||
sdp_slot_t *slot = NULL;
|
sdp_slot_t *slot = NULL;
|
||||||
|
|
||||||
BTC_TRACE_DEBUG("%s() id=%d to handle=0x%08x", __func__, id, handle);
|
BTC_TRACE_DEBUG("%s() id=%d to sdp_handle=0x%08x", __func__, id, sdp_handle);
|
||||||
|
|
||||||
if(id >= SDP_MAX_RECORDS) {
|
if(id >= SDP_MAX_RECORDS) {
|
||||||
BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id);
|
BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id);
|
||||||
@ -104,34 +100,64 @@ static void set_sdp_handle(int id, int handle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||||
|
|
||||||
|
do {
|
||||||
slot = sdp_local_param.sdp_slots[id];
|
slot = sdp_local_param.sdp_slots[id];
|
||||||
if (slot == NULL) {
|
if (slot == NULL) {
|
||||||
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
|
BTC_TRACE_ERROR("%s() id = %d ", __func__, id);
|
||||||
BTC_TRACE_ERROR("%s() id=%d to handle=0x%08x, set failed", __func__, id, handle);
|
break;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
slot->sdp_handle = handle;
|
|
||||||
|
if (slot->state != SDP_RECORD_ALLOCED) {
|
||||||
|
BTC_TRACE_ERROR("%s() failed - state for id %d is state = %d expected %d", __func__, id,
|
||||||
|
sdp_local_param.sdp_slots[id]->state, SDP_RECORD_ALLOCED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
slot->sdp_handle = sdp_handle;
|
||||||
|
slot->record_data = NULL;
|
||||||
|
if (uuid) {
|
||||||
|
memcpy(&slot->uuid, uuid, sizeof(esp_bt_uuid_t));
|
||||||
|
} else {
|
||||||
|
memset(&slot->uuid, 0, sizeof(esp_bt_uuid_t));
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
|
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_sdp_slot_info(int id, int *sdp_handle, esp_bt_uuid_t *uuid)
|
||||||
static bool get_sdp_record_by_handle(int handle, bluetooth_sdp_record* record)
|
|
||||||
{
|
{
|
||||||
sdp_slot_t *slot = NULL;
|
sdp_slot_t *slot = NULL;
|
||||||
|
|
||||||
|
if(id >= SDP_MAX_RECORDS) {
|
||||||
|
BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||||
|
|
||||||
for (int i = 0; i < SDP_MAX_RECORDS; i++) {
|
do {
|
||||||
slot = sdp_local_param.sdp_slots[i];
|
slot = sdp_local_param.sdp_slots[id];
|
||||||
if ((slot != NULL) && (slot->sdp_handle == handle)) {
|
if (slot == NULL) {
|
||||||
memcpy(record, slot->record_data, sizeof(bluetooth_sdp_record));
|
break;
|
||||||
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (slot->state != SDP_RECORD_ALLOCED) {
|
||||||
|
BTC_TRACE_ERROR("%s() failed - state for id %d is state = %d expected %d", __func__, id,
|
||||||
|
sdp_local_param.sdp_slots[id]->state, SDP_RECORD_ALLOCED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdp_handle) {
|
||||||
|
*sdp_handle = slot->sdp_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuid) {
|
||||||
|
memcpy(uuid, &slot->uuid, sizeof(esp_bt_uuid_t));
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
|
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_sdp_slot_id_by_handle(int handle)
|
static int get_sdp_slot_id_by_handle(int handle)
|
||||||
@ -152,9 +178,10 @@ static int get_sdp_slot_id_by_handle(int handle)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static sdp_slot_t *start_create_sdp(int id)
|
static bluetooth_sdp_record *start_create_sdp(int id)
|
||||||
{
|
{
|
||||||
sdp_slot_t *sdp_slot = NULL;
|
sdp_slot_t *slot = NULL;
|
||||||
|
bluetooth_sdp_record* record_data = NULL;
|
||||||
|
|
||||||
if(id >= SDP_MAX_RECORDS) {
|
if(id >= SDP_MAX_RECORDS) {
|
||||||
BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id);
|
BTC_TRACE_ERROR("%s() failed - id %d is invalid", __func__, id);
|
||||||
@ -162,62 +189,58 @@ static sdp_slot_t *start_create_sdp(int id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||||
sdp_slot = sdp_local_param.sdp_slots[id];
|
|
||||||
if (sdp_slot == NULL) {
|
do {
|
||||||
|
slot = sdp_local_param.sdp_slots[id];
|
||||||
|
if (slot == NULL) {
|
||||||
BTC_TRACE_ERROR("%s() id = %d ", __func__, id);
|
BTC_TRACE_ERROR("%s() id = %d ", __func__, id);
|
||||||
} else if(sdp_slot->state != SDP_RECORD_ALLOCED) {
|
break;
|
||||||
BTC_TRACE_ERROR("%s() failed - state for id %d is state = %d expected %d", __func__,
|
|
||||||
id, sdp_local_param.sdp_slots[id]->state, SDP_RECORD_ALLOCED);
|
|
||||||
/* The record have been removed before this event occurred - e.g. deinit */
|
|
||||||
sdp_slot = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (slot->state != SDP_RECORD_ALLOCED) {
|
||||||
|
BTC_TRACE_ERROR("%s() failed - state for id %d is state = %d expected %d", __func__, id,
|
||||||
|
sdp_local_param.sdp_slots[id]->state, SDP_RECORD_ALLOCED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
record_data = slot->record_data;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
|
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
|
||||||
|
|
||||||
return sdp_slot;
|
return record_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deep copy all content of in_records into out_records.
|
/* Deep copy all content of in_records into out_records.
|
||||||
* out_records must point to a chunk of memory large enough to contain all
|
* out_records must point to a chunk of memory large enough to contain all
|
||||||
* the data. Use getSdpRecordsSize() to calculate the needed size. */
|
* the data. Use getSdpRecordsSize() to calculate the needed size. */
|
||||||
static void copy_sdp_records(bluetooth_sdp_record* in_records, bluetooth_sdp_record* out_records, int count)
|
static void copy_sdp_record_common(bluetooth_sdp_record* in_record, bluetooth_sdp_record* out_record)
|
||||||
{
|
{
|
||||||
bluetooth_sdp_record *in_record;
|
uint8_t *free_ptr = (uint8_t *)(out_record + 1); /* set pointer to after the last entry */
|
||||||
bluetooth_sdp_record *out_record;
|
|
||||||
char *free_ptr = (char*)(&out_records[count]); /* set pointer to after the last entry */
|
|
||||||
|
|
||||||
for(int i = 0; i < count; i++) {
|
memcpy(out_record, in_record, sizeof(bluetooth_sdp_record));
|
||||||
in_record = &in_records[i];
|
|
||||||
out_record = &out_records[i];
|
|
||||||
*out_record = *in_record;
|
|
||||||
|
|
||||||
if(in_record->hdr.service_name == NULL || in_record->hdr.service_name_length == 0) {
|
if (in_record->hdr.service_name == NULL || in_record->hdr.service_name_length == 0) {
|
||||||
out_record->hdr.service_name = NULL;
|
out_record->hdr.service_name = NULL;
|
||||||
out_record->hdr.service_name_length = 0;
|
out_record->hdr.service_name_length = 0;
|
||||||
} else {
|
} else {
|
||||||
out_record->hdr.service_name = free_ptr; // Update service_name pointer
|
out_record->hdr.service_name = (char *)free_ptr; // Update service_name pointer
|
||||||
// Copy string
|
// Copy string
|
||||||
memcpy(free_ptr, in_record->hdr.service_name, in_record->hdr.service_name_length);
|
memcpy(free_ptr, in_record->hdr.service_name, in_record->hdr.service_name_length);
|
||||||
free_ptr += in_record->hdr.service_name_length;
|
free_ptr += in_record->hdr.service_name_length;
|
||||||
*(free_ptr) = '\0'; // Set '\0' termination of string
|
*(free_ptr) = '\0'; // Set '\0' termination of string
|
||||||
free_ptr++;
|
free_ptr++;
|
||||||
}
|
}
|
||||||
if(in_record->hdr.user1_ptr != NULL) {
|
}
|
||||||
out_record->hdr.user1_ptr = (UINT8*)free_ptr; // Update pointer
|
|
||||||
memcpy(free_ptr, in_record->hdr.user1_ptr, in_record->hdr.user1_ptr_len); // Copy content
|
static void copy_sdp_record(bluetooth_sdp_record* in_record, bluetooth_sdp_record* out_record)
|
||||||
free_ptr += in_record->hdr.user1_ptr_len;
|
{
|
||||||
}
|
copy_sdp_record_common(in_record, out_record);
|
||||||
if(in_record->hdr.user2_ptr != NULL) {
|
|
||||||
out_record->hdr.user2_ptr = (UINT8*)free_ptr; // Update pointer
|
|
||||||
memcpy(free_ptr, in_record->hdr.user2_ptr, in_record->hdr.user2_ptr_len); // Copy content
|
|
||||||
free_ptr += in_record->hdr.user2_ptr_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int alloc_sdp_slot(bluetooth_sdp_record* in_record)
|
static int alloc_sdp_slot(bluetooth_sdp_record* in_record)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int record_size = get_sdp_records_size(in_record, 1);
|
int record_size = get_sdp_record_size(in_record);
|
||||||
bluetooth_sdp_record *record = NULL;
|
bluetooth_sdp_record *record = NULL;
|
||||||
sdp_slot_t **slot = NULL;
|
sdp_slot_t **slot = NULL;
|
||||||
|
|
||||||
@ -227,11 +250,10 @@ static int alloc_sdp_slot(bluetooth_sdp_record* in_record)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy_sdp_records(in_record, record, 1);
|
copy_sdp_record(in_record, record);
|
||||||
|
|
||||||
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
||||||
for(i = 0; i < SDP_MAX_RECORDS; i++)
|
for (i = 0; i < SDP_MAX_RECORDS; i++) {
|
||||||
{
|
|
||||||
slot = &sdp_local_param.sdp_slots[i];
|
slot = &sdp_local_param.sdp_slots[i];
|
||||||
if ((*slot) == NULL) {
|
if ((*slot) == NULL) {
|
||||||
if (((*slot) = (sdp_slot_t *)osi_malloc(sizeof(sdp_slot_t))) == NULL) {
|
if (((*slot) = (sdp_slot_t *)osi_malloc(sizeof(sdp_slot_t))) == NULL) {
|
||||||
@ -292,7 +314,7 @@ static int free_sdp_slot(int id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create a raw SDP record based on information stored in a bluetooth_sdp_raw_record */
|
/* Create a raw SDP record based on information stored in a bluetooth_sdp_raw_record */
|
||||||
static int add_raw_sdp(const bluetooth_sdp_record* rec)
|
static int add_raw_sdp(const bluetooth_sdp_record *rec)
|
||||||
{
|
{
|
||||||
tSDP_PROTOCOL_ELEM protoList [2];
|
tSDP_PROTOCOL_ELEM protoList [2];
|
||||||
UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP;
|
UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP;
|
||||||
@ -301,6 +323,7 @@ static int add_raw_sdp(const bluetooth_sdp_record* rec)
|
|||||||
UINT8 temp[LEN_UUID_128];
|
UINT8 temp[LEN_UUID_128];
|
||||||
UINT8* p_temp = temp;
|
UINT8* p_temp = temp;
|
||||||
UINT32 sdp_handle = 0;
|
UINT32 sdp_handle = 0;
|
||||||
|
const esp_bt_uuid_t *p_uuid = &rec->hdr.uuid;
|
||||||
|
|
||||||
BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__,
|
BTC_TRACE_DEBUG("%s(): scn 0x%02x, psm = 0x%04x\n service name %s", __func__,
|
||||||
rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name);
|
rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name);
|
||||||
@ -310,15 +333,15 @@ static int add_raw_sdp(const bluetooth_sdp_record* rec)
|
|||||||
return sdp_handle;
|
return sdp_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_16) {
|
if (p_uuid->len == ESP_UUID_LEN_16) {
|
||||||
UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES);
|
UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES);
|
||||||
UINT16_TO_BE_STREAM (p_temp, rec->hdr.bt_uuid.uuid.uuid16);
|
UINT16_TO_BE_STREAM (p_temp, p_uuid->uuid.uuid16);
|
||||||
} else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_32) {
|
} else if (p_uuid->len == ESP_UUID_LEN_32) {
|
||||||
UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_FOUR_BYTES);
|
UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_FOUR_BYTES);
|
||||||
UINT32_TO_BE_STREAM (p_temp, rec->hdr.bt_uuid.uuid.uuid32);
|
UINT32_TO_BE_STREAM (p_temp, p_uuid->uuid.uuid32);
|
||||||
} else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_128) {
|
} else if (p_uuid->len == ESP_UUID_LEN_128) {
|
||||||
UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES);
|
UINT8_TO_BE_STREAM (p_temp, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES);
|
||||||
ARRAY_TO_BE_STREAM (p_temp, rec->hdr.bt_uuid.uuid.uuid128, LEN_UUID_128);
|
ARRAY_TO_BE_STREAM (p_temp, p_uuid->uuid.uuid128, LEN_UUID_128);
|
||||||
} else {
|
} else {
|
||||||
SDP_DeleteRecord(sdp_handle);
|
SDP_DeleteRecord(sdp_handle);
|
||||||
sdp_handle = 0;
|
sdp_handle = 0;
|
||||||
@ -357,7 +380,7 @@ static int add_raw_sdp(const bluetooth_sdp_record* rec)
|
|||||||
UINT_DESC_TYPE, (UINT32)2, temp);
|
UINT_DESC_TYPE, (UINT32)2, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the service browseable */
|
/* Make the service browsable */
|
||||||
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@ -365,12 +388,12 @@ static int add_raw_sdp(const bluetooth_sdp_record* rec)
|
|||||||
sdp_handle = 0;
|
sdp_handle = 0;
|
||||||
BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status);
|
BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status);
|
||||||
} else {
|
} else {
|
||||||
if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_16) {
|
if (p_uuid->len == ESP_UUID_LEN_16) {
|
||||||
bta_sys_add_uuid(rec->hdr.bt_uuid.uuid.uuid16);
|
bta_sys_add_uuid(p_uuid->uuid.uuid16);
|
||||||
} else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_32) {
|
} else if (p_uuid->len == ESP_UUID_LEN_32) {
|
||||||
bta_sys_add_uuid_32(rec->hdr.bt_uuid.uuid.uuid32);
|
bta_sys_add_uuid_32(p_uuid->uuid.uuid32);
|
||||||
} else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_128) {
|
} else if (p_uuid->len == ESP_UUID_LEN_128) {
|
||||||
bta_sys_add_uuid_128((UINT8 *)&rec->hdr.bt_uuid.uuid.uuid128);
|
bta_sys_add_uuid_128((UINT8 *)&p_uuid->uuid.uuid128);
|
||||||
}
|
}
|
||||||
BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle);
|
BTC_TRACE_DEBUG("%s(): SDP Registered (handle 0x%08x)", __func__, sdp_handle);
|
||||||
}
|
}
|
||||||
@ -448,7 +471,7 @@ static int add_maps_sdp(const bluetooth_sdp_mas_record* rec)
|
|||||||
UINT_DESC_TYPE, (UINT32)2, temp);
|
UINT_DESC_TYPE, (UINT32)2, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the service browseable */
|
/* Make the service browsable */
|
||||||
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@ -523,7 +546,7 @@ static int add_mapc_sdp(const bluetooth_sdp_mns_record* rec)
|
|||||||
UINT_DESC_TYPE, (UINT32)2, temp);
|
UINT_DESC_TYPE, (UINT32)2, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the service browseable */
|
/* Make the service browsable */
|
||||||
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@ -603,7 +626,7 @@ static int add_pbaps_sdp(const bluetooth_sdp_pse_record* rec)
|
|||||||
UINT_DESC_TYPE, (UINT32)2, temp);
|
UINT_DESC_TYPE, (UINT32)2, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the service browseable */
|
/* Make the service browsable */
|
||||||
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@ -649,7 +672,7 @@ static int add_pbapc_sdp(const bluetooth_sdp_pce_record* rec)
|
|||||||
UUID_SERVCLASS_PHONE_ACCESS,
|
UUID_SERVCLASS_PHONE_ACCESS,
|
||||||
rec->hdr.profile_version);
|
rec->hdr.profile_version);
|
||||||
|
|
||||||
/* Make the service browseable */
|
/* Make the service browsable */
|
||||||
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@ -736,7 +759,7 @@ static int add_opps_sdp(const bluetooth_sdp_ops_record* rec)
|
|||||||
UINT_DESC_TYPE, (UINT32)2, temp);
|
UINT_DESC_TYPE, (UINT32)2, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the service browseable */
|
/* Make the service browsable */
|
||||||
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@ -799,7 +822,7 @@ static int add_saps_sdp(const bluetooth_sdp_sap_record* rec)
|
|||||||
UUID_SERVCLASS_SAP,
|
UUID_SERVCLASS_SAP,
|
||||||
rec->hdr.profile_version);
|
rec->hdr.profile_version);
|
||||||
|
|
||||||
// Make the service browseable
|
// Make the service browsable
|
||||||
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@ -816,64 +839,183 @@ static int add_saps_sdp(const bluetooth_sdp_sap_record* rec)
|
|||||||
|
|
||||||
static int btc_handle_create_record_event(int id)
|
static int btc_handle_create_record_event(int id)
|
||||||
{
|
{
|
||||||
int handle = -1;
|
int sdp_handle = 0;
|
||||||
const sdp_slot_t *sdp_slot = NULL;
|
bluetooth_sdp_record *record = start_create_sdp(id);
|
||||||
|
esp_bt_uuid_t service_uuid = {0};
|
||||||
|
|
||||||
BTC_TRACE_DEBUG("Sdp Server %s", __func__);
|
BTC_TRACE_DEBUG("Sdp Server %s", __func__);
|
||||||
|
|
||||||
sdp_slot = start_create_sdp(id);
|
if (record != NULL) {
|
||||||
if(sdp_slot != NULL) {
|
switch (record->hdr.type) {
|
||||||
bluetooth_sdp_record* record = sdp_slot->record_data;
|
|
||||||
switch(record->hdr.type) {
|
|
||||||
case SDP_TYPE_RAW:
|
case SDP_TYPE_RAW:
|
||||||
handle = add_raw_sdp(record);
|
sdp_handle = add_raw_sdp(record);
|
||||||
|
memcpy(&service_uuid, &record->hdr.uuid, sizeof(esp_bt_uuid_t));
|
||||||
break;
|
break;
|
||||||
case SDP_TYPE_MAP_MAS:
|
case SDP_TYPE_MAP_MAS:
|
||||||
handle = add_maps_sdp(&record->mas);
|
sdp_handle = add_maps_sdp(&record->mas);
|
||||||
|
service_uuid.len = ESP_UUID_LEN_16;
|
||||||
|
service_uuid.uuid.uuid16 = UUID_SERVCLASS_MESSAGE_ACCESS;
|
||||||
break;
|
break;
|
||||||
case SDP_TYPE_MAP_MNS:
|
case SDP_TYPE_MAP_MNS:
|
||||||
handle = add_mapc_sdp(&record->mns);
|
sdp_handle = add_mapc_sdp(&record->mns);
|
||||||
|
service_uuid.len = ESP_UUID_LEN_16;
|
||||||
|
service_uuid.uuid.uuid16 = UUID_SERVCLASS_MESSAGE_NOTIFICATION;
|
||||||
break;
|
break;
|
||||||
case SDP_TYPE_PBAP_PSE:
|
case SDP_TYPE_PBAP_PSE:
|
||||||
handle = add_pbaps_sdp(&record->pse);
|
sdp_handle = add_pbaps_sdp(&record->pse);
|
||||||
|
service_uuid.len = ESP_UUID_LEN_16;
|
||||||
|
service_uuid.uuid.uuid16 = UUID_SERVCLASS_PBAP_PSE;
|
||||||
break;
|
break;
|
||||||
case SDP_TYPE_PBAP_PCE:
|
case SDP_TYPE_PBAP_PCE:
|
||||||
handle = add_pbapc_sdp(&record->pce);
|
sdp_handle = add_pbapc_sdp(&record->pce);
|
||||||
|
service_uuid.len = ESP_UUID_LEN_16;
|
||||||
|
service_uuid.uuid.uuid16 = UUID_SERVCLASS_PBAP_PCE;
|
||||||
break;
|
break;
|
||||||
case SDP_TYPE_OPP_SERVER:
|
case SDP_TYPE_OPP_SERVER:
|
||||||
handle = add_opps_sdp(&record->ops);
|
sdp_handle = add_opps_sdp(&record->ops);
|
||||||
|
service_uuid.len = ESP_UUID_LEN_16;
|
||||||
|
service_uuid.uuid.uuid16 = UUID_SERVCLASS_OBEX_OBJECT_PUSH;
|
||||||
break;
|
break;
|
||||||
case SDP_TYPE_SAP_SERVER:
|
case SDP_TYPE_SAP_SERVER:
|
||||||
handle = add_saps_sdp(&record->sap);
|
sdp_handle = add_saps_sdp(&record->sap);
|
||||||
|
service_uuid.len = ESP_UUID_LEN_16;
|
||||||
|
service_uuid.uuid.uuid16 = UUID_SERVCLASS_SAP;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BTC_TRACE_DEBUG("Record type %d is not supported",record->hdr.type);
|
BTC_TRACE_DEBUG("Record type %d is not supported", record->hdr.type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(handle != -1) {
|
|
||||||
set_sdp_handle(id, handle);
|
if(sdp_handle != 0) {
|
||||||
|
set_sdp_slot_info(id, sdp_handle, &service_uuid);
|
||||||
|
// free the record, since not use it anymore
|
||||||
|
osi_free(record);
|
||||||
|
} else {
|
||||||
|
sdp_handle = -1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
sdp_handle = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle;
|
if (sdp_handle == -1) {
|
||||||
|
free_sdp_slot(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sdp_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool btc_sdp_remove_record_event(int handle)
|
static bool btc_sdp_remove_record_event(int id, int *p_sdp_handle)
|
||||||
{
|
{
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
BTC_TRACE_DEBUG("Sdp Server %s", __func__);
|
BTC_TRACE_DEBUG("Sdp Server %s", __func__);
|
||||||
|
|
||||||
if(handle != -1 && handle != 0) {
|
bool result = false;
|
||||||
result = SDP_DeleteRecord(handle);
|
int sdp_handle = -1;
|
||||||
if(result == false) {
|
esp_bt_uuid_t service_uuid = {0};
|
||||||
BTC_TRACE_ERROR(" Unable to remove handle 0x%08x", handle);
|
|
||||||
|
get_sdp_slot_info(id, &sdp_handle, &service_uuid);
|
||||||
|
|
||||||
|
if (sdp_handle > 0) {
|
||||||
|
do {
|
||||||
|
result = SDP_DeleteRecord(sdp_handle);
|
||||||
|
if (!result) {
|
||||||
|
BTC_TRACE_ERROR("Unable to remove handle 0x%08x", sdp_handle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service_uuid.len == ESP_UUID_LEN_16) {
|
||||||
|
bta_sys_remove_uuid(service_uuid.uuid.uuid16);
|
||||||
|
} else if (service_uuid.len == ESP_UUID_LEN_32) {
|
||||||
|
bta_sys_remove_uuid_32(service_uuid.uuid.uuid32);
|
||||||
|
} else if (service_uuid.len == ESP_UUID_LEN_128) {
|
||||||
|
bta_sys_remove_uuid_128((UINT8 *)&service_uuid.uuid.uuid128);
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
if (p_sdp_handle) {
|
||||||
|
*p_sdp_handle = sdp_handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void btc_sdp_cb_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
|
||||||
|
{
|
||||||
|
switch (msg->act) {
|
||||||
|
case BTA_SDP_SEARCH_COMP_EVT: {
|
||||||
|
tBTA_SDP_SEARCH_COMP *src_search_comp = (tBTA_SDP_SEARCH_COMP *)p_src;
|
||||||
|
tBTA_SDP_SEARCH_COMP *dest_search_comp = (tBTA_SDP_SEARCH_COMP *)p_dest;
|
||||||
|
int record_count = src_search_comp->record_count;
|
||||||
|
|
||||||
|
for (int i = 0; i < record_count; i++) {
|
||||||
|
bluetooth_sdp_record *src_record = &src_search_comp->records[i];
|
||||||
|
bluetooth_sdp_record *dest_record = &dest_search_comp->records[i];
|
||||||
|
// deep copy service name
|
||||||
|
uint32_t src_service_name_length = src_record->hdr.service_name_length;
|
||||||
|
char *src_service_name = src_record->hdr.service_name;
|
||||||
|
dest_record->hdr.service_name_length = 0;
|
||||||
|
dest_record->hdr.service_name = NULL;
|
||||||
|
if (src_service_name && src_service_name_length) {
|
||||||
|
char *service_name = (char *)osi_malloc(src_service_name_length + 1);
|
||||||
|
if (service_name) {
|
||||||
|
memcpy(service_name, src_service_name, src_service_name_length);
|
||||||
|
service_name[src_service_name_length] = '\0';
|
||||||
|
|
||||||
|
dest_record->hdr.service_name_length = src_service_name_length;
|
||||||
|
dest_record->hdr.service_name = service_name;
|
||||||
|
} else {
|
||||||
|
BTC_TRACE_ERROR("%s malloc service name failed, orig service name:%s", __func__, src_service_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deep copy user1_ptr fow RAW type
|
||||||
|
int src_user1_ptr_len = src_record->hdr.user1_ptr_len;
|
||||||
|
uint8_t *src_user1_ptr = src_record->hdr.user1_ptr;
|
||||||
|
dest_record->hdr.user1_ptr_len = 0;
|
||||||
|
dest_record->hdr.user1_ptr = NULL;
|
||||||
|
if (src_record->hdr.type == SDP_TYPE_RAW && src_user1_ptr && src_user1_ptr_len) {
|
||||||
|
uint8_t *user1_ptr = (uint8_t *)osi_malloc(src_user1_ptr_len);
|
||||||
|
if (user1_ptr) {
|
||||||
|
memcpy(user1_ptr, src_user1_ptr, src_user1_ptr_len);
|
||||||
|
|
||||||
|
dest_record->hdr.user1_ptr_len = src_user1_ptr_len;
|
||||||
|
dest_record->hdr.user1_ptr = user1_ptr;
|
||||||
|
} else {
|
||||||
|
BTC_TRACE_ERROR("%s malloc user1_ptr failed", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void btc_sdp_cb_arg_deep_free(btc_msg_t *msg)
|
||||||
|
{
|
||||||
|
switch (msg->act) {
|
||||||
|
case BTA_SDP_SEARCH_COMP_EVT: {
|
||||||
|
tBTA_SDP_SEARCH_COMP *search_comp = (tBTA_SDP_SEARCH_COMP *)msg->arg;
|
||||||
|
for (size_t i = 0; i < search_comp->record_count; i++) {
|
||||||
|
bluetooth_sdp_record *record = &search_comp->records[i];
|
||||||
|
if (record->hdr.service_name) {
|
||||||
|
osi_free(record->hdr.service_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record->hdr.user1_ptr) {
|
||||||
|
osi_free(record->hdr.user1_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void btc_sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP* p_data, void* user_data)
|
static void btc_sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP* p_data, void* user_data)
|
||||||
{
|
{
|
||||||
btc_msg_t msg;
|
btc_msg_t msg;
|
||||||
@ -881,18 +1023,18 @@ static void btc_sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP* p_data, void* user_da
|
|||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case BTA_SDP_CREATE_RECORD_USER_EVT: {
|
case BTA_SDP_CREATE_RECORD_USER_EVT: {
|
||||||
if (p_data->status == BTA_SDP_SUCCESS) {
|
if (p_data->sdp_create_record.status == BTA_SDP_SUCCESS) {
|
||||||
p_data->sdp_create_record.handle = btc_handle_create_record_event((int)user_data);
|
p_data->sdp_create_record.handle = btc_handle_create_record_event((int)user_data);
|
||||||
if (p_data->sdp_create_record.handle < 0) {
|
if (p_data->sdp_create_record.handle < 0) {
|
||||||
p_data->status = BTA_SDP_FAILURE;
|
p_data->sdp_create_record.status = BTA_SDP_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BTA_SDP_REMOVE_RECORD_USER_EVT: {
|
case BTA_SDP_REMOVE_RECORD_USER_EVT: {
|
||||||
if (p_data->status == BTA_SDP_SUCCESS) {
|
if (p_data->sdp_remove_record.status == BTA_SDP_SUCCESS) {
|
||||||
if (btc_sdp_remove_record_event((int)user_data) == false) {
|
if (btc_sdp_remove_record_event((int)user_data, &p_data->sdp_remove_record.handle) == false) {
|
||||||
p_data->status = BTA_SDP_FAILURE;
|
p_data->sdp_remove_record.status = BTA_SDP_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -905,7 +1047,7 @@ static void btc_sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP* p_data, void* user_da
|
|||||||
msg.pid = BTC_PID_SDP;
|
msg.pid = BTC_PID_SDP;
|
||||||
msg.act = event;
|
msg.act = event;
|
||||||
|
|
||||||
status = btc_transfer_context(&msg, p_data, sizeof(tBTA_SDP), NULL, NULL);
|
status = btc_transfer_context(&msg, p_data, sizeof(tBTA_SDP), btc_sdp_cb_arg_deep_copy, btc_sdp_cb_arg_deep_free);
|
||||||
|
|
||||||
if (status != BT_STATUS_SUCCESS) {
|
if (status != BT_STATUS_SUCCESS) {
|
||||||
BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__);
|
BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__);
|
||||||
@ -930,23 +1072,27 @@ static void btc_sdp_init(void)
|
|||||||
ret = ESP_SDP_NO_RESOURCE;
|
ret = ESP_SDP_NO_RESOURCE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
memset((void *)sdp_local_param_ptr, 0, sizeof(sdp_local_param_t));
|
|
||||||
#endif
|
#endif
|
||||||
|
memset(&sdp_local_param, 0, sizeof(sdp_local_param_t));
|
||||||
|
|
||||||
if (osi_mutex_new(&sdp_local_param.sdp_slot_mutex) != 0) {
|
if (osi_mutex_new(&sdp_local_param.sdp_slot_mutex) != 0) {
|
||||||
#if SDP_DYNAMIC_MEMORY == TRUE
|
|
||||||
osi_free(sdp_local_param_ptr);
|
|
||||||
sdp_local_param_ptr = NULL;
|
|
||||||
#endif
|
|
||||||
BTC_TRACE_ERROR("%s osi_mutex_new failed\n", __func__);
|
BTC_TRACE_ERROR("%s osi_mutex_new failed\n", __func__);
|
||||||
ret = ESP_SDP_NO_RESOURCE;
|
ret = ESP_SDP_NO_RESOURCE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = BTA_SdpEnable(btc_sdp_dm_cback);
|
ret = BTA_SdpEnable(btc_sdp_dm_cback);
|
||||||
|
if (ret != ESP_SDP_SUCCESS) {
|
||||||
|
BTC_TRACE_ERROR("%s BTA_SdpEnable failed, ret = %d\n", __func__, ret);
|
||||||
|
ret = ESP_SDP_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdp_local_param.search_allowed = true;
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
if (ret != ESP_SDP_SUCCESS) {
|
if (ret != ESP_SDP_SUCCESS) {
|
||||||
|
btc_sdp_cleanup();
|
||||||
param.init.status = ret;
|
param.init.status = ret;
|
||||||
btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, ¶m);
|
btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, ¶m);
|
||||||
}
|
}
|
||||||
@ -956,7 +1102,6 @@ static void btc_sdp_deinit(void)
|
|||||||
{
|
{
|
||||||
esp_sdp_cb_param_t param;
|
esp_sdp_cb_param_t param;
|
||||||
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
|
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
|
||||||
int handle;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!is_sdp_init()) {
|
if (!is_sdp_init()) {
|
||||||
@ -966,12 +1111,13 @@ static void btc_sdp_deinit(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < SDP_MAX_RECORDS; i++) {
|
for(int i = 0; i < SDP_MAX_RECORDS; i++) {
|
||||||
handle = free_sdp_slot(i);
|
int sdp_handle = -1;
|
||||||
if (handle > 0) {
|
get_sdp_slot_info(i, &sdp_handle, NULL);
|
||||||
BTA_SdpRemoveRecordByUser((void*)handle);
|
if (sdp_handle > 0) {
|
||||||
|
BTA_SdpRemoveRecordByUser((void*)i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sdp_disable_handler();
|
BTA_SdpDisable();
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
if (ret != ESP_SDP_SUCCESS) {
|
if (ret != ESP_SDP_SUCCESS) {
|
||||||
@ -982,7 +1128,7 @@ static void btc_sdp_deinit(void)
|
|||||||
|
|
||||||
static void btc_sdp_create_record(btc_sdp_args_t *arg)
|
static void btc_sdp_create_record(btc_sdp_args_t *arg)
|
||||||
{
|
{
|
||||||
int handle;
|
int slot_id;
|
||||||
esp_sdp_cb_param_t param;
|
esp_sdp_cb_param_t param;
|
||||||
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
|
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
|
||||||
|
|
||||||
@ -993,13 +1139,13 @@ static void btc_sdp_create_record(btc_sdp_args_t *arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = alloc_sdp_slot(arg->creat_record.record);
|
slot_id = alloc_sdp_slot(arg->create_record.record);
|
||||||
if (handle < 0) {
|
if (slot_id < 0) {
|
||||||
ret = ESP_SDP_FAILURE;
|
ret = ESP_SDP_FAILURE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
BTA_SdpCreateRecordByUser((void *) handle);
|
BTA_SdpCreateRecordByUser((void *) slot_id);
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
if (ret != ESP_SDP_SUCCESS) {
|
if (ret != ESP_SDP_SUCCESS) {
|
||||||
@ -1011,7 +1157,6 @@ static void btc_sdp_create_record(btc_sdp_args_t *arg)
|
|||||||
|
|
||||||
static void btc_sdp_remove_record(btc_sdp_args_t *arg)
|
static void btc_sdp_remove_record(btc_sdp_args_t *arg)
|
||||||
{
|
{
|
||||||
int handle;
|
|
||||||
esp_sdp_cb_param_t param;
|
esp_sdp_cb_param_t param;
|
||||||
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
|
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
|
||||||
|
|
||||||
@ -1022,42 +1167,16 @@ static void btc_sdp_remove_record(btc_sdp_args_t *arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bluetooth_sdp_record rec;
|
|
||||||
if (get_sdp_record_by_handle(arg->remove_record.record_handle, &rec)) {
|
|
||||||
if (rec.hdr.bt_uuid.len == ESP_UUID_LEN_16) {
|
|
||||||
bta_sys_remove_uuid(rec.hdr.bt_uuid.uuid.uuid16);
|
|
||||||
} else if (rec.hdr.bt_uuid.len == ESP_UUID_LEN_32) {
|
|
||||||
bta_sys_remove_uuid_32(rec.hdr.bt_uuid.uuid.uuid32);
|
|
||||||
} else if (rec.hdr.bt_uuid.len == ESP_UUID_LEN_128) {
|
|
||||||
bta_sys_remove_uuid_128((UINT8 *)&rec.hdr.bt_uuid.uuid.uuid128);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BTC_TRACE_ERROR("%s SDP record with handle %d not found",
|
|
||||||
__func__, arg->remove_record.record_handle);
|
|
||||||
ret = ESP_SDP_NO_CREATE_RECORD;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the Record handle, and free the slot */
|
/* Get the Record handle, and free the slot */
|
||||||
/* The application layer record_handle is equivalent to the id of the btc layer */
|
/* The application layer record_handle is equivalent to the id of the btc layer */
|
||||||
int slot = get_sdp_slot_id_by_handle(arg->remove_record.record_handle);
|
int slot_id = get_sdp_slot_id_by_handle(arg->remove_record.record_handle);
|
||||||
if (slot < 0) {
|
if (slot_id < 0) {
|
||||||
|
BTC_TRACE_ERROR("%s SDP record with handle %d not found", __func__, arg->remove_record.record_handle);
|
||||||
ret = ESP_SDP_NO_CREATE_RECORD;
|
ret = ESP_SDP_NO_CREATE_RECORD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = free_sdp_slot(slot);
|
BTA_SdpRemoveRecordByUser((void *)slot_id);
|
||||||
|
|
||||||
BTC_TRACE_DEBUG("Sdp Server %s id=%d to handle=0x%08x",
|
|
||||||
__func__, arg->remove_record.record_handle, handle);
|
|
||||||
|
|
||||||
/* Pass the actual record handle */
|
|
||||||
if(handle > 0) {
|
|
||||||
BTA_SdpRemoveRecordByUser((void*) handle);
|
|
||||||
} else {
|
|
||||||
ret = ESP_SDP_NO_CREATE_RECORD;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
if (ret != ESP_SDP_SUCCESS) {
|
if (ret != ESP_SDP_SUCCESS) {
|
||||||
@ -1078,7 +1197,18 @@ static void btc_sdp_search(btc_sdp_args_t *arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!sdp_local_param.search_allowed) {
|
||||||
|
BTC_TRACE_ERROR("%s SDP search is not allowed!", __func__);
|
||||||
|
ret = ESP_SDP_NO_RESOURCE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
BTA_SdpSearch(arg->search.bd_addr, &arg->search.sdp_uuid);
|
BTA_SdpSearch(arg->search.bd_addr, &arg->search.sdp_uuid);
|
||||||
|
/**
|
||||||
|
* ESP_SDP_SEARCH_COMP_EVT will refer service name in BTA sdp database, so it is not allowed to be search until
|
||||||
|
* the previous search is completed
|
||||||
|
*/
|
||||||
|
sdp_local_param.search_allowed = false;
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
if (ret != ESP_SDP_SUCCESS) {
|
if (ret != ESP_SDP_SUCCESS) {
|
||||||
@ -1089,26 +1219,21 @@ static void btc_sdp_search(btc_sdp_args_t *arg)
|
|||||||
|
|
||||||
void btc_sdp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
|
void btc_sdp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
|
||||||
{
|
{
|
||||||
btc_sdp_args_t *dst = (btc_sdp_args_t *)p_dest;
|
|
||||||
btc_sdp_args_t *src = (btc_sdp_args_t *)p_src;
|
|
||||||
|
|
||||||
switch (msg->act) {
|
switch (msg->act) {
|
||||||
case BTC_SDP_ACT_CREATE_RECORD:
|
case BTC_SDP_ACT_CREATE_RECORD: {
|
||||||
dst->creat_record.record = (bluetooth_sdp_record *)osi_calloc(sizeof(bluetooth_sdp_record));
|
bluetooth_sdp_record **dst_record = &((btc_sdp_args_t *)p_dest)->create_record.record;
|
||||||
if (dst->creat_record.record) {
|
bluetooth_sdp_record *src_record = ((btc_sdp_args_t *)p_src)->create_record.record;
|
||||||
memcpy(dst->creat_record.record, src->creat_record.record, sizeof(bluetooth_sdp_record));
|
bluetooth_sdp_record *record = (bluetooth_sdp_record *)osi_calloc(get_sdp_record_size(src_record));
|
||||||
|
if (record) {
|
||||||
|
copy_sdp_record(src_record, record);
|
||||||
} else {
|
} else {
|
||||||
BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act);
|
BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
dst->creat_record.record->hdr.service_name = (char *)osi_calloc(src->creat_record.record->hdr.service_name_length);
|
*dst_record = record;
|
||||||
if (dst->creat_record.record->hdr.service_name) {
|
|
||||||
strcpy(dst->creat_record.record->hdr.service_name, src->creat_record.record->hdr.service_name);
|
|
||||||
} else {
|
|
||||||
BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1116,17 +1241,15 @@ void btc_sdp_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
|
|||||||
|
|
||||||
void btc_sdp_arg_deep_free(btc_msg_t *msg)
|
void btc_sdp_arg_deep_free(btc_msg_t *msg)
|
||||||
{
|
{
|
||||||
btc_sdp_args_t *arg = (btc_sdp_args_t *)msg->arg;
|
|
||||||
|
|
||||||
switch (msg->act) {
|
switch (msg->act) {
|
||||||
case BTC_SDP_ACT_CREATE_RECORD:
|
case BTC_SDP_ACT_CREATE_RECORD: {
|
||||||
if (arg->creat_record.record) {
|
btc_sdp_args_t *arg = (btc_sdp_args_t *)msg->arg;
|
||||||
osi_free(arg->creat_record.record);
|
bluetooth_sdp_record *record = arg->create_record.record;
|
||||||
}
|
if (record) {
|
||||||
if (arg->creat_record.record->hdr.service_name) {
|
osi_free(record);
|
||||||
osi_free(arg->creat_record.record->hdr.service_name);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1172,35 +1295,22 @@ void btc_sdp_cb_handler(btc_msg_t *msg)
|
|||||||
param.init.status = p_data->status;
|
param.init.status = p_data->status;
|
||||||
btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, ¶m);
|
btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, ¶m);
|
||||||
break;
|
break;
|
||||||
case BTA_SDP_DISENABLE_EVT:
|
case BTA_SDP_DISABLE_EVT:
|
||||||
BTA_SdpDisable();
|
BTA_SdpCleanup();
|
||||||
osi_mutex_free(&sdp_local_param.sdp_slot_mutex);
|
btc_sdp_cleanup();
|
||||||
#if SDP_DYNAMIC_MEMORY == TRUE
|
|
||||||
osi_free(sdp_local_param_ptr);
|
|
||||||
sdp_local_param_ptr = NULL;
|
|
||||||
#endif
|
|
||||||
param.deinit.status = ESP_SDP_SUCCESS;
|
param.deinit.status = ESP_SDP_SUCCESS;
|
||||||
btc_sdp_cb_to_app(ESP_SDP_DEINIT_EVT, ¶m);
|
btc_sdp_cb_to_app(ESP_SDP_DEINIT_EVT, ¶m);
|
||||||
break;
|
break;
|
||||||
case BTA_SDP_SEARCH_COMP_EVT:
|
case BTA_SDP_SEARCH_COMP_EVT:
|
||||||
|
// SDP search completed, now can be searched again
|
||||||
|
sdp_local_param.search_allowed = true;
|
||||||
|
|
||||||
param.search.status = p_data->sdp_search_comp.status;
|
param.search.status = p_data->sdp_search_comp.status;
|
||||||
if (param.search.status == ESP_SDP_SUCCESS) {
|
|
||||||
memcpy(param.search.remote_addr, p_data->sdp_search_comp.remote_addr, sizeof(BD_ADDR));
|
memcpy(param.search.remote_addr, p_data->sdp_search_comp.remote_addr, sizeof(BD_ADDR));
|
||||||
memcpy(¶m.search.sdp_uuid, &p_data->sdp_search_comp.uuid, sizeof(tSDP_UUID));
|
memcpy(¶m.search.sdp_uuid, &p_data->sdp_search_comp.uuid, sizeof(tSDP_UUID));
|
||||||
param.search.record_count = p_data->sdp_search_comp.record_count;
|
param.search.record_count = p_data->sdp_search_comp.record_count;
|
||||||
param.search.records = osi_malloc(sizeof(esp_bluetooth_sdp_record_t)*p_data->sdp_search_comp.record_count);
|
param.search.records = (esp_bluetooth_sdp_record_t *)p_data->sdp_search_comp.records;
|
||||||
if (param.search.records != NULL) {
|
|
||||||
memcpy(param.search.records, p_data->sdp_search_comp.records,
|
|
||||||
sizeof(esp_bluetooth_sdp_record_t)*p_data->sdp_search_comp.record_count);
|
|
||||||
} else {
|
|
||||||
BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, event);
|
|
||||||
param.search.status = ESP_SDP_NO_RESOURCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
btc_sdp_cb_to_app(ESP_SDP_SEARCH_COMP_EVT, ¶m);
|
btc_sdp_cb_to_app(ESP_SDP_SEARCH_COMP_EVT, ¶m);
|
||||||
if (param.search.records != NULL) {
|
|
||||||
osi_free(param.search.records);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case BTA_SDP_CREATE_RECORD_USER_EVT:
|
case BTA_SDP_CREATE_RECORD_USER_EVT:
|
||||||
param.create_record.status = p_data->sdp_create_record.status;
|
param.create_record.status = p_data->sdp_create_record.status;
|
||||||
@ -1208,13 +1318,25 @@ void btc_sdp_cb_handler(btc_msg_t *msg)
|
|||||||
btc_sdp_cb_to_app(ESP_SDP_CREATE_RECORD_COMP_EVT, ¶m);
|
btc_sdp_cb_to_app(ESP_SDP_CREATE_RECORD_COMP_EVT, ¶m);
|
||||||
break;
|
break;
|
||||||
case BTA_SDP_REMOVE_RECORD_USER_EVT:
|
case BTA_SDP_REMOVE_RECORD_USER_EVT:
|
||||||
param.remove_record.status = p_data->status;
|
if (p_data->sdp_remove_record.status == BTA_SDP_SUCCESS) {
|
||||||
|
int slot_id = get_sdp_slot_id_by_handle(p_data->sdp_remove_record.handle);
|
||||||
|
if (slot_id < 0) {
|
||||||
|
p_data->sdp_remove_record.status = ESP_SDP_NO_CREATE_RECORD;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
free_sdp_slot(slot_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
param.remove_record.status = p_data->sdp_remove_record.status;
|
||||||
btc_sdp_cb_to_app(ESP_SDP_REMOVE_RECORD_COMP_EVT, ¶m);
|
btc_sdp_cb_to_app(ESP_SDP_REMOVE_RECORD_COMP_EVT, ¶m);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BTC_TRACE_DEBUG("%s: Unhandled event (%d)!", __func__, msg->act);
|
BTC_TRACE_DEBUG("%s: Unhandled event (%d)!", __func__, msg->act);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
btc_sdp_cb_arg_deep_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE
|
#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE
|
||||||
|
@ -1274,7 +1274,7 @@
|
|||||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||||
#define BTM_BLE_ADV_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9}
|
#define BTM_BLE_ADV_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9}
|
||||||
#else
|
#else
|
||||||
#define BTM_BLE_ADV_TX_POWER {-24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21}
|
#define BTM_BLE_ADV_TX_POWER {-24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 20}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1282,7 +1282,7 @@
|
|||||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||||
#define BTM_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9}
|
#define BTM_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9}
|
||||||
#else
|
#else
|
||||||
#define BTM_TX_POWER {-24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21}
|
#define BTM_TX_POWER {-24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 20}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ static void start_up(void)
|
|||||||
#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||||
|
|
||||||
#if (BLE_50_FEATURE_SUPPORT == TRUE && BLE_42_FEATURE_SUPPORT == FALSE)
|
#if (BLE_50_FEATURE_SUPPORT == TRUE && BLE_42_FEATURE_SUPPORT == FALSE)
|
||||||
if (HCI_LE_ENHANCED_PRIVACY_SUPPORTED(controller_param.features_ble.as_array)) {
|
if (HCI_LE_EXT_ADV_SUPPORTED(controller_param.features_ble.as_array)) {
|
||||||
response = AWAIT_COMMAND(controller_param.packet_factory->make_read_max_adv_data_len());
|
response = AWAIT_COMMAND(controller_param.packet_factory->make_read_max_adv_data_len());
|
||||||
controller_param.packet_parser->parse_ble_read_adv_max_len_response(
|
controller_param.packet_parser->parse_ble_read_adv_max_len_response(
|
||||||
response,
|
response,
|
||||||
|
@ -186,7 +186,9 @@ static void parse_ble_read_resolving_list_size_response(
|
|||||||
{
|
{
|
||||||
|
|
||||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_RESOLVING_LIST_SIZE, 1 /* bytes after */);
|
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_RESOLVING_LIST_SIZE, 1 /* bytes after */);
|
||||||
|
if (stream) {
|
||||||
STREAM_TO_UINT8(*resolving_list_size_ptr, stream);
|
STREAM_TO_UINT8(*resolving_list_size_ptr, stream);
|
||||||
|
}
|
||||||
|
|
||||||
osi_free(response);
|
osi_free(response);
|
||||||
}
|
}
|
||||||
@ -198,10 +200,14 @@ static void parse_ble_read_suggested_default_data_length_response(
|
|||||||
{
|
{
|
||||||
|
|
||||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_DEFAULT_DATA_LENGTH, 2 /* bytes after */);
|
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_DEFAULT_DATA_LENGTH, 2 /* bytes after */);
|
||||||
|
if (stream) {
|
||||||
STREAM_TO_UINT16(*ble_default_packet_length_ptr, stream);
|
STREAM_TO_UINT16(*ble_default_packet_length_ptr, stream);
|
||||||
STREAM_TO_UINT16(*ble_default_packet_txtime_ptr, stream);
|
STREAM_TO_UINT16(*ble_default_packet_txtime_ptr, stream);
|
||||||
|
}
|
||||||
|
|
||||||
osi_free(response);
|
osi_free(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||||
static void parse_ble_read_adv_max_len_response(
|
static void parse_ble_read_adv_max_len_response(
|
||||||
BT_HDR *response,
|
BT_HDR *response,
|
||||||
@ -209,8 +215,10 @@ static void parse_ble_read_adv_max_len_response(
|
|||||||
{
|
{
|
||||||
|
|
||||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_RD_MAX_ADV_DATA_LEN, 1 /* bytes after */);
|
uint8_t *stream = read_command_complete_header(response, HCI_BLE_RD_MAX_ADV_DATA_LEN, 1 /* bytes after */);
|
||||||
|
if (stream) {
|
||||||
// Size: 2 Octets ; Value: 0x001F – 0x0672 ; Maximum supported advertising data length
|
// Size: 2 Octets ; Value: 0x001F – 0x0672 ; Maximum supported advertising data length
|
||||||
STREAM_TO_UINT16(*adv_max_len_ptr, stream);
|
STREAM_TO_UINT16(*adv_max_len_ptr, stream);
|
||||||
|
}
|
||||||
|
|
||||||
osi_free(response);
|
osi_free(response);
|
||||||
}
|
}
|
||||||
@ -254,6 +262,7 @@ static uint8_t *read_command_complete_header(
|
|||||||
STREAM_TO_UINT8(status, stream);
|
STREAM_TO_UINT8(status, stream);
|
||||||
|
|
||||||
if (status != HCI_SUCCESS) {
|
if (status != HCI_SUCCESS) {
|
||||||
|
HCI_TRACE_ERROR("%s failed: opcode 0x%04x, status 0x%02x", __func__, opcode, status);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,9 +430,9 @@ BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last)
|
|||||||
tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
|
tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
AVCT_TRACE_WARNING("avct_lcb_last_ccb");
|
AVCT_TRACE_DEBUG("avct_lcb_last_ccb");
|
||||||
for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) {
|
for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) {
|
||||||
AVCT_TRACE_WARNING("%x: aloc:%d, lcb:%p/%p, ccb:%p/%p",
|
AVCT_TRACE_DEBUG("%x: aloc:%d, lcb:%p/%p, ccb:%p/%p",
|
||||||
i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last);
|
i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last);
|
||||||
if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last)) {
|
if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last)) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -1867,42 +1867,50 @@ typedef struct {
|
|||||||
#define HCI_PING_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_PING_OFF] & HCI_EXT_FEATURE_PING_MASK)
|
#define HCI_PING_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_PING_OFF] & HCI_EXT_FEATURE_PING_MASK)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** LE features encoding - page 0 (the only page for now)
|
** LE features encoding - page 0
|
||||||
*/
|
*/
|
||||||
/* LE Encryption */
|
/* LE Encryption: bit 0 */
|
||||||
#define HCI_LE_FEATURE_LE_ENCRYPTION_MASK 0x01
|
#define HCI_LE_FEATURE_LE_ENCRYPTION_MASK 0x01
|
||||||
#define HCI_LE_FEATURE_LE_ENCRYPTION_OFF 0
|
#define HCI_LE_FEATURE_LE_ENCRYPTION_OFF 0
|
||||||
#define HCI_LE_ENCRYPTION_SUPPORTED(x) ((x)[HCI_LE_FEATURE_LE_ENCRYPTION_OFF] & HCI_LE_FEATURE_LE_ENCRYPTION_MASK)
|
#define HCI_LE_ENCRYPTION_SUPPORTED(x) ((x)[HCI_LE_FEATURE_LE_ENCRYPTION_OFF] & HCI_LE_FEATURE_LE_ENCRYPTION_MASK)
|
||||||
|
|
||||||
/* Connection Parameters Request Procedure */
|
/* Connection Parameters Request Procedure: bit 1 */
|
||||||
#define HCI_LE_FEATURE_CONN_PARAM_REQ_MASK 0x02
|
#define HCI_LE_FEATURE_CONN_PARAM_REQ_MASK 0x02
|
||||||
#define HCI_LE_FEATURE_CONN_PARAM_REQ_OFF 0
|
#define HCI_LE_FEATURE_CONN_PARAM_REQ_OFF 0
|
||||||
#define HCI_LE_CONN_PARAM_REQ_SUPPORTED(x) ((x)[HCI_LE_FEATURE_CONN_PARAM_REQ_OFF] & HCI_LE_FEATURE_CONN_PARAM_REQ_MASK)
|
#define HCI_LE_CONN_PARAM_REQ_SUPPORTED(x) ((x)[HCI_LE_FEATURE_CONN_PARAM_REQ_OFF] & HCI_LE_FEATURE_CONN_PARAM_REQ_MASK)
|
||||||
|
|
||||||
/* Extended Reject Indication */
|
/* Extended Reject Indication: bit 2 */
|
||||||
#define HCI_LE_FEATURE_EXT_REJ_IND_MASK 0x04
|
#define HCI_LE_FEATURE_EXT_REJ_IND_MASK 0x04
|
||||||
#define HCI_LE_FEATURE_EXT_REJ_IND_OFF 0
|
#define HCI_LE_FEATURE_EXT_REJ_IND_OFF 0
|
||||||
#define HCI_LE_EXT_REJ_IND_SUPPORTED(x) ((x)[HCI_LE_FEATURE_EXT_REJ_IND_OFF] & HCI_LE_FEATURE_EXT_REJ_IND_MASK)
|
#define HCI_LE_EXT_REJ_IND_SUPPORTED(x) ((x)[HCI_LE_FEATURE_EXT_REJ_IND_OFF] & HCI_LE_FEATURE_EXT_REJ_IND_MASK)
|
||||||
|
|
||||||
/* Slave-initiated Features Exchange */
|
/* Slave-initiated Features Exchange: bit 3 */
|
||||||
#define HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_MASK 0x08
|
#define HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_MASK 0x08
|
||||||
#define HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_OFF 0
|
#define HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_OFF 0
|
||||||
#define HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(x) ((x)[HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_OFF] & HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_MASK)
|
#define HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(x) ((x)[HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_OFF] & HCI_LE_FEATURE_SLAVE_INIT_FEAT_EXC_MASK)
|
||||||
|
|
||||||
|
/* LE Data Packet Length Extension: bit 5 */
|
||||||
|
#define HCI_LE_FEATURE_DATA_LEN_EXT_MASK 0x20
|
||||||
|
#define HCI_LE_FEATURE_DATA_LEN_EXT_OFF 0
|
||||||
|
#define HCI_LE_DATA_LEN_EXT_SUPPORTED(x) ((x)[HCI_LE_FEATURE_DATA_LEN_EXT_OFF] & HCI_LE_FEATURE_DATA_LEN_EXT_MASK)
|
||||||
|
|
||||||
/* Enhanced privacy Feature: bit 6 */
|
/* Enhanced privacy Feature: bit 6 */
|
||||||
#define HCI_LE_FEATURE_ENHANCED_PRIVACY_MASK 0x40
|
#define HCI_LE_FEATURE_ENHANCED_PRIVACY_MASK 0x40
|
||||||
#define HCI_LE_FEATURE_ENHANCED_PRIVACY_OFF 0
|
#define HCI_LE_FEATURE_ENHANCED_PRIVACY_OFF 0
|
||||||
#define HCI_LE_ENHANCED_PRIVACY_SUPPORTED(x) ((x)[HCI_LE_FEATURE_ENHANCED_PRIVACY_OFF] & HCI_LE_FEATURE_ENHANCED_PRIVACY_MASK)
|
#define HCI_LE_ENHANCED_PRIVACY_SUPPORTED(x) ((x)[HCI_LE_FEATURE_ENHANCED_PRIVACY_OFF] & HCI_LE_FEATURE_ENHANCED_PRIVACY_MASK)
|
||||||
|
|
||||||
/* Extended scanner filter policy : 7 */
|
/* Extended scanner filter policy: bit 7 */
|
||||||
#define HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_MASK 0x80
|
#define HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_MASK 0x80
|
||||||
#define HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_OFF 0
|
#define HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_OFF 0
|
||||||
#define HCI_LE_EXT_SCAN_FILTER_POLICY_SUPPORTED(x) ((x)[HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_OFF] & HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_MASK)
|
#define HCI_LE_EXT_SCAN_FILTER_POLICY_SUPPORTED(x) ((x)[HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_OFF] & HCI_LE_FEATURE_EXT_SCAN_FILTER_POLICY_MASK)
|
||||||
|
|
||||||
/* Slave-initiated Features Exchange */
|
/*
|
||||||
#define HCI_LE_FEATURE_DATA_LEN_EXT_MASK 0x20
|
** LE features encoding - page 1
|
||||||
#define HCI_LE_FEATURE_DATA_LEN_EXT_OFF 0
|
*/
|
||||||
#define HCI_LE_DATA_LEN_EXT_SUPPORTED(x) ((x)[HCI_LE_FEATURE_DATA_LEN_EXT_OFF] & HCI_LE_FEATURE_DATA_LEN_EXT_MASK)
|
/* LE Extended Advertising: bit 12 */
|
||||||
|
#define HCI_LE_FEATURE_EXT_ADV_MASK 0x10
|
||||||
|
#define HCI_LE_FEATURE_EXT_ADV_OFF 1
|
||||||
|
#define HCI_LE_EXT_ADV_SUPPORTED(x) ((x)[HCI_LE_FEATURE_EXT_ADV_OFF] & HCI_LE_FEATURE_EXT_ADV_MASK)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Local Supported Commands encoding
|
** Local Supported Commands encoding
|
||||||
|
@ -276,7 +276,7 @@ static int sdp_compose_proto_list( UINT8 *p, UINT16 num_elem,
|
|||||||
**
|
**
|
||||||
** Description This function is called to create a record in the database.
|
** Description This function is called to create a record in the database.
|
||||||
** This would be through the SDP database maintenance API. The
|
** This would be through the SDP database maintenance API. The
|
||||||
** record is created empty, teh application should then call
|
** record is created empty, the application should then call
|
||||||
** "add_attribute" to add the record's attributes.
|
** "add_attribute" to add the record's attributes.
|
||||||
**
|
**
|
||||||
** Returns Record handle if OK, else 0.
|
** Returns Record handle if OK, else 0.
|
||||||
@ -293,7 +293,7 @@ UINT32 SDP_CreateRecord (void)
|
|||||||
|
|
||||||
/* First, check if there is a free record */
|
/* First, check if there is a free record */
|
||||||
if (p_db->num_records < SDP_MAX_RECORDS) {
|
if (p_db->num_records < SDP_MAX_RECORDS) {
|
||||||
p_rec =(tSDP_RECORD *)osi_malloc(sizeof(tSDP_RECORD));
|
p_rec = (tSDP_RECORD *)osi_malloc(sizeof(tSDP_RECORD));
|
||||||
if (p_rec) {
|
if (p_rec) {
|
||||||
memset(p_rec, 0, sizeof(tSDP_RECORD));
|
memset(p_rec, 0, sizeof(tSDP_RECORD));
|
||||||
/* Save previous rec */
|
/* Save previous rec */
|
||||||
@ -321,10 +321,12 @@ UINT32 SDP_CreateRecord (void)
|
|||||||
4, buf);
|
4, buf);
|
||||||
|
|
||||||
return (p_rec->record_handle);
|
return (p_rec->record_handle);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
SDP_TRACE_ERROR("SDP_CreateRecord fail, memory allocation failed\n");
|
SDP_TRACE_ERROR("SDP_CreateRecord fail, memory allocation failed\n");
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
SDP_TRACE_ERROR("SDP_CreateRecord fail, exceed maximum records:%d\n", SDP_MAX_RECORDS);
|
SDP_TRACE_ERROR("SDP_CreateRecord fail, exceed maximum records:%d\n", SDP_MAX_RECORDS);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -354,7 +356,7 @@ BOOLEAN SDP_DeleteRecord (UINT32 handle)
|
|||||||
if (handle == 0 || sdp_cb.server_db.num_records == 0) {
|
if (handle == 0 || sdp_cb.server_db.num_records == 0) {
|
||||||
/* Delete all records in the database */
|
/* Delete all records in the database */
|
||||||
sdp_cb.server_db.num_records = 0;
|
sdp_cb.server_db.num_records = 0;
|
||||||
for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
|
for (p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
|
||||||
list_remove(sdp_cb.server_db.p_record_list, p_node);
|
list_remove(sdp_cb.server_db.p_record_list, p_node);
|
||||||
}
|
}
|
||||||
/* require new DI record to be created in SDP_SetLocalDiRecord */
|
/* require new DI record to be created in SDP_SetLocalDiRecord */
|
||||||
@ -363,7 +365,7 @@ BOOLEAN SDP_DeleteRecord (UINT32 handle)
|
|||||||
return (TRUE);
|
return (TRUE);
|
||||||
} else {
|
} else {
|
||||||
/* Find the record in the database */
|
/* Find the record in the database */
|
||||||
for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
|
for (p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
|
||||||
p_rec = list_node(p_node);
|
p_rec = list_node(p_node);
|
||||||
if (p_rec->record_handle == handle) {
|
if (p_rec->record_handle == handle) {
|
||||||
/* Found it. Shift everything up one */
|
/* Found it. Shift everything up one */
|
||||||
@ -374,7 +376,7 @@ BOOLEAN SDP_DeleteRecord (UINT32 handle)
|
|||||||
SDP_TRACE_DEBUG("SDP_DeleteRecord ok, num_records:%d\n", sdp_cb.server_db.num_records);
|
SDP_TRACE_DEBUG("SDP_DeleteRecord ok, num_records:%d\n", sdp_cb.server_db.num_records);
|
||||||
/* if we're deleting the primary DI record, clear the */
|
/* if we're deleting the primary DI record, clear the */
|
||||||
/* value in the control block */
|
/* value in the control block */
|
||||||
if ( sdp_cb.server_db.di_primary_handle == handle ) {
|
if (sdp_cb.server_db.di_primary_handle == handle) {
|
||||||
sdp_cb.server_db.di_primary_handle = 0;
|
sdp_cb.server_db.di_primary_handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,6 +338,12 @@ static void controller_rcv_pkt_ready(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dummy_controller_rcv_pkt_ready(void)
|
||||||
|
{
|
||||||
|
/* Dummy function */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void bt_record_hci_data(uint8_t *data, uint16_t len)
|
void bt_record_hci_data(uint8_t *data, uint16_t len)
|
||||||
{
|
{
|
||||||
#if (BT_HCI_LOG_INCLUDED == TRUE)
|
#if (BT_HCI_LOG_INCLUDED == TRUE)
|
||||||
@ -407,11 +413,23 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dummy_host_rcv_pkt(uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
/* Dummy function */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const esp_vhci_host_callback_t vhci_host_cb = {
|
static const esp_vhci_host_callback_t vhci_host_cb = {
|
||||||
.notify_host_send_available = controller_rcv_pkt_ready,
|
.notify_host_send_available = controller_rcv_pkt_ready,
|
||||||
.notify_host_recv = host_rcv_pkt,
|
.notify_host_recv = host_rcv_pkt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const esp_vhci_host_callback_t dummy_vhci_host_cb = {
|
||||||
|
.notify_host_send_available = dummy_controller_rcv_pkt_ready,
|
||||||
|
.notify_host_recv = dummy_host_rcv_pkt,
|
||||||
|
};
|
||||||
|
|
||||||
static void ble_buf_free(void)
|
static void ble_buf_free(void)
|
||||||
{
|
{
|
||||||
os_msys_buf_free();
|
os_msys_buf_free();
|
||||||
@ -520,6 +538,11 @@ esp_err_t esp_nimble_hci_deinit(void)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = esp_vhci_host_register_callback(&dummy_vhci_host_cb);
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ble_buf_free();
|
ble_buf_free();
|
||||||
|
|
||||||
#if MYNEWT_VAL(BLE_QUEUE_CONG_CHECK)
|
#if MYNEWT_VAL(BLE_QUEUE_CONG_CHECK)
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit e2522d60d9ffe4fefae1b4a9f8b47ddf02e65f2d
|
Subproject commit ebaa0fc73229eb89adcc816a5db13d13b4c41818
|
@ -386,7 +386,8 @@ typedef enum {
|
|||||||
ESP_PWR_LVL_P12 = 12, /*!< Corresponding to +12dbm */
|
ESP_PWR_LVL_P12 = 12, /*!< Corresponding to +12dbm */
|
||||||
ESP_PWR_LVL_P15 = 13, /*!< Corresponding to +15dbm */
|
ESP_PWR_LVL_P15 = 13, /*!< Corresponding to +15dbm */
|
||||||
ESP_PWR_LVL_P18 = 14, /*!< Corresponding to +18dbm */
|
ESP_PWR_LVL_P18 = 14, /*!< Corresponding to +18dbm */
|
||||||
ESP_PWR_LVL_P21 = 15, /*!< Corresponding to +21dbm */
|
ESP_PWR_LVL_P20 = 15, /*!< Corresponding to +20dbm */
|
||||||
|
ESP_PWR_LVL_P21 = 15, /*!< Corresponding to +20dbm, this enum variable has been deprecated */
|
||||||
ESP_PWR_LVL_INVALID = 0xFF, /*!< Indicates an invalid value */
|
ESP_PWR_LVL_INVALID = 0xFF, /*!< Indicates an invalid value */
|
||||||
} esp_power_level_t;
|
} esp_power_level_t;
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ hci_driver_uart_deinit(void)
|
|||||||
|
|
||||||
ESP_ERROR_CHECK(uart_driver_delete(s_hci_driver_uart_env.hci_uart_params->hci_uart_port));
|
ESP_ERROR_CHECK(uart_driver_delete(s_hci_driver_uart_env.hci_uart_params->hci_uart_port));
|
||||||
|
|
||||||
if (!s_hci_driver_uart_env.tx_sem) {
|
if (s_hci_driver_uart_env.tx_sem) {
|
||||||
vSemaphoreDelete(s_hci_driver_uart_env.tx_sem);
|
vSemaphoreDelete(s_hci_driver_uart_env.tx_sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,24 +351,6 @@ bool rtc_dig_8m_enabled(void)
|
|||||||
return clk_ll_rc_fast_digi_is_enabled();
|
return clk_ll_rc_fast_digi_is_enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for bootloader not calibrated well issue.
|
|
||||||
// Placed in IRAM because disabling BBPLL may influence the cache
|
|
||||||
void rtc_clk_recalib_bbpll(void)
|
|
||||||
{
|
|
||||||
rtc_cpu_freq_config_t old_config;
|
|
||||||
rtc_clk_cpu_freq_get_config(&old_config);
|
|
||||||
|
|
||||||
// There are two paths we arrive here: 1. CPU reset. 2. Other reset reasons.
|
|
||||||
// - For other reasons, the bootloader will set CPU source to BBPLL and enable it. But there are calibration issues.
|
|
||||||
// Turn off the BBPLL and do calibration again to fix the issue.
|
|
||||||
// - For CPU reset, the CPU source will be set to XTAL, while the BBPLL is kept to meet USB Serial JTAG's
|
|
||||||
// requirements. In this case, we don't touch BBPLL to avoid USJ disconnection.
|
|
||||||
if (old_config.source == SOC_CPU_CLK_SRC_PLL) {
|
|
||||||
rtc_clk_cpu_freq_set_xtal();
|
|
||||||
rtc_clk_cpu_freq_set_config(&old_config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Name used in libphy.a:phy_chip_v7.o
|
/* Name used in libphy.a:phy_chip_v7.o
|
||||||
* TODO: update the library to use rtc_clk_xtal_freq_get
|
* TODO: update the library to use rtc_clk_xtal_freq_get
|
||||||
*/
|
*/
|
||||||
|
@ -176,7 +176,7 @@ static void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq)
|
|||||||
*/
|
*/
|
||||||
static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
|
static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
|
||||||
{
|
{
|
||||||
/* There are totally 6 LDO slaves(all on by default). At the moment of swithing LDO slave, LDO voltage will also change instantaneously.
|
/* There are totally 6 LDO slaves(all on by default). At the moment of switching LDO slave, LDO voltage will also change instantaneously.
|
||||||
* LDO slave can reduce the voltage change caused by switching frequency.
|
* LDO slave can reduce the voltage change caused by switching frequency.
|
||||||
* CPU frequency <= 40M : just open 3 LDO slaves; CPU frequency = 80M : open 4 LDO slaves; CPU frequency = 160M : open 5 LDO slaves; CPU frequency = 240M : open 6 LDO slaves;
|
* CPU frequency <= 40M : just open 3 LDO slaves; CPU frequency = 80M : open 4 LDO slaves; CPU frequency = 160M : open 5 LDO slaves; CPU frequency = 240M : open 6 LDO slaves;
|
||||||
*
|
*
|
||||||
@ -459,25 +459,6 @@ static bool rtc_clk_set_bbpll_always_on(void)
|
|||||||
return is_bbpll_on;
|
return is_bbpll_on;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for bootloader not calibrated well issue.
|
|
||||||
// Placed in IRAM because disabling BBPLL may influence the cache
|
|
||||||
void rtc_clk_recalib_bbpll(void)
|
|
||||||
{
|
|
||||||
rtc_cpu_freq_config_t old_config;
|
|
||||||
rtc_clk_cpu_freq_get_config(&old_config);
|
|
||||||
|
|
||||||
// There are two paths we arrive here: 1. CPU reset. 2. Other reset reasons.
|
|
||||||
// - For other reasons, the bootloader will set CPU source to BBPLL and enable it. But there are calibration issues.
|
|
||||||
// Turn off the BBPLL and do calibration again to fix the issue.
|
|
||||||
// - For CPU reset, the CPU source will be set to XTAL, while the BBPLL is kept to meet USB Serial JTAG's
|
|
||||||
// requirements. In this case, we don't touch BBPLL to avoid USJ disconnection.
|
|
||||||
if (old_config.source == SOC_CPU_CLK_SRC_PLL) {
|
|
||||||
rtc_clk_cpu_freq_set_xtal();
|
|
||||||
rtc_clk_cpu_freq_set_config(&old_config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Name used in libphy.a:phy_chip_v7.o
|
/* Name used in libphy.a:phy_chip_v7.o
|
||||||
* TODO: update the library to use rtc_clk_xtal_freq_get
|
* TODO: update the library to use rtc_clk_xtal_freq_get
|
||||||
*/
|
*/
|
||||||
|
@ -510,6 +510,13 @@ static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer)
|
|||||||
return pdFALSE; //Byte buffers do not allow multiple retrievals before return
|
return pdFALSE; //Byte buffers do not allow multiple retrievals before return
|
||||||
}
|
}
|
||||||
if ((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))) {
|
if ((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))) {
|
||||||
|
// If the ring buffer is a no-split buffer, the read pointer must point to an item that has been written to.
|
||||||
|
if ((pxRingbuffer->uxRingbufferFlags & (rbBYTE_BUFFER_FLAG | rbALLOW_SPLIT_FLAG)) == 0) {
|
||||||
|
ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead;
|
||||||
|
if ((pxHeader->uxItemFlags & rbITEM_WRITTEN_FLAG) == 0) {
|
||||||
|
return pdFALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
return pdTRUE; //Items/data available for retrieval
|
return pdTRUE; //Items/data available for retrieval
|
||||||
} else {
|
} else {
|
||||||
return pdFALSE; //No items/data available for retrieval
|
return pdFALSE; //No items/data available for retrieval
|
||||||
@ -926,9 +933,6 @@ BaseType_t xRingbufferSendAcquire(RingbufHandle_t xRingbuffer, void **ppvItem, s
|
|||||||
if (xItemSize > pxRingbuffer->xMaxItemSize) {
|
if (xItemSize > pxRingbuffer->xMaxItemSize) {
|
||||||
return pdFALSE; //Data will never ever fit in the queue.
|
return pdFALSE; //Data will never ever fit in the queue.
|
||||||
}
|
}
|
||||||
if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && xItemSize == 0) {
|
|
||||||
return pdTRUE; //Sending 0 bytes to byte buffer has no effect
|
|
||||||
}
|
|
||||||
|
|
||||||
//Attempt to send an item
|
//Attempt to send an item
|
||||||
BaseType_t xReturn = pdFALSE;
|
BaseType_t xReturn = pdFALSE;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -804,6 +804,8 @@ TEST_CASE("Test ring buffer ISR", "[esp_ringbuf]")
|
|||||||
* tested.
|
* tested.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
|
|
||||||
#define SRAND_SEED 3 //Arbitrarily chosen srand() seed
|
#define SRAND_SEED 3 //Arbitrarily chosen srand() seed
|
||||||
#define SMP_TEST_ITERATIONS 4
|
#define SMP_TEST_ITERATIONS 4
|
||||||
|
|
||||||
@ -1018,6 +1020,7 @@ TEST_CASE("Test static ring buffer SMP", "[esp_ringbuf]")
|
|||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif //!CONFIG_FREERTOS_UNICORE
|
||||||
|
|
||||||
#if !CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH && !CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH
|
#if !CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH && !CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH
|
||||||
/* -------------------------- Test ring buffer IRAM ------------------------- */
|
/* -------------------------- Test ring buffer IRAM ------------------------- */
|
||||||
@ -1027,7 +1030,7 @@ static IRAM_ATTR __attribute__((noinline)) bool iram_ringbuf_test(void)
|
|||||||
bool result = true;
|
bool result = true;
|
||||||
uint8_t item[4];
|
uint8_t item[4];
|
||||||
size_t item_size;
|
size_t item_size;
|
||||||
RingbufHandle_t handle = xRingbufferCreate(CONT_DATA_TEST_BUFF_LEN, RINGBUF_TYPE_NOSPLIT);
|
RingbufHandle_t handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
|
||||||
result = result && (handle != NULL);
|
result = result && (handle != NULL);
|
||||||
spi_flash_guard_get()->start(); // Disables flash cache
|
spi_flash_guard_get()->start(); // Disables flash cache
|
||||||
|
|
||||||
@ -1046,3 +1049,80 @@ TEST_CASE("Test ringbuffer functions work with flash cache disabled", "[esp_ring
|
|||||||
TEST_ASSERT( iram_ringbuf_test() );
|
TEST_ASSERT( iram_ringbuf_test() );
|
||||||
}
|
}
|
||||||
#endif /* !CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH && !CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH */
|
#endif /* !CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH && !CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH */
|
||||||
|
|
||||||
|
/* ---------------------------- Test no-split ring buffer SendAquire and SendComplete ---------------------------
|
||||||
|
* The following test case tests the SendAquire and SendComplete functions of the no-split ring buffer.
|
||||||
|
*
|
||||||
|
* The test case will do the following...
|
||||||
|
* 1) Create a no-split ring buffer.
|
||||||
|
* 2) Acquire space on the buffer to send an item.
|
||||||
|
* 3) Send the item to the buffer.
|
||||||
|
* 4) Verify that the item is received correctly.
|
||||||
|
* 5) Acquire space on the buffer until the buffer is full.
|
||||||
|
* 6) Send the items out-of-order to the buffer.
|
||||||
|
* 7) Verify that the items are not received until the first item is sent.
|
||||||
|
* 8) Send the first item.
|
||||||
|
* 9) Verify that the items are received in the correct order.
|
||||||
|
*/
|
||||||
|
TEST_CASE("Test no-split buffers always receive items in order", "[esp_ringbuf]")
|
||||||
|
{
|
||||||
|
// Create buffer
|
||||||
|
RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
|
||||||
|
TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
|
||||||
|
|
||||||
|
// Acquire space on the buffer to send an item and write to the item
|
||||||
|
void *item1;
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendAcquire(buffer_handle, &item1, MEDIUM_ITEM_SIZE, TIMEOUT_TICKS));
|
||||||
|
*(uint32_t *)item1 = 0x123;
|
||||||
|
|
||||||
|
// Send the item to the buffer
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendComplete(buffer_handle, item1));
|
||||||
|
|
||||||
|
// Verify that the item is received correctly
|
||||||
|
size_t item_size;
|
||||||
|
uint32_t *received_item = xRingbufferReceive(buffer_handle, &item_size, TIMEOUT_TICKS);
|
||||||
|
TEST_ASSERT_NOT_NULL(received_item);
|
||||||
|
TEST_ASSERT_EQUAL(item_size, MEDIUM_ITEM_SIZE);
|
||||||
|
TEST_ASSERT_EQUAL(*(uint32_t *)received_item, 0x123);
|
||||||
|
|
||||||
|
// Return the space to the buffer after receiving the item
|
||||||
|
vRingbufferReturnItem(buffer_handle, received_item);
|
||||||
|
|
||||||
|
// At this point, the buffer should be empty
|
||||||
|
UBaseType_t items_waiting;
|
||||||
|
vRingbufferGetInfo(buffer_handle, NULL, NULL, NULL, NULL, &items_waiting);
|
||||||
|
TEST_ASSERT_MESSAGE(items_waiting == 0, "Incorrect items waiting");
|
||||||
|
|
||||||
|
// Acquire space on the buffer until the buffer is full
|
||||||
|
#define MAX_NUM_ITEMS ( BUFFER_SIZE / ( MEDIUM_ITEM_SIZE + ITEM_HDR_SIZE ) )
|
||||||
|
void *items[MAX_NUM_ITEMS];
|
||||||
|
for (int i = 0; i < MAX_NUM_ITEMS; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendAcquire(buffer_handle, &items[i], MEDIUM_ITEM_SIZE, TIMEOUT_TICKS));
|
||||||
|
TEST_ASSERT_NOT_NULL(items[i]);
|
||||||
|
*(uint32_t *)items[i] = (0x100 + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the buffer is full by attempting to acquire space for another item
|
||||||
|
void *another_item;
|
||||||
|
TEST_ASSERT_EQUAL(pdFALSE, xRingbufferSendAcquire(buffer_handle, &another_item, MEDIUM_ITEM_SIZE, TIMEOUT_TICKS));
|
||||||
|
|
||||||
|
// Send the items out-of-order to the buffer. Verify that the items are not received until the first item is sent.
|
||||||
|
// In this case, we send the items in the reverse order until the first item is sent.
|
||||||
|
for (int i = MAX_NUM_ITEMS - 1; i > 0; i--) {
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendComplete(buffer_handle, items[i]));
|
||||||
|
TEST_ASSERT_NULL(xRingbufferReceive(buffer_handle, &item_size, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the first item
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xRingbufferSendComplete(buffer_handle, items[0]));
|
||||||
|
|
||||||
|
// Verify that the items are received in the correct order
|
||||||
|
for (int i = 0; i < MAX_NUM_ITEMS; i++) {
|
||||||
|
received_item = xRingbufferReceive(buffer_handle, &item_size, TIMEOUT_TICKS);
|
||||||
|
TEST_ASSERT_EQUAL(*(uint32_t *)received_item, (0x100 + i));
|
||||||
|
vRingbufferReturnItem(buffer_handle, received_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
vRingbufferDelete(buffer_handle);
|
||||||
|
}
|
||||||
|
@ -60,6 +60,10 @@ void IRAM_ATTR bootloader_fill_random(void *buffer, size_t length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void esp_rtc_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void esp_clk_init(void)
|
void esp_clk_init(void)
|
||||||
{
|
{
|
||||||
s_warn();
|
s_warn();
|
||||||
|
@ -420,6 +420,10 @@ void IRAM_ATTR call_start_cpu0(void)
|
|||||||
// For Octal flash, it's hard to implement a read_id function in OPI mode for all vendors.
|
// For Octal flash, it's hard to implement a read_id function in OPI mode for all vendors.
|
||||||
// So we have to read it here in SPI mode, before entering the OPI mode.
|
// So we have to read it here in SPI mode, before entering the OPI mode.
|
||||||
bootloader_flash_update_id();
|
bootloader_flash_update_id();
|
||||||
|
|
||||||
|
// Configure the power related stuff. After this the MSPI timing tuning can be done.
|
||||||
|
esp_rtc_init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function initialise the Flash chip to the user-defined settings.
|
* This function initialise the Flash chip to the user-defined settings.
|
||||||
*
|
*
|
||||||
@ -428,15 +432,9 @@ void IRAM_ATTR call_start_cpu0(void)
|
|||||||
* In this stage, we re-configure the Flash (and MSPI) to required configuration
|
* In this stage, we re-configure the Flash (and MSPI) to required configuration
|
||||||
*/
|
*/
|
||||||
spi_flash_init_chip_state();
|
spi_flash_init_chip_state();
|
||||||
|
|
||||||
// In earlier version of ESP-IDF, the PLL provided by bootloader is not stable enough.
|
|
||||||
// Do calibration again here so that we can use better clock for the timing tuning.
|
|
||||||
#if CONFIG_ESP_SYSTEM_BBPLL_RECALIB
|
|
||||||
extern void rtc_clk_recalib_bbpll(void);
|
|
||||||
rtc_clk_recalib_bbpll();
|
|
||||||
#endif
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32S3
|
#if CONFIG_IDF_TARGET_ESP32S3
|
||||||
// This function needs to be called when PLL is enabled
|
// This function needs to be called when PLL is enabled. Needs to be called after spi_flash_init_chip_state in case
|
||||||
|
// some state of flash is modified.
|
||||||
// On other chips, this feature is not provided by HW, or hasn't been tested yet.
|
// On other chips, this feature is not provided by HW, or hasn't been tested yet.
|
||||||
spi_timing_flash_tuning();
|
spi_timing_flash_tuning();
|
||||||
#endif
|
#endif
|
||||||
|
@ -18,6 +18,15 @@ extern "C" {
|
|||||||
* Private clock-related functions
|
* Private clock-related functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize rtc-related settings
|
||||||
|
*
|
||||||
|
* Called from cpu_start.c, not intended to be called from other places.
|
||||||
|
* This function configures the power related stuff.
|
||||||
|
* After this the MSPI timing tuning can be done.
|
||||||
|
*/
|
||||||
|
void esp_rtc_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize clock-related settings
|
* @brief Initialize clock-related settings
|
||||||
*
|
*
|
||||||
|
@ -106,11 +106,14 @@ static void select_rtc_slow_clk(slow_clk_sel_t slow_clk)
|
|||||||
esp_clk_slowclk_cal_set(cal_val);
|
esp_clk_slowclk_cal_set(cal_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((weak)) void esp_clk_init(void)
|
void esp_rtc_init(void)
|
||||||
{
|
{
|
||||||
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
||||||
rtc_init(cfg);
|
rtc_init(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void esp_clk_init(void)
|
||||||
|
{
|
||||||
#if (CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS || CONFIG_APP_INIT_CLK)
|
#if (CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS || CONFIG_APP_INIT_CLK)
|
||||||
/* Check the bootloader set the XTAL frequency.
|
/* Check the bootloader set the XTAL frequency.
|
||||||
|
|
||||||
|
@ -51,12 +51,19 @@ typedef enum {
|
|||||||
} slow_clk_sel_t;
|
} slow_clk_sel_t;
|
||||||
|
|
||||||
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
||||||
|
static __attribute__((unused)) void recalib_bbpll(void);
|
||||||
|
|
||||||
static const char *TAG = "clk";
|
static const char *TAG = "clk";
|
||||||
|
|
||||||
|
|
||||||
__attribute__((weak)) void esp_clk_init(void)
|
void esp_rtc_init(void)
|
||||||
{
|
{
|
||||||
|
#if CONFIG_ESP_SYSTEM_BBPLL_RECALIB
|
||||||
|
// In earlier version of ESP-IDF, the PLL provided by bootloader is not stable enough.
|
||||||
|
// Do calibration again here so that we can use better clock for the timing tuning.
|
||||||
|
recalib_bbpll();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !CONFIG_IDF_ENV_FPGA
|
#if !CONFIG_IDF_ENV_FPGA
|
||||||
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
||||||
soc_reset_reason_t rst_reas;
|
soc_reset_reason_t rst_reas;
|
||||||
@ -65,7 +72,12 @@ static const char *TAG = "clk";
|
|||||||
cfg.cali_ocode = 1;
|
cfg.cali_ocode = 1;
|
||||||
}
|
}
|
||||||
rtc_init(cfg);
|
rtc_init(cfg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void esp_clk_init(void)
|
||||||
|
{
|
||||||
|
#if !CONFIG_IDF_ENV_FPGA
|
||||||
#ifndef CONFIG_XTAL_FREQ_AUTO
|
#ifndef CONFIG_XTAL_FREQ_AUTO
|
||||||
assert(rtc_clk_xtal_freq_get() == CONFIG_XTAL_FREQ);
|
assert(rtc_clk_xtal_freq_get() == CONFIG_XTAL_FREQ);
|
||||||
#endif
|
#endif
|
||||||
@ -266,3 +278,21 @@ __attribute__((weak)) void esp_perip_clk_init(void)
|
|||||||
*/
|
*/
|
||||||
periph_module_enable(PERIPH_TIMG0_MODULE);
|
periph_module_enable(PERIPH_TIMG0_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for bootloader not calibrated well issue.
|
||||||
|
// Placed in IRAM because disabling BBPLL may influence the cache
|
||||||
|
static void IRAM_ATTR NOINLINE_ATTR recalib_bbpll(void)
|
||||||
|
{
|
||||||
|
rtc_cpu_freq_config_t old_config;
|
||||||
|
rtc_clk_cpu_freq_get_config(&old_config);
|
||||||
|
|
||||||
|
// There are two paths we arrive here: 1. CPU reset. 2. Other reset reasons.
|
||||||
|
// - For other reasons, the bootloader will set CPU source to BBPLL and enable it. But there are calibration issues.
|
||||||
|
// Turn off the BBPLL and do calibration again to fix the issue.
|
||||||
|
// - For CPU reset, the CPU source will be set to XTAL, while the BBPLL is kept to meet USB Serial JTAG's
|
||||||
|
// requirements. In this case, we don't touch BBPLL to avoid USJ disconnection.
|
||||||
|
if (old_config.source == SOC_CPU_CLK_SRC_PLL) {
|
||||||
|
rtc_clk_cpu_freq_set_xtal();
|
||||||
|
rtc_clk_cpu_freq_set_config(&old_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -57,7 +57,7 @@ static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
|||||||
static const char *TAG = "clk";
|
static const char *TAG = "clk";
|
||||||
|
|
||||||
|
|
||||||
__attribute__((weak)) void esp_clk_init(void)
|
void esp_rtc_init(void)
|
||||||
{
|
{
|
||||||
#if !CONFIG_IDF_ENV_FPGA
|
#if !CONFIG_IDF_ENV_FPGA
|
||||||
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
||||||
@ -71,7 +71,10 @@ static const char *TAG = "clk";
|
|||||||
cfg.cali_ocode = 1;
|
cfg.cali_ocode = 1;
|
||||||
}
|
}
|
||||||
rtc_init(cfg);
|
rtc_init(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void esp_clk_init(void)
|
||||||
|
{
|
||||||
assert(rtc_clk_xtal_freq_get() == RTC_XTAL_FREQ_40M);
|
assert(rtc_clk_xtal_freq_get() == RTC_XTAL_FREQ_40M);
|
||||||
|
|
||||||
bool rc_fast_d256_is_enabled = rtc_clk_8md256_enabled();
|
bool rc_fast_d256_is_enabled = rtc_clk_8md256_enabled();
|
||||||
|
@ -60,7 +60,7 @@ typedef enum {
|
|||||||
|
|
||||||
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
||||||
|
|
||||||
__attribute__((weak)) void esp_clk_init(void)
|
void esp_rtc_init(void)
|
||||||
{
|
{
|
||||||
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
||||||
soc_reset_reason_t rst_reas = esp_rom_get_reset_reason(0);
|
soc_reset_reason_t rst_reas = esp_rom_get_reset_reason(0);
|
||||||
@ -74,7 +74,10 @@ static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
rtc_init(cfg);
|
rtc_init(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void esp_clk_init(void)
|
||||||
|
{
|
||||||
bool rc_fast_d256_is_enabled = rtc_clk_8md256_enabled();
|
bool rc_fast_d256_is_enabled = rtc_clk_8md256_enabled();
|
||||||
rtc_clk_8m_enable(true, rc_fast_d256_is_enabled);
|
rtc_clk_8m_enable(true, rc_fast_d256_is_enabled);
|
||||||
rtc_clk_fast_src_set(SOC_RTC_FAST_CLK_SRC_RC_FAST);
|
rtc_clk_fast_src_set(SOC_RTC_FAST_CLK_SRC_RC_FAST);
|
||||||
|
@ -53,9 +53,16 @@ typedef enum {
|
|||||||
} slow_clk_sel_t;
|
} slow_clk_sel_t;
|
||||||
|
|
||||||
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
||||||
|
static __attribute__((unused)) void recalib_bbpll(void);
|
||||||
|
|
||||||
__attribute__((weak)) void esp_clk_init(void)
|
void esp_rtc_init(void)
|
||||||
{
|
{
|
||||||
|
#if CONFIG_ESP_SYSTEM_BBPLL_RECALIB
|
||||||
|
// In earlier version of ESP-IDF, the PLL provided by bootloader is not stable enough.
|
||||||
|
// Do calibration again here so that we can use better clock for the timing tuning.
|
||||||
|
recalib_bbpll();
|
||||||
|
#endif
|
||||||
|
|
||||||
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
|
||||||
soc_reset_reason_t rst_reas;
|
soc_reset_reason_t rst_reas;
|
||||||
rst_reas = esp_rom_get_reset_reason(0);
|
rst_reas = esp_rom_get_reset_reason(0);
|
||||||
@ -64,7 +71,10 @@ static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
|
|||||||
cfg.cali_ocode = 1;
|
cfg.cali_ocode = 1;
|
||||||
}
|
}
|
||||||
rtc_init(cfg);
|
rtc_init(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void esp_clk_init(void)
|
||||||
|
{
|
||||||
assert(rtc_clk_xtal_freq_get() == RTC_XTAL_FREQ_40M);
|
assert(rtc_clk_xtal_freq_get() == RTC_XTAL_FREQ_40M);
|
||||||
|
|
||||||
bool rc_fast_d256_is_enabled = rtc_clk_8md256_enabled();
|
bool rc_fast_d256_is_enabled = rtc_clk_8md256_enabled();
|
||||||
@ -324,3 +334,21 @@ __attribute__((weak)) void esp_perip_clk_init(void)
|
|||||||
*/
|
*/
|
||||||
periph_module_enable(PERIPH_TIMG0_MODULE);
|
periph_module_enable(PERIPH_TIMG0_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for bootloader not calibrated well issue.
|
||||||
|
// Placed in IRAM because disabling BBPLL may influence the cache
|
||||||
|
static void IRAM_ATTR NOINLINE_ATTR recalib_bbpll(void)
|
||||||
|
{
|
||||||
|
rtc_cpu_freq_config_t old_config;
|
||||||
|
rtc_clk_cpu_freq_get_config(&old_config);
|
||||||
|
|
||||||
|
// There are two paths we arrive here: 1. CPU reset. 2. Other reset reasons.
|
||||||
|
// - For other reasons, the bootloader will set CPU source to BBPLL and enable it. But there are calibration issues.
|
||||||
|
// Turn off the BBPLL and do calibration again to fix the issue.
|
||||||
|
// - For CPU reset, the CPU source will be set to XTAL, while the BBPLL is kept to meet USB Serial JTAG's
|
||||||
|
// requirements. In this case, we don't touch BBPLL to avoid USJ disconnection.
|
||||||
|
if (old_config.source == SOC_CPU_CLK_SRC_PLL) {
|
||||||
|
rtc_clk_cpu_freq_set_xtal();
|
||||||
|
rtc_clk_cpu_freq_set_config(&old_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -487,6 +487,9 @@ static void test_pended_running_task(void *arg)
|
|||||||
TEST_ASSERT_EQUAL(true, has_run[i]);
|
TEST_ASSERT_EQUAL(true, has_run[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Short delay to let the tasks be suspended
|
||||||
|
vTaskDelay(10);
|
||||||
|
|
||||||
// Clean up the interrupt and tasks
|
// Clean up the interrupt and tasks
|
||||||
deregister_intr_cb();
|
deregister_intr_cb();
|
||||||
for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) {
|
for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) {
|
||||||
@ -506,6 +509,8 @@ TEST_CASE("Test xTaskResumeAll resumes pended tasks", "[freertos]")
|
|||||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_pended_running_task, "susp", 2048, (void *)xTaskGetCurrentTaskHandle(), UNITY_FREERTOS_PRIORITY + 1, &susp_tsk_hdl, i));
|
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_pended_running_task, "susp", 2048, (void *)xTaskGetCurrentTaskHandle(), UNITY_FREERTOS_PRIORITY + 1, &susp_tsk_hdl, i));
|
||||||
// Wait for to be notified to test completion
|
// Wait for to be notified to test completion
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
// Short delay to let the task be suspended
|
||||||
|
vTaskDelay(10);
|
||||||
vTaskDelete(susp_tsk_hdl);
|
vTaskDelete(susp_tsk_hdl);
|
||||||
}
|
}
|
||||||
// Add a short delay to allow the idle task to free any remaining task memory
|
// Add a short delay to allow the idle task to free any remaining task memory
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||||
|
|
||||||
|
examples/bluetooth/ble_get_started:
|
||||||
|
enable:
|
||||||
|
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3"]
|
||||||
|
temporary: true
|
||||||
|
reason: the other targets are not tested yet
|
||||||
|
|
||||||
examples/bluetooth/bluedroid/ble:
|
examples/bluetooth/bluedroid/ble:
|
||||||
enable:
|
enable:
|
||||||
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3"]
|
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3"]
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(nimble_beacon)
|
@ -0,0 +1,352 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# NimBLE Beacon Example
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This is a pretty simple example, aiming to introduce
|
||||||
|
|
||||||
|
1. How to initialize NimBLE stack
|
||||||
|
2. How to configure advertisement and scan response data
|
||||||
|
3. How to start advertising as a non-connectable beacon
|
||||||
|
|
||||||
|
It uses ESP32's Bluetooth controller and NimBLE host stack.
|
||||||
|
|
||||||
|
To test this demo, any BLE scanner application can be used.
|
||||||
|
|
||||||
|
## Try It Yourself
|
||||||
|
|
||||||
|
### Set Target
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
idf.py set-target <chip_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if you're using ESP32, then input
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py set-target esp32
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run the following command to build, flash and monitor the project.
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py -p <PORT> flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if the corresponding serial port is `/dev/ttyACM0`, then it goes
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py -p /dev/ttyACM0 flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Code Explained
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
1. Initialize NVS flash, NimBLE host stack and GAP service; configure NimBLE host stack and start NimBLE host task thread
|
||||||
|
2. Wait for NimBLE host stack to sync with BLE controller
|
||||||
|
3. Set advertisement and scan response data, then configure advertising parameters and start advertising
|
||||||
|
|
||||||
|
### Entry Point
|
||||||
|
|
||||||
|
`app_main` in `main.c` is the entry point of all ESP32 applications. In general, application initialization should be done here.
|
||||||
|
|
||||||
|
First, call `nvs_flash_init` function to initialize NVS flash, which is the dependency for BLE module to store configurations.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
void app_main(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc;
|
||||||
|
esp_err_t ret;
|
||||||
|
|
||||||
|
/* NVS flash initialization */
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nvs flash, error code: %d ", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, call `nimble_port_init` function to initialize NimBLE host stack.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
void app_main(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* NimBLE host stack initialization */
|
||||||
|
ret = nimble_port_init();
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nimble stack, error code: %d ",
|
||||||
|
ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, call `gap_init` defined in `gap.c`. We will initialize GAP service, set GAP device name and appearance in this function.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
int gap_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* Initialize GAP service */
|
||||||
|
ble_svc_gap_init();
|
||||||
|
|
||||||
|
/* Set GAP device name */
|
||||||
|
rc = ble_svc_gap_device_name_set(DEVICE_NAME);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set device name to %s, error code: %d",
|
||||||
|
DEVICE_NAME, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set GAP device appearance */
|
||||||
|
rc = ble_svc_gap_device_appearance_set(BLE_GAP_APPEARANCE_GENERIC_TAG);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set device appearance, error code: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* GAP service initialization */
|
||||||
|
rc = gap_init();
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And we need to configure some callback functions for NimBLE host stack to call and store the configurations in `nimble_host_config_init` in `main.c`.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void nimble_host_config_init(void) {
|
||||||
|
/* Set host callbacks */
|
||||||
|
ble_hs_cfg.reset_cb = on_stack_reset;
|
||||||
|
ble_hs_cfg.sync_cb = on_stack_sync;
|
||||||
|
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||||
|
|
||||||
|
/* Store host configuration */
|
||||||
|
ble_store_config_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* NimBLE host configuration initialization */
|
||||||
|
nimble_host_config_init();
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
So far, initialization has been done. We can call `xTaskCreate` to create `nimble_host_task` thread, and let NimBLE host stack run in the background.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void nimble_host_task(void *param) {
|
||||||
|
/* Task entry log */
|
||||||
|
ESP_LOGI(TAG, "nimble host task has been started!");
|
||||||
|
|
||||||
|
/* This function won't return until nimble_port_stop() is executed */
|
||||||
|
nimble_port_run();
|
||||||
|
|
||||||
|
/* Clean up at exit */
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Start NimBLE host task thread and return */
|
||||||
|
xTaskCreate(nimble_host_task, "NimBLE Host", 4*1024, NULL, 5, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### On Stack Sync
|
||||||
|
|
||||||
|
Once NimBLE host stack is synced with BLE controller, `on_stack_sync` in `gap.c` will be called by NimBLE host stack, which has been configured in `nimble_host_config_init`.
|
||||||
|
|
||||||
|
In this function, we will call `adv_init` function to ask NimBLE host stack to check if device MAC address is available by `ble_hs_util_ensure_addr` and `ble_hs_id_infer_auto` functions. If so, we will copy the address and try to start advertising by calling `start_advertising` in the same source file.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void on_stack_sync(void) {
|
||||||
|
/* On stack sync, do advertising initialization */
|
||||||
|
adv_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void adv_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
char addr_str[18] = {0};
|
||||||
|
|
||||||
|
/* Make sure we have proper BT identity address set */
|
||||||
|
rc = ble_hs_util_ensure_addr(0);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "device does not have any available bt address!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figure out BT address to use while advertising */
|
||||||
|
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to infer address type, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy device address to addr_val */
|
||||||
|
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to copy device address, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
format_addr(addr_str, addr_val);
|
||||||
|
ESP_LOGI(TAG, "device address: %s", addr_str);
|
||||||
|
|
||||||
|
/* Start advertising. */
|
||||||
|
start_advertising();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start Advertising
|
||||||
|
|
||||||
|
As a beacon device, we're going to start advertising and send scan response if a scan request is received. To make it happen, we need to set advertisement and scan response data before advertising starts. So the following are what we do:
|
||||||
|
|
||||||
|
1. Initialize advertisement and scan response fields structs `adv_fields` and `rsp_fields`, as well as advertising parameters struct `adv_params`
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void start_advertising(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
const char *name;
|
||||||
|
struct ble_hs_adv_fields adv_fields = {0};
|
||||||
|
struct ble_hs_adv_fields rsp_fields = {0};
|
||||||
|
struct ble_gap_adv_params adv_params = {0};
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Set advertising flags, device name, transmit power, appearance and LE role in `adv_fields`, and call `ble_gap_adv_set_fields`
|
||||||
|
1. For adveritisng flags, `BLE_HS_ADV_F_DISC_GEN` means advertising is general discoverable, and `BLE_HS_ADV_F_BREDR_UNSUP` means BLE support only (BR/EDR refers to Bluetooth Classic)
|
||||||
|
2. For appearance, it is used to tell scanner what does it look like; we use `BLE_GAP_APPEARANCE_GENERIC_TAG` here to make our device identified as a tag
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void start_advertising(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Set advertising flags */
|
||||||
|
adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
||||||
|
|
||||||
|
/* Set device name */
|
||||||
|
name = ble_svc_gap_device_name();
|
||||||
|
adv_fields.name = (uint8_t *)name;
|
||||||
|
adv_fields.name_len = strlen(name);
|
||||||
|
adv_fields.name_is_complete = 1;
|
||||||
|
|
||||||
|
/* Set device tx power */
|
||||||
|
adv_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||||
|
adv_fields.tx_pwr_lvl_is_present = 1;
|
||||||
|
|
||||||
|
/* Set device appearance */
|
||||||
|
adv_fields.appearance = BLE_GAP_APPEARANCE_GENERIC_TAG;
|
||||||
|
adv_fields.appearance_is_present = 1;
|
||||||
|
|
||||||
|
/* Set device LE role */
|
||||||
|
adv_fields.le_role = BLE_GAP_LE_ROLE_PERIPHERAL;
|
||||||
|
adv_fields.le_role_is_present = 1;
|
||||||
|
|
||||||
|
/* Set advertiement fields */
|
||||||
|
rc = ble_gap_adv_set_fields(&adv_fields);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set advertising data, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Set device address and URI in `rsp_fields`, and call `ble_gap_adv_rsp_set_fields`
|
||||||
|
1. Since `AdvData` in advertisement packet **should not be longer than 31 bytes**, additional information must be placed in scan response packet
|
||||||
|
2. We put the official website link of espressif into URI field
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void start_advertising(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Set device address */
|
||||||
|
rsp_fields.device_addr = addr_val;
|
||||||
|
rsp_fields.device_addr_type = own_addr_type;
|
||||||
|
rsp_fields.device_addr_is_present = 1;
|
||||||
|
|
||||||
|
/* Set URI */
|
||||||
|
rsp_fields.uri = esp_uri;
|
||||||
|
rsp_fields.uri_len = sizeof(esp_uri);
|
||||||
|
|
||||||
|
/* Set scan response fields */
|
||||||
|
rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set scan response data, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Set advertising mode and discoverable mode to non-connectable and general-discoverable respectively in `adv_params`, and finally, start advertising by calling `ble_gap_adv_start`
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void start_advertising(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Set non-connetable and general discoverable mode to be a beacon */
|
||||||
|
adv_params.conn_mode = BLE_GAP_CONN_MODE_NON;
|
||||||
|
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||||
|
|
||||||
|
/* Start advertising */
|
||||||
|
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params,
|
||||||
|
NULL, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to start advertising, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "advertising started!");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Observation
|
||||||
|
|
||||||
|
If everything goes well, you should be able to see `NimBLE_Beacon` on a BLE scanner device, broadcasting a lot of information including an URI of "https://espressif.com" (The official website of espressif), which is exactly what we expect.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
For any technical queries, please file an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,4 @@
|
|||||||
|
file(GLOB_RECURSE srcs "main.c" "src/*.c")
|
||||||
|
|
||||||
|
idf_component_register(SRCS "${srcs}"
|
||||||
|
INCLUDE_DIRS "./include")
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* STD APIs */
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* FreeRTOS APIs */
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
/* NimBLE stack APIs */
|
||||||
|
#include "host/ble_hs.h"
|
||||||
|
#include "host/ble_uuid.h"
|
||||||
|
#include "host/util/util.h"
|
||||||
|
#include "nimble/ble.h"
|
||||||
|
#include "nimble/nimble_port.h"
|
||||||
|
#include "nimble/nimble_port_freertos.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define TAG "NimBLE_Beacon"
|
||||||
|
#define DEVICE_NAME "NimBLE_Beacon"
|
||||||
|
|
||||||
|
#endif // COMMON_H
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef GAP_SVC_H
|
||||||
|
#define GAP_SVC_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* NimBLE GAP APIs */
|
||||||
|
#include "services/gap/ble_svc_gap.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define BLE_GAP_APPEARANCE_GENERIC_TAG 0x0200
|
||||||
|
#define BLE_GAP_URI_PREFIX_HTTPS 0x17
|
||||||
|
#define BLE_GAP_LE_ROLE_PERIPHERAL 0x00
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
void adv_init(void);
|
||||||
|
int gap_init(void);
|
||||||
|
|
||||||
|
#endif // GAP_SVC_H
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "common.h"
|
||||||
|
#include "gap.h"
|
||||||
|
|
||||||
|
/* Library function declarations */
|
||||||
|
void ble_store_config_init(void);
|
||||||
|
|
||||||
|
/* Private function declarations */
|
||||||
|
static void on_stack_reset(int reason);
|
||||||
|
static void on_stack_sync(void);
|
||||||
|
static void nimble_host_config_init(void);
|
||||||
|
static void nimble_host_task(void *param);
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
/*
|
||||||
|
* Stack event callback functions
|
||||||
|
* - on_stack_reset is called when host resets BLE stack due to errors
|
||||||
|
* - on_stack_sync is called when host has synced with controller
|
||||||
|
*/
|
||||||
|
static void on_stack_reset(int reason) {
|
||||||
|
/* On reset, print reset reason to console */
|
||||||
|
ESP_LOGI(TAG, "nimble stack reset, reset reason: %d", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_stack_sync(void) {
|
||||||
|
/* On stack sync, do advertising initialization */
|
||||||
|
adv_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nimble_host_config_init(void) {
|
||||||
|
/* Set host callbacks */
|
||||||
|
ble_hs_cfg.reset_cb = on_stack_reset;
|
||||||
|
ble_hs_cfg.sync_cb = on_stack_sync;
|
||||||
|
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||||
|
|
||||||
|
/* Store host configuration */
|
||||||
|
ble_store_config_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nimble_host_task(void *param) {
|
||||||
|
/* Task entry log */
|
||||||
|
ESP_LOGI(TAG, "nimble host task has been started!");
|
||||||
|
|
||||||
|
/* This function won't return until nimble_port_stop() is executed */
|
||||||
|
nimble_port_run();
|
||||||
|
|
||||||
|
/* Clean up at exit */
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
/* NVS flash initialization */
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nvs flash, error code: %d ", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NimBLE host stack initialization */
|
||||||
|
ret = nimble_port_init();
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nimble stack, error code: %d ",
|
||||||
|
ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GAP service initialization */
|
||||||
|
rc = gap_init();
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NimBLE host configuration initialization */
|
||||||
|
nimble_host_config_init();
|
||||||
|
|
||||||
|
/* Start NimBLE host task thread and return */
|
||||||
|
xTaskCreate(nimble_host_task, "NimBLE Host", 4*1024, NULL, 5, NULL);
|
||||||
|
return;
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "gap.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/* Private function declarations */
|
||||||
|
inline static void format_addr(char *addr_str, uint8_t addr[]);
|
||||||
|
static void start_advertising(void);
|
||||||
|
|
||||||
|
/* Private variables */
|
||||||
|
static uint8_t own_addr_type;
|
||||||
|
static uint8_t addr_val[6] = {0};
|
||||||
|
static uint8_t esp_uri[] = {BLE_GAP_URI_PREFIX_HTTPS, '/', '/', 'e', 's', 'p', 'r', 'e', 's', 's', 'i', 'f', '.', 'c', 'o', 'm'};
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
inline static void format_addr(char *addr_str, uint8_t addr[]) {
|
||||||
|
sprintf(addr_str, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1],
|
||||||
|
addr[2], addr[3], addr[4], addr[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_advertising(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
const char *name;
|
||||||
|
struct ble_hs_adv_fields adv_fields = {0};
|
||||||
|
struct ble_hs_adv_fields rsp_fields = {0};
|
||||||
|
struct ble_gap_adv_params adv_params = {0};
|
||||||
|
|
||||||
|
/* Set advertising flags */
|
||||||
|
adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
||||||
|
|
||||||
|
/* Set device name */
|
||||||
|
name = ble_svc_gap_device_name();
|
||||||
|
adv_fields.name = (uint8_t *)name;
|
||||||
|
adv_fields.name_len = strlen(name);
|
||||||
|
adv_fields.name_is_complete = 1;
|
||||||
|
|
||||||
|
/* Set device tx power */
|
||||||
|
adv_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||||
|
adv_fields.tx_pwr_lvl_is_present = 1;
|
||||||
|
|
||||||
|
/* Set device appearance */
|
||||||
|
adv_fields.appearance = BLE_GAP_APPEARANCE_GENERIC_TAG;
|
||||||
|
adv_fields.appearance_is_present = 1;
|
||||||
|
|
||||||
|
/* Set device LE role */
|
||||||
|
adv_fields.le_role = BLE_GAP_LE_ROLE_PERIPHERAL;
|
||||||
|
adv_fields.le_role_is_present = 1;
|
||||||
|
|
||||||
|
/* Set advertiement fields */
|
||||||
|
rc = ble_gap_adv_set_fields(&adv_fields);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set advertising data, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set device address */
|
||||||
|
rsp_fields.device_addr = addr_val;
|
||||||
|
rsp_fields.device_addr_type = own_addr_type;
|
||||||
|
rsp_fields.device_addr_is_present = 1;
|
||||||
|
|
||||||
|
/* Set URI */
|
||||||
|
rsp_fields.uri = esp_uri;
|
||||||
|
rsp_fields.uri_len = sizeof(esp_uri);
|
||||||
|
|
||||||
|
/* Set scan response fields */
|
||||||
|
rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set scan response data, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set non-connetable and general discoverable mode to be a beacon */
|
||||||
|
adv_params.conn_mode = BLE_GAP_CONN_MODE_NON;
|
||||||
|
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||||
|
|
||||||
|
/* Start advertising */
|
||||||
|
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params,
|
||||||
|
NULL, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to start advertising, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "advertising started!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Public functions */
|
||||||
|
void adv_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
char addr_str[18] = {0};
|
||||||
|
|
||||||
|
/* Make sure we have proper BT identity address set */
|
||||||
|
rc = ble_hs_util_ensure_addr(0);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "device does not have any available bt address!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figure out BT address to use while advertising */
|
||||||
|
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to infer address type, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy device address to addr_val */
|
||||||
|
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to copy device address, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
format_addr(addr_str, addr_val);
|
||||||
|
ESP_LOGI(TAG, "device address: %s", addr_str);
|
||||||
|
|
||||||
|
/* Start advertising. */
|
||||||
|
start_advertising();
|
||||||
|
}
|
||||||
|
|
||||||
|
int gap_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* Initialize GAP service */
|
||||||
|
ble_svc_gap_init();
|
||||||
|
|
||||||
|
/* Set GAP device name */
|
||||||
|
rc = ble_svc_gap_device_name_set(DEVICE_NAME);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set device name to %s, error code: %d",
|
||||||
|
DEVICE_NAME, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set GAP device appearance */
|
||||||
|
rc = ble_svc_gap_device_appearance_set(BLE_GAP_APPEARANCE_GENERIC_TAG);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set device appearance, error code: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_BT_ENABLED=y
|
||||||
|
CONFIG_BT_NIMBLE_ENABLED=y
|
||||||
|
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n
|
@ -0,0 +1,6 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(nimble_connection)
|
@ -0,0 +1,268 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# NimBLE Connection Example
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This example is extended from NimBLE Beacon Example, and further introduces
|
||||||
|
|
||||||
|
1. How to advertise as a connectable peripheral device
|
||||||
|
2. How to capture GAP events and handle them
|
||||||
|
3. How to update connection parameters
|
||||||
|
|
||||||
|
It uses ESP32's Bluetooth controller and NimBLE host stack.
|
||||||
|
|
||||||
|
To test this demo, any BLE scanner application can be used.
|
||||||
|
|
||||||
|
## Try It Yourself
|
||||||
|
|
||||||
|
### Set Target
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
idf.py set-target <chip_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if you're using ESP32, then input
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py set-target esp32
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run the following command to build, flash and monitor the project.
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py -p <PORT> flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if the corresponding serial port is `/dev/ttyACM0`, then it goes
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py -p /dev/ttyACM0 flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Code Explained
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
1. Initialize LED, NVS flash, NimBLE host stack and GAP service; configure NimBLE host stack and start NimBLE host task thread; wait for NimBLE host stack to sync with BLE controller
|
||||||
|
2. Set advertisement and scan response data, then configure advertising parameters and start advertising
|
||||||
|
3. On connect event
|
||||||
|
1. Turn on the LED on the dev board
|
||||||
|
2. Print out connection descriptions
|
||||||
|
3. Update connection parameters
|
||||||
|
4. On connection update event
|
||||||
|
1. Print out connection descriptions
|
||||||
|
5. On disconnect event
|
||||||
|
1. Turn off the LED on the dev board
|
||||||
|
2. Print out connection descriptions
|
||||||
|
|
||||||
|
### Entry Point & On Stack Sync
|
||||||
|
|
||||||
|
Please refer to the NimBLE Beacon Example for details.
|
||||||
|
|
||||||
|
### Start Advertising
|
||||||
|
|
||||||
|
There're some slight differences in this example when compared to NimBLE Beacon Example. First, in this example we are constructing a connectable peripheral, so connection mode is set to connectable, that is
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void start_advertising(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Set non-connetable and general discoverable mode to be a beacon */
|
||||||
|
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||||
|
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Also, to demonstrate advertising parameters settings, advertising interval parameters are modified to 500ms, and shown in scan response. Please note that the unit of advertising interval is 0.625ms.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void start_advertising(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Set advertising interval */
|
||||||
|
rsp_fields.adv_itvl = BLE_GAP_ADV_ITVL_MS(500);
|
||||||
|
rsp_fields.adv_itvl_is_present = 1;
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Set advertising interval */
|
||||||
|
adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(500);
|
||||||
|
adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(510);
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And finally, when calling the advertising start API, a callback function `gap_event_handler` is passed as argument to receive GAP events. We'll talk about it in the next section.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void start_advertising(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Start advertising */
|
||||||
|
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params,
|
||||||
|
gap_event_handler, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to start advertising, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "advertising started!");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### On GAP Events
|
||||||
|
|
||||||
|
To keep it simple, we're interested in 3 GAP events at the moment
|
||||||
|
|
||||||
|
- `BLE_GAP_EVENT_CONNECT` - Connect event
|
||||||
|
- `BLE_GAP_EVENT_DISCONNECT` - Disconnect event
|
||||||
|
- `BLE_GAP_EVENT_CONN_UPDATE` - Connection update event
|
||||||
|
|
||||||
|
#### Connect Event
|
||||||
|
|
||||||
|
When the device is connected to a peer device or a connection failed, a connect event will be passed to `gap_event_handler` by NimBLE host stack. We'll first check the connection status
|
||||||
|
|
||||||
|
- If succeeded
|
||||||
|
- Get connection descriptor by connection handle and print out
|
||||||
|
- Turn on the LED
|
||||||
|
- Try to update connection parameters
|
||||||
|
- If failed
|
||||||
|
- Re-start advertising
|
||||||
|
|
||||||
|
``` C
|
||||||
|
/* Connect event */
|
||||||
|
static int gap_event_handler(struct ble_gap_event *event, void *arg) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
struct ble_gap_conn_desc desc;
|
||||||
|
|
||||||
|
/* Handle different GAP event */
|
||||||
|
switch (event->type) {
|
||||||
|
|
||||||
|
/* Connect event */
|
||||||
|
case BLE_GAP_EVENT_CONNECT:
|
||||||
|
/* A new connection was established or a connection attempt failed. */
|
||||||
|
ESP_LOGI(TAG, "connection %s; status=%d",
|
||||||
|
event->connect.status == 0 ? "established" : "failed",
|
||||||
|
event->connect.status);
|
||||||
|
|
||||||
|
/* Connection succeeded */
|
||||||
|
if (event->connect.status == 0) {
|
||||||
|
/* Check connection handle */
|
||||||
|
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"failed to find connection by handle, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print connection descriptor and turn on the LED */
|
||||||
|
print_conn_desc(&desc);
|
||||||
|
led_on();
|
||||||
|
|
||||||
|
/* Try to update connection parameters */
|
||||||
|
struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl,
|
||||||
|
.itvl_max = desc.conn_itvl,
|
||||||
|
.latency = 3,
|
||||||
|
.supervision_timeout =
|
||||||
|
desc.supervision_timeout};
|
||||||
|
rc = ble_gap_update_params(event->connect.conn_handle, ¶ms);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(
|
||||||
|
TAG,
|
||||||
|
"failed to update connection parameters, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Connection failed, restart advertising */
|
||||||
|
else {
|
||||||
|
start_advertising();
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Disconnect Event
|
||||||
|
|
||||||
|
On disconnect event, we simply
|
||||||
|
|
||||||
|
1. Print out disconnect reason and connection descriptor
|
||||||
|
2. Turn off the LED
|
||||||
|
3. Re-start advertising
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static int gap_event_handler(struct ble_gap_event *event, void *arg) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Disconnect event */
|
||||||
|
case BLE_GAP_EVENT_DISCONNECT:
|
||||||
|
/* A connection was terminated, print connection descriptor */
|
||||||
|
ESP_LOGI(TAG, "disconnected from peer; reason=%d",
|
||||||
|
event->disconnect.reason);
|
||||||
|
|
||||||
|
/* Turn off the LED */
|
||||||
|
led_off();
|
||||||
|
|
||||||
|
/* Restart advertising */
|
||||||
|
start_advertising();
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Connection Update Event
|
||||||
|
|
||||||
|
On connection update event, the operation is also very simple
|
||||||
|
|
||||||
|
1. Print out connection status
|
||||||
|
2. Get connection descriptor by connection handle and print out
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static int gap_event_handler(struct ble_gap_event *event, void *arg) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Connection parameters update event */
|
||||||
|
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||||
|
/* The central has updated the connection parameters. */
|
||||||
|
ESP_LOGI(TAG, "connection updated; status=%d",
|
||||||
|
event->conn_update.status);
|
||||||
|
|
||||||
|
/* Print connection descriptor */
|
||||||
|
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to find connection by handle, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
print_conn_desc(&desc);
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Observation
|
||||||
|
|
||||||
|
If everything goes well, except for what we have seen in NimBLE Beacon example, you should be able to see LED turned on when device is connected, and see LED turned off on disconnection.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
For any technical queries, please file an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,4 @@
|
|||||||
|
file(GLOB_RECURSE srcs "main.c" "src/*.c")
|
||||||
|
|
||||||
|
idf_component_register(SRCS "${srcs}"
|
||||||
|
INCLUDE_DIRS "./include")
|
@ -0,0 +1,42 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||||
|
|
||||||
|
choice BLINK_LED
|
||||||
|
prompt "Blink LED type"
|
||||||
|
default BLINK_LED_GPIO
|
||||||
|
help
|
||||||
|
Select the LED type. A normal level controlled LED or an addressable LED strip.
|
||||||
|
The default selection is based on the Espressif DevKit boards.
|
||||||
|
You can change the default selection according to your board.
|
||||||
|
|
||||||
|
config BLINK_LED_GPIO
|
||||||
|
bool "GPIO"
|
||||||
|
config BLINK_LED_STRIP
|
||||||
|
bool "LED strip"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
choice BLINK_LED_STRIP_BACKEND
|
||||||
|
depends on BLINK_LED_STRIP
|
||||||
|
prompt "LED strip backend peripheral"
|
||||||
|
default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
|
||||||
|
default BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
help
|
||||||
|
Select the backend peripheral to drive the LED strip.
|
||||||
|
|
||||||
|
config BLINK_LED_STRIP_BACKEND_RMT
|
||||||
|
depends on SOC_RMT_SUPPORTED
|
||||||
|
bool "RMT"
|
||||||
|
config BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
bool "SPI"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config BLINK_GPIO
|
||||||
|
int "Blink GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 8
|
||||||
|
help
|
||||||
|
GPIO number (IOxx) to blink on and off the LED.
|
||||||
|
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
|
||||||
|
|
||||||
|
endmenu
|
@ -0,0 +1,2 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip: "^2.4.1"
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* STD APIs */
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* FreeRTOS APIs */
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
/* NimBLE stack APIs */
|
||||||
|
#include "host/ble_hs.h"
|
||||||
|
#include "host/ble_uuid.h"
|
||||||
|
#include "host/util/util.h"
|
||||||
|
#include "nimble/ble.h"
|
||||||
|
#include "nimble/nimble_port.h"
|
||||||
|
#include "nimble/nimble_port_freertos.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define TAG "NimBLE_Connection"
|
||||||
|
#define DEVICE_NAME "NimBLE_CONN"
|
||||||
|
|
||||||
|
#endif // COMMON_H
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef GAP_SVC_H
|
||||||
|
#define GAP_SVC_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* NimBLE GAP APIs */
|
||||||
|
#include "host/ble_gap.h"
|
||||||
|
#include "services/gap/ble_svc_gap.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define BLE_GAP_APPEARANCE_GENERIC_TAG 0x0200
|
||||||
|
#define BLE_GAP_URI_PREFIX_HTTPS 0x17
|
||||||
|
#define BLE_GAP_LE_ROLE_PERIPHERAL 0x00
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
void adv_init(void);
|
||||||
|
int gap_init(void);
|
||||||
|
|
||||||
|
#endif // GAP_SVC_H
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef LED_H
|
||||||
|
#define LED_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define BLINK_GPIO CONFIG_BLINK_GPIO
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
uint8_t get_led_state(void);
|
||||||
|
void led_on(void);
|
||||||
|
void led_off(void);
|
||||||
|
void led_init(void);
|
||||||
|
|
||||||
|
#endif // LED_H
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "common.h"
|
||||||
|
#include "gap.h"
|
||||||
|
#include "led.h"
|
||||||
|
|
||||||
|
/* Library function declarations */
|
||||||
|
void ble_store_config_init(void);
|
||||||
|
|
||||||
|
/* Private function declarations */
|
||||||
|
static void on_stack_reset(int reason);
|
||||||
|
static void on_stack_sync(void);
|
||||||
|
static void nimble_host_config_init(void);
|
||||||
|
static void nimble_host_task(void *param);
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
/*
|
||||||
|
* Stack event callback functions
|
||||||
|
* - on_stack_reset is called when host resets BLE stack due to errors
|
||||||
|
* - on_stack_sync is called when host has synced with controller
|
||||||
|
*/
|
||||||
|
static void on_stack_reset(int reason) {
|
||||||
|
/* On reset, print reset reason to console */
|
||||||
|
ESP_LOGI(TAG, "nimble stack reset, reset reason: %d", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_stack_sync(void) {
|
||||||
|
/* On stack sync, do advertising initialization */
|
||||||
|
adv_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nimble_host_config_init(void) {
|
||||||
|
/* Set host callbacks */
|
||||||
|
ble_hs_cfg.reset_cb = on_stack_reset;
|
||||||
|
ble_hs_cfg.sync_cb = on_stack_sync;
|
||||||
|
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||||
|
|
||||||
|
/* Store host configuration */
|
||||||
|
ble_store_config_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nimble_host_task(void *param) {
|
||||||
|
/* Task entry log */
|
||||||
|
ESP_LOGI(TAG, "nimble host task has been started!");
|
||||||
|
|
||||||
|
/* This function won't return until nimble_port_stop() is executed */
|
||||||
|
nimble_port_run();
|
||||||
|
|
||||||
|
/* Clean up at exit */
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
/* LED initialization */
|
||||||
|
led_init();
|
||||||
|
|
||||||
|
/* NVS flash initialization */
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nvs flash, error code: %d ", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NimBLE stack initialization */
|
||||||
|
ret = nimble_port_init();
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nimble stack, error code: %d ",
|
||||||
|
ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GAP service initialization */
|
||||||
|
rc = gap_init();
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NimBLE host configuration initialization */
|
||||||
|
nimble_host_config_init();
|
||||||
|
|
||||||
|
/* Start NimBLE host task thread and return */
|
||||||
|
xTaskCreate(nimble_host_task, "NimBLE Host", 4*1024, NULL, 5, NULL);
|
||||||
|
return;
|
||||||
|
}
|
@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "gap.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "led.h"
|
||||||
|
|
||||||
|
/* Private function declarations */
|
||||||
|
inline static void format_addr(char *addr_str, uint8_t addr[]);
|
||||||
|
static void print_conn_desc(struct ble_gap_conn_desc *desc);
|
||||||
|
static void start_advertising(void);
|
||||||
|
static int gap_event_handler(struct ble_gap_event *event, void *arg);
|
||||||
|
|
||||||
|
/* Private variables */
|
||||||
|
static uint8_t own_addr_type;
|
||||||
|
static uint8_t addr_val[6] = {0};
|
||||||
|
static uint8_t esp_uri[] = {BLE_GAP_URI_PREFIX_HTTPS, '/', '/', 'e', 's', 'p', 'r', 'e', 's', 's', 'i', 'f', '.', 'c', 'o', 'm'};
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
inline static void format_addr(char *addr_str, uint8_t addr[]) {
|
||||||
|
sprintf(addr_str, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1],
|
||||||
|
addr[2], addr[3], addr[4], addr[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_conn_desc(struct ble_gap_conn_desc *desc) {
|
||||||
|
/* Local variables */
|
||||||
|
char addr_str[18] = {0};
|
||||||
|
|
||||||
|
/* Connection handle */
|
||||||
|
ESP_LOGI(TAG, "connection handle: %d", desc->conn_handle);
|
||||||
|
|
||||||
|
/* Local ID address */
|
||||||
|
format_addr(addr_str, desc->our_id_addr.val);
|
||||||
|
ESP_LOGI(TAG, "device id address: type=%d, value=%s",
|
||||||
|
desc->our_id_addr.type, addr_str);
|
||||||
|
|
||||||
|
/* Peer ID address */
|
||||||
|
format_addr(addr_str, desc->peer_id_addr.val);
|
||||||
|
ESP_LOGI(TAG, "peer id address: type=%d, value=%s", desc->peer_id_addr.type,
|
||||||
|
addr_str);
|
||||||
|
|
||||||
|
/* Connection info */
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"conn_itvl=%d, conn_latency=%d, supervision_timeout=%d, "
|
||||||
|
"encrypted=%d, authenticated=%d, bonded=%d\n",
|
||||||
|
desc->conn_itvl, desc->conn_latency, desc->supervision_timeout,
|
||||||
|
desc->sec_state.encrypted, desc->sec_state.authenticated,
|
||||||
|
desc->sec_state.bonded);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_advertising(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
const char *name;
|
||||||
|
struct ble_hs_adv_fields adv_fields = {0};
|
||||||
|
struct ble_hs_adv_fields rsp_fields = {0};
|
||||||
|
struct ble_gap_adv_params adv_params = {0};
|
||||||
|
|
||||||
|
/* Set advertising flags */
|
||||||
|
adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
||||||
|
|
||||||
|
/* Set device name */
|
||||||
|
name = ble_svc_gap_device_name();
|
||||||
|
adv_fields.name = (uint8_t *)name;
|
||||||
|
adv_fields.name_len = strlen(name);
|
||||||
|
adv_fields.name_is_complete = 1;
|
||||||
|
|
||||||
|
/* Set device tx power */
|
||||||
|
adv_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||||
|
adv_fields.tx_pwr_lvl_is_present = 1;
|
||||||
|
|
||||||
|
/* Set device appearance */
|
||||||
|
adv_fields.appearance = BLE_GAP_APPEARANCE_GENERIC_TAG;
|
||||||
|
adv_fields.appearance_is_present = 1;
|
||||||
|
|
||||||
|
/* Set device LE role */
|
||||||
|
adv_fields.le_role = BLE_GAP_LE_ROLE_PERIPHERAL;
|
||||||
|
adv_fields.le_role_is_present = 1;
|
||||||
|
|
||||||
|
/* Set advertiement fields */
|
||||||
|
rc = ble_gap_adv_set_fields(&adv_fields);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set advertising data, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set device address */
|
||||||
|
rsp_fields.device_addr = addr_val;
|
||||||
|
rsp_fields.device_addr_type = own_addr_type;
|
||||||
|
rsp_fields.device_addr_is_present = 1;
|
||||||
|
|
||||||
|
/* Set URI */
|
||||||
|
rsp_fields.uri = esp_uri;
|
||||||
|
rsp_fields.uri_len = sizeof(esp_uri);
|
||||||
|
|
||||||
|
/* Set advertising interval */
|
||||||
|
rsp_fields.adv_itvl = BLE_GAP_ADV_ITVL_MS(500);
|
||||||
|
rsp_fields.adv_itvl_is_present = 1;
|
||||||
|
|
||||||
|
/* Set scan response fields */
|
||||||
|
rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set scan response data, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set non-connetable and general discoverable mode to be a beacon */
|
||||||
|
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||||
|
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||||
|
|
||||||
|
/* Set advertising interval */
|
||||||
|
adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(500);
|
||||||
|
adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(510);
|
||||||
|
|
||||||
|
/* Start advertising */
|
||||||
|
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params,
|
||||||
|
gap_event_handler, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to start advertising, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "advertising started!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NimBLE applies an event-driven model to keep GAP service going
|
||||||
|
* gap_event_handler is a callback function registered when calling
|
||||||
|
* ble_gap_adv_start API and called when a GAP event arrives
|
||||||
|
*/
|
||||||
|
static int gap_event_handler(struct ble_gap_event *event, void *arg) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
struct ble_gap_conn_desc desc;
|
||||||
|
|
||||||
|
/* Handle different GAP event */
|
||||||
|
switch (event->type) {
|
||||||
|
|
||||||
|
/* Connect event */
|
||||||
|
case BLE_GAP_EVENT_CONNECT:
|
||||||
|
/* A new connection was established or a connection attempt failed. */
|
||||||
|
ESP_LOGI(TAG, "connection %s; status=%d",
|
||||||
|
event->connect.status == 0 ? "established" : "failed",
|
||||||
|
event->connect.status);
|
||||||
|
|
||||||
|
/* Connection succeeded */
|
||||||
|
if (event->connect.status == 0) {
|
||||||
|
/* Check connection handle */
|
||||||
|
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"failed to find connection by handle, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print connection descriptor and turn on the LED */
|
||||||
|
print_conn_desc(&desc);
|
||||||
|
led_on();
|
||||||
|
|
||||||
|
/* Try to update connection parameters */
|
||||||
|
struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl,
|
||||||
|
.itvl_max = desc.conn_itvl,
|
||||||
|
.latency = 3,
|
||||||
|
.supervision_timeout =
|
||||||
|
desc.supervision_timeout};
|
||||||
|
rc = ble_gap_update_params(event->connect.conn_handle, ¶ms);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(
|
||||||
|
TAG,
|
||||||
|
"failed to update connection parameters, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Connection failed, restart advertising */
|
||||||
|
else {
|
||||||
|
start_advertising();
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Disconnect event */
|
||||||
|
case BLE_GAP_EVENT_DISCONNECT:
|
||||||
|
/* A connection was terminated, print connection descriptor */
|
||||||
|
ESP_LOGI(TAG, "disconnected from peer; reason=%d",
|
||||||
|
event->disconnect.reason);
|
||||||
|
|
||||||
|
/* Turn off the LED */
|
||||||
|
led_off();
|
||||||
|
|
||||||
|
/* Restart advertising */
|
||||||
|
start_advertising();
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Connection parameters update event */
|
||||||
|
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||||
|
/* The central has updated the connection parameters. */
|
||||||
|
ESP_LOGI(TAG, "connection updated; status=%d",
|
||||||
|
event->conn_update.status);
|
||||||
|
|
||||||
|
/* Print connection descriptor */
|
||||||
|
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to find connection by handle, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
print_conn_desc(&desc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Public functions */
|
||||||
|
void adv_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
char addr_str[18] = {0};
|
||||||
|
|
||||||
|
/* Make sure we have proper BT identity address set */
|
||||||
|
rc = ble_hs_util_ensure_addr(0);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "device does not have any available bt address!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figure out BT address to use while advertising */
|
||||||
|
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to infer address type, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy device address to addr_val */
|
||||||
|
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to copy device address, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
format_addr(addr_str, addr_val);
|
||||||
|
ESP_LOGI(TAG, "device address: %s", addr_str);
|
||||||
|
|
||||||
|
/* Start advertising. */
|
||||||
|
start_advertising();
|
||||||
|
}
|
||||||
|
|
||||||
|
int gap_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* Initialize GAP service */
|
||||||
|
ble_svc_gap_init();
|
||||||
|
|
||||||
|
/* Set GAP device name */
|
||||||
|
rc = ble_svc_gap_device_name_set(DEVICE_NAME);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set device name to %s, error code: %d",
|
||||||
|
DEVICE_NAME, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set GAP device appearance */
|
||||||
|
rc = ble_svc_gap_device_appearance_set(BLE_GAP_APPEARANCE_GENERIC_TAG);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set device appearance, error code: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "led.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/* Private variables */
|
||||||
|
static uint8_t led_state;
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLINK_LED_STRIP
|
||||||
|
static led_strip_handle_t led_strip;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Public functions */
|
||||||
|
uint8_t get_led_state(void) { return led_state; }
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLINK_LED_STRIP
|
||||||
|
|
||||||
|
void led_on(void) {
|
||||||
|
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||||
|
led_strip_set_pixel(led_strip, 0, 16, 16, 16);
|
||||||
|
|
||||||
|
/* Refresh the strip to send data */
|
||||||
|
led_strip_refresh(led_strip);
|
||||||
|
|
||||||
|
/* Update LED state */
|
||||||
|
led_state = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_off(void) {
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
led_strip_clear(led_strip);
|
||||||
|
|
||||||
|
/* Update LED state */
|
||||||
|
led_state = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_init(void) {
|
||||||
|
ESP_LOGI(TAG, "example configured to blink addressable led!");
|
||||||
|
/* LED strip initialization with the GPIO and pixels number*/
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = CONFIG_BLINK_GPIO,
|
||||||
|
.max_leds = 1, // at least one LED on board
|
||||||
|
};
|
||||||
|
#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT
|
||||||
|
led_strip_rmt_config_t rmt_config = {
|
||||||
|
.resolution_hz = 10 * 1000 * 1000, // 10MHz
|
||||||
|
.flags.with_dma = false,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(
|
||||||
|
led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||||
|
#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
led_strip_spi_config_t spi_config = {
|
||||||
|
.spi_bus = SPI2_HOST,
|
||||||
|
.flags.with_dma = true,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(
|
||||||
|
led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||||
|
#else
|
||||||
|
#error "unsupported LED strip backend"
|
||||||
|
#endif
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
led_off();
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif CONFIG_BLINK_LED_GPIO
|
||||||
|
|
||||||
|
void led_on(void) { gpio_set_level(CONFIG_BLINK_GPIO, true); }
|
||||||
|
|
||||||
|
void led_off(void) { gpio_set_level(CONFIG_BLINK_GPIO, false); }
|
||||||
|
|
||||||
|
void led_init(void) {
|
||||||
|
ESP_LOGI(TAG, "example configured to blink gpio led!");
|
||||||
|
gpio_reset_pin(CONFIG_BLINK_GPIO);
|
||||||
|
/* Set the GPIO as a push/pull output */
|
||||||
|
gpio_set_direction(CONFIG_BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "unsupported LED type"
|
||||||
|
#endif
|
@ -0,0 +1,6 @@
|
|||||||
|
CONFIG_BT_ENABLED=y
|
||||||
|
CONFIG_BT_NIMBLE_ENABLED=y
|
||||||
|
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n
|
||||||
|
|
||||||
|
CONFIG_BLINK_LED_GPIO=y
|
||||||
|
CONFIG_BLINK_GPIO=8
|
@ -0,0 +1 @@
|
|||||||
|
CONFIG_BLINK_GPIO=5
|
@ -0,0 +1 @@
|
|||||||
|
CONFIG_BLINK_LED_STRIP=y
|
@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_BLINK_LED_STRIP=y
|
||||||
|
CONFIG_BLINK_GPIO=48
|
@ -0,0 +1,6 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(nimble_gatt_server)
|
@ -0,0 +1,380 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# NimBLE GATT Server Example
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This example is extended from NimBLE Connection Example, and further introduces
|
||||||
|
|
||||||
|
1. How to implement a GATT server using GATT services table
|
||||||
|
2. How to handle characteristic access requests
|
||||||
|
1. Write access demonstrated by LED control
|
||||||
|
2. Read and indicate access demonstrated by heart rate measurement(mocked)
|
||||||
|
|
||||||
|
It uses ESP32's Bluetooth controller and NimBLE host stack.
|
||||||
|
|
||||||
|
To test this demo, any BLE scanner application can be used.
|
||||||
|
|
||||||
|
## Try It Yourself
|
||||||
|
|
||||||
|
### Set Target
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
idf.py set-target <chip_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if you're using ESP32, then input
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py set-target esp32
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run the following command to build, flash and monitor the project.
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py -p <PORT> flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if the corresponding serial port is `/dev/ttyACM0`, then it goes
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py -p /dev/ttyACM0 flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Code Explained
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
1. Initialization
|
||||||
|
1. Initialize LED, NVS flash, NimBLE host stack, GAP service
|
||||||
|
2. Initialize GATT service and add services to registration queue
|
||||||
|
3. Configure NimBLE host stack and start NimBLE host task thread, GATT services will be registered automatically when NimBLE host stack started
|
||||||
|
4. Start heart rate update task thread
|
||||||
|
2. Wait for NimBLE host stack to sync with BLE controller, and start advertising; wait for connection event to come
|
||||||
|
3. After connection established, wait for GATT characteristics access events to come
|
||||||
|
1. On write LED event, turn on or off the LED accordingly
|
||||||
|
2. On read heart rate event, send out current heart rate measurement value
|
||||||
|
3. On indicate heart rate event, enable heart rate indication
|
||||||
|
|
||||||
|
### Entry Point
|
||||||
|
|
||||||
|
In this example, we call GATT `gatt_svr_init` function to initialize GATT server in `app_main` before NimBLE host configuration. This is a custom function defined in `gatt_svc.c`, and basically we just call GATT service initialization API and add services to registration queue.
|
||||||
|
|
||||||
|
And there's another code added in `nimble_host_config_init`, which is
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void nimble_host_config_init(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That is GATT server register callback function. In this case it will only print out some registration information when services, characteristics or descriptors are registered.
|
||||||
|
|
||||||
|
Then, after NimBLE host task thread is created, we'll create another task defined in `heart_rate_task` to update heart rate measurement mock value and send indication if enabled.
|
||||||
|
|
||||||
|
### GAP Service Updates
|
||||||
|
|
||||||
|
`gap_event_handler` is updated with LED control removed, and more event handling branches, when compared to NimBLE Connection Example, including
|
||||||
|
|
||||||
|
- `BLE_GAP_EVENT_ADV_COMPLETE` - Advertising complete event
|
||||||
|
- `BLE_GAP_EVENT_NOTIFY_TX` - Notificate event
|
||||||
|
- `BLE_GAP_EVENT_SUBSCRIBE` - Subscribe event
|
||||||
|
- `BLE_GAP_EVENT_MTU` - MTU update event
|
||||||
|
|
||||||
|
`BLE_GAP_EVENT_ADV_COMPLETE` and `BLE_GAP_EVENT_MTU` events are actually not involved in this example, but we still put them down there for reference. `BLE_GAP_EVENT_NOTIFY_TX` and `BLE_GAP_EVENT_SUBSCRIBE` events will be discussed in the next section.
|
||||||
|
|
||||||
|
### GATT Services Table
|
||||||
|
|
||||||
|
GATT services are defined in `ble_gatt_svc_def` struct array, with a variable name `gatt_svr_svcs` in this demo. We'll call it as the GATT services table in the following content.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
/* Heart rate service */
|
||||||
|
static const ble_uuid16_t heart_rate_svc_uuid = BLE_UUID16_INIT(0x180D);
|
||||||
|
|
||||||
|
static uint8_t heart_rate_chr_val[2] = {0};
|
||||||
|
static uint16_t heart_rate_chr_val_handle;
|
||||||
|
static const ble_uuid16_t heart_rate_chr_uuid = BLE_UUID16_INIT(0x2A37);
|
||||||
|
|
||||||
|
static uint16_t heart_rate_chr_conn_handle = 0;
|
||||||
|
static bool heart_rate_chr_conn_handle_inited = false;
|
||||||
|
static bool heart_rate_ind_status = false;
|
||||||
|
|
||||||
|
/* Automation IO service */
|
||||||
|
static const ble_uuid16_t auto_io_svc_uuid = BLE_UUID16_INIT(0x1815);
|
||||||
|
static uint16_t led_chr_val_handle;
|
||||||
|
static const ble_uuid128_t led_chr_uuid =
|
||||||
|
BLE_UUID128_INIT(0x23, 0xd1, 0xbc, 0xea, 0x5f, 0x78, 0x23, 0x15, 0xde, 0xef,
|
||||||
|
0x12, 0x12, 0x25, 0x15, 0x00, 0x00);
|
||||||
|
|
||||||
|
/* GATT services table */
|
||||||
|
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
|
||||||
|
/* Heart rate service */
|
||||||
|
{.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||||
|
.uuid = &heart_rate_svc_uuid.u,
|
||||||
|
.characteristics =
|
||||||
|
(struct ble_gatt_chr_def[]){
|
||||||
|
{/* Heart rate characteristic */
|
||||||
|
.uuid = &heart_rate_chr_uuid.u,
|
||||||
|
.access_cb = heart_rate_chr_access,
|
||||||
|
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_INDICATE,
|
||||||
|
.val_handle = &heart_rate_chr_val_handle},
|
||||||
|
{
|
||||||
|
0, /* No more characteristics in this service. */
|
||||||
|
}}},
|
||||||
|
|
||||||
|
/* Automation IO service */
|
||||||
|
{
|
||||||
|
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||||
|
.uuid = &auto_io_svc_uuid.u,
|
||||||
|
.characteristics =
|
||||||
|
(struct ble_gatt_chr_def[]){/* LED characteristic */
|
||||||
|
{.uuid = &led_chr_uuid.u,
|
||||||
|
.access_cb = led_chr_access,
|
||||||
|
.flags = BLE_GATT_CHR_F_WRITE,
|
||||||
|
.val_handle = &led_chr_val_handle},
|
||||||
|
{0}},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
0, /* No more services. */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
In this table, there are two GATT primary services defined
|
||||||
|
|
||||||
|
- Heart rate service with a UUID of `0x180D`
|
||||||
|
- Automation IO service with a UUID of `0x1815`
|
||||||
|
|
||||||
|
#### Automation IO Service
|
||||||
|
|
||||||
|
Under automation IO service, there's a LED characteristic, with a vendor-specific UUID and write only permission.
|
||||||
|
|
||||||
|
The characteristic is binded with `led_chr_access` callback function, in which the write access event is captured. The LED will be turned on or off according to the write value, quite straight-forward.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static int led_chr_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||||
|
struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Handle access events */
|
||||||
|
/* Note: LED characteristic is write only */
|
||||||
|
switch (ctxt->op) {
|
||||||
|
|
||||||
|
/* Write characteristic event */
|
||||||
|
case BLE_GATT_ACCESS_OP_WRITE_CHR:
|
||||||
|
/* Verify connection handle */
|
||||||
|
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
ESP_LOGI(TAG, "characteristic write; conn_handle=%d attr_handle=%d",
|
||||||
|
conn_handle, attr_handle);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"characteristic write by nimble stack; attr_handle=%d",
|
||||||
|
attr_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify attribute handle */
|
||||||
|
if (attr_handle == led_chr_val_handle) {
|
||||||
|
/* Verify access buffer length */
|
||||||
|
if (ctxt->om->om_len == 1) {
|
||||||
|
/* Turn the LED on or off according to the operation bit */
|
||||||
|
if (ctxt->om->om_data[0]) {
|
||||||
|
led_on();
|
||||||
|
ESP_LOGI(TAG, "led turned on!");
|
||||||
|
} else {
|
||||||
|
led_off();
|
||||||
|
ESP_LOGI(TAG, "led turned off!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Unknown event */
|
||||||
|
default:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"unexpected access operation to led characteristic, opcode: %d",
|
||||||
|
ctxt->op);
|
||||||
|
return BLE_ATT_ERR_UNLIKELY;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Heart Rate Service
|
||||||
|
|
||||||
|
Under heart rate service, there's a heart rate measurement characteristic, with a UUID of `0x2A37` and read + indicate access permission.
|
||||||
|
|
||||||
|
The characteristic is binded with `heart_rate_chr_access` callback function, in which the read access event is captured. It should be mentioned that in SIG definition, heart rate measurement is a multi-byte data structure, with the first byte indicating the flags
|
||||||
|
|
||||||
|
- Bit 0: Heart rate value type
|
||||||
|
- 0: Heart rate value is `uint8_t` type
|
||||||
|
- 1: Heart rate value is `uint16_t` type
|
||||||
|
- Bit 1: Sensor contact status
|
||||||
|
- Bit 2: Sensor contact supported
|
||||||
|
- Bit 3: Energy expended status
|
||||||
|
- Bit 4: RR-interval status
|
||||||
|
- Bit 5-7: Reserved
|
||||||
|
|
||||||
|
and the rest of bytes are data fields. In this case, we use `uint8_t` type and disable other features, making the characteristic value a 2-byte array. So when characteristic read event arrives, we'll get the latest heart rate value and send it back to peer device.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static int heart_rate_chr_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||||
|
struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Handle access events */
|
||||||
|
/* Note: Heart rate characteristic is read only */
|
||||||
|
switch (ctxt->op) {
|
||||||
|
|
||||||
|
/* Read characteristic event */
|
||||||
|
case BLE_GATT_ACCESS_OP_READ_CHR:
|
||||||
|
/* Verify connection handle */
|
||||||
|
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
ESP_LOGI(TAG, "characteristic read; conn_handle=%d attr_handle=%d",
|
||||||
|
conn_handle, attr_handle);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "characteristic read by nimble stack; attr_handle=%d",
|
||||||
|
attr_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify attribute handle */
|
||||||
|
if (attr_handle == heart_rate_chr_val_handle) {
|
||||||
|
/* Update access buffer value */
|
||||||
|
heart_rate_chr_val[1] = get_heart_rate();
|
||||||
|
rc = os_mbuf_append(ctxt->om, &heart_rate_chr_val,
|
||||||
|
sizeof(heart_rate_chr_val));
|
||||||
|
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||||
|
}
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Unknown event */
|
||||||
|
default:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
ESP_LOGE(
|
||||||
|
TAG,
|
||||||
|
"unexpected access operation to heart rate characteristic, opcode: %d",
|
||||||
|
ctxt->op);
|
||||||
|
return BLE_ATT_ERR_UNLIKELY;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Indicate access, however, is a bit more complicated. As mentioned in *GAP Service Updates*, we'll handle another 2 events namely `BLE_GAP_EVENT_NOTIFY_TX` and `BLE_GAP_EVENT_SUBSCRIBE` in `gap_event_handler`. In this case, if peer device wants to enable heart rate measurement indication, it will send a subscribe request to the local device, and the request will be captured as a subscribe event in `gap_event_handler`. But from the perspective of software layering, the event should be handled in GATT server, so we just pass the event to GATT server by calling `gatt_svr_subscribe_cb`, as demonstrated in the demo
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static int gap_event_handler(struct ble_gap_event *event, void *arg) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Subscribe event */
|
||||||
|
case BLE_GAP_EVENT_SUBSCRIBE:
|
||||||
|
/* Print subscription info to log */
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"subscribe event; conn_handle=%d attr_handle=%d "
|
||||||
|
"reason=%d prevn=%d curn=%d previ=%d curi=%d",
|
||||||
|
event->subscribe.conn_handle, event->subscribe.attr_handle,
|
||||||
|
event->subscribe.reason, event->subscribe.prev_notify,
|
||||||
|
event->subscribe.cur_notify, event->subscribe.prev_indicate,
|
||||||
|
event->subscribe.cur_indicate);
|
||||||
|
|
||||||
|
/* GATT subscribe event callback */
|
||||||
|
gatt_svr_subscribe_cb(event);
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we'll check connection handle and attribute handle, if the attribute handle matches `heart_rate_chr_val_chandle`, `heart_rate_chr_conn_handle` and `heart_rate_ind_status` will be updated together.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
void gatt_svr_subscribe_cb(struct ble_gap_event *event) {
|
||||||
|
/* Check connection handle */
|
||||||
|
if (event->subscribe.conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d",
|
||||||
|
event->subscribe.conn_handle, event->subscribe.attr_handle);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "subscribe by nimble stack; attr_handle=%d",
|
||||||
|
event->subscribe.attr_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check attribute handle */
|
||||||
|
if (event->subscribe.attr_handle == heart_rate_chr_val_handle) {
|
||||||
|
/* Update heart rate subscription status */
|
||||||
|
heart_rate_chr_conn_handle = event->subscribe.conn_handle;
|
||||||
|
heart_rate_chr_conn_handle_inited = true;
|
||||||
|
heart_rate_ind_status = event->subscribe.cur_indicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice that heart rate measurement incation is handled in `heart_rate_task` by calling `send_heart_rate_indication` function periodically. Actually, this function will check heart rate indication status and send indication accordingly. In this way, heart rate indication is implemented.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
void send_heart_rate_indication(void) {
|
||||||
|
if (heart_rate_ind_status && heart_rate_chr_conn_handle_inited) {
|
||||||
|
ble_gatts_indicate(heart_rate_chr_conn_handle,
|
||||||
|
heart_rate_chr_val_handle);
|
||||||
|
ESP_LOGI(TAG, "heart rate indication sent!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void heart_rate_task(void *param) {
|
||||||
|
/* Task entry log */
|
||||||
|
ESP_LOGI(TAG, "heart rate task has been started!");
|
||||||
|
|
||||||
|
/* Loop forever */
|
||||||
|
while (1) {
|
||||||
|
/* Update heart rate value every 1 second */
|
||||||
|
update_heart_rate();
|
||||||
|
ESP_LOGI(TAG, "heart rate updated to %d", get_heart_rate());
|
||||||
|
|
||||||
|
/* Send heart rate indication if enabled */
|
||||||
|
send_heart_rate_indication();
|
||||||
|
|
||||||
|
/* Sleep */
|
||||||
|
vTaskDelay(HEART_RATE_TASK_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up at exit */
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Observation
|
||||||
|
|
||||||
|
If everything goes well, you should be able to see 4 services when connected to ESP32, including
|
||||||
|
|
||||||
|
- Generic Access
|
||||||
|
- Generic Attribute
|
||||||
|
- Heart Rate
|
||||||
|
- Automation IO
|
||||||
|
|
||||||
|
Click on Automation IO Service, you should be able to see LED characteristic. Click on upload button, you should be able to write `ON` or `OFF` value. Send it to the device, LED will be turned on or off following your instruction.
|
||||||
|
|
||||||
|
Click on Heart Rate Service, you should be able to see Heart Rate Measurement characteristic. Click on download button, you should be able to see the latest heart rate measurement mock value, and it should be consistent with what is shown on serial output. Click on subscribe button, you should be able to see the heart rate measurement mock value updated every second.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
For any technical queries, please file an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,4 @@
|
|||||||
|
file(GLOB_RECURSE srcs "main.c" "src/*.c")
|
||||||
|
|
||||||
|
idf_component_register(SRCS "${srcs}"
|
||||||
|
INCLUDE_DIRS "./include")
|
@ -0,0 +1,42 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||||
|
|
||||||
|
choice BLINK_LED
|
||||||
|
prompt "Blink LED type"
|
||||||
|
default BLINK_LED_GPIO
|
||||||
|
help
|
||||||
|
Select the LED type. A normal level controlled LED or an addressable LED strip.
|
||||||
|
The default selection is based on the Espressif DevKit boards.
|
||||||
|
You can change the default selection according to your board.
|
||||||
|
|
||||||
|
config BLINK_LED_GPIO
|
||||||
|
bool "GPIO"
|
||||||
|
config BLINK_LED_STRIP
|
||||||
|
bool "LED strip"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
choice BLINK_LED_STRIP_BACKEND
|
||||||
|
depends on BLINK_LED_STRIP
|
||||||
|
prompt "LED strip backend peripheral"
|
||||||
|
default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
|
||||||
|
default BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
help
|
||||||
|
Select the backend peripheral to drive the LED strip.
|
||||||
|
|
||||||
|
config BLINK_LED_STRIP_BACKEND_RMT
|
||||||
|
depends on SOC_RMT_SUPPORTED
|
||||||
|
bool "RMT"
|
||||||
|
config BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
bool "SPI"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config BLINK_GPIO
|
||||||
|
int "Blink GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 8
|
||||||
|
help
|
||||||
|
GPIO number (IOxx) to blink on and off the LED.
|
||||||
|
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
|
||||||
|
|
||||||
|
endmenu
|
@ -0,0 +1,2 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip: "^2.4.1"
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* STD APIs */
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* FreeRTOS APIs */
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
/* NimBLE stack APIs */
|
||||||
|
#include "host/ble_hs.h"
|
||||||
|
#include "host/ble_uuid.h"
|
||||||
|
#include "host/util/util.h"
|
||||||
|
#include "nimble/ble.h"
|
||||||
|
#include "nimble/nimble_port.h"
|
||||||
|
#include "nimble/nimble_port_freertos.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define TAG "NimBLE_GATT_Server"
|
||||||
|
#define DEVICE_NAME "NimBLE_GATT"
|
||||||
|
|
||||||
|
#endif // COMMON_H
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef GAP_SVC_H
|
||||||
|
#define GAP_SVC_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* NimBLE GAP APIs */
|
||||||
|
#include "host/ble_gap.h"
|
||||||
|
#include "services/gap/ble_svc_gap.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define BLE_GAP_APPEARANCE_GENERIC_TAG 0x0200
|
||||||
|
#define BLE_GAP_URI_PREFIX_HTTPS 0x17
|
||||||
|
#define BLE_GAP_LE_ROLE_PERIPHERAL 0x00
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
void adv_init(void);
|
||||||
|
int gap_init(void);
|
||||||
|
|
||||||
|
#endif // GAP_SVC_H
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef GATT_SVR_H
|
||||||
|
#define GATT_SVR_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* NimBLE GATT APIs */
|
||||||
|
#include "host/ble_gatt.h"
|
||||||
|
#include "services/gatt/ble_svc_gatt.h"
|
||||||
|
|
||||||
|
/* NimBLE GAP APIs */
|
||||||
|
#include "host/ble_gap.h"
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
void send_heart_rate_indication(void);
|
||||||
|
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
|
||||||
|
void gatt_svr_subscribe_cb(struct ble_gap_event *event);
|
||||||
|
int gatt_svc_init(void);
|
||||||
|
|
||||||
|
#endif // GATT_SVR_H
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef HEART_RATE_H
|
||||||
|
#define HEART_RATE_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "esp_random.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define HEART_RATE_TASK_PERIOD (1000 / portTICK_PERIOD_MS)
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
uint8_t get_heart_rate(void);
|
||||||
|
void update_heart_rate(void);
|
||||||
|
|
||||||
|
#endif // HEART_RATE_H
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef LED_H
|
||||||
|
#define LED_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define BLINK_GPIO CONFIG_BLINK_GPIO
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
uint8_t get_led_state(void);
|
||||||
|
void led_on(void);
|
||||||
|
void led_off(void);
|
||||||
|
void led_init(void);
|
||||||
|
|
||||||
|
#endif // LED_H
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "common.h"
|
||||||
|
#include "gap.h"
|
||||||
|
#include "gatt_svc.h"
|
||||||
|
#include "heart_rate.h"
|
||||||
|
#include "led.h"
|
||||||
|
|
||||||
|
/* Library function declarations */
|
||||||
|
void ble_store_config_init(void);
|
||||||
|
|
||||||
|
/* Private function declarations */
|
||||||
|
static void on_stack_reset(int reason);
|
||||||
|
static void on_stack_sync(void);
|
||||||
|
static void nimble_host_config_init(void);
|
||||||
|
static void nimble_host_task(void *param);
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
/*
|
||||||
|
* Stack event callback functions
|
||||||
|
* - on_stack_reset is called when host resets BLE stack due to errors
|
||||||
|
* - on_stack_sync is called when host has synced with controller
|
||||||
|
*/
|
||||||
|
static void on_stack_reset(int reason) {
|
||||||
|
/* On reset, print reset reason to console */
|
||||||
|
ESP_LOGI(TAG, "nimble stack reset, reset reason: %d", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_stack_sync(void) {
|
||||||
|
/* On stack sync, do advertising initialization */
|
||||||
|
adv_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nimble_host_config_init(void) {
|
||||||
|
/* Set host callbacks */
|
||||||
|
ble_hs_cfg.reset_cb = on_stack_reset;
|
||||||
|
ble_hs_cfg.sync_cb = on_stack_sync;
|
||||||
|
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
|
||||||
|
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||||
|
|
||||||
|
/* Store host configuration */
|
||||||
|
ble_store_config_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nimble_host_task(void *param) {
|
||||||
|
/* Task entry log */
|
||||||
|
ESP_LOGI(TAG, "nimble host task has been started!");
|
||||||
|
|
||||||
|
/* This function won't return until nimble_port_stop() is executed */
|
||||||
|
nimble_port_run();
|
||||||
|
|
||||||
|
/* Clean up at exit */
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void heart_rate_task(void *param) {
|
||||||
|
/* Task entry log */
|
||||||
|
ESP_LOGI(TAG, "heart rate task has been started!");
|
||||||
|
|
||||||
|
/* Loop forever */
|
||||||
|
while (1) {
|
||||||
|
/* Update heart rate value every 1 second */
|
||||||
|
update_heart_rate();
|
||||||
|
ESP_LOGI(TAG, "heart rate updated to %d", get_heart_rate());
|
||||||
|
|
||||||
|
/* Send heart rate indication if enabled */
|
||||||
|
send_heart_rate_indication();
|
||||||
|
|
||||||
|
/* Sleep */
|
||||||
|
vTaskDelay(HEART_RATE_TASK_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up at exit */
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc;
|
||||||
|
esp_err_t ret;
|
||||||
|
|
||||||
|
/* LED initialization */
|
||||||
|
led_init();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NVS flash initialization
|
||||||
|
* Dependency of BLE stack to store configurations
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nvs flash, error code: %d ", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NimBLE stack initialization */
|
||||||
|
ret = nimble_port_init();
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nimble stack, error code: %d ",
|
||||||
|
ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GAP service initialization */
|
||||||
|
rc = gap_init();
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GATT server initialization */
|
||||||
|
rc = gatt_svc_init();
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize GATT server, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NimBLE host configuration initialization */
|
||||||
|
nimble_host_config_init();
|
||||||
|
|
||||||
|
/* Start NimBLE host task thread and return */
|
||||||
|
xTaskCreate(nimble_host_task, "NimBLE Host", 4*1024, NULL, 5, NULL);
|
||||||
|
xTaskCreate(heart_rate_task, "Heart Rate", 4*1024, NULL, 5, NULL);
|
||||||
|
return;
|
||||||
|
}
|
@ -0,0 +1,306 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "gap.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "gatt_svc.h"
|
||||||
|
|
||||||
|
/* Private function declarations */
|
||||||
|
inline static void format_addr(char *addr_str, uint8_t addr[]);
|
||||||
|
static void print_conn_desc(struct ble_gap_conn_desc *desc);
|
||||||
|
static void start_advertising(void);
|
||||||
|
static int gap_event_handler(struct ble_gap_event *event, void *arg);
|
||||||
|
|
||||||
|
/* Private variables */
|
||||||
|
static uint8_t own_addr_type;
|
||||||
|
static uint8_t addr_val[6] = {0};
|
||||||
|
static uint8_t esp_uri[] = {BLE_GAP_URI_PREFIX_HTTPS, '/', '/', 'e', 's', 'p', 'r', 'e', 's', 's', 'i', 'f', '.', 'c', 'o', 'm'};
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
inline static void format_addr(char *addr_str, uint8_t addr[]) {
|
||||||
|
sprintf(addr_str, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1],
|
||||||
|
addr[2], addr[3], addr[4], addr[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_conn_desc(struct ble_gap_conn_desc *desc) {
|
||||||
|
/* Local variables */
|
||||||
|
char addr_str[18] = {0};
|
||||||
|
|
||||||
|
/* Connection handle */
|
||||||
|
ESP_LOGI(TAG, "connection handle: %d", desc->conn_handle);
|
||||||
|
|
||||||
|
/* Local ID address */
|
||||||
|
format_addr(addr_str, desc->our_id_addr.val);
|
||||||
|
ESP_LOGI(TAG, "device id address: type=%d, value=%s",
|
||||||
|
desc->our_id_addr.type, addr_str);
|
||||||
|
|
||||||
|
/* Peer ID address */
|
||||||
|
format_addr(addr_str, desc->peer_id_addr.val);
|
||||||
|
ESP_LOGI(TAG, "peer id address: type=%d, value=%s", desc->peer_id_addr.type,
|
||||||
|
addr_str);
|
||||||
|
|
||||||
|
/* Connection info */
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"conn_itvl=%d, conn_latency=%d, supervision_timeout=%d, "
|
||||||
|
"encrypted=%d, authenticated=%d, bonded=%d\n",
|
||||||
|
desc->conn_itvl, desc->conn_latency, desc->supervision_timeout,
|
||||||
|
desc->sec_state.encrypted, desc->sec_state.authenticated,
|
||||||
|
desc->sec_state.bonded);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_advertising(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
const char *name;
|
||||||
|
struct ble_hs_adv_fields adv_fields = {0};
|
||||||
|
struct ble_hs_adv_fields rsp_fields = {0};
|
||||||
|
struct ble_gap_adv_params adv_params = {0};
|
||||||
|
|
||||||
|
/* Set advertising flags */
|
||||||
|
adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
||||||
|
|
||||||
|
/* Set device name */
|
||||||
|
name = ble_svc_gap_device_name();
|
||||||
|
adv_fields.name = (uint8_t *)name;
|
||||||
|
adv_fields.name_len = strlen(name);
|
||||||
|
adv_fields.name_is_complete = 1;
|
||||||
|
|
||||||
|
/* Set device tx power */
|
||||||
|
adv_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||||
|
adv_fields.tx_pwr_lvl_is_present = 1;
|
||||||
|
|
||||||
|
/* Set device appearance */
|
||||||
|
adv_fields.appearance = BLE_GAP_APPEARANCE_GENERIC_TAG;
|
||||||
|
adv_fields.appearance_is_present = 1;
|
||||||
|
|
||||||
|
/* Set device LE role */
|
||||||
|
adv_fields.le_role = BLE_GAP_LE_ROLE_PERIPHERAL;
|
||||||
|
adv_fields.le_role_is_present = 1;
|
||||||
|
|
||||||
|
/* Set advertiement fields */
|
||||||
|
rc = ble_gap_adv_set_fields(&adv_fields);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set advertising data, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set device address */
|
||||||
|
rsp_fields.device_addr = addr_val;
|
||||||
|
rsp_fields.device_addr_type = own_addr_type;
|
||||||
|
rsp_fields.device_addr_is_present = 1;
|
||||||
|
|
||||||
|
/* Set URI */
|
||||||
|
rsp_fields.uri = esp_uri;
|
||||||
|
rsp_fields.uri_len = sizeof(esp_uri);
|
||||||
|
|
||||||
|
/* Set advertising interval */
|
||||||
|
rsp_fields.adv_itvl = BLE_GAP_ADV_ITVL_MS(500);
|
||||||
|
rsp_fields.adv_itvl_is_present = 1;
|
||||||
|
|
||||||
|
/* Set scan response fields */
|
||||||
|
rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set scan response data, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set non-connetable and general discoverable mode to be a beacon */
|
||||||
|
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||||
|
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||||
|
|
||||||
|
/* Set advertising interval */
|
||||||
|
adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(500);
|
||||||
|
adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(510);
|
||||||
|
|
||||||
|
/* Start advertising */
|
||||||
|
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params,
|
||||||
|
gap_event_handler, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to start advertising, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "advertising started!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NimBLE applies an event-driven model to keep GAP service going
|
||||||
|
* gap_event_handler is a callback function registered when calling
|
||||||
|
* ble_gap_adv_start API and called when a GAP event arrives
|
||||||
|
*/
|
||||||
|
static int gap_event_handler(struct ble_gap_event *event, void *arg) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
struct ble_gap_conn_desc desc;
|
||||||
|
|
||||||
|
/* Handle different GAP event */
|
||||||
|
switch (event->type) {
|
||||||
|
|
||||||
|
/* Connect event */
|
||||||
|
case BLE_GAP_EVENT_CONNECT:
|
||||||
|
/* A new connection was established or a connection attempt failed. */
|
||||||
|
ESP_LOGI(TAG, "connection %s; status=%d",
|
||||||
|
event->connect.status == 0 ? "established" : "failed",
|
||||||
|
event->connect.status);
|
||||||
|
|
||||||
|
/* Connection succeeded */
|
||||||
|
if (event->connect.status == 0) {
|
||||||
|
/* Check connection handle */
|
||||||
|
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"failed to find connection by handle, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print connection descriptor */
|
||||||
|
print_conn_desc(&desc);
|
||||||
|
|
||||||
|
/* Try to update connection parameters */
|
||||||
|
struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl,
|
||||||
|
.itvl_max = desc.conn_itvl,
|
||||||
|
.latency = 3,
|
||||||
|
.supervision_timeout =
|
||||||
|
desc.supervision_timeout};
|
||||||
|
rc = ble_gap_update_params(event->connect.conn_handle, ¶ms);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(
|
||||||
|
TAG,
|
||||||
|
"failed to update connection parameters, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Connection failed, restart advertising */
|
||||||
|
else {
|
||||||
|
start_advertising();
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Disconnect event */
|
||||||
|
case BLE_GAP_EVENT_DISCONNECT:
|
||||||
|
/* A connection was terminated, print connection descriptor */
|
||||||
|
ESP_LOGI(TAG, "disconnected from peer; reason=%d",
|
||||||
|
event->disconnect.reason);
|
||||||
|
|
||||||
|
/* Restart advertising */
|
||||||
|
start_advertising();
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Connection parameters update event */
|
||||||
|
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||||
|
/* The central has updated the connection parameters. */
|
||||||
|
ESP_LOGI(TAG, "connection updated; status=%d",
|
||||||
|
event->conn_update.status);
|
||||||
|
|
||||||
|
/* Print connection descriptor */
|
||||||
|
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to find connection by handle, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
print_conn_desc(&desc);
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Advertising complete event */
|
||||||
|
case BLE_GAP_EVENT_ADV_COMPLETE:
|
||||||
|
/* Advertising completed, restart advertising */
|
||||||
|
ESP_LOGI(TAG, "advertise complete; reason=%d",
|
||||||
|
event->adv_complete.reason);
|
||||||
|
start_advertising();
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Notification sent event */
|
||||||
|
case BLE_GAP_EVENT_NOTIFY_TX:
|
||||||
|
if ((event->notify_tx.status != 0) &&
|
||||||
|
(event->notify_tx.status != BLE_HS_EDONE)) {
|
||||||
|
/* Print notification info on error */
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"notify event; conn_handle=%d attr_handle=%d "
|
||||||
|
"status=%d is_indication=%d",
|
||||||
|
event->notify_tx.conn_handle, event->notify_tx.attr_handle,
|
||||||
|
event->notify_tx.status, event->notify_tx.indication);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Subscribe event */
|
||||||
|
case BLE_GAP_EVENT_SUBSCRIBE:
|
||||||
|
/* Print subscription info to log */
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"subscribe event; conn_handle=%d attr_handle=%d "
|
||||||
|
"reason=%d prevn=%d curn=%d previ=%d curi=%d",
|
||||||
|
event->subscribe.conn_handle, event->subscribe.attr_handle,
|
||||||
|
event->subscribe.reason, event->subscribe.prev_notify,
|
||||||
|
event->subscribe.cur_notify, event->subscribe.prev_indicate,
|
||||||
|
event->subscribe.cur_indicate);
|
||||||
|
|
||||||
|
/* GATT subscribe event callback */
|
||||||
|
gatt_svr_subscribe_cb(event);
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* MTU update event */
|
||||||
|
case BLE_GAP_EVENT_MTU:
|
||||||
|
/* Print MTU update info to log */
|
||||||
|
ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d",
|
||||||
|
event->mtu.conn_handle, event->mtu.channel_id,
|
||||||
|
event->mtu.value);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Public functions */
|
||||||
|
void adv_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
char addr_str[18] = {0};
|
||||||
|
|
||||||
|
/* Make sure we have proper BT identity address set (random preferred) */
|
||||||
|
rc = ble_hs_util_ensure_addr(0);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "device does not have any available bt address!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figure out BT address to use while advertising (no privacy for now) */
|
||||||
|
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to infer address type, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Printing ADDR */
|
||||||
|
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to copy device address, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
format_addr(addr_str, addr_val);
|
||||||
|
ESP_LOGI(TAG, "device address: %s", addr_str);
|
||||||
|
|
||||||
|
/* Start advertising. */
|
||||||
|
start_advertising();
|
||||||
|
}
|
||||||
|
|
||||||
|
int gap_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
/* Call NimBLE GAP initialization API */
|
||||||
|
ble_svc_gap_init();
|
||||||
|
|
||||||
|
/* Set GAP device name */
|
||||||
|
rc = ble_svc_gap_device_name_set(DEVICE_NAME);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to set device name to %s, error code: %d",
|
||||||
|
DEVICE_NAME, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "gatt_svc.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "heart_rate.h"
|
||||||
|
#include "led.h"
|
||||||
|
|
||||||
|
/* Private function declarations */
|
||||||
|
static int heart_rate_chr_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||||
|
struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||||
|
static int led_chr_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||||
|
struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||||
|
|
||||||
|
/* Private variables */
|
||||||
|
/* Heart rate service */
|
||||||
|
static const ble_uuid16_t heart_rate_svc_uuid = BLE_UUID16_INIT(0x180D);
|
||||||
|
|
||||||
|
static uint8_t heart_rate_chr_val[2] = {0};
|
||||||
|
static uint16_t heart_rate_chr_val_handle;
|
||||||
|
static const ble_uuid16_t heart_rate_chr_uuid = BLE_UUID16_INIT(0x2A37);
|
||||||
|
|
||||||
|
static uint16_t heart_rate_chr_conn_handle = 0;
|
||||||
|
static bool heart_rate_chr_conn_handle_inited = false;
|
||||||
|
static bool heart_rate_ind_status = false;
|
||||||
|
|
||||||
|
/* Automation IO service */
|
||||||
|
static const ble_uuid16_t auto_io_svc_uuid = BLE_UUID16_INIT(0x1815);
|
||||||
|
static uint16_t led_chr_val_handle;
|
||||||
|
static const ble_uuid128_t led_chr_uuid =
|
||||||
|
BLE_UUID128_INIT(0x23, 0xd1, 0xbc, 0xea, 0x5f, 0x78, 0x23, 0x15, 0xde, 0xef,
|
||||||
|
0x12, 0x12, 0x25, 0x15, 0x00, 0x00);
|
||||||
|
|
||||||
|
/* GATT services table */
|
||||||
|
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
|
||||||
|
/* Heart rate service */
|
||||||
|
{.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||||
|
.uuid = &heart_rate_svc_uuid.u,
|
||||||
|
.characteristics =
|
||||||
|
(struct ble_gatt_chr_def[]){
|
||||||
|
{/* Heart rate characteristic */
|
||||||
|
.uuid = &heart_rate_chr_uuid.u,
|
||||||
|
.access_cb = heart_rate_chr_access,
|
||||||
|
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_INDICATE,
|
||||||
|
.val_handle = &heart_rate_chr_val_handle},
|
||||||
|
{
|
||||||
|
0, /* No more characteristics in this service. */
|
||||||
|
}}},
|
||||||
|
|
||||||
|
/* Automation IO service */
|
||||||
|
{
|
||||||
|
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||||
|
.uuid = &auto_io_svc_uuid.u,
|
||||||
|
.characteristics =
|
||||||
|
(struct ble_gatt_chr_def[]){/* LED characteristic */
|
||||||
|
{.uuid = &led_chr_uuid.u,
|
||||||
|
.access_cb = led_chr_access,
|
||||||
|
.flags = BLE_GATT_CHR_F_WRITE,
|
||||||
|
.val_handle = &led_chr_val_handle},
|
||||||
|
{0}},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
0, /* No more services. */
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
static int heart_rate_chr_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||||
|
struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Handle access events */
|
||||||
|
/* Note: Heart rate characteristic is read only */
|
||||||
|
switch (ctxt->op) {
|
||||||
|
|
||||||
|
/* Read characteristic event */
|
||||||
|
case BLE_GATT_ACCESS_OP_READ_CHR:
|
||||||
|
/* Verify connection handle */
|
||||||
|
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
ESP_LOGI(TAG, "characteristic read; conn_handle=%d attr_handle=%d",
|
||||||
|
conn_handle, attr_handle);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "characteristic read by nimble stack; attr_handle=%d",
|
||||||
|
attr_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify attribute handle */
|
||||||
|
if (attr_handle == heart_rate_chr_val_handle) {
|
||||||
|
/* Update access buffer value */
|
||||||
|
heart_rate_chr_val[1] = get_heart_rate();
|
||||||
|
rc = os_mbuf_append(ctxt->om, &heart_rate_chr_val,
|
||||||
|
sizeof(heart_rate_chr_val));
|
||||||
|
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||||
|
}
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Unknown event */
|
||||||
|
default:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
ESP_LOGE(
|
||||||
|
TAG,
|
||||||
|
"unexpected access operation to heart rate characteristic, opcode: %d",
|
||||||
|
ctxt->op);
|
||||||
|
return BLE_ATT_ERR_UNLIKELY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int led_chr_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||||
|
struct ble_gatt_access_ctxt *ctxt, void *arg) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Handle access events */
|
||||||
|
/* Note: LED characteristic is write only */
|
||||||
|
switch (ctxt->op) {
|
||||||
|
|
||||||
|
/* Write characteristic event */
|
||||||
|
case BLE_GATT_ACCESS_OP_WRITE_CHR:
|
||||||
|
/* Verify connection handle */
|
||||||
|
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
ESP_LOGI(TAG, "characteristic write; conn_handle=%d attr_handle=%d",
|
||||||
|
conn_handle, attr_handle);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"characteristic write by nimble stack; attr_handle=%d",
|
||||||
|
attr_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify attribute handle */
|
||||||
|
if (attr_handle == led_chr_val_handle) {
|
||||||
|
/* Verify access buffer length */
|
||||||
|
if (ctxt->om->om_len == 1) {
|
||||||
|
/* Turn the LED on or off according to the operation bit */
|
||||||
|
if (ctxt->om->om_data[0]) {
|
||||||
|
led_on();
|
||||||
|
ESP_LOGI(TAG, "led turned on!");
|
||||||
|
} else {
|
||||||
|
led_off();
|
||||||
|
ESP_LOGI(TAG, "led turned off!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Unknown event */
|
||||||
|
default:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"unexpected access operation to led characteristic, opcode: %d",
|
||||||
|
ctxt->op);
|
||||||
|
return BLE_ATT_ERR_UNLIKELY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Public functions */
|
||||||
|
void send_heart_rate_indication(void) {
|
||||||
|
if (heart_rate_ind_status && heart_rate_chr_conn_handle_inited) {
|
||||||
|
ble_gatts_indicate(heart_rate_chr_conn_handle,
|
||||||
|
heart_rate_chr_val_handle);
|
||||||
|
ESP_LOGI(TAG, "heart rate indication sent!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle GATT attribute register events
|
||||||
|
* - Service register event
|
||||||
|
* - Characteristic register event
|
||||||
|
* - Descriptor register event
|
||||||
|
*/
|
||||||
|
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) {
|
||||||
|
/* Local variables */
|
||||||
|
char buf[BLE_UUID_STR_LEN];
|
||||||
|
|
||||||
|
/* Handle GATT attributes register events */
|
||||||
|
switch (ctxt->op) {
|
||||||
|
|
||||||
|
/* Service register event */
|
||||||
|
case BLE_GATT_REGISTER_OP_SVC:
|
||||||
|
ESP_LOGD(TAG, "registered service %s with handle=%d",
|
||||||
|
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
|
||||||
|
ctxt->svc.handle);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Characteristic register event */
|
||||||
|
case BLE_GATT_REGISTER_OP_CHR:
|
||||||
|
ESP_LOGD(TAG,
|
||||||
|
"registering characteristic %s with "
|
||||||
|
"def_handle=%d val_handle=%d",
|
||||||
|
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
|
||||||
|
ctxt->chr.def_handle, ctxt->chr.val_handle);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Descriptor register event */
|
||||||
|
case BLE_GATT_REGISTER_OP_DSC:
|
||||||
|
ESP_LOGD(TAG, "registering descriptor %s with handle=%d",
|
||||||
|
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
|
||||||
|
ctxt->dsc.handle);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Unknown event */
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GATT server subscribe event callback
|
||||||
|
* 1. Update heart rate subscription status
|
||||||
|
*/
|
||||||
|
|
||||||
|
void gatt_svr_subscribe_cb(struct ble_gap_event *event) {
|
||||||
|
/* Check connection handle */
|
||||||
|
if (event->subscribe.conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||||
|
ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d",
|
||||||
|
event->subscribe.conn_handle, event->subscribe.attr_handle);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "subscribe by nimble stack; attr_handle=%d",
|
||||||
|
event->subscribe.attr_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check attribute handle */
|
||||||
|
if (event->subscribe.attr_handle == heart_rate_chr_val_handle) {
|
||||||
|
/* Update heart rate subscription status */
|
||||||
|
heart_rate_chr_conn_handle = event->subscribe.conn_handle;
|
||||||
|
heart_rate_chr_conn_handle_inited = true;
|
||||||
|
heart_rate_ind_status = event->subscribe.cur_indicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GATT server initialization
|
||||||
|
* 1. Initialize GATT service
|
||||||
|
* 2. Update NimBLE host GATT services counter
|
||||||
|
* 3. Add GATT services to server
|
||||||
|
*/
|
||||||
|
int gatt_svc_init(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* 1. GATT service initialization */
|
||||||
|
ble_svc_gatt_init();
|
||||||
|
|
||||||
|
/* 2. Update GATT services counter */
|
||||||
|
rc = ble_gatts_count_cfg(gatt_svr_svcs);
|
||||||
|
if (rc != 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Add GATT services */
|
||||||
|
rc = ble_gatts_add_svcs(gatt_svr_svcs);
|
||||||
|
if (rc != 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "common.h"
|
||||||
|
#include "heart_rate.h"
|
||||||
|
|
||||||
|
/* Private variables */
|
||||||
|
static uint8_t heart_rate;
|
||||||
|
|
||||||
|
/* Public functions */
|
||||||
|
uint8_t get_heart_rate(void) { return heart_rate; }
|
||||||
|
|
||||||
|
void update_heart_rate(void) { heart_rate = 60 + (uint8_t)(esp_random() % 21); }
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "led.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/* Private variables */
|
||||||
|
static uint8_t led_state;
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLINK_LED_STRIP
|
||||||
|
static led_strip_handle_t led_strip;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Public functions */
|
||||||
|
uint8_t get_led_state(void) { return led_state; }
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLINK_LED_STRIP
|
||||||
|
|
||||||
|
void led_on(void) {
|
||||||
|
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||||
|
led_strip_set_pixel(led_strip, 0, 16, 16, 16);
|
||||||
|
|
||||||
|
/* Refresh the strip to send data */
|
||||||
|
led_strip_refresh(led_strip);
|
||||||
|
|
||||||
|
/* Update LED state */
|
||||||
|
led_state = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_off(void) {
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
led_strip_clear(led_strip);
|
||||||
|
|
||||||
|
/* Update LED state */
|
||||||
|
led_state = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void led_init(void) {
|
||||||
|
ESP_LOGI(TAG, "example configured to blink addressable led!");
|
||||||
|
/* LED strip initialization with the GPIO and pixels number*/
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = CONFIG_BLINK_GPIO,
|
||||||
|
.max_leds = 1, // at least one LED on board
|
||||||
|
};
|
||||||
|
#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT
|
||||||
|
led_strip_rmt_config_t rmt_config = {
|
||||||
|
.resolution_hz = 10 * 1000 * 1000, // 10MHz
|
||||||
|
.flags.with_dma = false,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(
|
||||||
|
led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||||
|
#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
led_strip_spi_config_t spi_config = {
|
||||||
|
.spi_bus = SPI2_HOST,
|
||||||
|
.flags.with_dma = true,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(
|
||||||
|
led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||||
|
#else
|
||||||
|
#error "unsupported LED strip backend"
|
||||||
|
#endif
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
led_off();
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif CONFIG_BLINK_LED_GPIO
|
||||||
|
|
||||||
|
void led_on(void) { gpio_set_level(CONFIG_BLINK_GPIO, true); }
|
||||||
|
|
||||||
|
void led_off(void) { gpio_set_level(CONFIG_BLINK_GPIO, false); }
|
||||||
|
|
||||||
|
void led_init(void) {
|
||||||
|
ESP_LOGI(TAG, "example configured to blink gpio led!");
|
||||||
|
gpio_reset_pin(CONFIG_BLINK_GPIO);
|
||||||
|
/* Set the GPIO as a push/pull output */
|
||||||
|
gpio_set_direction(CONFIG_BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "unsupported LED type"
|
||||||
|
#endif
|
@ -0,0 +1,6 @@
|
|||||||
|
CONFIG_BT_ENABLED=y
|
||||||
|
CONFIG_BT_NIMBLE_ENABLED=y
|
||||||
|
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n
|
||||||
|
|
||||||
|
CONFIG_BLINK_LED_GPIO=y
|
||||||
|
CONFIG_BLINK_GPIO=8
|
@ -0,0 +1 @@
|
|||||||
|
CONFIG_BLINK_GPIO=5
|
@ -0,0 +1 @@
|
|||||||
|
CONFIG_BLINK_LED_STRIP=y
|
@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_BLINK_LED_STRIP=y
|
||||||
|
CONFIG_BLINK_GPIO=48
|
@ -0,0 +1,6 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(nimble_security)
|
@ -0,0 +1,224 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# NimBLE Security Example
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This example is extended from NimBLE GATT Server Example, and further introduces
|
||||||
|
|
||||||
|
1. How to set random non-resolvable private address for device
|
||||||
|
2. How to ask for connection encryption from peripheral side on characteristic access
|
||||||
|
3. How to bond with peer device using a random generated 6-digit passkey
|
||||||
|
|
||||||
|
It uses ESP32's Bluetooth controller and NimBLE host stack.
|
||||||
|
|
||||||
|
To test this demo, any BLE scanner application can be used.
|
||||||
|
|
||||||
|
## Try It Yourself
|
||||||
|
|
||||||
|
### Set Target
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
idf.py set-target <chip_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if you're using ESP32, then input
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py set-target esp32
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run the following command to build, flash and monitor the project.
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py -p <PORT> flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if the corresponding serial port is `/dev/ttyACM0`, then it goes
|
||||||
|
|
||||||
|
``` Shell
|
||||||
|
idf.py -p /dev/ttyACM0 flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Code Explained
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
The following is additional content compared to the NimBLE GATT Server example.
|
||||||
|
|
||||||
|
1. Initialization procedure is generally similar to NimBLE GATT Server Example, but we'll initialize random number generator in the beginning, and in `nimble_host_config_init` we will configure security manager to enable related features
|
||||||
|
2. On stack sync, a random non-resolvable private address is generated and set as the device address
|
||||||
|
3. Characteristics' permission modified to require connection encryption when accessing
|
||||||
|
4. 3 more GAP event branches added in `gap_event_handler` to handle encryption related events
|
||||||
|
|
||||||
|
### Entry Point
|
||||||
|
|
||||||
|
In `nimble_host_config_init` function, we're going to enable some security manager features, including
|
||||||
|
|
||||||
|
- Bonding
|
||||||
|
- Man-in-the-middle protection
|
||||||
|
- Key distribution
|
||||||
|
|
||||||
|
Also, we're going to set the IO capability to `BLE_HS_IO_DISPLAY_ONLY`, since it's possible to print out the passkey to serial output.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void nimble_host_config_init(void) {
|
||||||
|
...
|
||||||
|
|
||||||
|
/* Security manager configuration */
|
||||||
|
ble_hs_cfg.sm_io_cap = BLE_HS_IO_DISPLAY_ONLY;
|
||||||
|
ble_hs_cfg.sm_bonding = 1;
|
||||||
|
ble_hs_cfg.sm_mitm = 1;
|
||||||
|
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
|
||||||
|
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GATT Server Updates
|
||||||
|
|
||||||
|
For heart rate characteristic and LED characteristic, `BLE_GATT_CHR_F_READ_ENC` and `BLE_GATT_CHR_F_WRITE_ENC` flag are added respectively to require connection encryption when GATT client tries to access the characteristic. Thanks to NimBLE host stack, the connection encryption will be initiated automatically by adding these flags.
|
||||||
|
|
||||||
|
However, heart rate characteristic is also indicatable, and NimBLE host stack does not offer an implementation for indication access to require connection encryption, so we need to do it ourselves. For GATT server, we simply check connection security status by calling an external function `is_connection_encrypted` in `send_heart_rate_indication` function to determine if the indication should be sent. This external function is defined in GAP layer, and we'll talk about it in *GAP Event Handler Updates* section.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
void send_heart_rate_indication(void) {
|
||||||
|
/* Check if connection handle is initialized */
|
||||||
|
if (!heart_rate_chr_conn_handle_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check indication and security status */
|
||||||
|
if (heart_rate_ind_status &&
|
||||||
|
is_connection_encrypted(heart_rate_chr_conn_handle)) {
|
||||||
|
ble_gatts_indicate(heart_rate_chr_conn_handle,
|
||||||
|
heart_rate_chr_val_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Random Address
|
||||||
|
|
||||||
|
In the following function, we can generate a random non-resolvable private address and set as the device address. We will call it in `adv_init` function before ensuring address availability.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
static void set_random_addr(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
ble_addr_t addr;
|
||||||
|
|
||||||
|
/* Generate new non-resolvable private address */
|
||||||
|
rc = ble_hs_id_gen_rnd(0, &addr);
|
||||||
|
assert(rc == 0);
|
||||||
|
|
||||||
|
/* Set address */
|
||||||
|
rc = ble_hs_id_set_rnd(addr.val);
|
||||||
|
assert(rc == 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Connection Encryption Status
|
||||||
|
|
||||||
|
By connection handle, we can fetch connection descriptor from NimBLE host stack, and there's a flag indicating connection encryption status, check the following codes
|
||||||
|
|
||||||
|
``` C
|
||||||
|
bool is_connection_encrypted(uint16_t conn_handle) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc = 0;
|
||||||
|
struct ble_gap_conn_desc desc;
|
||||||
|
|
||||||
|
/* Print connection descriptor */
|
||||||
|
rc = ble_gap_conn_find(conn_handle, &desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to find connection by handle, error code: %d",
|
||||||
|
rc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc.sec_state.encrypted;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GAP Event Handler Updates
|
||||||
|
|
||||||
|
3 more GAP event branches are added in `gap_event_handler`, which are
|
||||||
|
|
||||||
|
- `BLE_GAP_EVENT_ENC_CHANGE` - Encryption change event
|
||||||
|
- `BLE_GAP_EVENT_REPEAT_PAIRING` - Repeat pairing event
|
||||||
|
- `BLE_GAP_EVENT_PASSKEY_ACTION` - Passkey action event
|
||||||
|
|
||||||
|
On encryption change event, we're going to print the encryption change status to output.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
/* Encryption change event */
|
||||||
|
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||||
|
/* Encryption has been enabled or disabled for this connection. */
|
||||||
|
if (event->enc_change.status == 0) {
|
||||||
|
ESP_LOGI(TAG, "connection encrypted!");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "connection encryption failed, status: %d",
|
||||||
|
event->enc_change.status);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
```
|
||||||
|
|
||||||
|
On repeat pairing event, to make it simple, we will just delete the old bond and repeat pairing.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
/* Repeat pairing event */
|
||||||
|
case BLE_GAP_EVENT_REPEAT_PAIRING:
|
||||||
|
/* Delete the old bond */
|
||||||
|
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to find connection, error code %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||||
|
|
||||||
|
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
|
||||||
|
* continue with pairing operation */
|
||||||
|
ESP_LOGI(TAG, "repairing...");
|
||||||
|
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
||||||
|
```
|
||||||
|
|
||||||
|
On passkey action event, a random 6-digit passkey is generated, and you are supposed to enter the same passkey on pairing. If the input is consistent with the generated passkey, you should be able to bond with the device.
|
||||||
|
|
||||||
|
``` C
|
||||||
|
/* Passkey action event */
|
||||||
|
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||||
|
/* Display action */
|
||||||
|
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||||
|
/* Generate passkey */
|
||||||
|
struct ble_sm_io pkey = {0};
|
||||||
|
pkey.action = event->passkey.params.action;
|
||||||
|
pkey.passkey = 100000 + esp_random() % 900000;
|
||||||
|
ESP_LOGI(TAG, "enter passkey %" PRIu32 " on the peer side",
|
||||||
|
pkey.passkey);
|
||||||
|
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"failed to inject security manager io, error code: %d",
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Observation
|
||||||
|
|
||||||
|
If everything goes well, pairing will be required when you try to access any of the characteristics, that is, read or indicate heart rate characteristic, or write LED characteristic.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
For any technical queries, please file an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,4 @@
|
|||||||
|
file(GLOB_RECURSE srcs "main.c" "src/*.c")
|
||||||
|
|
||||||
|
idf_component_register(SRCS "${srcs}"
|
||||||
|
INCLUDE_DIRS "./include")
|
@ -0,0 +1,42 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||||
|
|
||||||
|
choice BLINK_LED
|
||||||
|
prompt "Blink LED type"
|
||||||
|
default BLINK_LED_GPIO
|
||||||
|
help
|
||||||
|
Select the LED type. A normal level controlled LED or an addressable LED strip.
|
||||||
|
The default selection is based on the Espressif DevKit boards.
|
||||||
|
You can change the default selection according to your board.
|
||||||
|
|
||||||
|
config BLINK_LED_GPIO
|
||||||
|
bool "GPIO"
|
||||||
|
config BLINK_LED_STRIP
|
||||||
|
bool "LED strip"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
choice BLINK_LED_STRIP_BACKEND
|
||||||
|
depends on BLINK_LED_STRIP
|
||||||
|
prompt "LED strip backend peripheral"
|
||||||
|
default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
|
||||||
|
default BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
help
|
||||||
|
Select the backend peripheral to drive the LED strip.
|
||||||
|
|
||||||
|
config BLINK_LED_STRIP_BACKEND_RMT
|
||||||
|
depends on SOC_RMT_SUPPORTED
|
||||||
|
bool "RMT"
|
||||||
|
config BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
bool "SPI"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config BLINK_GPIO
|
||||||
|
int "Blink GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 8
|
||||||
|
help
|
||||||
|
GPIO number (IOxx) to blink on and off the LED.
|
||||||
|
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
|
||||||
|
|
||||||
|
endmenu
|
@ -0,0 +1,2 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip: "^2.4.1"
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* STD APIs */
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_random.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* FreeRTOS APIs */
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
|
||||||
|
/* NimBLE stack APIs */
|
||||||
|
#include "host/ble_hs.h"
|
||||||
|
#include "host/ble_uuid.h"
|
||||||
|
#include "host/util/util.h"
|
||||||
|
#include "nimble/ble.h"
|
||||||
|
#include "nimble/nimble_port.h"
|
||||||
|
#include "nimble/nimble_port_freertos.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define TAG "NimBLE_Security"
|
||||||
|
#define DEVICE_NAME "NimBLE_SEC"
|
||||||
|
|
||||||
|
#endif // COMMON_H
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef GAP_SVC_H
|
||||||
|
#define GAP_SVC_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* NimBLE GAP APIs */
|
||||||
|
#include "host/ble_gap.h"
|
||||||
|
#include "services/gap/ble_svc_gap.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define BLE_GAP_APPEARANCE_GENERIC_TAG 0x0200
|
||||||
|
#define BLE_GAP_URI_PREFIX_HTTPS 0x17
|
||||||
|
#define BLE_GAP_LE_ROLE_PERIPHERAL 0x00
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
void adv_init(void);
|
||||||
|
bool is_connection_encrypted(uint16_t conn_handle);
|
||||||
|
int gap_init(void);
|
||||||
|
|
||||||
|
#endif // GAP_SVC_H
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef GATT_SVR_H
|
||||||
|
#define GATT_SVR_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* NimBLE GATT APIs */
|
||||||
|
#include "host/ble_gatt.h"
|
||||||
|
#include "services/gatt/ble_svc_gatt.h"
|
||||||
|
|
||||||
|
/* NimBLE GAP APIs */
|
||||||
|
#include "host/ble_gap.h"
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
void send_heart_rate_indication(void);
|
||||||
|
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
|
||||||
|
int gatt_svr_subscribe_cb(struct ble_gap_event *event);
|
||||||
|
int gatt_svc_init(void);
|
||||||
|
|
||||||
|
#endif // GATT_SVR_H
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef HEART_RATE_H
|
||||||
|
#define HEART_RATE_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "esp_random.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define HEART_RATE_TASK_PERIOD (1000 / portTICK_PERIOD_MS)
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
uint8_t get_heart_rate(void);
|
||||||
|
void update_heart_rate(void);
|
||||||
|
|
||||||
|
#endif // HEART_RATE_H
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#ifndef LED_H
|
||||||
|
#define LED_H
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
/* ESP APIs */
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define BLINK_GPIO CONFIG_BLINK_GPIO
|
||||||
|
|
||||||
|
/* Public function declarations */
|
||||||
|
uint8_t get_led_state(void);
|
||||||
|
void led_on(void);
|
||||||
|
void led_off(void);
|
||||||
|
void led_init(void);
|
||||||
|
|
||||||
|
#endif // LED_H
|
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
/* Includes */
|
||||||
|
#include "common.h"
|
||||||
|
#include "gap.h"
|
||||||
|
#include "gatt_svc.h"
|
||||||
|
#include "heart_rate.h"
|
||||||
|
#include "led.h"
|
||||||
|
|
||||||
|
/* Library function declarations */
|
||||||
|
void ble_store_config_init(void);
|
||||||
|
|
||||||
|
/* Private function declarations */
|
||||||
|
static void nimble_host_config_init(void);
|
||||||
|
static void nimble_host_task(void *param);
|
||||||
|
|
||||||
|
/* Private functions */
|
||||||
|
/*
|
||||||
|
* Stack event callback functions
|
||||||
|
* - on_stack_reset is called when host resets BLE stack due to errors
|
||||||
|
* - on_stack_sync is called when host has synced with controller
|
||||||
|
*/
|
||||||
|
static void on_stack_reset(int reason) {
|
||||||
|
/* On reset, print reset reason to console */
|
||||||
|
ESP_LOGI(TAG, "nimble stack reset, reset reason: %d", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_stack_sync(void) {
|
||||||
|
/* On stack sync, do advertising initialization */
|
||||||
|
adv_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nimble_host_config_init(void) {
|
||||||
|
/* Set host callbacks */
|
||||||
|
ble_hs_cfg.reset_cb = on_stack_reset;
|
||||||
|
ble_hs_cfg.sync_cb = on_stack_sync;
|
||||||
|
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
|
||||||
|
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||||
|
|
||||||
|
/* Security manager configuration */
|
||||||
|
ble_hs_cfg.sm_io_cap = BLE_HS_IO_DISPLAY_ONLY;
|
||||||
|
ble_hs_cfg.sm_bonding = 1;
|
||||||
|
ble_hs_cfg.sm_mitm = 1;
|
||||||
|
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
|
||||||
|
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
|
||||||
|
|
||||||
|
/* Store host configuration */
|
||||||
|
ble_store_config_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nimble_host_task(void *param) {
|
||||||
|
/* Task entry log */
|
||||||
|
ESP_LOGI(TAG, "nimble host task has been started!");
|
||||||
|
|
||||||
|
/* This function won't return until nimble_port_stop() is executed */
|
||||||
|
nimble_port_run();
|
||||||
|
|
||||||
|
/* Clean up at exit */
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void heart_rate_task(void *param) {
|
||||||
|
/* Task entry log */
|
||||||
|
ESP_LOGI(TAG, "heart rate task has been started!");
|
||||||
|
|
||||||
|
/* Loop forever */
|
||||||
|
while (1) {
|
||||||
|
/* Update heart rate value every 1 second */
|
||||||
|
update_heart_rate();
|
||||||
|
ESP_LOGI(TAG, "heart rate updated to %d", get_heart_rate());
|
||||||
|
|
||||||
|
/* Send heart rate indication if enabled */
|
||||||
|
send_heart_rate_indication();
|
||||||
|
|
||||||
|
/* Sleep */
|
||||||
|
vTaskDelay(HEART_RATE_TASK_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up at exit */
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
/* Local variables */
|
||||||
|
int rc;
|
||||||
|
uint32_t seed = esp_random();
|
||||||
|
esp_err_t ret;
|
||||||
|
|
||||||
|
/* LED initialization */
|
||||||
|
led_init();
|
||||||
|
|
||||||
|
/* Random generator initialization */
|
||||||
|
srand(seed);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NVS flash initialization
|
||||||
|
* Dependency of BLE stack to store configurations
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nvs flash, error code: %d ", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NimBLE stack initialization */
|
||||||
|
ret = nimble_port_init();
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize nimble stack, error code: %d ",
|
||||||
|
ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GAP service initialization */
|
||||||
|
rc = gap_init();
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GATT server initialization */
|
||||||
|
rc = gatt_svc_init();
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "failed to initialize GATT server, error code: %d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NimBLE host configuration initialization */
|
||||||
|
nimble_host_config_init();
|
||||||
|
|
||||||
|
/* Start NimBLE host task thread and return */
|
||||||
|
xTaskCreate(nimble_host_task, "NimBLE Host", 4*1024, NULL, 5, NULL);
|
||||||
|
xTaskCreate(heart_rate_task, "Heart Rate", 4*1024, NULL, 5, NULL);
|
||||||
|
return;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user