Compare commits

...

37 Commits

Author SHA1 Message Date
Jiang Jiang Jian
c5eb3eaef5 Merge branch 'bugfix/fix_sdp_bugs_v5.0' into 'release/v5.0'
Some checks failed
docker / docker (push) Has been cancelled
Bugfix/fix sdp bugs[backport 5.0]

See merge request espressif/esp-idf!33605
2024-09-21 15:02:58 +08:00
Jiang Jiang Jian
2d02e60d7d Merge branch 'bugfix/fix_missing_per_adv_sync_est_evt_v5.0' into 'release/v5.0'
fix(bt): Update bt lib for ESP32-C3 and ESP32-S3(c66a703) (Backport v5.0)

See merge request espressif/esp-idf!33454
2024-09-21 15:02:29 +08:00
Jiang Jiang Jian
b2dae87114 Merge branch 'bugfix/fix_ble_max_tx_power_on_esp32c3_v5.0' into 'release/v5.0'
fix(bt/controller): Change the max TX power to +20dBm on ESP32-C3 and ESP32-S3 (v5.0)

See merge request espressif/esp-idf!33614
2024-09-21 15:02:04 +08:00
Jiang Jiang Jian
f8a3ba1400 Merge branch 'refactor/rtc_init_before_mspi_tuning_v5.0' into 'release/v5.0'
fix(startup): move rtc initialization before MSPI timing tuning to improve stability (v5.0)

See merge request espressif/esp-idf!33627
2024-09-21 14:59:55 +08:00
Jiang Jiang Jian
c01e90daa8 Merge branch 'feat/support_blecrt_242_v5.0' into 'release/v5.0'
fix(bt/bluedroid): Fixed access fault when reading BLE controller information fails (v5.0)

See merge request espressif/esp-idf!33631
2024-09-21 14:57:25 +08:00
Jiang Jiang Jian
d067a99a6c Merge branch 'fix/ringbuf_receives_item_not_yet_sent_v5.0' into 'release/v5.0'
fix(esp_ringbuf): Fixed a bug where in a no-split buffer received items prematurely (v5.0)

See merge request espressif/esp-idf!33652
2024-09-21 14:56:40 +08:00
Sudeep Mohanty
d07c700af8 test(freertos): Added delay to freertos test to avoid memory leaks
This commit adds a small delay as tolerance to the
"Test xTaskResumeAll resumes pended tasks" test to let the idle task
clean up all suspended test tasks and avoid memory leaks at the end of
the test.
2024-09-20 10:18:40 +02:00
Sudeep Mohanty
645dc8addf fix(esp_ringbuf): Fixed a bug where in a no-split buffer received items prematurely
This commit fixes a bug in the no-split buffer which could receive an
item prematurely if the space on the buffer is acquired until the buffer
is full. The commit also adds a unit test for this scenario.

Closes https://github.com/espressif/esp-idf/issues/14568
2024-09-20 10:18:40 +02:00
Wang Meng Yang
8fc60de3d0 Merge branch 'bugfix/fix_some_bugs_in_avrcp_v5.0' into 'release/v5.0'
fix(bt/bluedroid): Fixed some bugs in AVRCP (Backport v5.0)

See merge request espressif/esp-idf!33581
2024-09-20 11:41:12 +08:00
Xiao Xufeng
563641f948 ci(h2): disable build test for h2 2024-09-19 22:57:13 +08:00
Island
39878b39ce Merge branch 'bugfix/fixed_c6_crash_c2_crash_issue_v5.0' into 'release/v5.0'
Bugfix/fixed c6 crash c2 crash issue (v5.0)

See merge request espressif/esp-idf!33596
2024-09-19 14:34:15 +08:00
Rahul Tank
a27a2ddfad Merge branch 'fix/added_a_change_to_print_address_of_advertising_instance_v5.0' into 'release/v5.0'
fix(nimble): Added a change to print address of an advertising instance (v5.0)

See merge request espressif/esp-idf!33564
2024-09-19 14:32:03 +08:00
Chen Jian Hua
856bc4bede fix(bt/bluedroid): Fixed access fault when reading BLE controller information fails
(cherry picked from commit e4e23087ee)

Co-authored-by: chenjianhua <chenjianhua@espressif.com>
2024-09-19 09:12:19 +08:00
Chen Jian Hua
0b8553067c feat(bt/bluedroid): Add BLE connection id param for ESP_GATTS_RESPONSE_EVT
(cherry picked from commit e4a372ab76)

Co-authored-by: chenjianhua <chenjianhua@espressif.com>
2024-09-19 09:12:17 +08:00
Xiao Xufeng
f1917f4db3 fix(startup): move rtc initialization before MSPI timing tuning to improve stability 2024-09-18 23:46:41 +08:00
Rahul Tank
ea9136226f Merge branch 'bugfix/authorize_perm_gatt_read_write' into 'release/v5.0'
fix(nimble): Add authroize permission for gatt read / write

See merge request espressif/esp-idf!33584
2024-09-18 21:07:08 +08:00
Island
4c993aeb48 Merge branch 'docs/add_nimble_get_started_v5.0' into 'release/v5.0'
feat(ble/example): Added NimBLE Get started examples (v5.0)

See merge request espressif/esp-idf!33031
2024-09-18 17:43:39 +08:00
Chen Jian Hua
fc754b2033 fix(bt/controller): Change the max TX power to +20dBm on ESP32-C3 and ESP32-S3
(cherry picked from commit fe43c8f1b5)

Co-authored-by: chenjianhua <chenjianhua@espressif.com>
2024-09-18 17:34:13 +08:00
liqigan
5e62d99961 fix(bt/bluedroid): Fixed not deep copy service_name and user1_ptr in RAW SDP search event 2024-09-18 16:43:53 +08:00
liqigan
bfd1ea82bd change(bt/bluedroid): Limited SDP service discovery operation 2024-09-18 16:43:46 +08:00
liqigan
a29b207ffd change(bt/bluedroid): Release record data after SDP record created 2024-09-18 16:43:28 +08:00
Zhao Wei Liang
a9d04549ce fix(ble): fixed memory leak issue when using uart hci
(cherry picked from commit 4d2c44f511)

Co-authored-by: zwl <zhaoweiliang@espressif.com>
2024-09-18 15:57:33 +08:00
Zhao Wei Liang
5f172ab251 fix(ble): fixed occasional assertion issue when enabling logging on ESP32-C2
(cherry picked from commit 0ef9ecb715)

Co-authored-by: zwl <zhaoweiliang@espressif.com>
2024-09-18 15:57:31 +08:00
shreeyash
d81180b0ec fix(nimble): Add authroize permission for gatt read / write 2024-09-18 11:38:52 +05:30
linruihao
cbd12f5ce5 fix(bt/bluedroid): Fixed some bugs in AVRCP 2024-09-18 11:29:40 +08:00
shreeyash
3da3748e61 fix(nimble): Added a change to print address of an advertising instance 2024-09-17 12:19:15 +05:30
Roland Dobai
f9effecd04 Merge branch 'feat/update_tools_in_tools_json_v5.0' into 'release/v5.0'
feat(tools): Update tools: cmake, ninja, ccache (v5.0)

See merge request espressif/esp-idf!33533
2024-09-16 19:43:23 +08:00
Rahul Tank
21aa38dcee Merge branch 'bugfix/add_dummy_cb_during_deinit_v5.0' into 'release/v5.0'
fix(nimble): De-register host cb in stack deinit (v5.0)

See merge request espressif/esp-idf!33376
2024-09-16 18:08:05 +08:00
Rahul Tank
9cd7e32a5b fix(nimble): De-register host cb in stack deinit 2024-09-16 11:00:06 +05:30
Marek Fiala
5e52ea47d1 feat(tools): Update tools: cmake, ninja, ccache
cmake  v3.24.0 -> v3.30.2
ninja  v1.11.1 -> v1.12.1
ccache v4.8    -> v4.10.2

Closes https://github.com/espressif/esp-idf/pull/14376
2024-09-13 17:13:08 +02:00
weiyuhan
2d018c5023 fix(ble): Added ble_get_started build rule in build-test-rules.yml 2024-09-13 09:50:39 +08:00
Wei Yu Han
e58b10b1f6 fix(ble): Increased the length of addr_str in ble_get_started nimble examples 2024-09-12 12:04:34 +08:00
Yuhan Wei
4ffd741232 fix(ble): Removed unsupported sdkconfig 2024-09-12 12:04:34 +08:00
Zhou Xiao
9a3ae21aa7 docs(ble): Replace advertising interval hex value with BLE_GAP_ADV_ITVL_MS 2024-09-12 12:04:34 +08:00
Zhou Xiao
3e6e68628d docs(ble): Improved as Weilong and Shenhang requested 2024-09-12 12:04:34 +08:00
zhouxiao
fe46bf48ce docs(ble): Added BLE Get Started 2024-09-12 12:04:34 +08:00
linruihao
32545d7edb fix(bt): Update bt lib for ESP32-C3 and ESP32-S3(c66a703)
- Fixed missing sync established event after canceling sync
2024-09-11 17:25:51 +08:00
113 changed files with 5253 additions and 575 deletions

View File

@ -434,14 +434,6 @@ build_examples_cmake_esp32c3:
IDF_TARGET: esp32c3
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:
extends:
- .build_cmake_template

View File

@ -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.
* @param msg message
* @param arg paramter
* @param arg_len length of paramter
* @param arg parameter
* @param arg_len length of parameter
* @param copy_func deep copy function
* @param free_func deep free function
* @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
* @return BT_STATUS_SUCCESS: success
* others: fail

View File

@ -200,8 +200,8 @@ choice BT_CTRL_DFT_TX_POWER_LEVEL
bool "+15dBm"
config BT_CTRL_DFT_TX_POWER_LEVEL_P18
bool "+18dBm"
config BT_CTRL_DFT_TX_POWER_LEVEL_P21
bool "+21dBm"
config BT_CTRL_DFT_TX_POWER_LEVEL_P20
bool "+20dBm"
endchoice
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 13 if BT_CTRL_DFT_TX_POWER_LEVEL_P15
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
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

View File

@ -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
*/
@ -15,6 +15,24 @@
#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_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);
if (record == NULL || 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");
if (!esp_sdp_record_integrity_check(record)) {
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;
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 */
stat = btc_transfer_context(&msg, &arg, sizeof(btc_sdp_args_t),

View File

@ -242,6 +242,7 @@ typedef union {
*/
struct gatts_rsp_evt_param {
esp_gatt_status_t status; /*!< Operation status */
uint16_t conn_id; /*!< Connection id */
uint16_t handle; /*!< Attribute handle which send response */
} rsp; /*!< Gatt server callback param of ESP_GATTS_RESPONSE_EVT */

View File

@ -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
*/
@ -14,123 +14,131 @@
extern "C" {
#endif
#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 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 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 {
ESP_SDP_SUCCESS = 0, /*!< Successful operation. */
ESP_SDP_FAILURE, /*!< Generic failure. */
ESP_SDP_NO_RESOURCE, /*!< No more resource */
ESP_SDP_NEED_INIT, /*!< SDP module shall init first */
ESP_SDP_NEED_DEINIT, /*!< SDP module shall deinit first */
ESP_SDP_NO_CREATE_RECORD, /*!< No record created */
ESP_SDP_SUCCESS = 0, /*!< Successful operation. */
ESP_SDP_FAILURE, /*!< Generic failure. */
ESP_SDP_NO_RESOURCE, /*!< No more resource */
ESP_SDP_NEED_INIT, /*!< SDP module shall init first */
ESP_SDP_NEED_DEINIT, /*!< SDP module shall deinit first */
ESP_SDP_NO_CREATE_RECORD, /*!< No record created */
} esp_sdp_status_t;
/**
* @brief SDP callback function events
*/
typedef enum {
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_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_REMOVE_RECORD_COMP_EVT = 4, /*!< When remove a SDP record complete, the event comes */
ESP_SDP_INIT_EVT = 0, /*!< When SDP is initialized, 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_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_cb_event_t;
/**
* @brief SDP record type
*/
typedef enum {
ESP_SDP_TYPE_RAW, /*!< Used to carry raw SDP search data for unknown UUIDs */
ESP_SDP_TYPE_MAP_MAS, /*!< Message Access Profile - Server */
ESP_SDP_TYPE_MAP_MNS, /*!< Message Access Profile - Client (Notification Server) */
ESP_SDP_TYPE_PBAP_PSE, /*!< Phone Book Profile - Server */
ESP_SDP_TYPE_PBAP_PCE, /*!< Phone Book Profile - Client */
ESP_SDP_TYPE_OPP_SERVER, /*!< Object Push Profile */
ESP_SDP_TYPE_SAP_SERVER /*!< SIM Access Profile */
ESP_SDP_TYPE_RAW, /*!< Used to carry raw SDP search data for unknown UUIDs */
ESP_SDP_TYPE_MAP_MAS, /*!< Message Access Profile - Server */
ESP_SDP_TYPE_MAP_MNS, /*!< Message Access Profile - Client (Notification Server) */
ESP_SDP_TYPE_PBAP_PSE, /*!< Phone Book Profile - Server */
ESP_SDP_TYPE_PBAP_PCE, /*!< Phone Book Profile - Client */
ESP_SDP_TYPE_OPP_SERVER, /*!< Object Push Profile */
ESP_SDP_TYPE_SAP_SERVER, /*!< SIM Access Profile */
} esp_bluetooth_sdp_types_t;
/**
* @brief Some signals need additional pointers, hence we introduce a
* generic way to handle these pointers.
* @brief SDP header structure
*/
typedef struct bluetooth_sdp_hdr_overlay {
esp_bluetooth_sdp_types_t type; /*!< SDP type */
esp_bt_uuid_t uuid; /*!< UUID type include uuid and uuid length */
uint32_t service_name_length; /*!< Service name length */
char *service_name; /*!< service name */
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 profile_version; /*!< profile version */
// User pointers, only used for some signals - see esp_bluetooth_sdp_ops_record_t
int user1_ptr_len; /*!< see esp_bluetooth_sdp_ops_record_t */
uint8_t *user1_ptr; /*!< see esp_bluetooth_sdp_ops_record_t */
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_types_t type; /*!< SDP type */
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 */
char *service_name; /*!< Service name */
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 profile_version; /*!< Profile version */
int user1_ptr_len; /*!< User data1 length, only used for searching RAW record */
uint8_t *user1_ptr; /*!< User data1 pointer to the raw SDP response data, only used for searching RAW record */
int user2_ptr_len; /*!< User data2 length, only used for searching RAW record */
uint8_t *user2_ptr; /*!< User data2 pointer, only used for searching RAW record */
} esp_bluetooth_sdp_hdr_overlay_t;
/**
* @brief Message Access Profile - Server parameters
*/
typedef struct bluetooth_sdp_mas_record {
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
uint32_t mas_instance_id; /*!< MAS Instance ID */
uint32_t supported_features; /*!< Map supported features */
uint32_t supported_message_types; /*!< Supported message types */
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
uint32_t mas_instance_id; /*!< MAS Instance ID */
uint32_t supported_features; /*!< Map supported features */
uint32_t supported_message_types; /*!< Supported message types */
} esp_bluetooth_sdp_mas_record_t;
/**
* @brief Message Access Profile - Client (Notification Server) parameters
*/
typedef struct bluetooth_sdp_mns_record {
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
uint32_t supported_features; /*!< Supported features */
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
uint32_t supported_features; /*!< Supported features */
} esp_bluetooth_sdp_mns_record_t;
/**
* @brief Phone Book Profile - Server parameters
*/
typedef struct bluetooth_sdp_pse_record {
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
uint32_t supported_features; /*!< Pbap Supported Features */
uint32_t supported_repositories; /*!< Supported Repositories */
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
uint32_t supported_features; /*!< PBAP Supported Features */
uint32_t supported_repositories; /*!< Supported Repositories */
} esp_bluetooth_sdp_pse_record_t;
/**
* @brief Phone Book Profile - Client parameters
*/
typedef struct bluetooth_sdp_pce_record {
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
} esp_bluetooth_sdp_pce_record_t;
/**
* @brief Object Push Profile parameters
*/
typedef struct bluetooth_sdp_ops_record {
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
int supported_formats_list_len; /*!< Supported formats list length */
uint8_t supported_formats_list[SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH]; /*!< Supported formats list */
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
int supported_formats_list_len; /*!< Supported formats list length */
uint8_t supported_formats_list[SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH]; /*!< Supported formats list */
} esp_bluetooth_sdp_ops_record_t;
/**
* @brief SIM Access Profile parameters
*/
typedef struct bluetooth_sdp_sap_record {
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
} esp_bluetooth_sdp_sap_record_t;
/**
* @brief SDP record parameters union
*/
typedef union {
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
esp_bluetooth_sdp_mas_record_t mas; /*!< Message Access Profile - Server */
esp_bluetooth_sdp_mns_record_t mns; /*!< Message Access Profile - Client (Notification Server) */
esp_bluetooth_sdp_pse_record_t pse; /*!< Phone Book Profile - Server */
esp_bluetooth_sdp_pce_record_t pce; /*!< Phone Book Profile - Client */
esp_bluetooth_sdp_ops_record_t ops; /*!< Object Push Profile */
esp_bluetooth_sdp_sap_record_t sap; /*!< SIM Access Profile */
esp_bluetooth_sdp_hdr_overlay_t hdr; /*!< General info */
esp_bluetooth_sdp_mas_record_t mas; /*!< Message Access Profile - Server */
esp_bluetooth_sdp_mns_record_t mns; /*!< Message Access Profile - Client (Notification Server) */
esp_bluetooth_sdp_pse_record_t pse; /*!< Phone Book Profile - Server */
esp_bluetooth_sdp_pce_record_t pce; /*!< Phone Book Profile - Client */
esp_bluetooth_sdp_ops_record_t ops; /*!< Object Push Profile */
esp_bluetooth_sdp_sap_record_t sap; /*!< SIM Access Profile */
} esp_bluetooth_sdp_record_t;
/**
@ -141,44 +149,43 @@ typedef union {
* @brief ESP_SDP_INIT_EVT
*/
struct sdp_init_evt_param {
esp_sdp_status_t status; /*!< status */
} init; /*!< SDP callback param of ESP_SDP_INIT_EVT */
esp_sdp_status_t status; /*!< Status */
} init; /*!< SDP callback param of ESP_SDP_INIT_EVT */
/**
* @brief ESP_SDP_DEINIT_EVT
*/
struct sdp_deinit_evt_param {
esp_sdp_status_t status; /*!< status */
} deinit; /*!< SDP callback param of ESP_SDP_DEINIT_EVT */
esp_sdp_status_t status; /*!< Status */
} deinit; /*!< SDP callback param of ESP_SDP_DEINIT_EVT */
/**
* @brief ESP_SDP_SEARCH_COMP_EVT
*/
struct sdp_search_evt_param {
esp_sdp_status_t status; /*!< status */
esp_bd_addr_t remote_addr; /*!< remote device address */
esp_bt_uuid_t sdp_uuid; /*!< service uuid */
int record_count; /*!< Number of SDP records */
esp_bluetooth_sdp_record_t *records;/*!< SDP records */
} search; /*!< SDP callback param of ESP_SDP_SEARCH_COMP_EVT */
esp_sdp_status_t status; /*!< Status */
esp_bd_addr_t remote_addr; /*!< Remote device address */
esp_bt_uuid_t sdp_uuid; /*!< Service uuid */
int record_count; /*!< Number of SDP records */
esp_bluetooth_sdp_record_t *records; /*!< SDP records */
} search; /*!< SDP callback param of ESP_SDP_SEARCH_COMP_EVT */
/**
* @brief ESP_SDP_CREATE_RECORD_COMP_EVT
*/
struct sdp_crate_record_evt_param {
esp_sdp_status_t status; /*!< status */
int record_handle; /*!< SDP record handle */
} create_record; /*!< SDP callback param of ESP_SDP_CREATE_RECORD_COMP_EVT */
struct sdp_create_record_evt_param {
esp_sdp_status_t status; /*!< Status */
int record_handle; /*!< SDP record handle */
} create_record; /*!< SDP callback param of ESP_SDP_CREATE_RECORD_COMP_EVT */
/**
* @brief ESP_SDP_REMOVE_RECORD_COMP_EVT
*/
struct sdp_remove_record_evt_param {
esp_sdp_status_t status; /*!< status */
} remove_record; /*!< SDP callback param of ESP_SDP_REMOVE_RECORD_COMP_EVT */
} esp_sdp_cb_param_t; /*!< SDP callback parameter union type */
esp_sdp_status_t status; /*!< Status */
} remove_record; /*!< SDP callback param of ESP_SDP_REMOVE_RECORD_COMP_EVT */
} esp_sdp_cb_param_t;
/**
* @brief SDP callback function type.

View File

@ -127,9 +127,6 @@ void bta_av_del_rc(tBTA_AV_RCB *p_rcb)
}
/* else ACP && connected. do not clear the handle yet */
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",
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;
status = BTA_AV_RC_ROLE_INT;
} 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);
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]);
if (p_lcb_rc->conn_msk && bdcmp(p_lcb_rc->addr, p_data->conn_chg.peer_addr) == 0) {
/* 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->lidx = 0;
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 {
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.peer_features = peer_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);
/* 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);
}
}

View File

@ -29,6 +29,7 @@
#include "stack/btm_api.h"
#include "btm_int.h"
#include <string.h>
#include <assert.h>
#include "bta/utl.h"
#include "osi/allocator.h"
@ -991,22 +992,67 @@ UINT16 BTA_DmGetConnectionState( BD_ADDR bd_addr )
tBTA_STATUS BTA_DmSetLocalDiRecord( tBTA_DI_RECORD *p_device_info,
UINT32 *p_handle )
{
tBTA_STATUS status = BTA_FAILURE;
tBTA_STATUS status = BTA_FAILURE;
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 (!p_device_info->primary_record) {
bta_dm_di_cb.di_handle[bta_dm_di_cb.di_num] = *p_handle;
bta_dm_di_cb.di_num ++;
for (uint8_t i = 1; i < BTA_DI_NUM_MAX; i++) {
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));
}
bta_sys_add_uuid(UUID_SERVCLASS_PNP_INFORMATION);
status = BTA_SUCCESS;
if (!bta_dm_di_cb.uuid_added) {
bta_sys_add_uuid(UUID_SERVCLASS_PNP_INFORMATION);
bta_dm_di_cb.uuid_added = TRUE;
}
status = BTA_SUCCESS;
}
}
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
/*******************************************************************************
**

View File

@ -1541,6 +1541,7 @@ typedef struct {
#if (SDP_INCLUDED == TRUE)
tSDP_DISCOVERY_DB *p_di_db; /* pointer to the DI discovery database */
#endif ///SDP_INCLUDED == TRUE
BOOLEAN uuid_added;
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 */
} tBTA_DM_DI_CB;

View File

@ -2125,6 +2125,17 @@ extern UINT16 BTA_DmGetConnectionState( BD_ADDR bd_addr );
*******************************************************************************/
extern tBTA_STATUS BTA_DmSetLocalDiRecord( tBTA_DI_RECORD *p_device_info,
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
/*******************************************************************************
**

View File

@ -41,7 +41,7 @@ typedef UINT8 tBTA_SDP_STATUS;
/* SDP I/F callback events */
/* events received by tBTA_SDP_DM_CBACK */
#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_COMP_EVT 3 /* SDP search complete */
#define BTA_SDP_CREATE_RECORD_USER_EVT 4 /* SDP create record complete */
@ -67,10 +67,17 @@ typedef struct {
int handle;
} 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 {
tBTA_SDP_STATUS status; /* BTA_SDP_SEARCH_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_REMOVE_RECORD_USER sdp_remove_record; /* BTA_SDP_REMOVE_RECORD_USER_EVT */
} tBTA_SDP;
/* SDP DM Interface callback */
@ -78,9 +85,11 @@ typedef void (tBTA_SDP_DM_CBACK)(tBTA_SDP_EVT event, tBTA_SDP *p_data, void *use
/* MCE configuration structure */
typedef struct {
UINT16 sdp_db_size; /* The size of p_sdp_db */
UINT16 sdp_raw_size; /* The size of p_sdp_raw_data */
UINT16 sdp_db_size; /* The size of p_sdp_db */
#if (SDP_INCLUDED == TRUE)
tSDP_DISCOVERY_DB *p_sdp_db; /* The data buffer to keep SDP database */
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 */
#endif ///SDP_INCLUDED == TRUE
} tBTA_SDP_CFG;
@ -108,14 +117,28 @@ extern tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback);
**
** 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.
**
** Returns BTA_SDP_SUCCESS if successful.
** BTA_SDP_FAIL if internal failure.
**
*******************************************************************************/
extern tBTA_SDP_STATUS BTA_SdpDisable(void);
extern tBTA_SDP_STATUS BTA_SdpCleanup(void);
/*******************************************************************************
**

View File

@ -49,10 +49,11 @@ typedef void (*tBTA_SDP_ACTION)(tBTA_SDP_MSG *p_data);
/* action function list */
const tBTA_SDP_ACTION bta_sdp_action[] = {
bta_sdp_enable, /* BTA_SDP_API_ENABLE_EVT */
bta_sdp_search, /* BTA_SDP_API_SEARCH_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_enable, /* BTA_SDP_API_ENABLE_EVT */
bta_sdp_search, /* BTA_SDP_API_SEARCH_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_disable, /* BTA_SDP_API_DISABLE_EVT */
};
/*******************************************************************************

View File

@ -363,8 +363,8 @@ static void bta_create_raw_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_RE
/* Try to extract a service name */
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->pse.hdr.service_name = (char *)p_attr->attr_value.v.array;
record->hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
record->hdr.service_name = (char *)p_attr->attr_value.v.array;
}
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 */
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;
}
@ -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,
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,
bta_sdp_search_cback, (void *)bta_sdp_search_uuid)) {
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);
tBTA_SDP_CREATE_RECORD_USER bta_sdp = {0};
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) {
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)
{
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;
bta_sdp.status = BTA_SDP_SUCCESS;
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);
}
}

View File

@ -65,7 +65,9 @@ tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback)
#if BTA_DYNAMIC_MEMORY == TRUE
/* 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);
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;
}
#endif
@ -101,15 +103,34 @@ tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback)
*******************************************************************************/
tBTA_SDP_STATUS BTA_SdpDisable(void)
{
BT_HDR *p_buf = NULL;
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);
#if BTA_DYNAMIC_MEMORY == TRUE
/* Free buffer for SDP configuration structure */
osi_free(p_bta_sdp_cfg->p_sdp_db);
p_bta_sdp_cfg->p_sdp_db = NULL;
if (p_bta_sdp_cfg->p_sdp_db) {
osi_free(p_bta_sdp_cfg->p_sdp_db);
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
return (status);
return BTA_SDP_SUCCESS;
}
/*******************************************************************************

View File

@ -30,16 +30,24 @@
#define BTA_SDP_DB_SIZE 1500
#endif
#ifndef BTA_SDP_RAW_DATA_SIZE
#define BTA_SDP_RAW_DATA_SIZE 1024
#endif
#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];
#endif
/* SDP configuration structure */
tBTA_SDP_CFG bta_sdp_cfg = {
BTA_SDP_RAW_DATA_SIZE,
BTA_SDP_DB_SIZE,
#if BTA_DYNAMIC_MEMORY == FALSE
bta_sdp_raw_data,
(tSDP_DISCOVERY_DB *)bta_sdp_db_data /* The data buffer to keep SDP database */
#else
NULL,
NULL
#endif
};

View File

@ -42,6 +42,7 @@ enum {
BTA_SDP_API_SEARCH_EVT,
BTA_SDP_API_CREATE_RECORD_USER_EVT,
BTA_SDP_API_REMOVE_RECORD_USER_EVT,
BTA_SDP_API_DISABLE_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_create_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

View File

@ -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
*/
@ -40,7 +40,7 @@ static void btc_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 c
** 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_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].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);
// set up callback
@ -947,7 +947,7 @@ void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data)
memset(&param, 0, sizeof(esp_avrc_ct_cb_param_t));
param.conn_stat.connected = true;
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, &param);
btc_avrc_tg_cb_to_app(ESP_AVRC_TG_CONNECTION_STATE_EVT, &param);
}
} while (0);
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__);
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) {
@ -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,
(tBTA_AV_RC)key_code, (tBTA_AV_STATE)key_state);
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 {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
@ -1298,7 +1298,7 @@ static void btc_avrc_tg_init(void)
}
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__);
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) {

View File

@ -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
*/
@ -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 &&
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) {
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);
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_AGG_FORMAT || uuid == ESP_GATT_UUID_CHAR_VALID_RANGE ||
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;
}
}
@ -694,6 +694,7 @@ void btc_gatts_call_handler(btc_msg_t *msg)
}
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), &param);
break;
}

View File

@ -34,55 +34,43 @@ typedef enum {
SDP_TYPE_PBAP_PSE, // Phone Book Profile - Server
SDP_TYPE_PBAP_PCE, // Phone Book Profile - Client
SDP_TYPE_OPP_SERVER, // Object Push Profile
SDP_TYPE_SAP_SERVER // SIM Access Profile
SDP_TYPE_SAP_SERVER, // SIM Access Profile
} 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
* generic way to handle these pointers.
*/
typedef struct _bluetooth_sdp_hdr_overlay {
bluetooth_sdp_types type;
esp_bt_uuid_t bt_uuid;
uint32_t service_name_length;
char *service_name;
int32_t rfcomm_channel_number;
int32_t l2cap_psm;
int32_t profile_version;
// User pointers, only used for some signals - see bluetooth_sdp_ops_record
int user1_ptr_len;
uint8_t *user1_ptr;
int user2_ptr_len;
uint8_t *user2_ptr;
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;
int user1_ptr_len;
uint8_t *user1_ptr;
int user2_ptr_len; // not used
uint8_t *user2_ptr; // not used
} bluetooth_sdp_hdr_overlay;
typedef struct _bluetooth_sdp_mas_record {
bluetooth_sdp_hdr_overlay hdr;
uint32_t mas_instance_id;
uint32_t supported_features;
uint32_t supported_message_types;
uint32_t mas_instance_id;
uint32_t supported_features;
uint32_t supported_message_types;
} bluetooth_sdp_mas_record;
typedef struct _bluetooth_sdp_mns_record {
bluetooth_sdp_hdr_overlay hdr;
uint32_t supported_features;
uint32_t supported_features;
} bluetooth_sdp_mns_record;
typedef struct _bluetooth_sdp_pse_record {
bluetooth_sdp_hdr_overlay hdr;
uint32_t supported_features;
uint32_t supported_repositories;
uint32_t supported_features;
uint32_t supported_repositories;
} bluetooth_sdp_pse_record;
typedef struct _bluetooth_sdp_pce_record {
@ -91,8 +79,8 @@ typedef struct _bluetooth_sdp_pce_record {
typedef struct _bluetooth_sdp_ops_record {
bluetooth_sdp_hdr_overlay hdr;
int supported_formats_list_len;
uint8_t supported_formats_list[SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH];
int supported_formats_list_len;
uint8_t supported_formats_list[SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH];
} bluetooth_sdp_ops_record;
typedef struct _bluetooth_sdp_sap_record {
@ -100,13 +88,13 @@ typedef struct _bluetooth_sdp_sap_record {
} bluetooth_sdp_sap_record;
typedef union {
bluetooth_sdp_hdr_overlay hdr;
bluetooth_sdp_mas_record mas;
bluetooth_sdp_mns_record mns;
bluetooth_sdp_pse_record pse;
bluetooth_sdp_pce_record pce;
bluetooth_sdp_ops_record ops;
bluetooth_sdp_sap_record sap;
bluetooth_sdp_hdr_overlay hdr;
bluetooth_sdp_mas_record mas;
bluetooth_sdp_mns_record mns;
bluetooth_sdp_pse_record pse;
bluetooth_sdp_pce_record pce;
bluetooth_sdp_ops_record ops;
bluetooth_sdp_sap_record sap;
} bluetooth_sdp_record;
#endif /* __BT_SDP_H__ */

View File

@ -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
*/
@ -32,9 +32,9 @@ typedef union {
} search;
//BTC_SDP_ACT_CREATE_RECORD
struct creat_record_arg {
struct create_record_arg {
bluetooth_sdp_record *record;
} creat_record;
} create_record;
//BTC_SDP_ACT_REMOVE_RECORD
struct remove_record_arg {

View File

@ -25,12 +25,14 @@ typedef enum {
} sdp_state_t;
typedef struct {
sdp_state_t state;
uint8_t state;
int sdp_handle;
bluetooth_sdp_record* record_data;
esp_bt_uuid_t uuid;
void* record_data;
} sdp_slot_t;
typedef struct {
bool search_allowed;
sdp_slot_t *sdp_slots[SDP_MAX_RECORDS];
osi_mutex_t sdp_slot_mutex;
} 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)
#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)
{
@ -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;
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;
bluetooth_sdp_record *record = in_record;
int records_size = 0;
for(int i = 0; i < count; i++) {
record = &in_record[i];
records_size += sizeof(bluetooth_sdp_record);
records_size += record->hdr.service_name_length;
if(record->hdr.service_name_length > 0){
records_size++; /* + '\0' termination of string */
}
records_size += record->hdr.user1_ptr_len;
records_size += record->hdr.user2_ptr_len;
records_size += sizeof(bluetooth_sdp_record);
records_size += record->hdr.service_name_length;
if (record->hdr.service_name_length > 0) {
records_size++; /* + '\0' termination of string */
}
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;
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) {
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);
slot = sdp_local_param.sdp_slots[id];
if (slot == NULL) {
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
BTC_TRACE_ERROR("%s() id=%d to handle=0x%08x, set failed", __func__, id, handle);
return;
}
slot->sdp_handle = handle;
do {
slot = sdp_local_param.sdp_slots[id];
if (slot == NULL) {
BTC_TRACE_ERROR("%s() id = %d ", __func__, id);
break;
}
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);
}
static bool get_sdp_record_by_handle(int handle, bluetooth_sdp_record* record)
static void get_sdp_slot_info(int id, int *sdp_handle, esp_bt_uuid_t *uuid)
{
sdp_slot_t *slot = NULL;
osi_mutex_lock(&sdp_local_param.sdp_slot_mutex, OSI_MUTEX_MAX_TIMEOUT);
for (int i = 0; i < SDP_MAX_RECORDS; i++) {
slot = sdp_local_param.sdp_slots[i];
if ((slot != NULL) && (slot->sdp_handle == handle)) {
memcpy(record, slot->record_data, sizeof(bluetooth_sdp_record));
osi_mutex_unlock(&sdp_local_param.sdp_slot_mutex);
return true;
}
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);
do {
slot = sdp_local_param.sdp_slots[id];
if (slot == NULL) {
break;
}
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);
return false;
}
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;
}
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) {
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);
sdp_slot = sdp_local_param.sdp_slots[id];
if (sdp_slot == NULL) {
BTC_TRACE_ERROR("%s() id = %d ", __func__, id);
} else if(sdp_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);
/* The record have been removed before this event occurred - e.g. deinit */
sdp_slot = NULL;
}
do {
slot = sdp_local_param.sdp_slots[id];
if (slot == NULL) {
BTC_TRACE_ERROR("%s() id = %d ", __func__, id);
break;
}
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);
return sdp_slot;
return record_data;
}
/* Deep copy all content of in_records into out_records.
* out_records must point to a chunk of memory large enough to contain all
* 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;
bluetooth_sdp_record *out_record;
char *free_ptr = (char*)(&out_records[count]); /* set pointer to after the last entry */
uint8_t *free_ptr = (uint8_t *)(out_record + 1); /* set pointer to after the last entry */
for(int i = 0; i < count; i++) {
in_record = &in_records[i];
out_record = &out_records[i];
*out_record = *in_record;
memcpy(out_record, in_record, sizeof(bluetooth_sdp_record));
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_length = 0;
} else {
out_record->hdr.service_name = free_ptr; // Update service_name pointer
// Copy string
memcpy(free_ptr, in_record->hdr.service_name, in_record->hdr.service_name_length);
free_ptr += in_record->hdr.service_name_length;
*(free_ptr) = '\0'; // Set '\0' termination of string
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
free_ptr += in_record->hdr.user1_ptr_len;
}
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;
}
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_length = 0;
} else {
out_record->hdr.service_name = (char *)free_ptr; // Update service_name pointer
// Copy string
memcpy(free_ptr, in_record->hdr.service_name, in_record->hdr.service_name_length);
free_ptr += in_record->hdr.service_name_length;
*(free_ptr) = '\0'; // Set '\0' termination of string
free_ptr++;
}
}
static void copy_sdp_record(bluetooth_sdp_record* in_record, bluetooth_sdp_record* out_record)
{
copy_sdp_record_common(in_record, out_record);
}
static int alloc_sdp_slot(bluetooth_sdp_record* in_record)
{
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;
sdp_slot_t **slot = NULL;
@ -227,11 +250,10 @@ static int alloc_sdp_slot(bluetooth_sdp_record* in_record)
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);
for(i = 0; i < SDP_MAX_RECORDS; i++)
{
for (i = 0; i < SDP_MAX_RECORDS; i++) {
slot = &sdp_local_param.sdp_slots[i];
if ((*slot) == NULL) {
if (((*slot) = (sdp_slot_t *)osi_malloc(sizeof(sdp_slot_t))) == NULL) {
@ -240,8 +262,8 @@ static int alloc_sdp_slot(bluetooth_sdp_record* in_record)
osi_free(record);
return -1;
}
(*slot)->state = SDP_RECORD_ALLOCED;
(*slot)->record_data = record;
(*slot)->state = SDP_RECORD_ALLOCED;
(*slot)->record_data = record;
break;
}
}
@ -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 */
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];
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* p_temp = temp;
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__,
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;
}
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);
UINT16_TO_BE_STREAM (p_temp, rec->hdr.bt_uuid.uuid.uuid16);
} else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_32) {
UINT16_TO_BE_STREAM (p_temp, p_uuid->uuid.uuid16);
} else if (p_uuid->len == ESP_UUID_LEN_32) {
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);
} else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_128) {
UINT32_TO_BE_STREAM (p_temp, p_uuid->uuid.uuid32);
} else if (p_uuid->len == ESP_UUID_LEN_128) {
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 {
SDP_DeleteRecord(sdp_handle);
sdp_handle = 0;
@ -357,7 +380,7 @@ static int add_raw_sdp(const bluetooth_sdp_record* rec)
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);
if (!status) {
@ -365,12 +388,12 @@ static int add_raw_sdp(const bluetooth_sdp_record* rec)
sdp_handle = 0;
BTC_TRACE_ERROR("%s() FAILED, status = %d", __func__, status);
} else {
if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_16) {
bta_sys_add_uuid(rec->hdr.bt_uuid.uuid.uuid16);
} else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_32) {
bta_sys_add_uuid_32(rec->hdr.bt_uuid.uuid.uuid32);
} else if (rec->hdr.bt_uuid.len == ESP_UUID_LEN_128) {
bta_sys_add_uuid_128((UINT8 *)&rec->hdr.bt_uuid.uuid.uuid128);
if (p_uuid->len == ESP_UUID_LEN_16) {
bta_sys_add_uuid(p_uuid->uuid.uuid16);
} else if (p_uuid->len == ESP_UUID_LEN_32) {
bta_sys_add_uuid_32(p_uuid->uuid.uuid32);
} else if (p_uuid->len == ESP_UUID_LEN_128) {
bta_sys_add_uuid_128((UINT8 *)&p_uuid->uuid.uuid128);
}
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);
}
/* Make the service browseable */
/* Make the service browsable */
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
if (!status) {
@ -523,7 +546,7 @@ static int add_mapc_sdp(const bluetooth_sdp_mns_record* rec)
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);
if (!status) {
@ -603,7 +626,7 @@ static int add_pbaps_sdp(const bluetooth_sdp_pse_record* rec)
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);
if (!status) {
@ -649,7 +672,7 @@ static int add_pbapc_sdp(const bluetooth_sdp_pce_record* rec)
UUID_SERVCLASS_PHONE_ACCESS,
rec->hdr.profile_version);
/* Make the service browseable */
/* Make the service browsable */
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
if (!status) {
@ -736,7 +759,7 @@ static int add_opps_sdp(const bluetooth_sdp_ops_record* rec)
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);
if (!status) {
@ -799,7 +822,7 @@ static int add_saps_sdp(const bluetooth_sdp_sap_record* rec)
UUID_SERVCLASS_SAP,
rec->hdr.profile_version);
// Make the service browseable
// Make the service browsable
status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
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)
{
int handle = -1;
const sdp_slot_t *sdp_slot = NULL;
int sdp_handle = 0;
bluetooth_sdp_record *record = start_create_sdp(id);
esp_bt_uuid_t service_uuid = {0};
BTC_TRACE_DEBUG("Sdp Server %s", __func__);
sdp_slot = start_create_sdp(id);
if(sdp_slot != NULL) {
bluetooth_sdp_record* record = sdp_slot->record_data;
switch(record->hdr.type) {
if (record != NULL) {
switch (record->hdr.type) {
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;
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;
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;
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;
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;
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;
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;
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;
}
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__);
if(handle != -1 && handle != 0) {
result = SDP_DeleteRecord(handle);
if(result == false) {
BTC_TRACE_ERROR(" Unable to remove handle 0x%08x", handle);
bool result = false;
int sdp_handle = -1;
esp_bt_uuid_t service_uuid = {0};
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;
}
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)
{
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) {
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);
if (p_data->sdp_create_record.handle < 0) {
p_data->status = BTA_SDP_FAILURE;
p_data->sdp_create_record.status = BTA_SDP_FAILURE;
}
}
}
break;
case BTA_SDP_REMOVE_RECORD_USER_EVT: {
if (p_data->status == BTA_SDP_SUCCESS) {
if (btc_sdp_remove_record_event((int)user_data) == false) {
p_data->status = BTA_SDP_FAILURE;
if (p_data->sdp_remove_record.status == BTA_SDP_SUCCESS) {
if (btc_sdp_remove_record_event((int)user_data, &p_data->sdp_remove_record.handle) == false) {
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.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) {
BTC_TRACE_ERROR("%s btc_transfer_context failed", __func__);
@ -930,23 +1072,27 @@ static void btc_sdp_init(void)
ret = ESP_SDP_NO_RESOURCE;
break;
}
memset((void *)sdp_local_param_ptr, 0, sizeof(sdp_local_param_t));
#endif
memset(&sdp_local_param, 0, sizeof(sdp_local_param_t));
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__);
ret = ESP_SDP_NO_RESOURCE;
break;
}
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);
if (ret != ESP_SDP_SUCCESS) {
btc_sdp_cleanup();
param.init.status = ret;
btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, &param);
}
@ -956,7 +1102,6 @@ static void btc_sdp_deinit(void)
{
esp_sdp_cb_param_t param;
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
int handle;
do {
if (!is_sdp_init()) {
@ -966,12 +1111,13 @@ static void btc_sdp_deinit(void)
}
for(int i = 0; i < SDP_MAX_RECORDS; i++) {
handle = free_sdp_slot(i);
if (handle > 0) {
BTA_SdpRemoveRecordByUser((void*)handle);
int sdp_handle = -1;
get_sdp_slot_info(i, &sdp_handle, NULL);
if (sdp_handle > 0) {
BTA_SdpRemoveRecordByUser((void*)i);
}
}
sdp_disable_handler();
BTA_SdpDisable();
} while(0);
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)
{
int handle;
int slot_id;
esp_sdp_cb_param_t param;
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
@ -993,13 +1139,13 @@ static void btc_sdp_create_record(btc_sdp_args_t *arg)
break;
}
handle = alloc_sdp_slot(arg->creat_record.record);
if (handle < 0) {
slot_id = alloc_sdp_slot(arg->create_record.record);
if (slot_id < 0) {
ret = ESP_SDP_FAILURE;
break;
}
BTA_SdpCreateRecordByUser((void *) handle);
BTA_SdpCreateRecordByUser((void *) slot_id);
} while(0);
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)
{
int handle;
esp_sdp_cb_param_t param;
esp_sdp_status_t ret = ESP_SDP_SUCCESS;
@ -1022,42 +1167,16 @@ static void btc_sdp_remove_record(btc_sdp_args_t *arg)
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 */
/* 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);
if (slot < 0) {
int slot_id = get_sdp_slot_id_by_handle(arg->remove_record.record_handle);
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;
break;
}
handle = free_sdp_slot(slot);
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;
}
BTA_SdpRemoveRecordByUser((void *)slot_id);
} while(0);
if (ret != ESP_SDP_SUCCESS) {
@ -1078,7 +1197,18 @@ static void btc_sdp_search(btc_sdp_args_t *arg)
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);
/**
* 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);
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)
{
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) {
case BTC_SDP_ACT_CREATE_RECORD:
dst->creat_record.record = (bluetooth_sdp_record *)osi_calloc(sizeof(bluetooth_sdp_record));
if (dst->creat_record.record) {
memcpy(dst->creat_record.record, src->creat_record.record, sizeof(bluetooth_sdp_record));
case BTC_SDP_ACT_CREATE_RECORD: {
bluetooth_sdp_record **dst_record = &((btc_sdp_args_t *)p_dest)->create_record.record;
bluetooth_sdp_record *src_record = ((btc_sdp_args_t *)p_src)->create_record.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 {
BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act);
break;
}
dst->creat_record.record->hdr.service_name = (char *)osi_calloc(src->creat_record.record->hdr.service_name_length);
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);
}
*dst_record = record;
break;
}
default:
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)
{
btc_sdp_args_t *arg = (btc_sdp_args_t *)msg->arg;
switch (msg->act) {
case BTC_SDP_ACT_CREATE_RECORD:
if (arg->creat_record.record) {
osi_free(arg->creat_record.record);
}
if (arg->creat_record.record->hdr.service_name) {
osi_free(arg->creat_record.record->hdr.service_name);
case BTC_SDP_ACT_CREATE_RECORD: {
btc_sdp_args_t *arg = (btc_sdp_args_t *)msg->arg;
bluetooth_sdp_record *record = arg->create_record.record;
if (record) {
osi_free(record);
}
break;
}
default:
break;
}
@ -1172,35 +1295,22 @@ void btc_sdp_cb_handler(btc_msg_t *msg)
param.init.status = p_data->status;
btc_sdp_cb_to_app(ESP_SDP_INIT_EVT, &param);
break;
case BTA_SDP_DISENABLE_EVT:
BTA_SdpDisable();
osi_mutex_free(&sdp_local_param.sdp_slot_mutex);
#if SDP_DYNAMIC_MEMORY == TRUE
osi_free(sdp_local_param_ptr);
sdp_local_param_ptr = NULL;
#endif
case BTA_SDP_DISABLE_EVT:
BTA_SdpCleanup();
btc_sdp_cleanup();
param.deinit.status = ESP_SDP_SUCCESS;
btc_sdp_cb_to_app(ESP_SDP_DEINIT_EVT, &param);
break;
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;
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.sdp_uuid, &p_data->sdp_search_comp.uuid, sizeof(tSDP_UUID));
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);
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;
}
}
memcpy(param.search.remote_addr, p_data->sdp_search_comp.remote_addr, sizeof(BD_ADDR));
memcpy(&param.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.records = (esp_bluetooth_sdp_record_t *)p_data->sdp_search_comp.records;
btc_sdp_cb_to_app(ESP_SDP_SEARCH_COMP_EVT, &param);
if (param.search.records != NULL) {
osi_free(param.search.records);
}
break;
case BTA_SDP_CREATE_RECORD_USER_EVT:
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, &param);
break;
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, &param);
break;
default:
BTC_TRACE_DEBUG("%s: Unhandled event (%d)!", __func__, msg->act);
break;
}
btc_sdp_cb_arg_deep_free(msg);
}
#endif ///defined BTC_SDP_INCLUDED && BTC_SDP_INCLUDED == TRUE

View File

@ -48,16 +48,16 @@
//L2CAP
#ifdef CONFIG_BT_L2CAP_ENABLED
#define UC_BT_L2CAP_ENABLED CONFIG_BT_L2CAP_ENABLED
#define UC_BT_L2CAP_ENABLED CONFIG_BT_L2CAP_ENABLED
#else
#define UC_BT_L2CAP_ENABLED FALSE
#define UC_BT_L2CAP_ENABLED FALSE
#endif
//HFP(AG)
#ifdef CONFIG_BT_HFP_AG_ENABLE
#define UC_BT_HFP_AG_ENABLED CONFIG_BT_HFP_AG_ENABLE
#define UC_BT_HFP_AG_ENABLED CONFIG_BT_HFP_AG_ENABLE
#else
#define UC_BT_HFP_AG_ENABLED FALSE
#define UC_BT_HFP_AG_ENABLED FALSE
#endif
//HFP(Client)

View File

@ -1274,7 +1274,7 @@
#ifdef CONFIG_IDF_TARGET_ESP32
#define BTM_BLE_ADV_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9}
#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
@ -1282,7 +1282,7 @@
#ifdef CONFIG_IDF_TARGET_ESP32
#define BTM_TX_POWER {-12, -9, -6, -3, 0, 3, 6, 9}
#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

View File

@ -271,7 +271,7 @@ static void start_up(void)
#endif //#if (BLE_50_FEATURE_SUPPORT == TRUE)
#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());
controller_param.packet_parser->parse_ble_read_adv_max_len_response(
response,

View File

@ -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 */);
STREAM_TO_UINT8(*resolving_list_size_ptr, stream);
if (stream) {
STREAM_TO_UINT8(*resolving_list_size_ptr, stream);
}
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 */);
STREAM_TO_UINT16(*ble_default_packet_length_ptr, stream);
STREAM_TO_UINT16(*ble_default_packet_txtime_ptr, stream);
if (stream) {
STREAM_TO_UINT16(*ble_default_packet_length_ptr, stream);
STREAM_TO_UINT16(*ble_default_packet_txtime_ptr, stream);
}
osi_free(response);
}
#if (BLE_50_FEATURE_SUPPORT == TRUE)
static void parse_ble_read_adv_max_len_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 */);
// Size: 2 Octets ; Value: 0x001F 0x0672 ; Maximum supported advertising data length
STREAM_TO_UINT16(*adv_max_len_ptr, stream);
if (stream) {
// Size: 2 Octets ; Value: 0x001F 0x0672 ; Maximum supported advertising data length
STREAM_TO_UINT16(*adv_max_len_ptr, stream);
}
osi_free(response);
}
@ -254,6 +262,7 @@ static uint8_t *read_command_complete_header(
STREAM_TO_UINT8(status, stream);
if (status != HCI_SUCCESS) {
HCI_TRACE_ERROR("%s failed: opcode 0x%04x, status 0x%02x", __func__, opcode, status);
return NULL;
}

View File

@ -430,10 +430,10 @@ BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last)
tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
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++) {
AVCT_TRACE_WARNING("%x: aloc:%d, lcb:%p/%p, ccb:%p/%p",
i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last);
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);
if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last)) {
return FALSE;
}

View File

@ -1867,42 +1867,50 @@ typedef struct {
#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_OFF 0
#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_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)
/* Extended Reject Indication */
/* Extended Reject Indication: bit 2 */
#define HCI_LE_FEATURE_EXT_REJ_IND_MASK 0x04
#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)
/* 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_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)
/* 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 */
#define HCI_LE_FEATURE_ENHANCED_PRIVACY_MASK 0x40
#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)
/* 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_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)
/* Slave-initiated Features Exchange */
#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)
/*
** LE features encoding - page 1
*/
/* 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

View File

@ -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.
** 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.
**
** Returns Record handle if OK, else 0.
@ -293,15 +293,15 @@ UINT32 SDP_CreateRecord (void)
/* First, check if there is a free record */
if (p_db->num_records < SDP_MAX_RECORDS) {
p_rec =(tSDP_RECORD *)osi_malloc(sizeof(tSDP_RECORD));
if (p_rec) {
memset(p_rec, 0, sizeof(tSDP_RECORD));
/* Save previous rec */
if (p_db->num_records) {
p_rec_prev = list_back(p_db->p_record_list);
}
/* Append new record */
list_append(p_db->p_record_list, p_rec);
p_rec = (tSDP_RECORD *)osi_malloc(sizeof(tSDP_RECORD));
if (p_rec) {
memset(p_rec, 0, sizeof(tSDP_RECORD));
/* Save previous rec */
if (p_db->num_records) {
p_rec_prev = list_back(p_db->p_record_list);
}
/* Append new record */
list_append(p_db->p_record_list, p_rec);
/* We will use a handle of the first unreserved handle plus last record
** number + 1 */
@ -321,10 +321,12 @@ UINT32 SDP_CreateRecord (void)
4, buf);
return (p_rec->record_handle);
} else {
}
else {
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);
}
#endif
@ -354,17 +356,17 @@ BOOLEAN SDP_DeleteRecord (UINT32 handle)
if (handle == 0 || sdp_cb.server_db.num_records == 0) {
/* Delete all records in the database */
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)) {
list_remove(sdp_cb.server_db.p_record_list, 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);
}
/* require new DI record to be created in SDP_SetLocalDiRecord */
sdp_cb.server_db.di_primary_handle = 0;
return (TRUE);
} else {
/* 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)) {
p_rec = list_node(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);
if (p_rec->record_handle == handle) {
/* Found it. Shift everything up one */
list_remove(sdp_cb.server_db.p_record_list, p_rec);
@ -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);
/* if we're deleting the primary DI record, clear the */
/* 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;
}

View File

@ -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)
{
#if (BT_HCI_LOG_INCLUDED == TRUE)
@ -407,11 +413,23 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len)
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 = {
.notify_host_send_available = controller_rcv_pkt_ready,
.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)
{
os_msys_buf_free();
@ -520,6 +538,11 @@ esp_err_t esp_nimble_hci_deinit(void)
return ret;
}
ret = esp_vhci_host_register_callback(&dummy_vhci_host_cb);
if (ret != ESP_OK) {
return ret;
}
ble_buf_free();
#if MYNEWT_VAL(BLE_QUEUE_CONG_CHECK)

@ -1 +1 @@
Subproject commit e2522d60d9ffe4fefae1b4a9f8b47ddf02e65f2d
Subproject commit ebaa0fc73229eb89adcc816a5db13d13b4c41818

View File

@ -376,17 +376,18 @@ typedef enum {
ESP_PWR_LVL_N18 = 2, /*!< Corresponding to -18dbm */
ESP_PWR_LVL_N15 = 3, /*!< Corresponding to -15dbm */
ESP_PWR_LVL_N12 = 4, /*!< Corresponding to -12dbm */
ESP_PWR_LVL_N9 = 5, /*!< Corresponding to -9dbm */
ESP_PWR_LVL_N6 = 6, /*!< Corresponding to -6dbm */
ESP_PWR_LVL_N3 = 7, /*!< Corresponding to -3dbm */
ESP_PWR_LVL_N0 = 8, /*!< Corresponding to 0dbm */
ESP_PWR_LVL_P3 = 9, /*!< Corresponding to +3dbm */
ESP_PWR_LVL_P6 = 10, /*!< Corresponding to +6dbm */
ESP_PWR_LVL_P9 = 11, /*!< Corresponding to +9dbm */
ESP_PWR_LVL_P12 = 12, /*!< Corresponding to +12dbm */
ESP_PWR_LVL_P15 = 13, /*!< Corresponding to +15dbm */
ESP_PWR_LVL_P18 = 14, /*!< Corresponding to +18dbm */
ESP_PWR_LVL_P21 = 15, /*!< Corresponding to +21dbm */
ESP_PWR_LVL_N9 = 5, /*!< Corresponding to -9dbm */
ESP_PWR_LVL_N6 = 6, /*!< Corresponding to -6dbm */
ESP_PWR_LVL_N3 = 7, /*!< Corresponding to -3dbm */
ESP_PWR_LVL_N0 = 8, /*!< Corresponding to 0dbm */
ESP_PWR_LVL_P3 = 9, /*!< Corresponding to +3dbm */
ESP_PWR_LVL_P6 = 10, /*!< Corresponding to +6dbm */
ESP_PWR_LVL_P9 = 11, /*!< Corresponding to +9dbm */
ESP_PWR_LVL_P12 = 12, /*!< Corresponding to +12dbm */
ESP_PWR_LVL_P15 = 13, /*!< Corresponding to +15dbm */
ESP_PWR_LVL_P18 = 14, /*!< Corresponding to +18dbm */
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_power_level_t;

View File

@ -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));
if (!s_hci_driver_uart_env.tx_sem) {
if (s_hci_driver_uart_env.tx_sem) {
vSemaphoreDelete(s_hci_driver_uart_env.tx_sem);
}

View File

@ -351,24 +351,6 @@ bool rtc_dig_8m_enabled(void)
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
* TODO: update the library to use rtc_clk_xtal_freq_get
*/

View File

@ -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)
{
/* 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.
* 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;
}
// 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
* TODO: update the library to use rtc_clk_xtal_freq_get
*/

View File

@ -510,6 +510,13 @@ static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer)
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 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
} else {
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) {
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
BaseType_t xReturn = pdFALSE;

View File

@ -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
*/
@ -804,6 +804,8 @@ TEST_CASE("Test ring buffer ISR", "[esp_ringbuf]")
* tested.
*/
#if !CONFIG_FREERTOS_UNICORE
#define SRAND_SEED 3 //Arbitrarily chosen srand() seed
#define SMP_TEST_ITERATIONS 4
@ -1018,6 +1020,7 @@ TEST_CASE("Test static ring buffer SMP", "[esp_ringbuf]")
cleanup();
}
#endif
#endif //!CONFIG_FREERTOS_UNICORE
#if !CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH && !CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH
/* -------------------------- Test ring buffer IRAM ------------------------- */
@ -1027,7 +1030,7 @@ static IRAM_ATTR __attribute__((noinline)) bool iram_ringbuf_test(void)
bool result = true;
uint8_t item[4];
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);
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() );
}
#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);
}

View File

@ -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)
{
s_warn();

View File

@ -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.
// So we have to read it here in SPI mode, before entering the OPI mode.
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.
*
@ -428,15 +432,9 @@ void IRAM_ATTR call_start_cpu0(void)
* In this stage, we re-configure the Flash (and MSPI) to required configuration
*/
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
// 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.
spi_timing_flash_tuning();
#endif

View File

@ -18,6 +18,15 @@ extern "C" {
* 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
*

View File

@ -106,11 +106,14 @@ static void select_rtc_slow_clk(slow_clk_sel_t slow_clk)
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_init(cfg);
}
__attribute__((weak)) void esp_clk_init(void)
{
#if (CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS || CONFIG_APP_INIT_CLK)
/* Check the bootloader set the XTAL frequency.

View File

@ -51,12 +51,19 @@ typedef enum {
} slow_clk_sel_t;
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
static __attribute__((unused)) void recalib_bbpll(void);
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
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
soc_reset_reason_t rst_reas;
@ -65,7 +72,12 @@ static const char *TAG = "clk";
cfg.cali_ocode = 1;
}
rtc_init(cfg);
#endif
}
__attribute__((weak)) void esp_clk_init(void)
{
#if !CONFIG_IDF_ENV_FPGA
#ifndef CONFIG_XTAL_FREQ_AUTO
assert(rtc_clk_xtal_freq_get() == CONFIG_XTAL_FREQ);
#endif
@ -266,3 +278,21 @@ __attribute__((weak)) void esp_perip_clk_init(void)
*/
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);
}
}

View File

@ -57,7 +57,7 @@ static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
static const char *TAG = "clk";
__attribute__((weak)) void esp_clk_init(void)
void esp_rtc_init(void)
{
#if !CONFIG_IDF_ENV_FPGA
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
@ -71,7 +71,10 @@ static const char *TAG = "clk";
cfg.cali_ocode = 1;
}
rtc_init(cfg);
}
__attribute__((weak)) void esp_clk_init(void)
{
assert(rtc_clk_xtal_freq_get() == RTC_XTAL_FREQ_40M);
bool rc_fast_d256_is_enabled = rtc_clk_8md256_enabled();

View File

@ -60,7 +60,7 @@ typedef enum {
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();
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);
}
__attribute__((weak)) void esp_clk_init(void)
{
bool rc_fast_d256_is_enabled = rtc_clk_8md256_enabled();
rtc_clk_8m_enable(true, rc_fast_d256_is_enabled);
rtc_clk_fast_src_set(SOC_RTC_FAST_CLK_SRC_RC_FAST);

View File

@ -53,9 +53,16 @@ typedef enum {
} slow_clk_sel_t;
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();
soc_reset_reason_t rst_reas;
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;
}
rtc_init(cfg);
}
__attribute__((weak)) void esp_clk_init(void)
{
assert(rtc_clk_xtal_freq_get() == RTC_XTAL_FREQ_40M);
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);
}
// 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);
}
}

View File

@ -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
*/
@ -487,6 +487,9 @@ static void test_pended_running_task(void *arg)
TEST_ASSERT_EQUAL(true, has_run[i]);
}
// Short delay to let the tasks be suspended
vTaskDelay(10);
// Clean up the interrupt and tasks
deregister_intr_cb();
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));
// Wait for to be notified to test completion
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// Short delay to let the task be suspended
vTaskDelay(10);
vTaskDelete(susp_tsk_hdl);
}
// Add a short delay to allow the idle task to free any remaining task memory

View File

@ -1,5 +1,11 @@
# 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:
enable:
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3"]

View File

@ -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)

View File

@ -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.

View File

@ -0,0 +1,4 @@
file(GLOB_RECURSE srcs "main.c" "src/*.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "./include")

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,3 @@
CONFIG_BT_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n

View File

@ -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)

View File

@ -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, &params);
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.

View File

@ -0,0 +1,4 @@
file(GLOB_RECURSE srcs "main.c" "src/*.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "./include")

View File

@ -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

View File

@ -0,0 +1,2 @@
dependencies:
espressif/led_strip: "^2.4.1"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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, &params);
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;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
CONFIG_BLINK_GPIO=5

View File

@ -0,0 +1 @@
CONFIG_BLINK_LED_STRIP=y

View File

@ -0,0 +1,2 @@
CONFIG_BLINK_LED_STRIP=y
CONFIG_BLINK_GPIO=48

View File

@ -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)

View File

@ -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.

View File

@ -0,0 +1,4 @@
file(GLOB_RECURSE srcs "main.c" "src/*.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "./include")

View File

@ -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

View File

@ -0,0 +1,2 @@
dependencies:
espressif/led_strip: "^2.4.1"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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, &params);
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;
}

View File

@ -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;
}

View File

@ -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); }

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
CONFIG_BLINK_GPIO=5

View File

@ -0,0 +1 @@
CONFIG_BLINK_LED_STRIP=y

View File

@ -0,0 +1,2 @@
CONFIG_BLINK_LED_STRIP=y
CONFIG_BLINK_GPIO=48

View File

@ -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)

View File

@ -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.

View File

@ -0,0 +1,4 @@
file(GLOB_RECURSE srcs "main.c" "src/*.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "./include")

View File

@ -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

View File

@ -0,0 +1,2 @@
dependencies:
espressif/led_strip: "^2.4.1"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More