diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7645a0b311fd..60c0e4a913ed 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,7 +21,7 @@ jobs: # Disable the job in forks if: ${{ github.repository_owner == 'espressif' }} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-X64-large steps: # Depending on the branch/tag, set CLONE_BRANCH_OR_TAG variable (used in the Dockerfile # as a build arg) and TAG_NAME (used when tagging the image). diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b374b12dcc6..73470291adf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,12 @@ cmake_minimum_required(VERSION 3.16) -project(esp-idf C CXX ASM) if(CMAKE_CURRENT_LIST_DIR STREQUAL CMAKE_SOURCE_DIR) message(FATAL_ERROR "Current directory '${CMAKE_CURRENT_LIST_DIR}' is not buildable. " - "Change directories to one of the example projects in '${CMAKE_CURRENT_LIST_DIR}/examples' and try " - "again.") + "Change directories to one of the example projects in '${CMAKE_CURRENT_LIST_DIR}/examples' and try again.") endif() +project(esp-idf C CXX ASM) + # Variables compile_options, c_compile_options, cxx_compile_options, compile_definitions, link_options shall # not be unset as they may already contain flags, set by toolchain-TARGET.cmake files. diff --git a/components/bt/common/ble_log/ble_log_spi_out.c b/components/bt/common/ble_log/ble_log_spi_out.c index d2441431831c..2a96fec6a3c0 100644 --- a/components/bt/common/ble_log/ble_log_spi_out.c +++ b/components/bt/common/ble_log/ble_log_spi_out.c @@ -1365,12 +1365,13 @@ int ble_log_spi_out_hci_write(uint8_t source, const uint8_t *addr, uint16_t len) return -1; } - if (source == BLE_LOG_SPI_OUT_SOURCE_HCI_UPSTREAM) { #if SPI_OUT_LL_ENABLED + if (source == BLE_LOG_SPI_OUT_SOURCE_HCI_UPSTREAM) { ble_log_spi_out_ll_write(len, addr, 0, NULL, BIT(LL_LOG_FLAG_HCI_UPSTREAM)); -#endif // SPI_OUT_LL_ENABLED } - if (source == BLE_LOG_SPI_OUT_SOURCE_HCI_DOWNSTREAM) { + if (source == BLE_LOG_SPI_OUT_SOURCE_HCI_DOWNSTREAM) +#endif /* SPI_OUT_LL_ENABLED */ + { spi_out_log_cb_t *log_cb; bool fallback = false; if (!spi_out_get_task_mapping(LOG_MODULE_TASK_MAP(hci), diff --git a/components/bt/common/btc/core/btc_manage.c b/components/bt/common/btc/core/btc_manage.c index e65e10798833..4b63f1efbabe 100644 --- a/components/bt/common/btc/core/btc_manage.c +++ b/components/bt/common/btc/core/btc_manage.c @@ -8,19 +8,20 @@ #include "btc/btc_task.h" #include "osi/thread.h" -#if BTC_DYNAMIC_MEMORY == FALSE -void *btc_profile_cb_tab[BTC_PID_NUM] = {}; -#else +#if BTC_DYNAMIC_MEMORY == TRUE void **btc_profile_cb_tab; +#else +void *btc_profile_cb_tab[BTC_PID_NUM] = {}; #endif void esp_profile_cb_reset(void) { - #if BTC_DYNAMIC_MEMORY == TRUE - if (btc_profile_cb_tab == NULL) { +#if BTC_DYNAMIC_MEMORY == TRUE + void *p = btc_profile_cb_tab; + if (p == NULL) { return; } - #endif +#endif int i; @@ -31,11 +32,12 @@ void esp_profile_cb_reset(void) int btc_profile_cb_set(btc_pid_t profile_id, void *cb) { - #if BTC_DYNAMIC_MEMORY == TRUE - if (btc_profile_cb_tab == NULL) { +#if BTC_DYNAMIC_MEMORY == TRUE + void *p = btc_profile_cb_tab; + if (p == NULL) { return -1; } - #endif +#endif if (profile_id < 0 || profile_id >= BTC_PID_NUM) { return -1; @@ -48,11 +50,12 @@ int btc_profile_cb_set(btc_pid_t profile_id, void *cb) void *btc_profile_cb_get(btc_pid_t profile_id) { - #if BTC_DYNAMIC_MEMORY == TRUE - if (btc_profile_cb_tab == NULL) { +#if BTC_DYNAMIC_MEMORY == TRUE + void *p = btc_profile_cb_tab; + if (p == NULL) { return NULL; } - #endif +#endif if (profile_id < 0 || profile_id >= BTC_PID_NUM) { return NULL; diff --git a/components/bt/controller/esp32c2/Kconfig.in b/components/bt/controller/esp32c2/Kconfig.in index 016baf511c96..9dd21b161edc 100644 --- a/components/bt/controller/esp32c2/Kconfig.in +++ b/components/bt/controller/esp32c2/Kconfig.in @@ -458,10 +458,12 @@ config BT_LE_LL_DUP_SCAN_LIST_COUNT config BT_LE_LL_SCA int "BLE Sleep clock accuracy" - range 0 500 + range 0 3000 default 60 help Sleep clock accuracy of our device (in ppm) + The Bluetooth LE spec requires a Sleep Clock Accuracy (SCA) of < ±500 ppm. + This options allows for a larger value to enable the use of less accurate clock sources. config BT_LE_LL_PEER_SCA_SET_ENABLE bool "Enable to set constant peer SCA" diff --git a/components/bt/controller/esp32c2/bt.c b/components/bt/controller/esp32c2/bt.c index bdfdc48ac129..c9b227a5f7a1 100644 --- a/components/bt/controller/esp32c2/bt.c +++ b/components/bt/controller/esp32c2/bt.c @@ -749,7 +749,7 @@ void controller_wakeup_cb(void *arg) #if CONFIG_FREERTOS_USE_TICKLESS_IDLE static bool esp_bt_check_wakeup_by_bt(void) { - return (esp_sleep_get_wakeup_causes() & ESP_SLEEP_WAKEUP_BT); + return (esp_sleep_get_wakeup_causes() & BIT(ESP_SLEEP_WAKEUP_BT)); } #endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE diff --git a/components/bt/controller/esp32c6/Kconfig.in b/components/bt/controller/esp32c6/Kconfig.in index 29072a1432cd..93ba37845426 100644 --- a/components/bt/controller/esp32c6/Kconfig.in +++ b/components/bt/controller/esp32c6/Kconfig.in @@ -552,10 +552,12 @@ config BT_LE_LL_DUP_SCAN_LIST_COUNT config BT_LE_LL_SCA int "BLE Sleep clock accuracy" - range 0 500 + range 0 3000 default 60 help Sleep clock accuracy of our device (in ppm) + The Bluetooth LE spec requires a Sleep Clock Accuracy (SCA) of < ±500 ppm. + This options allows for a larger value to enable the use of less accurate clock sources. config BT_LE_LL_PEER_SCA_SET_ENABLE bool "Enable to set constant peer SCA" @@ -913,3 +915,65 @@ endmenu config BT_LE_DTM_ENABLED bool "Enable Direct Test Mode (DTM) feature" default n + +menu "Scheduling Priority Level Config" + choice BT_LE_ADV_SCHED_PRIO_LEVEL + prompt "The Adv scheduling priority level" + default BT_LE_ADV_SCHED_PRIO_LOW_LEVEL + help + The Adv scheduling priority level is used for arbitration when internal scheduling conflicts. + config BT_LE_ADV_SCHED_PRIO_LOW_LEVEL + bool "low priority level" + config BT_LE_ADV_SCHED_PRIO_MID_LEVEL + bool "medium priority level" + config BT_LE_ADV_SCHED_PRIO_HIGH_LEVEL + bool "high priority level" + endchoice + + config BT_LE_DFT_ADV_SCHED_PRIO_LEVEL + int + default 0 if BT_LE_ADV_SCHED_PRIO_LOW_LEVEL + default 1 if BT_LE_ADV_SCHED_PRIO_MID_LEVEL + default 2 if BT_LE_ADV_SCHED_PRIO_HIGH_LEVEL + default 0 + + choice BT_LE_PERIODIC_ADV_SCHED_PRIO_LEVEL + prompt "The Periodic Adv scheduling priority level" + default BT_LE_PERIODIC_ADV_SCHED_PRIO_MID_LEVEL + help + The Periodic Adv scheduling priority level is used for arbitration when internal scheduling conflicts. + config BT_LE_PERIODIC_ADV_SCHED_PRIO_LOW_LEVEL + bool "low priority level" + config BT_LE_PERIODIC_ADV_SCHED_PRIO_MID_LEVEL + bool "medium priority level" + config BT_LE_PERIODIC_ADV_SCHED_PRIO_HIGH_LEVEL + bool "high priority level" + endchoice + + config BT_LE_DFT_PERIODIC_ADV_SCHED_PRIO_LEVEL + int + default 0 if BT_LE_PERIODIC_ADV_SCHED_PRIO_LOW_LEVEL + default 1 if BT_LE_PERIODIC_ADV_SCHED_PRIO_MID_LEVEL + default 2 if BT_LE_PERIODIC_ADV_SCHED_PRIO_HIGH_LEVEL + default 1 + + choice BT_LE_SYNC_SCHED_PRIO_LEVEL + prompt "The Sync scheduling priority level" + default BT_LE_SYNC_SCHED_PRIO_MID_LEVEL + help + The SYNC scheduling priority level is used for arbitration when internal scheduling conflicts. + config BT_LE_SYNC_SCHED_PRIO_LOW_LEVEL + bool "low priority level" + config BT_LE_SYNC_SCHED_PRIO_MID_LEVEL + bool "medium priority level" + config BT_LE_SYNC_SCHED_PRIO_HIGH_LEVEL + bool "high priority level" + endchoice + + config BT_LE_DFT_SYNC_SCHED_PRIO_LEVEL + int + default 0 if BT_LE_SYNC_SCHED_PRIO_LOW_LEVEL + default 1 if BT_LE_SYNC_SCHED_PRIO_MID_LEVEL + default 2 if BT_LE_SYNC_SCHED_PRIO_HIGH_LEVEL + default 1 +endmenu diff --git a/components/bt/controller/esp32c6/bt.c b/components/bt/controller/esp32c6/bt.c index 437b284a5db2..9d09f014442e 100644 --- a/components/bt/controller/esp32c6/bt.c +++ b/components/bt/controller/esp32c6/bt.c @@ -799,7 +799,7 @@ IRAM_ATTR void controller_wakeup_cb(void *arg) #if CONFIG_FREERTOS_USE_TICKLESS_IDLE static bool esp_bt_check_wakeup_by_bt(void) { - return (esp_sleep_get_wakeup_causes() & ESP_SLEEP_WAKEUP_BT); + return (esp_sleep_get_wakeup_causes() & BIT(ESP_SLEEP_WAKEUP_BT)); } static esp_err_t sleep_modem_ble_mac_retention_init(void *arg) diff --git a/components/bt/controller/esp32c6/esp_bt_cfg.h b/components/bt/controller/esp32c6/esp_bt_cfg.h index 07a3acdb5eb7..2586976c90d5 100644 --- a/components/bt/controller/esp32c6/esp_bt_cfg.h +++ b/components/bt/controller/esp32c6/esp_bt_cfg.h @@ -300,6 +300,7 @@ extern "C" { #define RUN_BQB_TEST (0) #define RUN_QA_TEST (0) #define NIMBLE_DISABLE_SCAN_BACKOFF (0) +#define BT_LL_CTRL_PRIO_LVL_CFG ((CONFIG_BT_LE_DFT_SYNC_SCHED_PRIO_LEVEL << 4) | (CONFIG_BT_LE_DFT_PERIODIC_ADV_SCHED_PRIO_LEVEL << 2) | CONFIG_BT_LE_DFT_ADV_SCHED_PRIO_LEVEL) #ifdef __cplusplus } diff --git a/components/bt/controller/esp32h2/Kconfig.in b/components/bt/controller/esp32h2/Kconfig.in index 6d6e38af5a76..17a1d9abf2ce 100644 --- a/components/bt/controller/esp32h2/Kconfig.in +++ b/components/bt/controller/esp32h2/Kconfig.in @@ -546,10 +546,12 @@ config BT_LE_LL_DUP_SCAN_LIST_COUNT config BT_LE_LL_SCA int "BLE Sleep clock accuracy" - range 0 500 + range 0 3000 default 60 help Sleep clock accuracy of our device (in ppm) + The Bluetooth LE spec requires a Sleep Clock Accuracy (SCA) of < ±500 ppm. + This options allows for a larger value to enable the use of less accurate clock sources. config BT_LE_LL_PEER_SCA_SET_ENABLE bool "Enable to set constant peer SCA" @@ -917,3 +919,65 @@ endmenu config BT_LE_DTM_ENABLED bool "Enable Direct Test Mode (DTM) feature" default n + +menu "Scheduling Priority Level Config" + choice BT_LE_ADV_SCHED_PRIO_LEVEL + prompt "The Adv scheduling priority level" + default BT_LE_ADV_SCHED_PRIO_LOW_LEVEL + help + The Adv scheduling priority level is used for arbitration when internal scheduling conflicts. + config BT_LE_ADV_SCHED_PRIO_LOW_LEVEL + bool "low priority level" + config BT_LE_ADV_SCHED_PRIO_MID_LEVEL + bool "medium priority level" + config BT_LE_ADV_SCHED_PRIO_HIGH_LEVEL + bool "high priority level" + endchoice + + config BT_LE_DFT_ADV_SCHED_PRIO_LEVEL + int + default 0 if BT_LE_ADV_SCHED_PRIO_LOW_LEVEL + default 1 if BT_LE_ADV_SCHED_PRIO_MID_LEVEL + default 2 if BT_LE_ADV_SCHED_PRIO_HIGH_LEVEL + default 0 + + choice BT_LE_PERIODIC_ADV_SCHED_PRIO_LEVEL + prompt "The Periodic Adv scheduling priority level" + default BT_LE_PERIODIC_ADV_SCHED_PRIO_MID_LEVEL + help + The Periodic Adv scheduling priority level is used for arbitration when internal scheduling conflicts. + config BT_LE_PERIODIC_ADV_SCHED_PRIO_LOW_LEVEL + bool "low priority level" + config BT_LE_PERIODIC_ADV_SCHED_PRIO_MID_LEVEL + bool "medium priority level" + config BT_LE_PERIODIC_ADV_SCHED_PRIO_HIGH_LEVEL + bool "high priority level" + endchoice + + config BT_LE_DFT_PERIODIC_ADV_SCHED_PRIO_LEVEL + int + default 0 if BT_LE_PERIODIC_ADV_SCHED_PRIO_LOW_LEVEL + default 1 if BT_LE_PERIODIC_ADV_SCHED_PRIO_MID_LEVEL + default 2 if BT_LE_PERIODIC_ADV_SCHED_PRIO_HIGH_LEVEL + default 1 + + choice BT_LE_SYNC_SCHED_PRIO_LEVEL + prompt "The Sync scheduling priority level" + default BT_LE_SYNC_SCHED_PRIO_MID_LEVEL + help + The SYNC scheduling priority level is used for arbitration when internal scheduling conflicts. + config BT_LE_SYNC_SCHED_PRIO_LOW_LEVEL + bool "low priority level" + config BT_LE_SYNC_SCHED_PRIO_MID_LEVEL + bool "medium priority level" + config BT_LE_SYNC_SCHED_PRIO_HIGH_LEVEL + bool "high priority level" + endchoice + + config BT_LE_DFT_SYNC_SCHED_PRIO_LEVEL + int + default 0 if BT_LE_SYNC_SCHED_PRIO_LOW_LEVEL + default 1 if BT_LE_SYNC_SCHED_PRIO_MID_LEVEL + default 2 if BT_LE_SYNC_SCHED_PRIO_HIGH_LEVEL + default 1 +endmenu diff --git a/components/bt/controller/esp32h2/bt.c b/components/bt/controller/esp32h2/bt.c index b23c018d8d07..4daa9d3fbeba 100644 --- a/components/bt/controller/esp32h2/bt.c +++ b/components/bt/controller/esp32h2/bt.c @@ -764,7 +764,7 @@ IRAM_ATTR void controller_wakeup_cb(void *arg) #ifdef CONFIG_FREERTOS_USE_TICKLESS_IDLE static bool esp_bt_check_wakeup_by_bt(void) { - return (esp_sleep_get_wakeup_causes() & ESP_SLEEP_WAKEUP_BT); + return (esp_sleep_get_wakeup_causes() & BIT(ESP_SLEEP_WAKEUP_BT)); } static esp_err_t sleep_modem_ble_mac_retention_init(void *arg) diff --git a/components/bt/controller/esp32h2/esp_bt_cfg.h b/components/bt/controller/esp32h2/esp_bt_cfg.h index 4734737c5a4c..e79eb742e2dd 100644 --- a/components/bt/controller/esp32h2/esp_bt_cfg.h +++ b/components/bt/controller/esp32h2/esp_bt_cfg.h @@ -297,7 +297,7 @@ extern "C" { #define RUN_BQB_TEST (0) #define RUN_QA_TEST (0) #define NIMBLE_DISABLE_SCAN_BACKOFF (0) - +#define BT_LL_CTRL_PRIO_LVL_CFG ((CONFIG_BT_LE_DFT_SYNC_SCHED_PRIO_LEVEL << 4) | (CONFIG_BT_LE_DFT_PERIODIC_ADV_SCHED_PRIO_LEVEL << 2) | CONFIG_BT_LE_DFT_ADV_SCHED_PRIO_LEVEL) #ifdef __cplusplus } #endif diff --git a/components/bt/controller/lib_esp32c2/esp32c2-bt-lib b/components/bt/controller/lib_esp32c2/esp32c2-bt-lib index 7778b43e3550..c82c623de457 160000 --- a/components/bt/controller/lib_esp32c2/esp32c2-bt-lib +++ b/components/bt/controller/lib_esp32c2/esp32c2-bt-lib @@ -1 +1 @@ -Subproject commit 7778b43e35501ed4bf945dce37b15278a0724b5c +Subproject commit c82c623de457e1b06cf0dad5c963d023dbb6fe76 diff --git a/components/bt/controller/lib_esp32c6/esp32c6-bt-lib b/components/bt/controller/lib_esp32c6/esp32c6-bt-lib index bd5914cd7da1..1495595b82f5 160000 --- a/components/bt/controller/lib_esp32c6/esp32c6-bt-lib +++ b/components/bt/controller/lib_esp32c6/esp32c6-bt-lib @@ -1 +1 @@ -Subproject commit bd5914cd7da1b178e60f899d2acc14a9d3df6f2c +Subproject commit 1495595b82f5423d12b325960ae89bc604ebdcd4 diff --git a/components/bt/controller/lib_esp32h2/esp32h2-bt-lib b/components/bt/controller/lib_esp32h2/esp32h2-bt-lib index 3874210bfea2..9cfbeb5f1637 160000 --- a/components/bt/controller/lib_esp32h2/esp32h2-bt-lib +++ b/components/bt/controller/lib_esp32h2/esp32h2-bt-lib @@ -1 +1 @@ -Subproject commit 3874210bfea2a49a8c29d5102a6ae1ecb9fdfbce +Subproject commit 9cfbeb5f163788174073da19b3cd09c4d00cc860 diff --git a/components/bt/host/bluedroid/api/esp_gap_ble_api.c b/components/bt/host/bluedroid/api/esp_gap_ble_api.c index 1da02cf877a8..3c3a48e5b937 100644 --- a/components/bt/host/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/host/bluedroid/api/esp_gap_ble_api.c @@ -805,6 +805,28 @@ esp_err_t esp_ble_create_sc_oob_data(void) return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } + +esp_err_t esp_ble_gap_get_local_irk(uint8_t local_irk[16]) +{ + if (local_irk == NULL) { + ESP_LOGE(__func__, "local_irk is NULL"); + return ESP_ERR_INVALID_ARG; + } + + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + ESP_LOGE(__func__, "Bluedroid is not enabled"); + return ESP_ERR_INVALID_STATE; + } + + /* Use BTM API to safely retrieve local IRK */ + if (BTM_GetLocalIRK(local_irk)) { + ESP_LOGD(__func__, "Local IRK retrieved successfully"); + return ESP_OK; + } else { + ESP_LOGW(__func__, "Local IRK not available"); + return ESP_ERR_INVALID_STATE; + } +} #endif /* #if (SMP_INCLUDED == TRUE) */ esp_err_t esp_ble_gap_disconnect(esp_bd_addr_t remote_device) diff --git a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h index 47d2953cf568..fee2d4823cda 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h @@ -2088,6 +2088,7 @@ esp_err_t esp_ble_gap_set_resolvable_private_address_timeout(uint16_t rpa_timeou * */ esp_err_t esp_ble_gap_add_device_to_resolving_list(esp_bd_addr_t peer_addr, uint8_t addr_type, uint8_t *peer_irk); + /** * @brief This function clears the random address for the application * @@ -2493,6 +2494,28 @@ esp_err_t esp_ble_sc_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t p_c[16], uint8 * */ esp_err_t esp_ble_create_sc_oob_data(void); + +/** + * @brief Get the local Identity Resolving Key (IRK). + * + * @note This API retrieves the local IRK stored in the device's security database. + * The IRK is used by the controller to generate and resolve Resolvable Private Addresses (RPA). + * The IRK length is always 16 bytes (ESP_BT_OCTET16_LEN). + * + * @note Usage Restrictions: Do NOT call this API during a disconnection event or while + * a BLE disconnection is in progress. Calling this API during disconnection may lead + * to undefined behavior or accessing invalid information. + * + * @param[out] local_irk: Buffer to hold the 16-byte IRK. The array notation [16] explicitly + * indicates the required buffer size (ESP_BT_OCTET16_LEN). + * + * @return + * - ESP_OK : success + * - ESP_ERR_INVALID_ARG : local_irk is NULL + * - ESP_ERR_INVALID_STATE : BLE stack not initialized or IRK not available + */ +esp_err_t esp_ble_gap_get_local_irk(uint8_t local_irk[16]); + #endif /* #if (SMP_INCLUDED == TRUE) */ /** diff --git a/components/bt/host/bluedroid/stack/avrc/avrc_opt.c b/components/bt/host/bluedroid/stack/avrc/avrc_opt.c index 6f0663c08ad3..cd933df73ea8 100644 --- a/components/bt/host/bluedroid/stack/avrc/avrc_opt.c +++ b/components/bt/host/bluedroid/stack/avrc/avrc_opt.c @@ -48,17 +48,28 @@ ******************************************************************************/ static BT_HDR *avrc_vendor_msg(tAVRC_MSG_VENDOR *p_msg) { - BT_HDR *p_cmd; + BT_HDR *p_cmd = NULL; UINT8 *p_data; - assert(p_msg != NULL); +/* + A vendor dependent command consists of at least of: + - A BT_HDR, plus + - AVCT_MSG_OFFSET, plus + - 3 bytes for ctype, subunit_type and op_vendor, plus + - 3 bytes for company_id +*/ +#define AVRC_MIN_VENDOR_CMD_LEN (BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE) + + if (!p_msg) { + return NULL; + } #if AVRC_METADATA_INCLUDED == TRUE - assert(AVRC_META_CMD_BUF_SIZE > (AVRC_MIN_CMD_LEN + p_msg->vendor_len)); - if ((p_cmd = (BT_HDR *) osi_malloc(AVRC_META_CMD_BUF_SIZE)) != NULL) + if ((AVRC_META_CMD_BUF_SIZE > AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len) && + ((p_cmd = (BT_HDR *) osi_malloc(AVRC_META_CMD_BUF_SIZE)) != NULL)) #else - assert(AVRC_CMD_BUF_SIZE > (AVRC_MIN_CMD_LEN + p_msg->vendor_len)); - if ((p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL) + if ((AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)) && + (p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL) #endif { p_cmd->offset = AVCT_MSG_OFFSET; diff --git a/components/bt/host/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/host/bluedroid/stack/btm/btm_ble_gap.c index b8d050a4271d..faf2e1848f0a 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/host/bluedroid/stack/btm/btm_ble_gap.c @@ -4869,4 +4869,16 @@ bool btm_ble_adv_pkt_post(pkt_linked_item_t *pkt) } #endif // #if (BLE_42_SCAN_EN == TRUE) +#if (SMP_INCLUDED == TRUE) +/* Retrieve local IRK safely */ +bool BTM_GetLocalIRK(uint8_t *irk) +{ + if (!irk) { + return false; + } + + memcpy(irk, btm_cb.devcb.id_keys.irk, sizeof(btm_cb.devcb.id_keys.irk)); + return true; +} +#endif // (SMP_INCLUDED == TRUE) #endif /* BLE_INCLUDED */ diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_main.c b/components/bt/host/bluedroid/stack/gatt/gatt_main.c index 53d9be747dec..e5d982371252 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_main.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_main.c @@ -169,7 +169,7 @@ void gatt_free(void) gatt_cb.sign_op_queue = NULL; fixed_queue_free(gatt_cb.srv_chg_clt_q, NULL); gatt_cb.srv_chg_clt_q = NULL; - fixed_queue_free(gatt_cb.pending_new_srv_start_q, NULL); + fixed_queue_free(gatt_cb.pending_new_srv_start_q, osi_free_func); gatt_cb.pending_new_srv_start_q = NULL; list_node_t *p_node = NULL; diff --git a/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h b/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h index 7ec384ef1bf1..e696fe65cfbd 100644 --- a/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h @@ -2561,6 +2561,20 @@ void BTM_BleReadControllerFeatures(tBTM_BLE_CTRL_FEATURES_CBACK *p_vsc_cback); //extern UINT8 *BTM_CheckAdvData( UINT8 *p_adv, UINT16 adv_data_len, UINT8 type, UINT8 *p_length); +/****************************************************************************** +** +** Function BTM_GetLocalIRK +** +** Description Get the local Identity Resolving Key (IRK) from the device control block. +** +** Parameters irk : output buffer (16 bytes) to copy IRK into +** +** Returns true if IRK is available, false otherwise +** +******************************************************************************/ +bool BTM_GetLocalIRK(uint8_t *irk); + + /******************************************************************************* ** ** Function BTM_BleGetCurrentAddress diff --git a/components/bt/host/nimble/Kconfig.in b/components/bt/host/nimble/Kconfig.in index 0076394e3900..a5f56692f6c8 100644 --- a/components/bt/host/nimble/Kconfig.in +++ b/components/bt/host/nimble/Kconfig.in @@ -581,7 +581,7 @@ menu "BLE 5.x Features" config BT_NIMBLE_EXT_SCAN bool "Enable extended scanning" - depends on BT_NIMBLE_50_FEATURE_SUPPORT && BT_NIMBLE_ROLE_OBSERVER + depends on BT_NIMBLE_50_FEATURE_SUPPORT default y help Enable this option to do extended scanning. @@ -721,11 +721,15 @@ menu "BLE 6.x Features" if BT_NIMBLE_60_FEATURE_SUPPORT config BT_NIMBLE_CHANNEL_SOUNDING bool "ble channel souding feature" - depends on BT_NIMBLE_ENABLED default n help Used to enable/disable the channel sounding feature + config BT_NIMBLE_MONITOR_ADV + bool "Enable Monitor Advertising" + default n + help + Enable support for Monitor Advertisers endif endmenu #BLE6.x diff --git a/components/bt/host/nimble/nimble b/components/bt/host/nimble/nimble index 9464ecd6e32f..8fd41d86c4ed 160000 --- a/components/bt/host/nimble/nimble +++ b/components/bt/host/nimble/nimble @@ -1 +1 @@ -Subproject commit 9464ecd6e32ff80d09b0c1678805c0fa4e722821 +Subproject commit 8fd41d86c4ed8657e6c97d6da8b3d214622db737 diff --git a/components/bt/host/nimble/port/include/ble_svc_gap_stub.h b/components/bt/host/nimble/port/include/ble_svc_gap_stub.h new file mode 100644 index 000000000000..1fb456f2194d --- /dev/null +++ b/components/bt/host/nimble/port/include/ble_svc_gap_stub.h @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_log.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_SVC_GAP_TAG "ble_svc_gap" + +/** + * Stub implementations for GAP service APIs. + * These are compiled when CONFIG_BT_NIMBLE_GAP_SERVICE is disabled. + */ + +static inline void ble_svc_gap_init(void) +{ + ESP_LOGE(BLE_SVC_GAP_TAG, "GAP service not enabled. Enable CONFIG_BT_NIMBLE_GAP_SERVICE to use this API."); +} + +static inline int ble_svc_gap_device_name_set(const char *name) +{ + (void)name; + ESP_LOGE(BLE_SVC_GAP_TAG, "GAP service not enabled. Enable CONFIG_BT_NIMBLE_GAP_SERVICE to use this API."); + return -1; +} + +static inline const char *ble_svc_gap_device_name(void) +{ + ESP_LOGE(BLE_SVC_GAP_TAG, "GAP service not enabled. Enable CONFIG_BT_NIMBLE_GAP_SERVICE to use this API."); + return NULL; +} + +static inline int ble_svc_gap_device_appearance_set(uint16_t appearance) +{ + ESP_LOGE(BLE_SVC_GAP_TAG, "GAP service not enabled. Enable CONFIG_BT_NIMBLE_GAP_SERVICE to use this API."); + return -1; +} + +static inline int ble_svc_gap_device_key_material_set(uint8_t *session_key, uint8_t *iv) +{ + ESP_LOGE(BLE_SVC_GAP_TAG, "GAP service not enabled. Enable CONFIG_BT_NIMBLE_GAP_SERVICE to use this API."); + return -1; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/bt/host/nimble/port/include/esp_nimble_cfg.h b/components/bt/host/nimble/port/include/esp_nimble_cfg.h index 1d6f8a4e7be1..0d063aeca40e 100644 --- a/components/bt/host/nimble/port/include/esp_nimble_cfg.h +++ b/components/bt/host/nimble/port/include/esp_nimble_cfg.h @@ -1804,9 +1804,11 @@ #endif /*** @apache-mynewt-nimble/nimble/host/services/gap */ +#ifdef CONFIG_BT_NIMBLE_GAP_SERVICE #ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE #define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE #endif +#endif #ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE_WRITE_PERM #if CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM @@ -1819,10 +1821,12 @@ #endif //CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM #endif //MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE_WRITE_PERM +#ifdef CONFIG_BT_NIMBLE_GAP_SERVICE #ifndef MYNEWT_VAL_BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION #define MYNEWT_VAL_BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION \ CONFIG_BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION #endif +#endif #ifndef CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME #define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME "nimble" @@ -1830,9 +1834,11 @@ #define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME #endif +#ifdef CONFIG_BT_NIMBLE_GAP_SERVICE #ifndef MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH #define MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN // According to the specification, the maximum length should be 248 #endif +#endif #ifndef MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM #if CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM @@ -2200,4 +2206,12 @@ #endif #endif +#ifndef MYNEWT_VAL_BLE_MONITOR_ADV +#ifdef CONFIG_BT_NIMBLE_MONITOR_ADV +#define MYNEWT_VAL_BLE_MONITOR_ADV CONFIG_BT_NIMBLE_MONITOR_ADV +#else +#define MYNEWT_VAL_BLE_MONITOR_ADV (0) +#endif +#endif + #endif diff --git a/components/bt/include/esp32c6/include/esp_bt.h b/components/bt/include/esp32c6/include/esp_bt.h index 94f099184ab3..2621f3fb52bf 100644 --- a/components/bt/include/esp32c6/include/esp_bt.h +++ b/components/bt/include/esp32c6/include/esp_bt.h @@ -156,7 +156,7 @@ esp_err_t esp_ble_tx_power_set_enhanced(esp_ble_enhanced_power_type_t power_type */ esp_power_level_t esp_ble_tx_power_get_enhanced(esp_ble_enhanced_power_type_t power_type, uint16_t handle); -#define CONFIG_VERSION 0x20250606 +#define CONFIG_VERSION 0x20251022 #define CONFIG_MAGIC 0x5A5AA5A5 /** @@ -232,6 +232,7 @@ typedef struct { int8_t ch39_txpwr; /*!< BLE transmit power (in dBm) used for BLE advertising on channel 39. */ uint8_t adv_rsv_cnt; /*!< BLE adv state machine reserve count number */ uint8_t conn_rsv_cnt; /*!< BLE conn state machine reserve count number */ + uint8_t priority_level_cfg; /*!< The option for priority level configuration */ uint32_t config_magic; /*!< Magic number for configuration validation */ } esp_bt_controller_config_t; @@ -295,6 +296,7 @@ typedef struct { .ch39_txpwr = BLE_LL_TX_PWR_DBM_N, \ .adv_rsv_cnt = BLE_LL_ADV_SM_RESERVE_CNT_N, \ .conn_rsv_cnt = BLE_LL_CONN_SM_RESERVE_CNT_N, \ + .priority_level_cfg = BT_LL_CTRL_PRIO_LVL_CFG, \ .config_magic = CONFIG_MAGIC, \ } #elif CONFIG_IDF_TARGET_ESP32C61 @@ -356,6 +358,7 @@ typedef struct { .ch39_txpwr = BLE_LL_TX_PWR_DBM_N, \ .adv_rsv_cnt = BLE_LL_ADV_SM_RESERVE_CNT_N, \ .conn_rsv_cnt = BLE_LL_CONN_SM_RESERVE_CNT_N, \ + .priority_level_cfg = BT_LL_CTRL_PRIO_LVL_CFG, \ .config_magic = CONFIG_MAGIC, \ } #endif diff --git a/components/bt/include/esp32h2/include/esp_bt.h b/components/bt/include/esp32h2/include/esp_bt.h index 601f5816a37f..b6bb5bfb23bd 100644 --- a/components/bt/include/esp32h2/include/esp_bt.h +++ b/components/bt/include/esp32h2/include/esp_bt.h @@ -161,7 +161,7 @@ esp_err_t esp_ble_tx_power_set_enhanced(esp_ble_enhanced_power_type_t power_type */ esp_power_level_t esp_ble_tx_power_get_enhanced(esp_ble_enhanced_power_type_t power_type, uint16_t handle); -#define CONFIG_VERSION 0x20250606 +#define CONFIG_VERSION 0x20251022 #define CONFIG_MAGIC 0x5A5AA5A5 /** @@ -236,6 +236,7 @@ typedef struct { int8_t ch39_txpwr; /*!< BLE transmit power (in dBm) used for BLE advertising on channel 39. */ uint8_t adv_rsv_cnt; /*!< BLE adv state machine reserve count number */ uint8_t conn_rsv_cnt; /*!< BLE conn state machine reserve count number */ + uint8_t priority_level_cfg; /*!< The option for priority level configuration */ uint32_t config_magic; /*!< Configuration magic value */ } esp_bt_controller_config_t; @@ -298,6 +299,7 @@ typedef struct { .ch39_txpwr = BLE_LL_TX_PWR_DBM_N, \ .adv_rsv_cnt = BLE_LL_ADV_SM_RESERVE_CNT_N, \ .conn_rsv_cnt = BLE_LL_CONN_SM_RESERVE_CNT_N, \ + .priority_level_cfg = BT_LL_CTRL_PRIO_LVL_CFG, \ .config_magic = CONFIG_MAGIC, \ } diff --git a/components/driver/test_apps/legacy_pcnt_driver/main/test_legacy_pcnt.c b/components/driver/test_apps/legacy_pcnt_driver/main/test_legacy_pcnt.c index 9c2c4a9287cb..00e519ced28b 100644 --- a/components/driver/test_apps/legacy_pcnt_driver/main/test_legacy_pcnt.c +++ b/components/driver/test_apps/legacy_pcnt_driver/main/test_legacy_pcnt.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -292,7 +292,7 @@ static void count_mode_test(gpio_num_t ctl_io) vTaskDelay(1000 / portTICK_PERIOD_MS); TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); printf("value: %d\n", test_counter); - TEST_ASSERT_INT16_WITHIN(1, test_counter, result[0]); + TEST_ASSERT_INT16_WITHIN(2, test_counter, result[0]); //2, 0, 0, 0 TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0)); @@ -304,7 +304,7 @@ static void count_mode_test(gpio_num_t ctl_io) vTaskDelay(1000 / portTICK_PERIOD_MS); TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); printf("value: %d\n", test_counter); - TEST_ASSERT_INT16_WITHIN(1, test_counter, result[1]); + TEST_ASSERT_INT16_WITHIN(2, test_counter, result[1]); //0,0,0,0 TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0)); @@ -316,7 +316,7 @@ static void count_mode_test(gpio_num_t ctl_io) vTaskDelay(1000 / portTICK_PERIOD_MS); TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); printf("value: %d\n", test_counter); - TEST_ASSERT_INT16_WITHIN(1, test_counter, result[2]); + TEST_ASSERT_INT16_WITHIN(2, test_counter, result[2]); //1,0,1,0 TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0)); @@ -328,7 +328,7 @@ static void count_mode_test(gpio_num_t ctl_io) vTaskDelay(1000 / portTICK_PERIOD_MS); TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); printf("value: %d\n", test_counter); - TEST_ASSERT_INT16_WITHIN(1, test_counter, result[3]); + TEST_ASSERT_INT16_WITHIN(2, test_counter, result[3]); //1,0,0,1 TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0)); @@ -340,7 +340,7 @@ static void count_mode_test(gpio_num_t ctl_io) vTaskDelay(1000 / portTICK_PERIOD_MS); TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); printf("value: %d\n", test_counter); - TEST_ASSERT_INT16_WITHIN(1, test_counter, result[4]); + TEST_ASSERT_INT16_WITHIN(2, test_counter, result[4]); //2,0,0,1 TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0)); @@ -352,7 +352,7 @@ static void count_mode_test(gpio_num_t ctl_io) vTaskDelay(1000 / portTICK_PERIOD_MS); TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); printf("value: %d\n", test_counter); - TEST_ASSERT_INT16_WITHIN(1, test_counter, result[5]); + TEST_ASSERT_INT16_WITHIN(2, test_counter, result[5]); //1,0,2,0 TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0)); @@ -364,7 +364,7 @@ static void count_mode_test(gpio_num_t ctl_io) vTaskDelay(1000 / portTICK_PERIOD_MS); TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); printf("value: %d\n", test_counter); - TEST_ASSERT_INT16_WITHIN(1, test_counter, result[6]); + TEST_ASSERT_INT16_WITHIN(2, test_counter, result[6]); //1,0,0,2 TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0)); @@ -376,7 +376,7 @@ static void count_mode_test(gpio_num_t ctl_io) vTaskDelay(1000 / portTICK_PERIOD_MS); TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); printf("value: %d\n", test_counter); - TEST_ASSERT_INT16_WITHIN(1, test_counter, result[7]); + TEST_ASSERT_INT16_WITHIN(2, test_counter, result[7]); } /* PCNT basic property: @@ -524,24 +524,24 @@ TEST_CASE("PCNT_interrupt_method_test_control_IO_high", "[pcnt][timeout=120]") // test event event_calculate(&event); - TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 2); - TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 2); + TEST_ASSERT_INT_WITHIN(3, event.h_threshold, 2); + TEST_ASSERT_INT_WITHIN(3, event.l_threshold, 2); TEST_ASSERT(event.l_limit == 0); - TEST_ASSERT_INT_WITHIN(2, event.h_limit, 2); - TEST_ASSERT_INT_WITHIN(2, event.zero_times, 2); - TEST_ASSERT_INT_WITHIN(3, event.filter_time, 4); + TEST_ASSERT_INT_WITHIN(3, event.h_limit, 2); + TEST_ASSERT_INT_WITHIN(3, event.zero_times, 2); + TEST_ASSERT_INT_WITHIN(4, event.filter_time, 4); // test interrupt disable TEST_ESP_OK(pcnt_intr_disable(PCNT_UNIT_0)); TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0)); // for the original control io disable interrupt status event_calculate(&event); - TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 2); - TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 2); + TEST_ASSERT_INT_WITHIN(3, event.h_threshold, 2); + TEST_ASSERT_INT_WITHIN(3, event.l_threshold, 2); TEST_ASSERT(event.l_limit == 0); - TEST_ASSERT_INT_WITHIN(2, event.h_limit, 2); - TEST_ASSERT_INT_WITHIN(2, event.zero_times, 2); - TEST_ASSERT_INT_WITHIN(3, event.filter_time, 4); + TEST_ASSERT_INT_WITHIN(3, event.h_limit, 2); + TEST_ASSERT_INT_WITHIN(3, event.zero_times, 2); + TEST_ASSERT_INT_WITHIN(4, event.filter_time, 4); // enable the intr TEST_ESP_OK(pcnt_intr_enable(PCNT_UNIT_0)); @@ -549,24 +549,24 @@ TEST_CASE("PCNT_interrupt_method_test_control_IO_high", "[pcnt][timeout=120]") TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0)); TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0)); event_calculate(&event); - TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 4); - TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 4); + TEST_ASSERT_INT_WITHIN(3, event.h_threshold, 4); + TEST_ASSERT_INT_WITHIN(3, event.l_threshold, 4); TEST_ASSERT(event.l_limit == 0); - TEST_ASSERT_INT_WITHIN(2, event.h_limit, 4); - TEST_ASSERT_INT_WITHIN(2, event.zero_times, 4); - TEST_ASSERT_INT_WITHIN(3, event.filter_time, 10); + TEST_ASSERT_INT_WITHIN(3, event.h_limit, 4); + TEST_ASSERT_INT_WITHIN(3, event.zero_times, 4); + TEST_ASSERT_INT_WITHIN(4, event.filter_time, 10); // disable part of events TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_ZERO)); TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_L_LIM)); TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_THRES_0)); event_calculate(&event); - TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 5); - TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 4); + TEST_ASSERT_INT_WITHIN(3, event.h_threshold, 5); + TEST_ASSERT_INT_WITHIN(3, event.l_threshold, 4); TEST_ASSERT(event.l_limit == 0); - TEST_ASSERT_INT_WITHIN(3, event.h_limit, 6); - TEST_ASSERT_INT_WITHIN(2, event.zero_times, 4); - TEST_ASSERT_INT_WITHIN(3, event.filter_time, 14); + TEST_ASSERT_INT_WITHIN(4, event.h_limit, 6); + TEST_ASSERT_INT_WITHIN(3, event.zero_times, 4); + TEST_ASSERT_INT_WITHIN(4, event.filter_time, 14); // Because this test uses its own ISR, we need to release it with `pcnt_isr_unregister` instead of `pcnt_isr_service_uninstall` TEST_ESP_OK(pcnt_isr_unregister(pcnt_isr_service)); @@ -624,24 +624,24 @@ TEST_CASE("PCNT_interrupt_method_test_control_IO_low", "[pcnt][timeout=120]") // test event event_calculate(&event); - TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 1); - TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 1); - TEST_ASSERT_INT_WITHIN(2, event.l_limit, 1); - TEST_ASSERT_INT_WITHIN(2, event.h_limit, 0); - TEST_ASSERT_INT_WITHIN(2, event.zero_times, 1); - TEST_ASSERT_INT_WITHIN(2, event.filter_time, 2); + TEST_ASSERT_INT_WITHIN(3, event.h_threshold, 1); + TEST_ASSERT_INT_WITHIN(3, event.l_threshold, 1); + TEST_ASSERT_INT_WITHIN(3, event.l_limit, 1); + TEST_ASSERT_INT_WITHIN(3, event.h_limit, 0); + TEST_ASSERT_INT_WITHIN(3, event.zero_times, 1); + TEST_ASSERT_INT_WITHIN(3, event.filter_time, 2); // test interrupt disable TEST_ESP_OK(pcnt_intr_disable(PCNT_UNIT_0)); TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0)); // for the original control io disable interrupt status event_calculate(&event); - TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 1); - TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 1); - TEST_ASSERT_INT_WITHIN(2, event.l_limit, 1); - TEST_ASSERT_INT_WITHIN(2, event.h_limit, 0); - TEST_ASSERT_INT_WITHIN(2, event.zero_times, 1); - TEST_ASSERT_INT_WITHIN(2, event.filter_time, 2); + TEST_ASSERT_INT_WITHIN(3, event.h_threshold, 1); + TEST_ASSERT_INT_WITHIN(3, event.l_threshold, 1); + TEST_ASSERT_INT_WITHIN(3, event.l_limit, 1); + TEST_ASSERT_INT_WITHIN(3, event.h_limit, 0); + TEST_ASSERT_INT_WITHIN(3, event.zero_times, 1); + TEST_ASSERT_INT_WITHIN(3, event.filter_time, 2); // enable the intr pcnt_unit_config(&config); @@ -651,24 +651,24 @@ TEST_CASE("PCNT_interrupt_method_test_control_IO_low", "[pcnt][timeout=120]") TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0)); TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0)); event_calculate(&event); - TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 2); - TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 3); - TEST_ASSERT_INT_WITHIN(2, event.l_limit, 2); - TEST_ASSERT_INT_WITHIN(2, event.h_limit, 0); - TEST_ASSERT_INT_WITHIN(2, event.zero_times, 2); - TEST_ASSERT_INT_WITHIN(2, event.filter_time, 6); + TEST_ASSERT_INT_WITHIN(3, event.h_threshold, 2); + TEST_ASSERT_INT_WITHIN(3, event.l_threshold, 3); + TEST_ASSERT_INT_WITHIN(3, event.l_limit, 2); + TEST_ASSERT_INT_WITHIN(3, event.h_limit, 0); + TEST_ASSERT_INT_WITHIN(3, event.zero_times, 2); + TEST_ASSERT_INT_WITHIN(3, event.filter_time, 6); // disable part of events TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_ZERO)); TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_L_LIM)); TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_THRES_0)); event_calculate(&event); - TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 4); - TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 3); - TEST_ASSERT_INT_WITHIN(2, event.l_limit, 2); - TEST_ASSERT_INT_WITHIN(2, event.h_limit, 0); - TEST_ASSERT_INT_WITHIN(2, event.zero_times, 2); - TEST_ASSERT_INT_WITHIN(2, event.filter_time, 8); + TEST_ASSERT_INT_WITHIN(3, event.h_threshold, 4); + TEST_ASSERT_INT_WITHIN(3, event.l_threshold, 3); + TEST_ASSERT_INT_WITHIN(3, event.l_limit, 2); + TEST_ASSERT_INT_WITHIN(3, event.h_limit, 0); + TEST_ASSERT_INT_WITHIN(3, event.zero_times, 2); + TEST_ASSERT_INT_WITHIN(3, event.filter_time, 8); // Because this test uses its own ISR, we need to release it with `pcnt_isr_unregister` instead of `pcnt_isr_service_uninstall` TEST_ESP_OK(pcnt_isr_unregister(pcnt_isr_service)); diff --git a/components/esp_coex/src/lib_printf.c b/components/esp_coex/src/lib_printf.c index 265874399f2a..deba7289126d 100644 --- a/components/esp_coex/src/lib_printf.c +++ b/components/esp_coex/src/lib_printf.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -38,13 +38,15 @@ static int lib_printf(const char* tag, const char* format, va_list arg) if (i > 0) { ESP_LOGI(tag, "%s", temp); } - va_end(arg); return len; } int coexist_printf(const char* format, ...) { va_list arg; + /* coverity[uninit_use_in_call] + Event uninit_use_in_call: Using uninitialized value arg when calling __builtin_c23_va_start. + False-positive: arg will be initialized in the function va_start() */ va_start(arg, format); int res = lib_printf("coexist", format, arg); va_end(arg); diff --git a/components/esp_driver_jpeg/jpeg_parse_marker.c b/components/esp_driver_jpeg/jpeg_parse_marker.c index cafaabc9e326..5a6db487b776 100644 --- a/components/esp_driver_jpeg/jpeg_parse_marker.c +++ b/components/esp_driver_jpeg/jpeg_parse_marker.c @@ -20,6 +20,11 @@ static const char *TAG = "jpeg.decoder"; static uint8_t jpeg_get_char(jpeg_dec_header_info_t *header_info) { + // Check if there are bytes left to read before decrementing buffer_left + if (header_info->buffer_left == 0) { + ESP_LOGE(TAG, "Buffer underflow detected in jpeg_get_char: no more bytes left to read"); + return 0; + } uint8_t c = header_info->buffer_offset[0]; header_info->buffer_offset++; header_info->header_size++; @@ -39,20 +44,26 @@ uint32_t jpeg_get_bytes(jpeg_dec_header_info_t *header_info, uint8_t num_bytes) esp_err_t jpeg_parse_appn_marker(jpeg_dec_header_info_t *header_info) { - uint32_t skip_num = jpeg_get_bytes(header_info, 2); - header_info->buffer_offset += (skip_num - 2); - header_info->header_size += (skip_num - 2); - header_info->buffer_left -= (skip_num - 2); + uint16_t skip_num = jpeg_get_bytes(header_info, 2); + ESP_RETURN_ON_FALSE(skip_num >= 2, ESP_ERR_INVALID_ARG, TAG, "Invalid APPn marker length: %d", skip_num); + uint16_t bytes_to_skip = skip_num - 2; + ESP_RETURN_ON_FALSE(header_info->buffer_left >= bytes_to_skip, ESP_ERR_INVALID_ARG, TAG, "APPn marker data underflow for buffer_left: %ld", header_info->buffer_left); + header_info->buffer_offset += bytes_to_skip; + header_info->header_size += bytes_to_skip; + header_info->buffer_left -= bytes_to_skip; return ESP_OK; } esp_err_t jpeg_parse_com_marker(jpeg_dec_header_info_t *header_info) { - uint32_t skip_num = jpeg_get_bytes(header_info, 2); - header_info->buffer_offset += (skip_num - 2); - header_info->header_size += (skip_num - 2); - header_info->buffer_left -= (skip_num - 2); + uint16_t skip_num = jpeg_get_bytes(header_info, 2); + ESP_RETURN_ON_FALSE(skip_num >= 2, ESP_ERR_INVALID_ARG, TAG, "Invalid COM marker length: %d", skip_num); + uint32_t bytes_to_skip = skip_num - 2; + ESP_RETURN_ON_FALSE(header_info->header_size >= bytes_to_skip, ESP_ERR_INVALID_ARG, TAG, "COM marker data underflow for header_size: %ld", header_info->header_size); + header_info->buffer_offset += bytes_to_skip; + header_info->header_size += bytes_to_skip; + header_info->buffer_left -= bytes_to_skip; return ESP_OK; } @@ -61,21 +72,25 @@ esp_err_t jpeg_parse_dqt_marker(jpeg_dec_header_info_t *header_info) uint32_t n = 0, i = 0, prec = 0; uint32_t temp = 0; - uint32_t length_num = jpeg_get_bytes(header_info, 2); + uint16_t length_num = jpeg_get_bytes(header_info, 2); + ESP_RETURN_ON_FALSE(length_num >= 2, ESP_ERR_INVALID_ARG, TAG, "Invalid DQT marker length: %d", length_num); length_num -= 2; while (length_num) { n = jpeg_get_bytes(header_info, 1); prec = n >> 4; n &= 0x0F; + ESP_RETURN_ON_FALSE(length_num >= 1, ESP_ERR_INVALID_ARG, TAG, "DQT marker length error: %d", length_num); length_num -= 1; // read quantization entries, in zig-zag order for (i = 0; i < 64; i++) { temp = jpeg_get_bytes(header_info, 1); + ESP_RETURN_ON_FALSE(length_num >= 1, ESP_ERR_INVALID_ARG, TAG, "DQT marker length error: %d", length_num); length_num -= 1; if (prec) { temp = (temp << 8) + jpeg_get_bytes(header_info, 1); + ESP_RETURN_ON_FALSE(length_num >= 1, ESP_ERR_INVALID_ARG, TAG, "DQT marker length error: %d", length_num); length_num -= 1; } header_info->qt_tbl[n][zigzag_arr[i]] = temp; @@ -142,7 +157,10 @@ esp_err_t jpeg_parse_sof_marker(jpeg_dec_header_info_t *header_info) esp_err_t jpeg_parse_dht_marker(jpeg_dec_header_info_t *header_info) { // Recording num_left in DHT sector, not including length bytes (2 bytes). - uint32_t num_left = jpeg_get_bytes(header_info, 2) - 2; + uint16_t raw_length = jpeg_get_bytes(header_info, 2); + // Check for integer underflow before subtraction + ESP_RETURN_ON_FALSE(raw_length >= 2, ESP_ERR_INVALID_ARG, TAG, "Invalid DHT marker length: %d", raw_length); + uint16_t num_left = raw_length - 2; while (num_left) { uint32_t np = 0; @@ -159,6 +177,8 @@ esp_err_t jpeg_parse_dht_marker(jpeg_dec_header_info_t *header_info) header_info->huffcode[header_info->huffinfo.type][header_info->huffinfo.id][i] = jpeg_get_bytes(header_info, 1); } + // Check for integer underflow before subtraction + ESP_RETURN_ON_FALSE(num_left >= (JPEG_HUFFMAN_BITS_LEN_TABLE_LEN + np + 1), ESP_ERR_INVALID_ARG, TAG, "DHT marker data underflow after parsing huffcode: %d", num_left); num_left -= (1 + JPEG_HUFFMAN_BITS_LEN_TABLE_LEN + np); } @@ -171,7 +191,7 @@ esp_err_t jpeg_parse_dri_marker(jpeg_dec_header_info_t *header_info) { uint16_t lr = jpeg_get_bytes(header_info, 2); if (lr != 4) { - ESP_LOGE(TAG, "DRI marker got but stream length is insufficient, the length you got is %" PRIu16, lr); + ESP_LOGE(TAG, "DRI marker got but stream length is insufficient, the length you got is %d", lr); return ESP_ERR_INVALID_SIZE; } header_info->ri = jpeg_get_bytes(header_info, 2); @@ -181,6 +201,7 @@ esp_err_t jpeg_parse_dri_marker(jpeg_dec_header_info_t *header_info) esp_err_t jpeg_parse_sos_marker(jpeg_dec_header_info_t *header_info) { // Got the SOS marker, but need to recover this and feed to 2DDMA. + ESP_RETURN_ON_FALSE(header_info->header_size >= 2, ESP_ERR_INVALID_ARG, TAG, "SOS marker header_size underflow: %ld", header_info->header_size); header_info->buffer_offset -= 2; header_info->header_size -= 2; header_info->buffer_left += 2; @@ -191,6 +212,7 @@ esp_err_t jpeg_parse_inv_marker(jpeg_dec_header_info_t *header_info) { // Got invalid 0xFFFF, (followed by a valid marker type) // Go one byte back, to skip the first 0xFF + ESP_RETURN_ON_FALSE(header_info->header_size >= 1, ESP_ERR_INVALID_ARG, TAG, "INV marker header_size underflow: %ld", header_info->header_size); header_info->buffer_offset--; header_info->header_size--; header_info->buffer_left++; diff --git a/components/esp_driver_touch_sens/hw_ver3/touch_version_specific.c b/components/esp_driver_touch_sens/hw_ver3/touch_version_specific.c index e2da59d5ccc7..c2aa224f98e8 100644 --- a/components/esp_driver_touch_sens/hw_ver3/touch_version_specific.c +++ b/components/esp_driver_touch_sens/hw_ver3/touch_version_specific.c @@ -64,6 +64,10 @@ void IRAM_ATTR touch_priv_default_intr_handler(void *arg) touch_base_event_data_t data; touch_ll_get_active_channel_mask(&data.status_mask); int ch_offset = touch_ll_get_current_meas_channel() - TOUCH_MIN_CHAN_ID; + if (ch_offset < 0 || ch_offset >= (int)SOC_TOUCH_SENSOR_NUM) { + /* Not a valid channel */ + return; + } data.chan = g_touch->ch[ch_offset]; /* If the channel is not registered, return directly */ if (!data.chan) { diff --git a/components/esp_driver_touch_sens/test_apps/touch_sens/main/test_touch_sens_common.c b/components/esp_driver_touch_sens/test_apps/touch_sens/main/test_touch_sens_common.c index c5c0adc3ceee..255f979133ef 100644 --- a/components/esp_driver_touch_sens/test_apps/touch_sens/main/test_touch_sens_common.c +++ b/components/esp_driver_touch_sens/test_apps/touch_sens/main/test_touch_sens_common.c @@ -196,3 +196,53 @@ TEST_CASE("touch_sens_active_inactive_test", "[touch]") TEST_ASSERT_EQUAL_INT32(touch_cnt, cb_data.active_count); TEST_ASSERT_EQUAL_INT32(touch_cnt, cb_data.inactive_count); } + +#if SOC_TOUCH_SENSOR_VERSION > 1 +TEST_CASE("touch_sens_current_meas_channel_test", "[touch]") +{ + touch_sensor_handle_t touch = NULL; + touch_channel_handle_t touch_chan = NULL; + + touch_sensor_config_t sens_cfg = TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(TOUCH_SAMPLE_CFG_NUM, s_sample_cfg); + TEST_ESP_OK(touch_sensor_new_controller(&sens_cfg, &touch)); + + /* Configuring the filter */ + touch_sensor_filter_config_t filter_cfg = TOUCH_SENSOR_DEFAULT_FILTER_CONFIG(); + TEST_ESP_OK(touch_sensor_config_filter(touch, &filter_cfg)); + + int err_chan[TOUCH_MAX_CHAN_ID - TOUCH_MIN_CHAN_ID + 1] = {[0 ...(TOUCH_MAX_CHAN_ID - TOUCH_MIN_CHAN_ID)] = -1}; + int scan_times = 100; + uint32_t curr_chan[scan_times]; + /* Loop all channels */ + for (int ch_id = TOUCH_MIN_CHAN_ID; ch_id <= TOUCH_MAX_CHAN_ID; ch_id++) { + /* New a channel */ + TEST_ESP_OK(touch_sensor_new_channel(touch, ch_id, &s_chan_cfg, &touch_chan)); + TEST_ESP_OK(touch_sensor_enable(touch)); + /* Trigger one-shot scanning to update the current measuring channel */ + touch_sensor_trigger_oneshot_scanning(touch, 2000); + + /* Read the current measuring channel for several times */ + for (int i = 0; i < scan_times; i++) { + curr_chan[i] = touch_ll_get_current_meas_channel(); + /* Check if the current measuring channel is the same as the channel id */ + if (curr_chan[i] != ch_id) { + err_chan[ch_id - TOUCH_MIN_CHAN_ID] = curr_chan[i]; + } + } + /* Check if there is any error */ + TEST_ESP_OK(touch_sensor_disable(touch)); + TEST_ESP_OK(touch_sensor_del_channel(touch_chan)); + } + TEST_ESP_OK(touch_sensor_del_controller(touch)); + + /* Check if there is any error in the current measuring channel from any channel */ + bool has_error = false; + for (int i = 0; i < TOUCH_MAX_CHAN_ID - TOUCH_MIN_CHAN_ID + 1; i++) { + if (err_chan[i] >= 0) { + ESP_LOGE("TOUCH_TEST", "actual channel is %d, but current measuring channel reads %d", i + TOUCH_MIN_CHAN_ID, err_chan[i]); + has_error = true; + } + } + TEST_ASSERT_FALSE(has_error); +} +#endif // SOC_TOUCH_SENSOR_VERSION > 1 diff --git a/components/esp_driver_uart/include/driver/uhci.h b/components/esp_driver_uart/include/driver/uhci.h index c01af0d9b5aa..fd466ab9249f 100644 --- a/components/esp_driver_uart/include/driver/uhci.h +++ b/components/esp_driver_uart/include/driver/uhci.h @@ -22,8 +22,8 @@ typedef struct { uart_port_t uart_port; /*!< UART port that connect to UHCI controller */ size_t tx_trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background */ size_t max_transmit_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */ - size_t max_receive_internal_mem; /*!< Maximum transfer size in one transaction, in bytes. Each DMA node can point to a maximum of 4096 bytes. This value determines the number of DMA nodes used for each transaction. When your transfer size is large enough, it is recommended to set this value greater than 4096 to facilitate efficient ping-pong operations, such as 10 * 1024. */ - size_t dma_burst_size; /*!< DMA burst size, in bytes */ + size_t max_receive_internal_mem; /*!< Internal DMA usage memory. Each DMA node can point to a maximum of x bytes (depends on chip). This value determines the number of DMA nodes used for each transaction. When your transfer size is large enough, it is recommended to set this value greater than x to facilitate efficient ping-pong operations, such as 2 * x. */ + size_t dma_burst_size; /*!< DMA burst size, in bytes. Set to 0 to disable data burst. Otherwise, use a power of 2. */ size_t max_packet_receive; /*!< Max receive size, auto stop receiving after reach this value, only valid when `length_eof` set true */ struct { diff --git a/components/esp_driver_uart/src/uhci.c b/components/esp_driver_uart/src/uhci.c index 30dca6a3cfd1..c5538acae33c 100644 --- a/components/esp_driver_uart/src/uhci.c +++ b/components/esp_driver_uart/src/uhci.c @@ -199,7 +199,6 @@ static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const // Initialize DMA RX channel gdma_channel_alloc_config_t rx_alloc_config = { .direction = GDMA_CHANNEL_DIRECTION_RX, - .sibling_chan = uhci_ctrl->tx_dir.dma_chan, }; ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&rx_alloc_config, &uhci_ctrl->rx_dir.dma_chan), TAG, "DMA rx channel alloc failed"); gdma_connect(uhci_ctrl->rx_dir.dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)); diff --git a/components/esp_eth/src/spi/w5500/esp_eth_mac_w5500.c b/components/esp_eth/src/spi/w5500/esp_eth_mac_w5500.c index 2f0ea028fe7e..c76ddf7916c4 100644 --- a/components/esp_eth/src/spi/w5500/esp_eth_mac_w5500.c +++ b/components/esp_eth/src/spi/w5500/esp_eth_mac_w5500.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,6 +13,7 @@ #include "esp_attr.h" #include "esp_log.h" #include "esp_check.h" +#include "esp_timer.h" #include "esp_system.h" #include "esp_intr_alloc.h" #include "esp_heap_caps.h" @@ -30,6 +31,8 @@ static const char *TAG = "w5500.mac"; #define W5500_SPI_LOCK_TIMEOUT_MS (50) #define W5500_TX_MEM_SIZE (0x4000) #define W5500_RX_MEM_SIZE (0x4000) +#define W5500_100M_TX_TMO_US (200) +#define W5500_10M_TX_TMO_US (1500) #define W5500_ETH_MAC_RX_BUF_SIZE_AUTO (0) typedef struct { @@ -64,6 +67,7 @@ typedef struct { uint8_t addr[6]; bool packets_remain; uint8_t *rx_buffer; + uint32_t tx_tmo; } emac_w5500_t; static void *w5500_spi_init(const void *spi_config) @@ -245,18 +249,8 @@ static esp_err_t w5500_get_rx_received_size(emac_w5500_t *emac, uint16_t *size) static esp_err_t w5500_write_buffer(emac_w5500_t *emac, const void *buffer, uint32_t len, uint16_t offset) { esp_err_t ret = ESP_OK; - uint32_t remain = len; - const uint8_t *buf = buffer; - offset %= W5500_TX_MEM_SIZE; - if (offset + len > W5500_TX_MEM_SIZE) { - remain = (offset + len) % W5500_TX_MEM_SIZE; - len = W5500_TX_MEM_SIZE - offset; - ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_MEM_SOCK_TX(0, offset), buf, len), err, TAG, "write TX buffer failed"); - offset += len; - buf += len; - } - ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_MEM_SOCK_TX(0, offset), buf, remain), err, TAG, "write TX buffer failed"); + ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_MEM_SOCK_TX(0, offset), buffer, len), err, TAG, "write TX buffer failed"); err: return ret; } @@ -264,18 +258,7 @@ static esp_err_t w5500_write_buffer(emac_w5500_t *emac, const void *buffer, uint static esp_err_t w5500_read_buffer(emac_w5500_t *emac, void *buffer, uint32_t len, uint16_t offset) { esp_err_t ret = ESP_OK; - uint32_t remain = len; - uint8_t *buf = buffer; - offset %= W5500_RX_MEM_SIZE; - if (offset + len > W5500_RX_MEM_SIZE) { - remain = (offset + len) % W5500_RX_MEM_SIZE; - len = W5500_RX_MEM_SIZE - offset; - ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_MEM_SOCK_RX(0, offset), buf, len), err, TAG, "read RX buffer failed"); - offset += len; - buf += len; - } - ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_MEM_SOCK_RX(0, offset), buf, remain), err, TAG, "read RX buffer failed"); - + ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_MEM_SOCK_RX(0, offset), buffer, len), err, TAG, "read RX buffer failed"); err: return ret; } @@ -284,7 +267,6 @@ static esp_err_t w5500_set_mac_addr(emac_w5500_t *emac) { esp_err_t ret = ESP_OK; ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_MAC, emac->addr, 6), err, TAG, "write MAC address register failed"); - err: return ret; } @@ -338,7 +320,8 @@ static esp_err_t w5500_setup_default(emac_w5500_t *emac) esp_err_t ret = ESP_OK; uint8_t reg_value = 16; - // Only SOCK0 can be used as MAC RAW mode, so we give the whole buffer (16KB TX and 16KB RX) to SOCK0 + // Only SOCK0 can be used as MAC RAW mode, so we give the whole buffer (16KB TX and 16KB RX) to SOCK0, which doesn't have any effect for TX though. + // A larger TX buffer doesn't buy us pipelining - each SEND is one frame and must complete before the next. ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_RXBUF_SIZE(0), ®_value, sizeof(reg_value)), err, TAG, "set rx buffer size failed"); ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_TXBUF_SIZE(0), ®_value, sizeof(reg_value)), err, TAG, "set tx buffer size failed"); reg_value = 0; @@ -490,11 +473,14 @@ static esp_err_t emac_w5500_set_link(esp_eth_mac_t *mac, eth_link_t link) static esp_err_t emac_w5500_set_speed(esp_eth_mac_t *mac, eth_speed_t speed) { esp_err_t ret = ESP_OK; + emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent); switch (speed) { case ETH_SPEED_10M: + emac->tx_tmo = W5500_10M_TX_TMO_US; ESP_LOGD(TAG, "working in 10Mbps"); break; case ETH_SPEED_100M: + emac->tx_tmo = W5500_100M_TX_TMO_US; ESP_LOGD(TAG, "working in 100Mbps"); break; default: @@ -589,14 +575,16 @@ static esp_err_t emac_w5500_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t ESP_GOTO_ON_ERROR(w5500_send_command(emac, W5500_SCR_SEND, 100), err, TAG, "issue SEND command failed"); // pooling the TX done event - int retry = 0; uint8_t status = 0; - while (!(status & W5500_SIR_SEND)) { - ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)), err, TAG, "read SOCK0 IR failed"); - if ((retry++ > 3 && !is_w5500_sane_for_rxtx(emac)) || retry > 10) { + uint64_t start = esp_timer_get_time(); + uint64_t now = 0; + do { + now = esp_timer_get_time(); + if (!is_w5500_sane_for_rxtx(emac) || (now - start) > emac->tx_tmo) { return ESP_FAIL; } - } + ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)), err, TAG, "read SOCK0 IR failed"); + } while (!(status & W5500_SIR_SEND)); // clear the event bit status = W5500_SIR_SEND; ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)), err, TAG, "write SOCK0 IR failed"); diff --git a/components/esp_hw_support/test_apps/etm/main/test_ana_cmpr_etm.c b/components/esp_hw_support/test_apps/etm/main/test_ana_cmpr_etm.c index 53d1fbde880e..cc9218905b23 100644 --- a/components/esp_hw_support/test_apps/etm/main/test_ana_cmpr_etm.c +++ b/components/esp_hw_support/test_apps/etm/main/test_ana_cmpr_etm.c @@ -157,13 +157,16 @@ TEST_CASE("analog_comparator_etm_event", "[etm]") esp_rom_delay_us(TEST_TIME_US); gpio_set_level(src_gpio, 1); + // the gptimer should already stopped, so delay any time here is ok + vTaskDelay(10); + uint64_t cnt_us = 0; TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cnt_us)); printf("Count: %" PRIu64 "\n", cnt_us); // gptimer timer should stopped uint64_t cnt_us_again = 0; TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cnt_us_again)); - TEST_ASSERT(cnt_us_again == cnt_us); + TEST_ASSERT_EQUAL(cnt_us, cnt_us_again); test_ana_cmpr_deinit_etm(handles); test_ana_cmpr_deinit(cmpr); diff --git a/components/esp_rom/esp32c2/ld/esp32c2.rom.ble-eco4.ld b/components/esp_rom/esp32c2/ld/esp32c2.rom.ble-eco4.ld index 0eadb3bc6739..02c7bb9e4873 100644 --- a/components/esp_rom/esp32c2/ld/esp32c2.rom.ble-eco4.ld +++ b/components/esp_rom/esp32c2/ld/esp32c2.rom.ble-eco4.ld @@ -661,7 +661,7 @@ r_ble_lll_adv_sync_tx_start_cb = 0x400014a8; r_ble_lll_adv_tx_done = 0x400014ac; r_ble_lll_adv_tx_start_cb = 0x400014b0; r_ble_lll_adv_update_rsp_offset = 0x400014b4; -r_ble_lll_aux_scan_cb = 0x400014b8; +//r_ble_lll_aux_scan_cb = 0x400014b8; r_ble_lll_aux_scan_drop = 0x400014bc; r_ble_lll_aux_scan_drop_event_cb = 0x400014c0; r_ble_lll_calc_us_convert_tick_unit = 0x400014c4; @@ -773,7 +773,7 @@ r_ble_lll_rfmgmt_release_ev = 0x40001668; //r_ble_lll_rfmgmt_reset = 0x4000166c; r_ble_lll_rfmgmt_scan_changed = 0x40001670; r_ble_lll_rfmgmt_sched_changed = 0x40001674; -r_ble_lll_rfmgmt_set_sleep_cb = 0x40001678; +//r_ble_lll_rfmgmt_set_sleep_cb = 0x40001678; r_ble_lll_rfmgmt_ticks_to_enabled = 0x4000167c; //r_ble_lll_rfmgmt_timer_exp = 0x40001680; //r_ble_lll_rfmgmt_timer_reschedule = 0x40001684; @@ -825,7 +825,7 @@ r_ble_lll_sched_dtm = 0x40001738; r_ble_lll_sched_env_init = 0x4000173c; r_ble_lll_sched_execute_check = 0x40001740; r_ble_lll_sched_execute_item = 0x40001744; -r_ble_lll_sched_init = 0x40001748; +//r_ble_lll_sched_init = 0x40001748; r_ble_lll_sched_insert_if_empty = 0x4000174c; r_ble_lll_sched_is_overlap = 0x40001750; r_ble_lll_sched_master_new = 0x40001754; @@ -904,7 +904,7 @@ r_ble_phy_sequence_is_running = 0x40001874; r_ble_phy_sequence_is_waiting_rsp = 0x40001878; r_ble_phy_sequence_single_end = 0x4000187c; r_ble_phy_sequence_tx_end_invoke = 0x40001880; -r_ble_phy_sequence_update_conn_ind_params = 0x40001884; +//r_ble_phy_sequence_update_conn_ind_params = 0x40001884; r_ble_phy_set_adv_mode = 0x40001888; r_ble_phy_set_coex_pti = 0x4000188c; r_ble_phy_set_conn_ind_pdu = 0x40001890; diff --git a/components/esp_rom/esp32c2/ld/esp32c2.rom.ble.ld b/components/esp_rom/esp32c2/ld/esp32c2.rom.ble.ld index a0d2795b4d67..90154f4b0ebd 100644 --- a/components/esp_rom/esp32c2/ld/esp32c2.rom.ble.ld +++ b/components/esp_rom/esp32c2/ld/esp32c2.rom.ble.ld @@ -533,7 +533,7 @@ r_ble_lll_adv_sync_tx_start_cb = 0x400014a8; r_ble_lll_adv_tx_done = 0x400014ac; r_ble_lll_adv_tx_start_cb = 0x400014b0; r_ble_lll_adv_update_rsp_offset = 0x400014b4; -r_ble_lll_aux_scan_cb = 0x400014b8; +//r_ble_lll_aux_scan_cb = 0x400014b8; r_ble_lll_aux_scan_drop = 0x400014bc; r_ble_lll_aux_scan_drop_event_cb = 0x400014c0; r_ble_lll_calc_us_convert_tick_unit = 0x400014c4; @@ -613,7 +613,7 @@ r_ble_lll_per_adv_coex_dpc_update_on_start = 0x40001640; //r_ble_lll_rfmgmt_release = 0x40001664; r_ble_lll_rfmgmt_scan_changed = 0x40001670; r_ble_lll_rfmgmt_sched_changed = 0x40001674; -r_ble_lll_rfmgmt_set_sleep_cb = 0x40001678; +//r_ble_lll_rfmgmt_set_sleep_cb = 0x40001678; r_ble_lll_rfmgmt_ticks_to_enabled = 0x4000167c; r_ble_lll_rx_pdu_in = 0x40001688; r_ble_lll_rx_pkt_in = 0x4000168c; @@ -715,7 +715,7 @@ r_ble_phy_sequence_is_running = 0x40001874; r_ble_phy_sequence_is_waiting_rsp = 0x40001878; r_ble_phy_sequence_single_end = 0x4000187c; r_ble_phy_sequence_tx_end_invoke = 0x40001880; -r_ble_phy_sequence_update_conn_ind_params = 0x40001884; +//r_ble_phy_sequence_update_conn_ind_params = 0x40001884; r_ble_phy_set_coex_pti = 0x4000188c; r_ble_phy_set_conn_ind_pdu = 0x40001890; r_ble_phy_set_dev_address = 0x40001898; diff --git a/components/esp_system/port/soc/esp32/system_internal.c b/components/esp_system/port/soc/esp32/system_internal.c index 6089ea696817..56e1a4479d0e 100644 --- a/components/esp_system/port/soc/esp32/system_internal.c +++ b/components/esp_system/port/soc/esp32/system_internal.c @@ -47,8 +47,13 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void) DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, //UART TX FIFO cannot be reset correctly on ESP32, so reset the UART memory by DPORT here. DPORT_TIMERS_RST | DPORT_SPI01_RST | DPORT_SPI2_RST | DPORT_SPI3_RST | - DPORT_SPI_DMA_RST | DPORT_UART_RST | DPORT_UART1_RST | DPORT_UART2_RST | - DPORT_UART_MEM_RST | DPORT_PWM0_RST | DPORT_PWM1_RST); + // The DMA inside SPI needs to be reset to avoid memory corruption after restart. + DPORT_SPI_DMA_RST | + DPORT_UART_RST | DPORT_UART1_RST | DPORT_UART2_RST | DPORT_UART_MEM_RST | + DPORT_PWM0_RST | DPORT_PWM1_RST | + // The DMA inside I2S needs to be reset to avoid memory corruption after restart. + DPORT_I2S0_RST | DPORT_I2S1_RST | + DPORT_UHCI0_RST | DPORT_UHCI1_RST); DPORT_REG_WRITE(DPORT_PERIP_RST_EN_REG, 0); // Reset crypto peripherals. This ensures a clean state for the crypto peripherals after a CPU restart and hence diff --git a/components/esp_system/port/soc/esp32c6/system_internal.c b/components/esp_system/port/soc/esp32c6/system_internal.c index 94dac0aa2505..cdb289aef413 100644 --- a/components/esp_system/port/soc/esp32c6/system_internal.c +++ b/components/esp_system/port/soc/esp32c6/system_internal.c @@ -49,6 +49,8 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void) SET_PERI_REG_MASK(PCR_SDIO_SLAVE_CONF_REG, PCR_SDIO_SLAVE_RST_EN); SET_PERI_REG_MASK(PCR_MODEM_APB_CONF_REG, PCR_MODEM_RST_EN); SET_PERI_REG_MASK(PCR_PWM_CONF_REG, PCR_PWM_RST_EN); + //ETM may directly control the GPIO or other peripherals even after CPU reset. Reset to stop these control. + SET_PERI_REG_MASK(PCR_ETM_CONF_REG, PCR_ETM_RST_EN); // Clear Peripheral clk rst CLEAR_PERI_REG_MASK(PCR_MSPI_CONF_REG, PCR_MSPI_RST_EN); @@ -59,6 +61,7 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void) CLEAR_PERI_REG_MASK(PCR_SDIO_SLAVE_CONF_REG, PCR_SDIO_SLAVE_RST_EN); CLEAR_PERI_REG_MASK(PCR_MODEM_APB_CONF_REG, PCR_MODEM_RST_EN); CLEAR_PERI_REG_MASK(PCR_PWM_CONF_REG, PCR_PWM_RST_EN); + CLEAR_PERI_REG_MASK(PCR_ETM_CONF_REG, PCR_ETM_RST_EN); // Reset crypto peripherals. This ensures a clean state for the crypto peripherals after a CPU restart // and hence avoiding any possibility with crypto failure in ROM security workflows. diff --git a/components/esp_system/port/soc/esp32h2/system_internal.c b/components/esp_system/port/soc/esp32h2/system_internal.c index 4202d2afc13d..7857f9974752 100644 --- a/components/esp_system/port/soc/esp32h2/system_internal.c +++ b/components/esp_system/port/soc/esp32h2/system_internal.c @@ -46,6 +46,8 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void) SET_PERI_REG_MASK(PCR_GDMA_CONF_REG, PCR_GDMA_RST_EN); SET_PERI_REG_MASK(PCR_MODEM_CONF_REG, PCR_MODEM_RST_EN); SET_PERI_REG_MASK(PCR_PWM_CONF_REG, PCR_PWM_RST_EN); + //ETM may directly control the GPIO or other peripherals even after CPU reset. Reset to stop these control. + SET_PERI_REG_MASK(PCR_ETM_CONF_REG, PCR_ETM_RST_EN); // Clear Peripheral clk rst CLEAR_PERI_REG_MASK(PCR_MSPI_CONF_REG, PCR_MSPI_RST_EN); @@ -55,6 +57,7 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void) CLEAR_PERI_REG_MASK(PCR_GDMA_CONF_REG, PCR_GDMA_RST_EN); CLEAR_PERI_REG_MASK(PCR_MODEM_CONF_REG, PCR_MODEM_RST_EN); CLEAR_PERI_REG_MASK(PCR_PWM_CONF_REG, PCR_PWM_RST_EN); + CLEAR_PERI_REG_MASK(PCR_ETM_CONF_REG, PCR_ETM_RST_EN); // Reset crypto peripherals. This ensures a clean state for the crypto peripherals after a CPU restart // and hence avoiding any possibility with crypto failure in ROM security workflows. diff --git a/components/esp_system/port/soc/esp32p4/system_internal.c b/components/esp_system/port/soc/esp32p4/system_internal.c index dfe1be33cbf5..98e669d0360b 100644 --- a/components/esp_system/port/soc/esp32p4/system_internal.c +++ b/components/esp_system/port/soc/esp32p4/system_internal.c @@ -68,28 +68,38 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void) } // Set Peripheral clk rst + SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_MSPI_AXI); + SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_DUAL_MSPI_AXI); + // DMA needs to be reset to avoid memory corruption after restart. Now only AHB supports this. + // For other AXI DMAs, we have already stop them above. + SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_AHB_PDMA); SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_TIMERGRP1); SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_STIMER); - SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_DUAL_MSPI_AXI); - SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_MSPI_AXI); SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART0_CORE); SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART1_CORE); SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART2_CORE); SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART3_CORE); SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART4_CORE); SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN2_REG, HP_SYS_CLKRST_REG_RST_EN_ADC); + SET_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN2_REG, HP_SYS_CLKRST_REG_RST_EN_H264); // Clear Peripheral clk rst + CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_MSPI_AXI); + CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_DUAL_MSPI_AXI); + CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_AHB_PDMA); CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_TIMERGRP1); CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_STIMER); - CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_DUAL_MSPI_AXI); - CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_MSPI_AXI); CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART0_CORE); CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART1_CORE); CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART2_CORE); CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART3_CORE); CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN1_REG, HP_SYS_CLKRST_REG_RST_EN_UART4_CORE); CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN2_REG, HP_SYS_CLKRST_REG_RST_EN_ADC); + CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN2_REG, HP_SYS_CLKRST_REG_RST_EN_H264); + + // The DMA inside SDMMC Host needs to be reset to avoid memory corruption after restart. + SET_PERI_REG_MASK(LP_CLKRST_HP_SDMMC_EMAC_RST_CTRL_REG, LP_CLKRST_RST_EN_SDMMC); + CLEAR_PERI_REG_MASK(LP_CLKRST_HP_SDMMC_EMAC_RST_CTRL_REG, LP_CLKRST_RST_EN_SDMMC); // Reset crypto peripherals. This ensures a clean state for the crypto peripherals after a CPU restart // and hence avoiding any possibility with crypto failure in ROM security workflows. diff --git a/components/esp_system/port/soc/esp32s3/system_internal.c b/components/esp_system/port/soc/esp32s3/system_internal.c index 663fedc84758..23b4f56a9867 100644 --- a/components/esp_system/port/soc/esp32s3/system_internal.c +++ b/components/esp_system/port/soc/esp32s3/system_internal.c @@ -55,7 +55,9 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void) // Reset dma and crypto peripherals. This ensures a clean state for the crypto peripherals after a CPU restart // and hence avoiding any possibility with crypto failure in ROM security workflows. SET_PERI_REG_MASK(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST | SYSTEM_CRYPTO_AES_RST | SYSTEM_CRYPTO_DS_RST | - SYSTEM_CRYPTO_HMAC_RST | SYSTEM_CRYPTO_RSA_RST | SYSTEM_CRYPTO_SHA_RST); + SYSTEM_CRYPTO_HMAC_RST | SYSTEM_CRYPTO_RSA_RST | SYSTEM_CRYPTO_SHA_RST | + // The DMA inside SDMMC Host needs to be reset to avoid memory corruption after restart. + SYSTEM_SDIO_HOST_RST); REG_WRITE(SYSTEM_PERIP_RST_EN1_REG, 0); SET_PERI_REG_MASK(SYSTEM_EDMA_CTRL_REG, SYSTEM_EDMA_RESET); diff --git a/components/hal/esp32p4/include/hal/touch_sensor_ll.h b/components/hal/esp32p4/include/hal/touch_sensor_ll.h index 5d47aa137ec3..cc0339d5c8d2 100644 --- a/components/hal/esp32p4/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32p4/include/hal/touch_sensor_ll.h @@ -482,11 +482,7 @@ static inline void touch_ll_set_idle_channel_connect(touch_pad_conn_type_t type) __attribute__((always_inline)) static inline uint32_t touch_ll_get_current_meas_channel(void) { - uint32_t curr_chan = LP_TOUCH.chn_status.scan_curr; - HAL_ASSERT(curr_chan < 14); - // Workaround: the curr channel read 0 when the actual channel is 14 - curr_chan = curr_chan == 0 ? 14 : curr_chan; - return curr_chan; + return LP_TOUCH.chn_status.scan_curr; } /** diff --git a/components/ieee802154/Kconfig b/components/ieee802154/Kconfig index 5d0b46666d2a..54e7229affe9 100644 --- a/components/ieee802154/Kconfig +++ b/components/ieee802154/Kconfig @@ -53,9 +53,9 @@ menu "IEEE 802.15.4" int "CCA detection threshold" depends on IEEE802154_ENABLED range -120 0 - default -60 + default -75 help - set the CCA threshold, in dB + set the CCA threshold, in dBm config IEEE802154_PENDING_TABLE_SIZE int "Pending table size" diff --git a/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp index dbca6e084349..27222bb7b9a3 100644 --- a/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -3805,6 +3805,82 @@ TEST_CASE("nvs multiple write with same key but different types", "[nvs][xxx]") TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } +TEST_CASE("nvs multiple write with same key blob and string involved", "[nvs]") +{ + PartitionEmulationFixture f(0, 10); + + nvs_handle_t handle_1; + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);) + + for (uint16_t j = NVS_FLASH_SECTOR; j < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++j) { + f.erase(j); + } + TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1)); + + nvs_erase_all(handle_1); + + const char key_name[] = "foo"; + + // integer variables + int32_t v32; + int8_t v8; + + // string + #define str_data_len 64 + const char str_data[] = "string data"; + char str_buf[str_data_len] = {0}; + size_t str_len = str_data_len; + + // blob + #define blob_data_len 64 + uint8_t blob_data[blob_data_len] = {0}; + uint8_t blob_buf[blob_data_len] = {0}; + size_t blob_read_size; + + // first write is i32 + TEST_ESP_OK(nvs_set_i32(handle_1, key_name, (int32_t)12345678)); + + TEST_ESP_ERR(nvs_get_i8(handle_1, key_name, &v8), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_get_i32(handle_1, key_name, &v32)); + TEST_ESP_ERR(nvs_get_str(handle_1, key_name, str_buf, &str_len), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(nvs_get_blob(handle_1, key_name, blob_buf, &blob_read_size), ESP_ERR_NVS_NOT_FOUND); + + + // second write is string + TEST_ESP_OK(nvs_set_str(handle_1, key_name, str_data)); + + TEST_ESP_ERR(nvs_get_i8(handle_1, key_name, &v8), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(nvs_get_i32(handle_1, key_name, &v32), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_get_str(handle_1, key_name, str_buf, &str_len)); + TEST_ESP_ERR(nvs_get_blob(handle_1, key_name, blob_buf, &blob_read_size), ESP_ERR_NVS_NOT_FOUND); + + // third write is blob + TEST_ESP_OK(nvs_set_blob(handle_1, key_name, blob_data, blob_data_len)); + + TEST_ESP_ERR(nvs_get_i8(handle_1, key_name, &v8), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(nvs_get_i32(handle_1, key_name, &v32), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(nvs_get_str(handle_1, key_name, str_buf, &str_len), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(nvs_get_blob(handle_1, key_name, blob_buf, &blob_read_size)); + + // fourth write is i8 + TEST_ESP_OK(nvs_set_i8(handle_1, key_name, (int8_t)12)); + + TEST_ESP_OK(nvs_get_i8(handle_1, key_name, &v8)); + TEST_ESP_ERR(nvs_get_i32(handle_1, key_name, &v32), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(nvs_get_str(handle_1, key_name, str_buf, &str_len), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(nvs_get_blob(handle_1, key_name, blob_buf, &blob_read_size), ESP_ERR_NVS_NOT_FOUND); + + nvs_close(handle_1); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} + TEST_CASE("nvs find key tests", "[nvs]") { const size_t buff_len = 4096; diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index d9d666261630..354d0c0be4b4 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -247,33 +247,19 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c return ESP_OK; } -esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx, VerOffset chunkStart) +// Reads the data entries of the variable length item. +// The metadata entry is already read in the item object. +// index is the index of the metadata entry on the page. +// data is pointer to the buffer where the data will be copied to. It has to be at least +// item.varLength.dataSize bytes long. +// The function returns ESP_OK if the data was read successfully, or an error code if there was an error. +esp_err_t Page::readVariableLengthItemData(const Item& item, const size_t index, void* data) { - size_t index = 0; - Item item; - if (mState == PageState::INVALID) { return ESP_ERR_NVS_INVALID_STATE; } - esp_err_t rc = findItem(nsIndex, datatype, key, index, item, chunkIdx, chunkStart); - if (rc != ESP_OK) { - return rc; - } - - if (!isVariableLengthType(datatype)) { - if (dataSize != getAlignmentForType(datatype)) { - return ESP_ERR_NVS_TYPE_MISMATCH; - } - - memcpy(data, item.data, dataSize); - return ESP_OK; - } - - if (dataSize < static_cast(item.varLength.dataSize)) { - return ESP_ERR_NVS_INVALID_LENGTH; - } - + esp_err_t rc; uint8_t* dst = reinterpret_cast(data); size_t left = item.varLength.dataSize; for (size_t i = index + 1; i < index + item.span; ++i) { @@ -298,6 +284,36 @@ esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, vo return ESP_OK; } +esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx, VerOffset chunkStart) +{ + size_t index = 0; + Item item; + + if (mState == PageState::INVALID) { + return ESP_ERR_NVS_INVALID_STATE; + } + + esp_err_t rc = findItem(nsIndex, datatype, key, index, item, chunkIdx, chunkStart); + if (rc != ESP_OK) { + return rc; + } + + if (!isVariableLengthType(datatype)) { + if (dataSize != getAlignmentForType(datatype)) { + return ESP_ERR_NVS_TYPE_MISMATCH; + } + + memcpy(data, item.data, dataSize); + return ESP_OK; + } + + if (dataSize < static_cast(item.varLength.dataSize)) { + return ESP_ERR_NVS_INVALID_LENGTH; + } + + return readVariableLengthItemData(item, index, data); +} + esp_err_t Page::cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx, VerOffset chunkStart) { size_t index = 0; @@ -327,9 +343,23 @@ esp_err_t Page::cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, con return ESP_ERR_NVS_INVALID_LENGTH; } + // We have metadata of the variable length data chunk. It contains the length of the data and the crc32. + // As a first step we can calculate the crc32 of the data buffer to be compared with the crc32 of the item in the flash. + // If they are not equal, immediately return ESP_ERR_NVS_CONTENT_DIFFERS. + // If they are equal, to avoid crc32 collision false positive, we will read the data from the flash entry by entry and compare + // it with the respective chunk of input data buffer. The crc32 of the data read from the flash will be calculated on the fly. + // At the end, we will compare the crc32 of the data read from the flash with the crc32 of the metadata item in the flash to make sure + // that the data in the flash is not corrupted. + if (Item::calculateCrc32(reinterpret_cast(data), item.varLength.dataSize) != item.varLength.dataCrc32) { + return ESP_ERR_NVS_CONTENT_DIFFERS; + } + const uint8_t* dst = reinterpret_cast(data); size_t left = item.varLength.dataSize; - for (size_t i = index + 1; i < index + item.span; ++i) { + uint32_t accumulatedCRC32; + size_t initial_index = index + 1; + + for (size_t i = initial_index; i < index + item.span; ++i) { Item ditem; rc = readEntry(i, ditem); if (rc != ESP_OK) { @@ -340,11 +370,18 @@ esp_err_t Page::cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, con if (memcmp(dst, ditem.rawData, willCopy)) { return ESP_ERR_NVS_CONTENT_DIFFERS; } + + // Calculate the crc32 of the actual ditem.rawData buffer. Do not pass accumulatedCRC32 in the first call. + // In the first call, calculateCrc32 will use its default. In the subsequent calls, accumulatedCRC32 is the crc32 of the previous buffer. + accumulatedCRC32 = Item::calculateCrc32(ditem.rawData, willCopy, (i == initial_index) ? nullptr : &accumulatedCRC32); + left -= willCopy; dst += willCopy; } - if (Item::calculateCrc32(reinterpret_cast(data), item.varLength.dataSize) != item.varLength.dataCrc32) { - return ESP_ERR_NVS_NOT_FOUND; + // Check if the CRC32 calculated on the fly matches the variable length data CRC32 indicated in the metadata entry. + // If they are not equal, it means the data in the flash is corrupt, we will return ESP_ERR_NVS_CONTENT_DIFFERS. + if (accumulatedCRC32 != item.varLength.dataCrc32) { + return ESP_ERR_NVS_CONTENT_DIFFERS; } return ESP_OK; diff --git a/components/nvs_flash/src/nvs_page.hpp b/components/nvs_flash/src/nvs_page.hpp index 4aff32ad012e..2acaa64e832d 100644 --- a/components/nvs_flash/src/nvs_page.hpp +++ b/components/nvs_flash/src/nvs_page.hpp @@ -88,6 +88,8 @@ class Page : public intrusive_list_node, public ExceptionlessAllocatable esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY); + esp_err_t readVariableLengthItemData(const Item& item, const size_t index, void* data); + esp_err_t readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); esp_err_t cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index 0cf28149ab8b..17b42ac438e0 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -50,7 +50,7 @@ void Storage::clearNamespaces() esp_err_t Storage::populateBlobIndices(TBlobIndexList& blobIdxList) { - for (auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { + for(auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { Page& p = *it; size_t itemIndex = 0; Item item; @@ -59,10 +59,10 @@ esp_err_t Storage::populateBlobIndices(TBlobIndexList& blobIdxList) * logic in pagemanager will remove the earlier index. So we should never find a * duplicate index at this point */ - while (p.findItem(Page::NS_ANY, ItemType::BLOB_IDX, nullptr, itemIndex, item) == ESP_OK) { + while(p.findItem(Page::NS_ANY, ItemType::BLOB_IDX, nullptr, itemIndex, item) == ESP_OK) { BlobIndexNode* entry = new (std::nothrow) BlobIndexNode; - if (!entry) return ESP_ERR_NO_MEM; + if(!entry) return ESP_ERR_NO_MEM; item.getKey(entry->key, sizeof(entry->key)); entry->nsIndex = item.nsIndex; @@ -88,7 +88,7 @@ esp_err_t Storage::populateBlobIndices(TBlobIndexList& blobIdxList) // later by the call to eraseOrphanDataBlobs(). void Storage::eraseMismatchedBlobIndexes(TBlobIndexList& blobIdxList) { - for (auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { + for(auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { Page& p = *it; size_t itemIndex = 0; Item item; @@ -97,7 +97,7 @@ void Storage::eraseMismatchedBlobIndexes(TBlobIndexList& blobIdxList) * 1) VER_0_OFFSET <= chunkIndex < VER_1_OFFSET-1 => Version0 chunks * 2) VER_1_OFFSET <= chunkIndex < VER_ANY => Version1 chunks */ - while (p.findItem(Page::NS_ANY, ItemType::BLOB_DATA, nullptr, itemIndex, item) == ESP_OK) { + while(p.findItem(Page::NS_ANY, ItemType::BLOB_DATA, nullptr, itemIndex, item) == ESP_OK) { auto iter = std::find_if(blobIdxList.begin(), blobIdxList.end(), @@ -106,7 +106,7 @@ void Storage::eraseMismatchedBlobIndexes(TBlobIndexList& blobIdxList) && (item.nsIndex == e.nsIndex) && (item.chunkIndex >= static_cast (e.chunkStart)) && (item.chunkIndex < static_cast ((e.chunkStart == nvs::VerOffset::VER_0_OFFSET) ? nvs::VerOffset::VER_1_OFFSET : nvs::VerOffset::VER_ANY));}); - if (iter != std::end(blobIdxList)) { + if(iter != std::end(blobIdxList)) { // accumulate the size iter->observedDataSize += item.varLength.dataSize; iter->observedChunkCount++; @@ -116,15 +116,15 @@ void Storage::eraseMismatchedBlobIndexes(TBlobIndexList& blobIdxList) } auto iter = blobIdxList.begin(); - while (iter != blobIdxList.end()) + while(iter != blobIdxList.end()) { - if ( (iter->observedDataSize != iter->dataSize) || (iter->observedChunkCount != iter->chunkCount) ) + if( (iter->observedDataSize != iter->dataSize) || (iter->observedChunkCount != iter->chunkCount) ) { // Delete blob_index from flash // This is very rare case, so we can loop over all pages - for (auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { + for(auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { // skip pages in non eligible states - if (it->state() == nvs::Page::PageState::CORRUPT + if(it->state() == nvs::Page::PageState::CORRUPT || it->state() == nvs::Page::PageState::INVALID || it->state() == nvs::Page::PageState::UNINITIALIZED){ continue; @@ -152,7 +152,7 @@ void Storage::eraseMismatchedBlobIndexes(TBlobIndexList& blobIdxList) void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList) { - for (auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { + for(auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { Page& p = *it; size_t itemIndex = 0; Item item; @@ -161,7 +161,7 @@ void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList) * 1) VER_0_OFFSET <= chunkIndex < VER_1_OFFSET-1 => Version0 chunks * 2) VER_1_OFFSET <= chunkIndex < VER_ANY => Version1 chunks */ - while (p.findItem(Page::NS_ANY, ItemType::BLOB_DATA, nullptr, itemIndex, item) == ESP_OK) { + while(p.findItem(Page::NS_ANY, ItemType::BLOB_DATA, nullptr, itemIndex, item) == ESP_OK) { auto iter = std::find_if(blobIdxList.begin(), blobIdxList.end(), @@ -170,7 +170,7 @@ void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList) && (item.nsIndex == e.nsIndex) && (item.chunkIndex >= static_cast (e.chunkStart)) && (item.chunkIndex < static_cast (e.chunkStart) + e.chunkCount);}); - if (iter == std::end(blobIdxList)) { + if(iter == std::end(blobIdxList)) { p.eraseItem(item.nsIndex, item.datatype, item.key, item.chunkIndex); } @@ -182,7 +182,7 @@ void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList) esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) { auto err = mPageManager.load(mPartition, baseSector, sectorCount); - if (err != ESP_OK) { + if(err != ESP_OK) { mState = StorageState::INVALID; return err; } @@ -190,25 +190,25 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) // load namespaces list clearNamespaces(); std::fill_n(mNamespaceUsage.data(), mNamespaceUsage.byteSize() / 4, 0); - for (auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { + for(auto it = mPageManager.begin(); it != mPageManager.end(); ++it) { Page& p = *it; size_t itemIndex = 0; Item item; - while (p.findItem(Page::NS_INDEX, ItemType::U8, nullptr, itemIndex, item) == ESP_OK) { + while(p.findItem(Page::NS_INDEX, ItemType::U8, nullptr, itemIndex, item) == ESP_OK) { NamespaceEntry* entry = new (std::nothrow) NamespaceEntry; - if (!entry) { + if(!entry) { mState = StorageState::INVALID; return ESP_ERR_NO_MEM; } item.getKey(entry->mName, sizeof(entry->mName)); err = item.getValue(entry->mIndex); - if (err != ESP_OK) { + if(err != ESP_OK) { delete entry; return err; } - if (mNamespaceUsage.set(entry->mIndex, true) != ESP_OK) { + if(mNamespaceUsage.set(entry->mIndex, true) != ESP_OK) { delete entry; return ESP_FAIL; } @@ -216,17 +216,17 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) itemIndex += item.span; } } - if (mNamespaceUsage.set(0, true) != ESP_OK) { + if(mNamespaceUsage.set(0, true) != ESP_OK) { return ESP_FAIL; } - if (mNamespaceUsage.set(255, true) != ESP_OK) { + if(mNamespaceUsage.set(255, true) != ESP_OK) { return ESP_FAIL; } // Populate list of multi-page index entries. TBlobIndexList blobIdxList; err = populateBlobIndices(blobIdxList); - if (err != ESP_OK) { + if(err != ESP_OK) { mState = StorageState::INVALID; return ESP_ERR_NO_MEM; } @@ -253,13 +253,16 @@ bool Storage::isValid() const return mState == StorageState::ACTIVE; } -esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item, uint8_t chunkIdx, VerOffset chunkStart) +esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item, uint8_t chunkIdx, VerOffset chunkStart, size_t* itemIndex) { - for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { - size_t itemIndex = 0; - auto err = it->findItem(nsIndex, datatype, key, itemIndex, item, chunkIdx, chunkStart); - if (err == ESP_OK) { + for(auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { + size_t tmpItemIndex = 0; + auto err = it->findItem(nsIndex, datatype, key, tmpItemIndex, item, chunkIdx, chunkStart); + if(err == ESP_OK) { page = it; + if(itemIndex) { + *itemIndex = tmpItemIndex; + } return ESP_OK; } } @@ -281,7 +284,7 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo max_pages = (Page::CHUNK_ANY-1)/2; } - if (dataSize > max_pages * Page::CHUNK_MAX_SIZE) { + if(dataSize > max_pages * Page::CHUNK_MAX_SIZE) { return ESP_ERR_NVS_VALUE_TOO_LONG; } @@ -289,16 +292,16 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo Page& page = getCurrentPage(); size_t tailroom = page.getVarDataTailroom(); size_t chunkSize = 0; - if (chunkCount == 0U && ((tailroom < dataSize) || (tailroom == 0 && dataSize == 0)) && tailroom < Page::CHUNK_MAX_SIZE/10) { + if(chunkCount == 0U && ((tailroom < dataSize) || (tailroom == 0 && dataSize == 0)) && tailroom < Page::CHUNK_MAX_SIZE/10) { /** This is the first chunk and tailroom is too small ***/ - if (page.state() != Page::PageState::FULL) { + if(page.state() != Page::PageState::FULL) { err = page.markFull(); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } } err = mPageManager.requestNewPage(); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } else if(getCurrentPage().getVarDataTailroom() == tailroom) { /* We got the same page or we are not improving.*/ @@ -306,7 +309,7 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo } else { continue; } - } else if (!tailroom) { + } else if(!tailroom) { err = ESP_ERR_NVS_NOT_ENOUGH_SPACE; break; } @@ -321,32 +324,32 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo static_cast (data) + offset, chunkSize, static_cast (chunkStart) + chunkCount); chunkCount++; - if (err != ESP_OK) { + if(err != ESP_OK) { NVS_ASSERT_OR_RETURN(err != ESP_ERR_NVS_PAGE_FULL, err); break; } else { UsedPageNode* node = new (std::nothrow) UsedPageNode(); - if (!node) { + if(!node) { err = ESP_ERR_NO_MEM; break; } node->mPage = &page; usedPages.push_back(node); - if (remainingSize || (tailroom - chunkSize) < Page::ENTRY_SIZE) { - if (page.state() != Page::PageState::FULL) { + if(remainingSize || (tailroom - chunkSize) < Page::ENTRY_SIZE) { + if(page.state() != Page::PageState::FULL) { err = page.markFull(); - if (err != ESP_OK) { + if(err != ESP_OK) { break; } } err = mPageManager.requestNewPage(); - if (err != ESP_OK) { + if(err != ESP_OK) { break; } } } offset += chunkSize; - if (!remainingSize) { + if(!remainingSize) { /* All pages are stored. Now store the index.*/ Item item; std::fill_n(item.data, sizeof(item.data), 0xff); @@ -358,12 +361,12 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo NVS_ASSERT_OR_RETURN(err != ESP_ERR_NVS_PAGE_FULL, err); break; } - } while (1); + } while(1); - if (err != ESP_OK) { + if(err != ESP_OK) { /* Anything failed, then we should erase all the written chunks*/ int ii=0; - for (auto it = std::begin(usedPages); it != std::end(usedPages); it++) { + for(auto it = std::begin(usedPages); it != std::end(usedPages); it++) { it->mPage->eraseItem(nsIndex, ItemType::BLOB_DATA, key, ii++); } } @@ -371,200 +374,265 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo return err; } +// datatype BLOB is written as BLOB_INDEX and BLOB_DATA and is searched for previous value as BLOB_INDEX and/or BLOB +// datatype BLOB_INDEX and BLOB_DATA are not supported as input parameters, the layer above should always use BLOB esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize) { - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } + // pointer to the page where the existing item was found Page* findPage = nullptr; + // index of the item in the page where the existing item was found + size_t itemIndex = 0; + // page sequence number helping to detect whether the page with old value was relocated during the new write + uint32_t findPageSeqNumber = UINT32_MAX; + + // indicates the datatype representation match between the old value and the new one bool matchedTypePageFound = false; + + // contains the item with the old value, if found Item item; - esp_err_t err; - if (datatype == ItemType::BLOB) { - err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item); - if(err == ESP_OK) { + esp_err_t err = ESP_OK; + + // Try to find existing item with the same key and namespace index + // We are performing the findItem with datatype specified (it is not ANY) to ensure the hash list lookup is done. + if(datatype == ItemType::BLOB) { + // Specific lookup if performed for input datatype BLOB. The searched datatype for exact match is BLOB_INDEX. + // The BLOB_INDEX is used to store the metadata of the (originally typed) BLOB data in current V2 implementation. + err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item, Page::CHUNK_ANY, VerOffset::VER_ANY, &itemIndex); + if(err == ESP_OK && findPage != nullptr) { matchedTypePageFound = true; } - } else { #ifdef CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY - err = findItem(nsIndex, datatype, key, findPage, item); + // In legacy mode, we also try to find the item as BLOB if not found as BLOB_INDEX. + // In this mode, it is possible to have multiple active values under the same (logical) key. + // For BLOBs (which may have different physical representations in V1 it is BLOB, in V2 it is BLOB_INDEX) it in turn means + // that we have to check both datatypes to find the old value. + // The general case for compatibility flag disabled is below and handles all datatypes including BLOB. + // To save some cycles, we do not compile both findItem calls in this case. + if(err == ESP_ERR_NVS_NOT_FOUND) { + // If not found as BLOB_INDEX, try to find it as BLOB (legacy support). + err = findItem(nsIndex, ItemType::BLOB, key, findPage, item, Page::CHUNK_ANY, VerOffset::VER_ANY, &itemIndex); + if(err == ESP_OK && findPage != nullptr) { + matchedTypePageFound = false; // datatype does not match, we cannot extract chunkStart from the item + + // keep the sequence number of the page where the item was found for later check of relocation + err = findPage->getSeqNumber(findPageSeqNumber); + if(err != ESP_OK) { + return err; + } + } + } +#endif + } else { + // Handle all other data types than BLOB + err = findItem(nsIndex, datatype, key, findPage, item, Page::CHUNK_ANY, VerOffset::VER_ANY, &itemIndex); if(err == ESP_OK && findPage != nullptr) { matchedTypePageFound = true; + + // keep the sequence number of the page where the item was found for later check of relocation + err = findPage->getSeqNumber(findPageSeqNumber); + if(err != ESP_OK) { + return err; + } } -#else - err = findItem(nsIndex, ItemType::ANY, key, findPage, item); - if(err == ESP_OK && datatype == item.datatype) { - matchedTypePageFound = true; + } + +#ifndef CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY + // If the item was not found under assumed datatype, try to find it as ANY. + if(findPage == nullptr) { + + // We should not find BLOB_DATA chunks as CHUNK_ANY is never used by the BLOB_DATA. + err = findItem(nsIndex, nvs::ItemType::ANY, key, findPage, item, Page::CHUNK_ANY, VerOffset::VER_ANY, &itemIndex); + if(err == ESP_OK && findPage != nullptr) { + // keep the sequence number of the page where the item was found for later check of relocation + err = findPage->getSeqNumber(findPageSeqNumber); + if(err != ESP_OK) { + return err; + } + // item was found with the same key and namespace index but data type is different + matchedTypePageFound = false; } -#endif } +#endif + // Here the findPage is either nullptr or points to the page where the item was found. + // The matchedTypePageFound is true if the old value item was found and its datatype representation matches the new one. + // This flag is used to determine if the item should be checked for same value. if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) { return err; } + // Handle value update if (datatype == ItemType::BLOB) { VerOffset prevStart, nextStart; prevStart = nextStart = VerOffset::VER_0_OFFSET; if (matchedTypePageFound) { - // Do a sanity check that the item in question is actually being modified. + // Do a check that the item in question is actually being modified. // If it isn't, it is cheaper to purposefully not write out new data. // since it may invoke an erasure of flash. - if (cmpMultiPageBlob(nsIndex, key, data, dataSize) == ESP_OK) { + if(cmpMultiPageBlob(nsIndex, key, data, dataSize) == ESP_OK) { return ESP_OK; } - if (findPage->state() == Page::PageState::UNINITIALIZED || - findPage->state() == Page::PageState::INVALID) { - err = findItem(nsIndex, datatype, key, findPage, item); - if (err != ESP_OK) { - return err; - } - } - /* Get the version of the previous index with same */ + // Get the version of the previous index with same prevStart = item.blobIndex.chunkStart; NVS_ASSERT_OR_RETURN(prevStart == VerOffset::VER_0_OFFSET || prevStart == VerOffset::VER_1_OFFSET, ESP_FAIL); - - /* Toggle the version by changing the offset */ + // Toggle the version by changing the offset nextStart = (prevStart == VerOffset::VER_1_OFFSET) ? VerOffset::VER_0_OFFSET : VerOffset::VER_1_OFFSET; } - /* Write the blob with new version*/ + // Write the blob with new version err = writeMultiPageBlob(nsIndex, key, data, dataSize, nextStart); - if (err == ESP_ERR_NVS_PAGE_FULL) { + if(err == ESP_ERR_NVS_PAGE_FULL) { return ESP_ERR_NVS_NOT_ENOUGH_SPACE; } + if (err != ESP_OK) { return err; } - - if (matchedTypePageFound) { - /* Erase the blob with earlier version*/ - err = eraseMultiPageBlob(nsIndex, key, prevStart); - - if (err == ESP_ERR_FLASH_OP_FAIL) { - return ESP_ERR_NVS_REMOVE_FAILED; - } - if (err != ESP_OK) { - return err; - } - - findPage = nullptr; - } else { - /* Support for earlier versions where BLOBS were stored without index */ - err = findItem(nsIndex, datatype, key, findPage, item); - if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) { - return err; - } - } } else { - // Do a sanity check that the item in question is actually being modified. + // Do a check that the item in question is actually being modified. // If it isn't, it is cheaper to purposefully not write out new data. // since it may invoke an erasure of flash. - if (matchedTypePageFound && + if(matchedTypePageFound && findPage->cmpItem(nsIndex, datatype, key, data, dataSize) == ESP_OK) { return ESP_OK; } Page& page = getCurrentPage(); err = page.writeItem(nsIndex, datatype, key, data, dataSize); - if (err == ESP_ERR_NVS_PAGE_FULL) { - if (page.state() != Page::PageState::FULL) { + if(err == ESP_ERR_NVS_PAGE_FULL) { + if(page.state() != Page::PageState::FULL) { err = page.markFull(); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } } err = mPageManager.requestNewPage(); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } err = getCurrentPage().writeItem(nsIndex, datatype, key, data, dataSize); - if (err == ESP_ERR_NVS_PAGE_FULL) { + if(err == ESP_ERR_NVS_PAGE_FULL) { return ESP_ERR_NVS_NOT_ENOUGH_SPACE; } - if (err != ESP_OK) { - return err; - } - } else if (err != ESP_OK) { + } + + if (err != ESP_OK) { return err; } } - if (findPage) { - if (findPage->state() == Page::PageState::UNINITIALIZED || - findPage->state() == Page::PageState::INVALID) { -#ifdef CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY - err = findItem(nsIndex, datatype, key, findPage, item); -#else - err = findItem(nsIndex, ItemType::ANY, key, findPage, item); -#endif - if (err != ESP_OK) { + // Delete previous value + // Note: The old entry won't be deleted if the new value is the same as the old value - code won't reach here in that case. + + // If findPage is null then previous value was not present in NVS and nothig is to be deleted. + if(findPage == nullptr) { + return err; + } + + if(item.datatype == ItemType::BLOB_IDX) { + // If the item found was BLOB_INDEX, the eraseMultiPageBlob is used to erase the whole multi-page blob. + // It is not necessary to check the potential page relocation as the function will find the blob again anyway. + VerOffset prevStart = item.blobIndex.chunkStart; + err = eraseMultiPageBlob(nsIndex, key, prevStart); + + if(err == ESP_ERR_FLASH_OP_FAIL) { + return ESP_ERR_NVS_REMOVE_FAILED; + } + } else { + // For all other data types, we have to check the potential page relocation. + + // The findPage might have been relocated as a part of space reclaiming. + // First indication is the page state. It might become the "spare" page thus changing the state from FULL or ACTIVE. + bool wasRelocated = false; + + if( findPage->state() != Page::PageState::ACTIVE && + findPage->state() != Page::PageState::FULL) { + wasRelocated = true; + } + + // Other indication of the multi step relocation is the page sequence number. If the sequence number is different than page + // sequence number at the moment initial item was found, the page was relocated. + if(!wasRelocated) { + uint32_t newPageSeqNumber; + err = findPage->getSeqNumber(newPageSeqNumber); + if(err != ESP_OK) { return err; } + if(newPageSeqNumber != findPageSeqNumber) { + wasRelocated = true; + } } -#ifdef CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY - err = findPage->eraseItem(nsIndex, datatype, key); -#else - err = findPage->eraseItem(nsIndex, ItemType::ANY, key); -#endif - if (err == ESP_ERR_FLASH_OP_FAIL) { - return ESP_ERR_NVS_REMOVE_FAILED; + + if(wasRelocated) { + // The page was relocated. We have to find the old value again from the beginning. + // As the item was already found before relocation, we can use the exact datatype from item. + err = findItem(nsIndex, item.datatype, key, findPage, item, Page::CHUNK_ANY, VerOffset::VER_ANY, &itemIndex); + if(err != ESP_OK) { + return err; + } } - if (err != ESP_OK) { - return err; + + // Page containing the old value is now refreshed. We can erase the old value. + err = findPage->eraseEntryAndSpan(itemIndex); + if(err == ESP_ERR_FLASH_OP_FAIL) { + return ESP_ERR_NVS_REMOVE_FAILED; } } + #ifdef DEBUG_STORAGE debugCheck(); #endif - return ESP_OK; + return err; } esp_err_t Storage::createOrOpenNamespace(const char* nsName, bool canCreate, uint8_t& nsIndex) { - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } auto it = std::find_if(mNamespaces.begin(), mNamespaces.end(), [=] (const NamespaceEntry& e) -> bool { return strncmp(nsName, e.mName, sizeof(e.mName) - 1) == 0; }); - if (it == std::end(mNamespaces)) { - if (!canCreate) { + if(it == std::end(mNamespaces)) { + if(!canCreate) { return ESP_ERR_NVS_NOT_FOUND; } uint8_t ns; bool ns_state; - for (ns = 1; ns < 255; ++ns) { - if (mNamespaceUsage.get(ns, &ns_state) != ESP_OK) { + for(ns = 1; ns < 255; ++ns) { + if(mNamespaceUsage.get(ns, &ns_state) != ESP_OK) { return ESP_FAIL; } - if (!ns_state) { + if(!ns_state) { break; } } - if (ns == 255) { + if(ns == 255) { return ESP_ERR_NVS_NOT_ENOUGH_SPACE; } auto err = writeItem(Page::NS_INDEX, ItemType::U8, nsName, &ns, sizeof(ns)); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } - if (mNamespaceUsage.set(ns, true) != ESP_OK) { + if(mNamespaceUsage.set(ns, true) != ESP_OK) { return ESP_FAIL; } nsIndex = ns; NamespaceEntry* entry = new (std::nothrow) NamespaceEntry; - if (!entry) { + if(!entry) { return ESP_ERR_NO_MEM; } @@ -583,10 +651,12 @@ esp_err_t Storage::readMultiPageBlob(uint8_t nsIndex, const char* key, void* dat { Item item; Page* findPage = nullptr; + size_t itemIndex = 0; + - /* First read the blob index */ + // First read the blob index auto err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } @@ -596,30 +666,34 @@ esp_err_t Storage::readMultiPageBlob(uint8_t nsIndex, const char* key, void* dat NVS_ASSERT_OR_RETURN(dataSize == item.blobIndex.dataSize, ESP_FAIL); - /* Now read corresponding chunks */ - for (uint8_t chunkNum = 0; chunkNum < chunkCount; chunkNum++) { - err = findItem(nsIndex, ItemType::BLOB_DATA, key, findPage, item, static_cast (chunkStart) + chunkNum); - if (err != ESP_OK) { - if (err == ESP_ERR_NVS_NOT_FOUND) { + // Now read related blob data chunks + // Remember the itemIndex as it is used to fast locate the entry in the page + for(uint8_t chunkNum = 0; chunkNum < chunkCount; chunkNum++) { + err = findItem(nsIndex, ItemType::BLOB_DATA, key, findPage, item, static_cast (chunkStart) + chunkNum, nvs::VerOffset::VER_ANY, &itemIndex); + if(err != ESP_OK) { + if(err == ESP_ERR_NVS_NOT_FOUND) { break; } return err; } - if (item.varLength.dataSize > dataSize - offset) { - /* The size of the entry in the index is inconsistent with the sum of the sizes of chunks */ + + // Check if the blob data chunk length indicated for actual item still fits into the total length of the buffer + if(item.varLength.dataSize > dataSize - offset) { err = ESP_ERR_NVS_INVALID_LENGTH; break; } - err = findPage->readItem(nsIndex, ItemType::BLOB_DATA, key, static_cast(data) + offset, item.varLength.dataSize, static_cast (chunkStart) + chunkNum); - if (err != ESP_OK) { + + err = findPage->readVariableLengthItemData(item, itemIndex, static_cast(data) + offset); + if(err != ESP_OK) { return err; } + NVS_ASSERT_OR_RETURN(static_cast (chunkStart) + chunkNum == item.chunkIndex, ESP_FAIL); offset += item.varLength.dataSize; } - if (err == ESP_ERR_NVS_NOT_FOUND || err == ESP_ERR_NVS_INVALID_LENGTH) { + if(err == ESP_ERR_NVS_NOT_FOUND || err == ESP_ERR_NVS_INVALID_LENGTH) { // cleanup if a chunk is not found or the size is inconsistent eraseMultiPageBlob(nsIndex, key); } @@ -633,10 +707,11 @@ esp_err_t Storage::cmpMultiPageBlob(uint8_t nsIndex, const char* key, const void { Item item; Page* findPage = nullptr; + size_t itemIndex = 0; - /* First read the blob index */ + // First read the blob index auto err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } @@ -645,21 +720,34 @@ esp_err_t Storage::cmpMultiPageBlob(uint8_t nsIndex, const char* key, const void size_t readSize = item.blobIndex.dataSize; size_t offset = 0; - if (dataSize != readSize) { + if(dataSize != readSize) { return ESP_ERR_NVS_CONTENT_DIFFERS; } - /* Now read corresponding chunks */ - for (uint8_t chunkNum = 0; chunkNum < chunkCount; chunkNum++) { - err = findItem(nsIndex, ItemType::BLOB_DATA, key, findPage, item, static_cast (chunkStart) + chunkNum); - if (err != ESP_OK) { - if (err == ESP_ERR_NVS_NOT_FOUND) { + // Now read corresponding chunks + for(uint8_t chunkNum = 0; chunkNum < chunkCount; chunkNum++) { + err = findItem(nsIndex, ItemType::BLOB_DATA, key, findPage, item, static_cast (chunkStart) + chunkNum, nvs::VerOffset::VER_ANY, &itemIndex); + if(err != ESP_OK) { + if(err == ESP_ERR_NVS_NOT_FOUND) { break; } return err; } + + if(item.varLength.dataSize > dataSize - offset) { + // The size of the entry in the index is bigger than the size of the remaining data to be compared + return ESP_ERR_NVS_CONTENT_DIFFERS; + } + + // calculate crc32 of the incoming data window related to the BLOB_DATA chunk and compare it with the crc32 from the BLOB_DATA metadata entry + // Different crc32 indicates data mismatch. + // If crc32 matches, we have to compare the data in the chunk with the buffer data to exclude crc32 collision. + if (Item::calculateCrc32(reinterpret_cast(data), item.varLength.dataSize) != item.varLength.dataCrc32) { + return ESP_ERR_NVS_CONTENT_DIFFERS; + } + err = findPage->cmpItem(nsIndex, ItemType::BLOB_DATA, key, static_cast(data) + offset, item.varLength.dataSize, static_cast (chunkStart) + chunkNum); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } NVS_ASSERT_OR_RETURN(static_cast (chunkStart) + chunkNum == item.chunkIndex, ESP_FAIL); @@ -673,21 +761,21 @@ esp_err_t Storage::cmpMultiPageBlob(uint8_t nsIndex, const char* key, const void esp_err_t Storage::readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize) { - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } Item item; Page* findPage = nullptr; - if (datatype == ItemType::BLOB) { + if(datatype == ItemType::BLOB) { auto err = readMultiPageBlob(nsIndex, key, data, dataSize); - if (err != ESP_ERR_NVS_NOT_FOUND) { + if(err != ESP_ERR_NVS_NOT_FOUND) { return err; } // else check if the blob is stored with earlier version format without index } auto err = findItem(nsIndex, datatype, key, findPage, item); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } return findPage->readItem(nsIndex, datatype, key, data, dataSize); @@ -696,67 +784,100 @@ esp_err_t Storage::readItem(uint8_t nsIndex, ItemType datatype, const char* key, esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffset chunkStart) { - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } Item item; Page* findPage = nullptr; + size_t itemIndex = 0; + uint8_t chunkCount = 0; - auto err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item, Page::CHUNK_ANY, chunkStart); - if (err != ESP_OK) { + auto err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item, Page::CHUNK_ANY, chunkStart, &itemIndex); + if(err != ESP_OK) { return err; } + + chunkCount = item.blobIndex.chunkCount; + // Erase the index first and make children blobs orphan - err = findPage->eraseItem(nsIndex, ItemType::BLOB_IDX, key, Page::CHUNK_ANY, chunkStart); - if (err != ESP_OK) { + err = findPage->eraseEntryAndSpan(itemIndex); + if(err != ESP_OK) { return err; } // If caller requires delete of VER_ANY // We may face dirty NVS partition and version duplicates can be there // Make second attempt to delete index and ignore eventual not found - if(chunkStart == VerOffset::VER_ANY) - { - err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item, Page::CHUNK_ANY, chunkStart); - if (err == ESP_OK) { - err = findPage->eraseItem(nsIndex, ItemType::BLOB_IDX, key, Page::CHUNK_ANY, chunkStart); - if (err != ESP_OK) { + if(chunkStart == VerOffset::VER_ANY) { + // Specific case called during initialisation of the storage + // We need to delete all chunks with the same key and namespace index + + // If there exists another BLOB_IDX with the same key and namespace index, delete it + // Ignore potential error if the item is not found + err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item, Page::CHUNK_ANY, chunkStart, &itemIndex); + if(err == ESP_OK) { + err = findPage->eraseEntryAndSpan(itemIndex); + if(err != ESP_OK) { return err; } - } else if (err != ESP_ERR_NVS_NOT_FOUND) { + } else if(err != ESP_ERR_NVS_NOT_FOUND) { return err; } - } - - // setup limits for chunkIndex-es to be deleted - uint8_t minChunkIndex = (uint8_t) VerOffset::VER_0_OFFSET; - uint8_t maxChunkIndex = (uint8_t) VerOffset::VER_ANY; - if(chunkStart == VerOffset::VER_0_OFFSET) { - maxChunkIndex = (uint8_t) VerOffset::VER_1_OFFSET; - } else if (chunkStart == VerOffset::VER_1_OFFSET) { - minChunkIndex = (uint8_t) VerOffset::VER_1_OFFSET; - } - - for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { - size_t itemIndex = 0; - do { - err = it->findItem(nsIndex, ItemType::BLOB_DATA, key, itemIndex, item); - if (err == ESP_ERR_NVS_NOT_FOUND) { - break; - } else if (err == ESP_OK) { - // check if item.chunkIndex is within the version range indicated by chunkStart, if so, delete it - if((item.chunkIndex >= minChunkIndex) && (item.chunkIndex < maxChunkIndex)) { + // To delete all chunks, we will visit every page and delete all chunks regardless of chunkIndex + // This approach cannot use the hash list as the chunkIndex is not known. + for(auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { + // reset itemIndex to zero for each page to search from the beginning + itemIndex = 0; + do { + // (Re)Try to find the item at the position starting at the itemIndex + err = it->findItem(nsIndex, ItemType::BLOB_DATA, key, itemIndex, item); + + // If the item is not found, we can break the actual loop and continue with the next page + if(err == ESP_ERR_NVS_NOT_FOUND) { + break; + } else if(err == ESP_OK) { err = it->eraseEntryAndSpan(itemIndex); + + // advance itemIndex to the next potential entry on the page + // findItem checks the consistency of the entry metadata so we can safely assume the span is non-zero + itemIndex += item.span; } + if(err != ESP_OK) { + return err; + } + // Continue the loop until all items on the current page are found and erased + } while(err == ESP_OK && itemIndex < Page::ENTRY_COUNT); + } - // continue findItem until end of page - itemIndex += item.span; + } else { + // Most common condition + // The caller has specified the chunk version, delete all chunks within the chunk index range indicated by the BLOB_IDX entry + // The loop will iterate the chunk index, page will be found and chunk index will be erased + // This approach uses the hash list to find the item on the page, so it is efficient. + uint8_t minChunkIndex = (uint8_t) VerOffset::VER_ANY; + uint8_t maxChunkIndex = (uint8_t) VerOffset::VER_ANY; + + if(chunkStart == VerOffset::VER_0_OFFSET) { + minChunkIndex = (uint8_t) VerOffset::VER_0_OFFSET; + maxChunkIndex = minChunkIndex + chunkCount; + } else if(chunkStart == VerOffset::VER_1_OFFSET) { + minChunkIndex = (uint8_t) VerOffset::VER_1_OFFSET; + maxChunkIndex = minChunkIndex + chunkCount; + } + + for(uint8_t chunkIndex = minChunkIndex; chunkIndex < maxChunkIndex; chunkIndex++) { + err = findItem(nsIndex, ItemType::BLOB_DATA, key, findPage, item, chunkIndex, nvs::VerOffset::VER_ANY, &itemIndex); + if(err != ESP_OK) { + return err; } + + // Erase the entry + err = findPage->eraseEntryAndSpan(itemIndex); if(err != ESP_OK) { return err; } - } while (err == ESP_OK && itemIndex < Page::ENTRY_COUNT); + } } return ESP_OK; @@ -764,41 +885,40 @@ esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffse esp_err_t Storage::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key) { - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } - if (datatype == ItemType::BLOB) { - return eraseMultiPageBlob(nsIndex, key); - } - Item item; Page* findPage = nullptr; - auto err = findItem(nsIndex, datatype, key, findPage, item); - if (err != ESP_OK) { + esp_err_t err = ESP_OK; + size_t itemIndex = 0; + + err = findItem(nsIndex, datatype, key, findPage, item, Page::CHUNK_ANY, VerOffset::VER_ANY, &itemIndex); + if(err != ESP_OK) { return err; } - - if (item.datatype == ItemType::BLOB_DATA || item.datatype == ItemType::BLOB_IDX) { - return eraseMultiPageBlob(nsIndex, key); + // If the item found is BLOB_IDX, the eraseMultiPageBlob is used to erase the whole multi-page blob. + if (item.datatype == ItemType::BLOB_IDX) { + return eraseMultiPageBlob(nsIndex, key, item.blobIndex.chunkStart); } - return findPage->eraseItem(nsIndex, datatype, key); + return findPage->eraseEntryAndSpan(itemIndex); } esp_err_t Storage::eraseNamespace(uint8_t nsIndex) { - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } - for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { - while (true) { + for(auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { + while(true) { auto err = it->eraseItem(nsIndex, ItemType::ANY, nullptr); - if (err == ESP_ERR_NVS_NOT_FOUND) { + if(err == ESP_ERR_NVS_NOT_FOUND) { break; } - else if (err != ESP_OK) { + else if(err != ESP_OK) { return err; } } @@ -809,14 +929,14 @@ esp_err_t Storage::eraseNamespace(uint8_t nsIndex) esp_err_t Storage::findKey(const uint8_t nsIndex, const char* key, ItemType* datatype) { - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } Item item; Page* findPage = nullptr; auto err = findItem(nsIndex, ItemType::ANY, key, findPage, item); - if (err != ESP_OK) { + if(err != ESP_OK) { return err; } @@ -829,32 +949,37 @@ esp_err_t Storage::findKey(const uint8_t nsIndex, const char* key, ItemType* dat esp_err_t Storage::getItemDataSize(uint8_t nsIndex, ItemType datatype, const char* key, size_t& dataSize) { - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } Item item; Page* findPage = nullptr; - auto err = findItem(nsIndex, datatype, key, findPage, item); - if (err != ESP_OK) { - if (datatype != ItemType::BLOB) { - return err; - } + esp_err_t err = ESP_OK; + + // If requested datatype is BLOB, first try to find the item with datatype BLOB_IDX - new format + // If not found, try to find the item with datatype BLOB - old format. + if(datatype == ItemType::BLOB) { err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item); - if (err != ESP_OK) { + if(err == ESP_OK) { + dataSize = item.blobIndex.dataSize; + return err; + } else if(err != ESP_ERR_NVS_NOT_FOUND) { return err; } - dataSize = item.blobIndex.dataSize; - return ESP_OK; } + err = findItem(nsIndex, datatype, key, findPage, item); + if(err != ESP_OK) { + return err; + } dataSize = item.varLength.dataSize; return ESP_OK; } void Storage::debugDump() { - for (auto p = mPageManager.begin(); p != mPageManager.end(); ++p) { + for(auto p = mPageManager.begin(); p != mPageManager.end(); ++p) { p->debugDump(); } } @@ -864,15 +989,15 @@ void Storage::debugCheck() { std::map keys; - for (auto p = mPageManager.begin(); p != mPageManager.end(); ++p) { + for(auto p = mPageManager.begin(); p != mPageManager.end(); ++p) { size_t itemIndex = 0; size_t usedCount = 0; Item item; - while (p->findItem(Page::NS_ANY, ItemType::ANY, nullptr, itemIndex, item) == ESP_OK) { + while(p->findItem(Page::NS_ANY, ItemType::ANY, nullptr, itemIndex, item) == ESP_OK) { std::stringstream keyrepr; keyrepr << static_cast(item.nsIndex) << "_" << static_cast(item.datatype) << "_" << item.key <<"_"<(item.chunkIndex); std::string keystr = keyrepr.str(); - if (keys.find(keystr) != std::end(keys)) { + if(keys.find(keystr) != std::end(keys)) { printf("Duplicate key: %s\n", keystr.c_str()); debugDump(); assert(0); @@ -896,24 +1021,24 @@ esp_err_t Storage::calcEntriesInNamespace(uint8_t nsIndex, size_t& usedEntries) { usedEntries = 0; - if (mState != StorageState::ACTIVE) { + if(mState != StorageState::ACTIVE) { return ESP_ERR_NVS_NOT_INITIALIZED; } - for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { + for(auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { size_t itemIndex = 0; Item item; - while (true) { + while(true) { auto err = it->findItem(nsIndex, ItemType::ANY, nullptr, itemIndex, item); - if (err == ESP_ERR_NVS_NOT_FOUND) { + if(err == ESP_ERR_NVS_NOT_FOUND) { break; } - else if (err != ESP_OK) { + else if(err != ESP_OK) { return err; } usedEntries += item.span; itemIndex += item.span; - if (itemIndex >= it->ENTRY_COUNT) break; + if(itemIndex >= it->ENTRY_COUNT) break; } } return ESP_OK; @@ -925,7 +1050,7 @@ void Storage::fillEntryInfo(Item &item, nvs_entry_info_t &info) strncpy(info.key, item.key, sizeof(info.key) - 1); info.key[sizeof(info.key) - 1] = '\0'; - for (auto &name : mNamespaces) { + for(auto &name : mNamespaces) { if(item.nsIndex == name.mIndex) { strlcpy(info.namespace_name, name.mName, sizeof(info.namespace_name)); break; @@ -939,7 +1064,7 @@ bool Storage::findEntry(nvs_opaque_iterator_t* it, const char* namespace_name) it->nsIndex = Page::NS_ANY; it->page = mPageManager.begin(); - if (namespace_name != nullptr) { + if(namespace_name != nullptr) { if(createOrOpenNamespace(namespace_name, false, it->nsIndex) != ESP_OK) { return false; } @@ -976,7 +1101,7 @@ bool Storage::nextEntry(nvs_opaque_iterator_t* it) Item item; esp_err_t err; - for (auto page = it->page; page != mPageManager.end(); ++page) { + for(auto page = it->page; page != mPageManager.end(); ++page) { do { err = page->findItem(it->nsIndex, (ItemType)it->type, nullptr, it->entryIndex, item); it->entryIndex += item.span; @@ -985,7 +1110,7 @@ bool Storage::nextEntry(nvs_opaque_iterator_t* it) it->page = page; return true; } - } while (err != ESP_ERR_NVS_NOT_FOUND); + } while(err != ESP_ERR_NVS_NOT_FOUND); it->entryIndex = 0; } diff --git a/components/nvs_flash/src/nvs_storage.hpp b/components/nvs_flash/src/nvs_storage.hpp index 3ea642e773b5..1c63d1c8f087 100644 --- a/components/nvs_flash/src/nvs_storage.hpp +++ b/components/nvs_flash/src/nvs_storage.hpp @@ -153,7 +153,7 @@ class Storage : public intrusive_list_node, public ExceptionlessAllocat void fillEntryInfo(Item &item, nvs_entry_info_t &info); - esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item, uint8_t chunkIdx = Page::CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY); + esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item, uint8_t chunkIdx = Page::CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY, size_t* itemIndex = NULL); protected: Partition *mPartition; diff --git a/components/nvs_flash/src/nvs_types.cpp b/components/nvs_flash/src/nvs_types.cpp index 24073fc30697..81379c8591ed 100644 --- a/components/nvs_flash/src/nvs_types.cpp +++ b/components/nvs_flash/src/nvs_types.cpp @@ -33,9 +33,12 @@ uint32_t Item::calculateCrc32WithoutValue() const return result; } -uint32_t Item::calculateCrc32(const uint8_t* data, size_t size) +uint32_t Item::calculateCrc32(const uint8_t* data, size_t size, uint32_t* initial_crc32) { uint32_t result = 0xffffffff; + if(initial_crc32) { + result = *initial_crc32; + } result = esp_rom_crc32_le(result, data, size); return result; } diff --git a/components/nvs_flash/src/nvs_types.hpp b/components/nvs_flash/src/nvs_types.hpp index 5eda9abcd990..5cb3056a45e0 100644 --- a/components/nvs_flash/src/nvs_types.hpp +++ b/components/nvs_flash/src/nvs_types.hpp @@ -93,7 +93,7 @@ class Item uint32_t calculateCrc32() const; uint32_t calculateCrc32WithoutValue() const; - static uint32_t calculateCrc32(const uint8_t* data, size_t size); + static uint32_t calculateCrc32(const uint8_t* data, size_t size, uint32_t* initial_crc32 = nullptr); void getKey(char* dst, size_t dstSize) { diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index 7d7c0ff0ab12..b113cfd1f4c0 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -6,6 +6,28 @@ menu "OpenThread" help Select this option to enable OpenThread and show the submenu with OpenThread configuration choices. + menu "Thread Task Parameters" + depends on OPENTHREAD_ENABLED + + config OPENTHREAD_TASK_NAME + string "OpenThread task name" + default "ot_main" + help + The OpenThread task name. + + config OPENTHREAD_TASK_SIZE + int "Size of OpenThread task" + default 8192 + help + The size in bytes of OpenThread task. + + config OPENTHREAD_TASK_PRIORITY + int "Priority of OpenThread task" + default 5 + help + The priority of OpenThread task. + endmenu + menu "Thread Version Message" depends on OPENTHREAD_ENABLED @@ -23,10 +45,18 @@ menu "OpenThread" endmenu menu "Thread Console" - depends on OPENTHREAD_ENABLED + config OPENTHREAD_CONSOLE_ENABLE + bool "Enable OpenThread console" + depends on OPENTHREAD_ENABLED + default y + help + Enable the OpenThread-specific console provided by the SDK. This only controls whether + the SDK sets up a dedicated console for OpenThread. Even if disabled, the default + ESP-IDF console (if initialized elsewhere) can still be used independently. choice OPENTHREAD_CONSOLE_TYPE prompt "OpenThread console type" + depends on OPENTHREAD_CONSOLE_ENABLE default OPENTHREAD_CONSOLE_TYPE_UART help Select OpenThread console type @@ -277,10 +307,17 @@ menu "OpenThread" Set the DNS server IPv4 address. endmenu + config OPENTHREAD_TIMING_OPTIMIZATION + bool "Enable timing optimization" + default n + help + Select this option to enable timing optimization for link metrics / CSL features. + config OPENTHREAD_LINK_METRICS bool "Enable link metrics feature" default n select IEEE802154_TIMING_OPTIMIZATION if IEEE802154_ENABLED + select OPENTHREAD_TIMING_OPTIMIZATION help Select this option to enable link metrics feature @@ -301,6 +338,7 @@ menu "OpenThread" bool "Enable CSL feature" default n select IEEE802154_TIMING_OPTIMIZATION if IEEE802154_ENABLED + select OPENTHREAD_TIMING_OPTIMIZATION help Select this option to enable CSL feature menu "CSL Configurations" diff --git a/components/openthread/include/esp_openthread.h b/components/openthread/include/esp_openthread.h index a3f0528f935d..7ce38a58844e 100644 --- a/components/openthread/include/esp_openthread.h +++ b/components/openthread/include/esp_openthread.h @@ -7,6 +7,7 @@ #pragma once #include "esp_err.h" +#include "esp_netif.h" #include "esp_openthread_types.h" #include "openthread/dataset.h" #include "openthread/error.h" @@ -89,6 +90,29 @@ otInstance *esp_openthread_get_instance(void); */ esp_err_t esp_openthread_mainloop_exit(void); +/** + * @brief Starts the full OpenThread stack and create a handle task. + * + * @note The OpenThread instance will also be initialized in this function. + * + * @param[in] config The OpenThread platform configuration. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if already initialized + * + */ +esp_err_t esp_openthread_start(const esp_openthread_config_t *config); + +/** + * @brief This function performs OpenThread stack and platform driver deinitialization and delete the handle task. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if Thread is already active + * + */ +esp_err_t esp_openthread_stop(void); + #ifdef __cplusplus } // end of extern "C" #endif diff --git a/components/openthread/include/esp_openthread_types.h b/components/openthread/include/esp_openthread_types.h index c5f4f4cba672..e384603b7366 100644 --- a/components/openthread/include/esp_openthread_types.h +++ b/components/openthread/include/esp_openthread_types.h @@ -11,6 +11,7 @@ #include #include "esp_event_base.h" +#include "esp_netif_types.h" #include "driver/gpio.h" #include "driver/spi_master.h" #include "driver/spi_slave.h" @@ -199,6 +200,15 @@ typedef struct { esp_openthread_port_config_t port_config; /*!< The port configuration */ } esp_openthread_platform_config_t; +/** + * @brief The OpenThread configuration + * + */ +typedef struct { + esp_netif_config_t netif_config; /*!< The netif configuration */ + esp_openthread_platform_config_t platform_config; /*!< The platform configuration */ +} esp_openthread_config_t; + /** * @brief The OpenThread rcp failure handler * diff --git a/components/openthread/lib b/components/openthread/lib index e9b8513c1cc2..b49c7a847142 160000 --- a/components/openthread/lib +++ b/components/openthread/lib @@ -1 +1 @@ -Subproject commit e9b8513c1cc255e24cc484f3537a2b9c35b6056e +Subproject commit b49c7a8471427d5ec6ac79c9a5dc8489dac3b32e diff --git a/components/openthread/linker.lf b/components/openthread/linker.lf index ffadcdff9129..58c830c8e7a4 100644 --- a/components/openthread/linker.lf +++ b/components/openthread/linker.lf @@ -1,15 +1,17 @@ [mapping:openthread] archive: libopenthread.a entries: - if OPENTHREAD_CSL_ENABLE = y || OPENTHREAD_LINK_METRICS = y: - csl_tx_scheduler (noflash) + if OPENTHREAD_TIMING_OPTIMIZATION = y: link_metrics (noflash) link_quality (noflash) - mac (noflash) mac_frame (noflash) - mesh_forwarder (noflash) radio (noflash) sub_mac (noflash) + if OPENTHREAD_TIMING_OPTIMIZATION = y && OPENTHREAD_RADIO = n: + mesh_forwarder (noflash) + csl_tx_scheduler (noflash) + mac (noflash) + if OPENTHREAD_RCP_SPI = y: ncp_spi (noflash) diff --git a/components/openthread/openthread b/components/openthread/openthread index 3b3dd203be69..36b14d3ef74f 160000 --- a/components/openthread/openthread +++ b/components/openthread/openthread @@ -1 +1 @@ -Subproject commit 3b3dd203be690995d1a89b110322e4eafafc3a3b +Subproject commit 36b14d3ef74f5e37e5be8902e1c1955a642fdfbf diff --git a/components/openthread/private_include/esp_openthread_ncp.h b/components/openthread/private_include/esp_openthread_ncp.h index 507d305daa19..65053bb56a8a 100644 --- a/components/openthread/private_include/esp_openthread_ncp.h +++ b/components/openthread/private_include/esp_openthread_ncp.h @@ -1,10 +1,11 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include +#include #if CONFIG_OPENTHREAD_NCP_VENDOR_HOOK @@ -15,3 +16,13 @@ #define SPINEL_PROP_VENDOR_ESP_COEX_EVENT (SPINEL_PROP_VENDOR_ESP__BEGIN + 3) #endif + +#ifdef __cplusplus +extern "C" { +#endif + +void otAppNcpInit(otInstance *aInstance); + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/esp_openthread_platform.h b/components/openthread/private_include/esp_openthread_platform.h index 3999322a8d1b..582599f5d7fa 100644 --- a/components/openthread/private_include/esp_openthread_platform.h +++ b/components/openthread/private_include/esp_openthread_platform.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h b/components/openthread/private_include/openthread-core-esp32x-ftd-config.h index 6e62ee5f390f..284c5493fe19 100644 --- a/components/openthread/private_include/openthread-core-esp32x-ftd-config.h +++ b/components/openthread/private_include/openthread-core-esp32x-ftd-config.h @@ -457,6 +457,36 @@ #define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1 #endif +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE + * + * Define to 1 to enable Border Routing DHCPv6 PD. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE +#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE + * + * Define to 1 to enable Border Routing DHCPv6 client. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE +#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_MIN_LIFETIME + * + * This parameter sets the minimum preferred lifetime (in seconds) for the Border Router's built-in OpenThread + * DHCPv6 Prefix Delegation (PD) client feature. The default value is set to 30 to pass the certification case. + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_MIN_LIFETIME +#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_CLIENT_MIN_LIFETIME 30 +#endif + /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE * diff --git a/components/openthread/sbom_openthread.yml b/components/openthread/sbom_openthread.yml index 73b3caa70bf1..74398658c691 100644 --- a/components/openthread/sbom_openthread.yml +++ b/components/openthread/sbom_openthread.yml @@ -5,4 +5,4 @@ supplier: 'Organization: Espressif Systems (Shanghai) CO LTD' originator: 'Organization: Google LLC' description: OpenThread released by Google is an open-source implementation of the Thread networking url: https://github.com/espressif/openthread -hash: 3b3dd203be690995d1a89b110322e4eafafc3a3b +hash: 36b14d3ef74f5e37e5be8902e1c1955a642fdfbf diff --git a/components/openthread/src/esp_openthread.cpp b/components/openthread/src/esp_openthread.cpp index d816cd121d54..1868cf0f2506 100644 --- a/components/openthread/src/esp_openthread.cpp +++ b/components/openthread/src/esp_openthread.cpp @@ -4,12 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "sdkconfig.h" #include "esp_openthread.h" #include "esp_check.h" +#include "esp_heap_caps.h" #include "esp_openthread_border_router.h" #include "esp_openthread_common_macro.h" +#include "esp_openthread_cli.h" #include "esp_openthread_dns64.h" #include "esp_openthread_lock.h" +#include "esp_openthread_ncp.h" +#include "esp_openthread_netif_glue.h" #include "esp_openthread_platform.h" #include "esp_openthread_sleep.h" #include "esp_openthread_state.h" @@ -18,15 +23,19 @@ #include "freertos/FreeRTOS.h" #include "lwip/dns.h" #include "openthread/instance.h" +#include "openthread/logging.h" #include "openthread/netdata.h" #include "openthread/tasklet.h" #include "openthread/thread.h" +#include #if CONFIG_OPENTHREAD_FTD #include "openthread/dataset_ftd.h" #endif static bool s_ot_mainloop_running = false; +static SemaphoreHandle_t s_ot_syn_semaphore = NULL; +static TaskHandle_t s_ot_task_handle = NULL; static int hex_digit_to_int(char hex) { @@ -223,3 +232,80 @@ esp_err_t esp_openthread_deinit(void) otInstanceFinalize(esp_openthread_get_instance()); return esp_openthread_platform_deinit(); } + +static void ot_task_worker(void *aContext) +{ + const esp_openthread_config_t* config = *(esp_openthread_config_t **)aContext; + // Initialize the OpenThread stack + ESP_ERROR_CHECK(esp_openthread_init(&(config->platform_config))); + +#if CONFIG_OPENTHREAD_FTD || CONFIG_OPENTHREAD_MTD + esp_netif_t *openthread_netif = esp_netif_new(&(config->netif_config)); + assert(openthread_netif != NULL); + ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(&(config->platform_config)))); +#endif + +#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC + // The OpenThread log level directly matches ESP log level + (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); +#endif // CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC + +#if CONFIG_OPENTHREAD_CLI + esp_openthread_cli_init(); + esp_openthread_cli_console_command_register(); +#endif // CONFIG_OPENTHREAD_CLI + +#if CONFIG_OPENTHREAD_RADIO + otAppNcpInit(esp_openthread_get_instance()); +#endif + xSemaphoreGive(s_ot_syn_semaphore); + + // Run the main loop + esp_openthread_launch_mainloop(); + +#if CONFIG_OPENTHREAD_RADIO + ESP_LOGE(OT_PLAT_LOG_TAG, "RCP deinitialization is not supported for now"); + assert(false); +#endif + + // esp_openthread_cli_console_command_unregister is not provided on idf 5.3 + +#if CONFIG_OPENTHREAD_FTD || CONFIG_OPENTHREAD_MTD + // Clean up + esp_openthread_netif_glue_deinit(); + esp_netif_destroy(openthread_netif); +#endif + + ESP_ERROR_CHECK(esp_openthread_deinit()); + + xSemaphoreGive(s_ot_syn_semaphore); + vTaskDelay(portMAX_DELAY); +} + +esp_err_t esp_openthread_start(const esp_openthread_config_t *config) +{ + assert(config); + ESP_RETURN_ON_FALSE(s_ot_syn_semaphore == NULL, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "OpenThread has been initialized"); + s_ot_syn_semaphore = xSemaphoreCreateBinary(); + ESP_RETURN_ON_FALSE(s_ot_syn_semaphore != NULL, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "Failed to create s_ot_syn_semaphore"); + assert(xTaskCreate(ot_task_worker, CONFIG_OPENTHREAD_TASK_NAME, CONFIG_OPENTHREAD_TASK_SIZE, &config, CONFIG_OPENTHREAD_TASK_PRIORITY, &s_ot_task_handle) == pdPASS); + xSemaphoreTake(s_ot_syn_semaphore, portMAX_DELAY); + return ESP_OK; +} + +esp_err_t esp_openthread_stop(void) +{ + ESP_RETURN_ON_FALSE(s_ot_syn_semaphore != NULL, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "OpenThread is not initialized"); + esp_openthread_lock_acquire(portMAX_DELAY); + otInstance *instance = esp_openthread_get_instance(); + bool is_thread_not_active = (otThreadGetDeviceRole(instance) == OT_DEVICE_ROLE_DISABLED && otIp6IsEnabled(instance) == false); + esp_openthread_lock_release(); + ESP_RETURN_ON_FALSE(is_thread_not_active, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG, "Thread interface is still active"); + esp_openthread_mainloop_exit(); + xSemaphoreTake(s_ot_syn_semaphore, portMAX_DELAY); + vTaskDelete(s_ot_task_handle); + vSemaphoreDelete(s_ot_syn_semaphore); + s_ot_task_handle = NULL; + s_ot_syn_semaphore = NULL; + return ESP_OK; +} diff --git a/components/protocomm/src/transports/protocomm_nimble.c b/components/protocomm/src/transports/protocomm_nimble.c index 5d83f5635958..f9c7acffca20 100644 --- a/components/protocomm/src/transports/protocomm_nimble.c +++ b/components/protocomm/src/transports/protocomm_nimble.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -545,6 +545,7 @@ static int simple_ble_start(const simple_ble_cfg_t *cfg) 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; +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(cfg); if (rc != 0) { ESP_LOGE(TAG, "Error initializing GATT server"); @@ -563,6 +564,7 @@ static int simple_ble_start(const simple_ble_cfg_t *cfg) resp_data.name_len = strlen(ble_svc_gap_device_name()); resp_data.name_is_complete = 1; } +#endif /* Set manufacturer data if protocomm_ble_mfg_data points to valid data */ if (protocomm_ble_mfg_data != NULL) { diff --git a/components/ulp/test_apps/ulp_fsm/pytest_ulp_fsm_app.py b/components/ulp/test_apps/ulp_fsm/pytest_ulp_fsm_app.py index 9db9abb6a9e9..67c57f2ade55 100644 --- a/components/ulp/test_apps/ulp_fsm/pytest_ulp_fsm_app.py +++ b/components/ulp/test_apps/ulp_fsm/pytest_ulp_fsm_app.py @@ -1,6 +1,5 @@ # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 - import pytest from pytest_embedded import Dut @@ -10,4 +9,4 @@ @pytest.mark.esp32s3 @pytest.mark.generic def test_ulp_fsm(dut: Dut) -> None: - dut.run_all_single_board_cases() + dut.run_all_single_board_cases(reset=True) diff --git a/docs/conf_common.py b/docs/conf_common.py index 14f7de9f8219..231dcdc259f5 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -29,6 +29,7 @@ 'api-guides/ble/ble-feature-support-status.rst', 'api-guides/ble/host-feature-support-status.rst', 'api-guides/ble/ble-qualification.rst', + 'api-guides/ble/ble-low-power-mode.rst', 'api-guides/ble/get-started/ble-introduction.rst', 'api-guides/ble/get-started/ble-device-discovery.rst', 'api-guides/ble/get-started/ble-connection.rst', @@ -98,6 +99,8 @@ UART_DOCS = ['api-reference/peripherals/uart.rst'] +UHCI_DOCS = ['api-reference/peripherals/uhci.rst'] + SDMMC_DOCS = ['api-reference/peripherals/sdmmc_host.rst'] SDIO_SLAVE_DOCS = ['api-reference/peripherals/sdio_slave.rst', @@ -256,6 +259,7 @@ 'SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE':MM_SYNC_DOCS, 'SOC_CLK_TREE_SUPPORTED':CLK_TREE_DOCS, 'SOC_UART_SUPPORTED':UART_DOCS, + 'SOC_UHCI_SUPPORTED':UHCI_DOCS, 'SOC_SDMMC_HOST_SUPPORTED':SDMMC_DOCS, 'SOC_SDIO_SLAVE_SUPPORTED':SDIO_SLAVE_DOCS, 'SOC_MCPWM_SUPPORTED':MCPWM_DOCS, diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index c0df41e1ec0c..f3a1db0dbed8 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -146,6 +146,8 @@ INPUT = \ $(PROJECT_PATH)/components/esp_driver_tsens/include/driver/temperature_sensor_etm.h \ $(PROJECT_PATH)/components/esp_driver_uart/include/driver/uart.h \ $(PROJECT_PATH)/components/esp_driver_uart/include/driver/uart_vfs.h \ + $(PROJECT_PATH)/components/esp_driver_uart/include/driver/uhci.h \ + $(PROJECT_PATH)/components/esp_driver_uart/include/driver/uhci_types.h \ $(PROJECT_PATH)/components/esp_eth/include/esp_eth_com.h \ $(PROJECT_PATH)/components/esp_eth/include/esp_eth_driver.h \ $(PROJECT_PATH)/components/esp_eth/include/esp_eth_mac.h \ @@ -267,6 +269,7 @@ INPUT = \ $(PROJECT_PATH)/components/hal/include/hal/efuse_hal.h \ $(PROJECT_PATH)/components/hal/include/hal/eth_types.h \ $(PROJECT_PATH)/components/hal/include/hal/lp_core_types.h \ + $(PROJECT_PATH)/components/hal/include/hal/uhci_types.h \ $(PROJECT_PATH)/components/heap/include/esp_heap_caps_init.h \ $(PROJECT_PATH)/components/heap/include/esp_heap_caps.h \ $(PROJECT_PATH)/components/heap/include/esp_heap_trace.h \ diff --git a/docs/en/api-guides/ble/ble-feature-support-status.rst b/docs/en/api-guides/ble/ble-feature-support-status.rst index 7c9b557c84a3..67490bdd4196 100644 --- a/docs/en/api-guides/ble/ble-feature-support-status.rst +++ b/docs/en/api-guides/ble/ble-feature-support-status.rst @@ -332,7 +332,7 @@ If none of our chip series meet your needs, please contact `customer support tea please consult `SIG Bluetooth Product Database `__. For certain features, if the majority of the development is completed on the Controller, the Host's support status will be limited by the Controller's support status. -If you want BLE Controller and Host to run on different Espressif chips, the functionality of the Host will not be limited by the Controller's support status on the chip running the Host, +If you want Bluetooth LE Controller and Host to run on different Espressif chips, the functionality of the Host will not be limited by the Controller's support status on the chip running the Host, please check the :doc:`ESP Host Feature Support Status Table ` . It is important to clarify that this document is not a binding commitment to our customers. diff --git a/docs/en/api-guides/ble/ble-low-power-mode.rst b/docs/en/api-guides/ble/ble-low-power-mode.rst new file mode 100644 index 000000000000..6a2760507f61 --- /dev/null +++ b/docs/en/api-guides/ble/ble-low-power-mode.rst @@ -0,0 +1,143 @@ +Introduction to Low Power Mode in Bluetooth\ :sup:`®` Low Energy Scenarios +================================================================================ + +:link_to_translation:`zh_CN:[中文]` + +This section introduces clock source selection in low power modes for Bluetooth Low Energy (Bluetooth LE), along with common related issues. + +Clock Source Selection in Low Power Mode +-------------------------------------------- + +According to the Bluetooth specification, the sleep clock accuracy must be within 500 PPM. Make sure the clock source selected for Bluetooth LE low power mode meets this requirement. Otherwise, Bluetooth LE may not perform normally and can cause a series of problems, such as ACL connection establishment failure or ACL connection timeout. + +Selecting Main XTAL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To select the main XTAL as the Bluetooth LE internal clock source, configure the following option: + +.. only:: esp32 + + :ref:`CONFIG_BTDM_CTRL_LOW_POWER_CLOCK` = Main crystal (`CONFIG_BTDM_CTRL_LPCLK_SEL_MAIN_XTAL`) + +.. only:: esp32c3 or esp32s3 + + :ref:`CONFIG_BT_CTRL_LOW_POWER_CLOCK` = Main crystal (`CONFIG_BT_CTRL_LPCLK_SEL_MAIN_XTAL`) + +.. only:: esp32c2 or esp32c6 or esp32h2 or esp32c5 or esp32c61 + + :ref:`CONFIG_BT_LE_LP_CLK_SRC` = Use main XTAL as RTC clock source (`CONFIG_BT_LE_LP_CLK_SRC_MAIN_XTAL`) + +When this is selected, the main XTAL remains powered on during light-sleep, resulting in higher current consumption. Please refer to :example_file:`Power Save README ` for the typical current consumption in light-sleep using XTAL versus a 32 kHz external crystal. + +Selecting 32 kHz External Crystal +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To use a 32 kHz external crystal as the Bluetooth LE internal clock source, configure the following options: + +**Configuration Path 1:** + +.. only:: esp32 + + :ref:`CONFIG_BTDM_CTRL_LOW_POWER_CLOCK` = External 32 kHz crystal/oscillator (`CONFIG_BTDM_CTRL_LPCLK_SEL_EXT_32K_XTAL`) + +.. only:: esp32c3 or esp32s3 + + :ref:`CONFIG_BT_CTRL_LOW_POWER_CLOCK` = External 32 kHz crystal/oscillator (`CONFIG_BT_CTRL_LPCLK_SEL_EXT_32K_XTAL`) + +.. only:: esp32c2 or esp32c6 or esp32h2 or esp32c5 or esp32c61 + + :ref:`CONFIG_BT_LE_LP_CLK_SRC` = Use system RTC slow clock source (`CONFIG_BT_LE_LP_CLK_SRC_DEFAULT`) + +**Configuration Path 2:** + +:ref:`CONFIG_RTC_CLK_SRC` = External 32 kHz crystal (`CONFIG_RTC_CLK_SRC_EXT_CRYS`) + +**Note:** Even if 32 kHz is selected in menuconfig, the system will fall back to the main XTAL if the external crystal is not detected during Bluetooth LE initialization. This may lead to unexpected current consumption in light-sleep mode. + +Selecting 136 kHz RC Oscillator +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. only:: esp32c3 or esp32s3 + + To use a 136 kHz internal RC oscillator as the Bluetooth LE internal clock source, configure the following option: + + **Configuration Path 1:** + + :ref:`CONFIG_BT_CTRL_LOW_POWER_CLOCK` = Internal 136kHz RC oscillator (`CONFIG_BT_CTRL_LPCLK_SEL_RTC_SLOW`) + + Generally, the 136 kHz RC oscillator cannot meet the accuracy requirement of Bluetooth LE. It is only suitable for scenarios with low clock accuracy requirements, such as legacy advertising (ADV) or scanning. It does not support connections in central or peripheral roles. + +.. only:: esp32 + + **Note:** ESP32 does not support using 136 kHz RC oscillator as the Bluetooth LE clock source. + +.. only:: esp32c2 or esp32c6 or esp32h2 or esp32c5 or esp32c61 + + To use a 136 kHz internal RC oscillator as the Bluetooth LE internal clock source, configure the following options: + + **Configuration Path 1:** + + :ref:`CONFIG_BT_LE_LP_CLK_SRC` = Use system RTC slow clock source (`CONFIG_BT_LE_LP_CLK_SRC_DEFAULT`) + +.. only:: not esp32 + + **Configuration Path 2:** + + :ref:`CONFIG_RTC_CLK_SRC` = Internal 136 kHz RC oscillator (`CONFIG_RTC_CLK_SRC_INT_RC`) + +.. only:: esp32c2 or esp32c6 or esp32h2 or esp32c5 or esp32c61 + + If low current consumption is required but there is no access to the External 32 kHz Crystal, this clock source is recommended. However, this clock source has a sleep clock accuracy exceeding 500 PPM, which is only supported when pairing with another ESP chip. For non-ESP peer devices, the following Bluetooth LE features are not supported: + + 1. Central role of Connection + 2. Advertiser of Periodic Advertising + + If the peer device also uses 136 kHz RC as the clock source, the following configuration should be set: + + **Configuration Path:** + + - :ref:`CONFIG_BT_LE_LL_PEER_SCA_SET_ENABLE` = y + - :ref:`CONFIG_BT_LE_LL_PEER_SCA` = 3000 + + **Note:** Using the 136 kHz RC oscillator may occasionally cause issues such as connection establishment failures or connection timeouts. + + +How to Check the Current Clock Source Used by Bluetooth LE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can check the current Bluetooth LE clock source from the initialization logs: + +.. list-table:: Bluetooth LE Initialization Logs and Clock Sources + :widths: 50 50 + :header-rows: 1 + + * - Log Message + - Clock Source + * - Using main XTAL as clock source + - Main XTAL + * - Using 136 kHz RC as clock source + - Internal 136 kHz RC oscillator + * - Using external 32.768 kHz crystal as clock source + - External 32 kHz crystal + * - Using external 32.768 kHz oscillator at 32K_XP pin as clock source + - External 32 kHz oscillator at 32K_XP pin + + +FAQ +-------------------------------------- + +1. Bluetooth LE ACL Connection Fails or Disconnects in Low Power Mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As explained in the clock source selection section above, when ACL connections fail to establish or unexpectedly disconnect in low power mode, first verify whether the current clock source meets Bluetooth LE accuracy requirements. + + +2. Measured light-sleep Current Higher Than Expected +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As described in the clock source selection section above, if the main XTAL is used as the clock source, it remains powered on during light-sleep, resulting in higher current consumption than other clock sources. The average current may vary depending on the specific application, Bluetooth LE configuration, and the duration spent in light-sleep. Some applications may have higher average current because Bluetooth LE is active for a larger proportion of the time transmitting and receiving. + +3. Unable to Enter light-sleep Mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If Auto light-sleep is enabled but the device fails to enter light-sleep, it's usually due to insufficient IDLE time, which prevents the automatic entry conditions from being met. This can be caused by excessive logging or Bluetooth LE configurations that reduce IDLE time, such as continuous scanning. diff --git a/docs/en/api-guides/ble/ble-qualification.rst b/docs/en/api-guides/ble/ble-qualification.rst index f58331e4d95d..6231d9be2733 100644 --- a/docs/en/api-guides/ble/ble-qualification.rst +++ b/docs/en/api-guides/ble/ble-qualification.rst @@ -49,11 +49,11 @@ The table below shows the latest qualification for Espressif Bluetooth LE Host. - .. centered:: Design Number / Qualified Design ID [1]_ - .. centered:: Specification Version [2]_ * - ESP-Bluedroid - - .. centered:: `198312 `__ + - .. centered:: |198312| - .. centered:: 5.3 * - ESP-NimBLE - - .. centered:: `141499 `__ - - .. centered:: 5.1 + - .. centered:: |Q371597| + - .. centered:: 6.1 .. |141661| replace:: `141661 `__ .. |147845| replace:: `147845 `__ @@ -61,7 +61,7 @@ The table below shows the latest qualification for Espressif Bluetooth LE Host. .. |194009| replace:: `194009 `__ .. |199258| replace:: `199258 `__ .. |198312| replace:: `198312 `__ -.. |141499| replace:: `141499 `__ +.. |Q371597| replace:: `Q371597 `__ .. |Q331318| replace:: `Q331318 `__ .. |Q335877| replace:: `Q335877 `__ diff --git a/docs/en/api-guides/ble/blufi.rst b/docs/en/api-guides/ble/blufi.rst index 023d3a708f8e..862cfda39e26 100644 --- a/docs/en/api-guides/ble/blufi.rst +++ b/docs/en/api-guides/ble/blufi.rst @@ -223,9 +223,9 @@ The format of ACK Frame: - * - 0x8 (b’001000) - - Disconnect the BLE GATT link. + - Disconnect the Bluetooth LE GATT link. - - - The ESP device will disconnect the BLE GATT link after receives this command. + - The ESP device will disconnect the Bluetooth LE GATT link after receives this command. * - 0x9 (b’001001) - Get the Wi-Fi list. diff --git a/docs/en/api-guides/ble/get-started/ble-data-exchange.rst b/docs/en/api-guides/ble/get-started/ble-data-exchange.rst index e8a00f57ef17..006c134438fb 100644 --- a/docs/en/api-guides/ble/get-started/ble-data-exchange.rst +++ b/docs/en/api-guides/ble/get-started/ble-data-exchange.rst @@ -296,7 +296,7 @@ If you have not completed the ESP-IDF development environment setup, please refe Try It Out ^^^^^^^^^^^^^^^^^^ -Please refer to :ref:`BLE Introduction Try It Out ` 。 +Please refer to :ref:`Bluetooth LE Introduction Try It Out `. Code Explanation diff --git a/docs/en/api-guides/ble/host-feature-support-status.rst b/docs/en/api-guides/ble/host-feature-support-status.rst index 05432a005efb..6dcc45d59adb 100644 --- a/docs/en/api-guides/ble/host-feature-support-status.rst +++ b/docs/en/api-guides/ble/host-feature-support-status.rst @@ -6,7 +6,7 @@ ESP Host Major Feature Support Status :link_to_translation:`zh_CN:[中文]` The table below shows the support status of major features on ESP-Bluedroid and ESP-NimBLE Host. -If you plan to run the BLE Controller and Host on {IDF_TARGET_NAME} together, the functionality of the Host may be limited by the support status of the Controller, +If you plan to run the Bluetooth LE Controller and Host on {IDF_TARGET_NAME} together, the functionality of the Host may be limited by the support status of the Controller, please check the :doc:`{IDF_TARGET_NAME} Major Feature Support Status Table ` . |supported_def| **This feature has completed development and internal testing.** [1]_ diff --git a/docs/en/api-guides/ble/index.rst b/docs/en/api-guides/ble/index.rst index 2b855e43a1ed..5598db3cbd25 100644 --- a/docs/en/api-guides/ble/index.rst +++ b/docs/en/api-guides/ble/index.rst @@ -13,6 +13,7 @@ Overview overview ble-feature-support-status ble-qualification + Low Power Mode Introduction *************** Get Started diff --git a/docs/en/api-reference/peripherals/temp_sensor.rst b/docs/en/api-reference/peripherals/temp_sensor.rst index 192dfae27e41..6f7abd8a1891 100644 --- a/docs/en/api-reference/peripherals/temp_sensor.rst +++ b/docs/en/api-reference/peripherals/temp_sensor.rst @@ -8,29 +8,9 @@ Introduction The {IDF_TARGET_NAME} has a built-in sensor used to measure the chip's internal temperature. The temperature sensor module contains an 8-bit Sigma-Delta analog-to-digital converter (ADC) and a digital-to-analog converter (DAC) to compensate for the temperature measurement. -Due to restrictions of hardware, the sensor has predefined measurement ranges with specific measurement errors. See the table below for details. - -.. list-table:: - :header-rows: 1 - :widths: 50 50 - :align: center - - * - Predefined Range (°C) - - Error (°C) - * - 50 ~ 125 - - < 3 - * - 20 ~ 100 - - < 2 - * - -10 ~ 80 - - < 1 - * - -30 ~ 50 - - < 2 - * - -40 ~ 20 - - < 3 - .. note:: - The temperature sensor is designed primarily to measure the temperature changes inside the chip. The internal temperature of a chip is usually higher than the ambient temperature, and is affected by factors such as the microcontroller's clock frequency or I/O load, and the external thermal environment. + The temperature sensor is designed primarily to measure the temperature **inside** the silicon. The sensor can reflect the temperature changes very well but it can't give a precise measurement value. So it's not recommended to use it for ambient temperature measurement. Functional Overview ------------------- diff --git a/docs/en/api-reference/peripherals/uart.rst b/docs/en/api-reference/peripherals/uart.rst index 1f7bdc0ab547..94d4ff3fcd60 100644 --- a/docs/en/api-reference/peripherals/uart.rst +++ b/docs/en/api-reference/peripherals/uart.rst @@ -18,6 +18,15 @@ Each UART controller is independently configurable with parameters such as baud Additionally, the {IDF_TARGET_NAME} chip has one low-power (LP) UART controller. It is the cut-down version of regular UART. Usually, the LP UART controller only support basic UART functionality with a much smaller RAM size, and does not support IrDA or RS485 protocols. For a full list of difference between UART and LP UART, please refer to the **{IDF_TARGET_NAME} Technical Reference Manual** > **UART Controller (UART)** > **Features** [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]). +.. only:: SOC_UHCI_SUPPORTED + + .. toctree:: + :hidden: + + uhci + + The {IDF_TARGET_NAME} chip also supports using DMA with UART. For details, see to :doc:`uhci`. + Functional Overview ------------------- @@ -230,7 +239,7 @@ The UART controller supports a number of communication modes. A mode can be sele Use Interrupts ^^^^^^^^^^^^^^^^ -There are many interrupts that can be generated depending on specific UART states or detected errors. The full list of available interrupts is provided in *{IDF_TARGET_NAME} Technical Reference Manual* > *UART Controller (UART)* > *UART Interrupts* and *UHCI Interrupts* [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]. You can enable or disable specific interrupts by calling :cpp:func:`uart_enable_intr_mask` or :cpp:func:`uart_disable_intr_mask` respectively. +There are many interrupts that can be generated depending on specific UART states or detected errors. The full list of available interrupts is provided in *{IDF_TARGET_NAME} Technical Reference Manual* > *UART Controller (UART)* > *UART Interrupts* [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]. You can enable or disable specific interrupts by calling :cpp:func:`uart_enable_intr_mask` or :cpp:func:`uart_disable_intr_mask` respectively. The UART driver provides a convenient way to handle specific interrupts by wrapping them into corresponding events. Events defined in :cpp:type:`uart_event_type_t` can be reported to a user application using the FreeRTOS queue functionality. diff --git a/docs/en/api-reference/peripherals/uhci.rst b/docs/en/api-reference/peripherals/uhci.rst new file mode 100644 index 000000000000..2eed6bb59349 --- /dev/null +++ b/docs/en/api-reference/peripherals/uhci.rst @@ -0,0 +1,299 @@ +UART DMA (UHCI) +=============== + +:link_to_translation:`zh_CN:[中文]` + +This document describes the functionality of the UART DMA(UHCI) driver in ESP-IDF. The table of contents is as follows: + +.. contents:: + :local: + :depth: 2 + +Introduction +------------ + +This document shows how to use UART and DMA together for transmitting or receiving large data volumes using high baud rates. {IDF_TARGET_SOC_UART_HP_NUM} HP UART controllers on {IDF_TARGET_NAME} share one group of DMA TX/RX channels via host controller interface (HCI). This document assumes that UART DMA is controlled by UHCI entity. + +.. note:: + + The UART DMA shares the HCI hardware with Bluetooth, so please don't use BT HCI together with UART DMA, even if they use different UART ports. + +Quick Start +----------- + +This section will quickly guide you on how to use the UHCI driver. Through a simple example including transmitting and receiving, it demonstrates how to create and start a UHCI, initiate a transmit and receive transactions, and register event callback functions. The general usage process is as follows: + +Creating and Enabling the UHCI controller +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +UHCI controller requires the configuration specified by :cpp:type:`uhci_controller_config_t`. + +If the configurations in :cpp:type:`uhci_controller_config_t` is specified, users can call :cpp:func:`uhci_new_controller` to allocate and initialize a uhci controller. This function will return a uhci controller handle if it runs correctly. Besides, UHCI must work with the installed UART driver. As a reference, see the code below. + +.. code:: c + + #define EX_UART_NUM 1 // Define UART port number + + // For uart port configuration, please refer to UART programming guide. + // Please double-check as the baud rate might be limited by serial port chips. + uart_config_t uart_config = { + .baud_rate = 1 * 1000 * 1000, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + + //UART parameter config + ESP_ERROR_CHECK(uart_param_config(EX_UART_NUM, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_RX_IO, -1, -1)); + + uhci_controller_config_t uhci_cfg = { + .uart_port = EX_UART_NUM, // Connect uart port to UHCI hardware. + .tx_trans_queue_depth = 30, // Queue depth of transaction queue. + .max_receive_internal_mem = 10 * 1024, // internal memory usage, for more information, please refer to API reference. + .max_transmit_size = 10 * 1024, // Maximum transfer size in one transaction, in bytes. + .dma_burst_size = 32, // Burst size. + .rx_eof_flags.idle_eof = 1, // When to trigger a end of frame event, you can choose `idle_eof`, `rx_brk_eof`, `length_eof`, for more information, please refer to API reference. + }; + + uhci_controller_handle_t uhci_ctrl; + + ESP_ERROR_CHECK(uhci_new_controller(&uhci_cfg, &uhci_ctrl)); + +Register Event Callbacks +^^^^^^^^^^^^^^^^^^^^^^^^ + +When an event occurs on the UHCI controller (e.g., transmission or receiving is completed), the CPU is notified of this event via an interrupt. If there is a function that needs to be called when a particular events occur, you can register a callback for that event with the ISR for UHCI (Interrupt Service Routine) by calling :cpp:func:`uhci_register_event_callbacks` for both TX and RX respectively. Since the registered callback functions are called in the interrupt context, the user should ensure that the callback function is non-blocking, e.g., by making sure that only FreeRTOS APIs with the ``FromISR`` suffix are called from within the function. The callback function has a boolean return value used to indicate whether a higher priority task has been unblocked by the callback. + +The UHCI event callbacks are listed in the :cpp:type:`uhci_event_callbacks_t`: + +- :cpp:member:`uhci_event_callbacks_t::on_tx_trans_done` sets a callback function for the "trans-done" event. The function prototype is declared in :cpp:type:`uhci_tx_done_callback_t`. + +- :cpp:member:`uhci_event_callbacks_t::on_rx_trans_event` sets a callback function for "receive" event. The function prototype is declared in :cpp:type:`uhci_rx_event_callback_t`. + +.. note:: + + The "rx-trans-event" is not equivalent to "receive-finished". This callback can also be called at a "partial-received" time, for many times during one receive transaction, which can be notified by :cpp:member:`uhci_rx_event_data_t::flags::totally_received`. + +Users can save their own context in :cpp:func:`uhci_register_event_callbacks` as well, via the parameter ``user_data``. The user data is directly passed to each callback function. + +In the callback function, users can fetch the event-specific data that is filled by the driver in the ``edata``. Note that the ``edata`` pointer is **only** valid during the callback, please do not try to save this pointer and use that outside of the callback function. + +The TX event data is defined in :cpp:type:`uhci_tx_done_event_data_t`: + +- :cpp:member:`uhci_tx_done_event_data_t::buffer` indicates the buffer has been sent out. + +The RX event data is defined in :cpp:type:`uhci_rx_event_data_t`: + +- :cpp:member:`uhci_rx_event_data_t::data` points to the received data. The data is saved in the ``buffer`` parameter of the :cpp:func:`uhci_receive` function. Users should not free this receive buffer before the callback returns. +- :cpp:member:`uhci_rx_event_data_t::recv_size` indicates the number of received data. This value is not larger than the ``buffer_size`` parameter of :cpp:func:`uhci_receive` function. +- :cpp:member:`uhci_rx_event_data_t::flags::totally_received` indicates whether the current received buffer is the last one in the transaction. + +Initiating UHCI Transmission +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:cpp:func:`uhci_transmit` is a non-blocking function, which means this function will immediately return after you call it. The related callback can be obtained via :cpp:member:`uhci_event_callbacks_t::on_tx_trans_done` to indicate that the transaction is done. The function :cpp:func:`uhci_wait_all_tx_transaction_done` can be used to indicate that all transactions are finished. + +Data can be transmitted via UHCI as follows: + +.. code:: c + + uint8_t data_wr[DATA_LENGTH]; + for (int i = 0; i < DATA_LENGTH; i++) { + data_wr[i] = i; + } + ESP_ERROR_CHECK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH)); + // Wait all transaction finishes + ESP_ERROR_CHECK(uhci_wait_all_tx_transaction_done(uhci_ctrl, -1)); + +Initiating UHCI Reception +^^^^^^^^^^^^^^^^^^^^^^^^^ + +:cpp:func:`uhci_receive` is a non-blocking function, which means this function will immediately return after it is called. The related callback can be obtained via :cpp:member:`uhci_rx_event_data_t::recv_size` to indicate the receive event. It can be useful to determine if a transaction has been finished. + +Data can be transmitted via UHCI as follows: + +.. code:: c + + // global variable: handle of queue. + QueueHandle_t uhci_queue; + + IRAM_ATTR static bool s_uhci_rx_event_cbs(uhci_controller_handle_t uhci_ctrl, const uhci_rx_event_data_t *edata, void *user_ctx) + { + // parameter `user_ctx` is parsed by the third parameter of function `uhci_register_event_callbacks` + uhci_context_t *ctx = (uhci_context_t *)user_ctx; + BaseType_t xTaskWoken = 0; + uhci_event_t evt = 0; + if (edata->flags.totally_received) { + evt = UHCI_EVT_EOF; + ctx->receive_size += edata->recv_size; + memcpy(ctx->p_receive_data, edata->data, edata->recv_size); + } else { + evt = UHCI_EVT_PARTIAL_DATA; + ctx->receive_size += edata->recv_size; + memcpy(ctx->p_receive_data, edata->data, edata->recv_size); + ctx->p_receive_data += edata->recv_size; + } + + xQueueSendFromISR(ctx->uhci_queue, &evt, &xTaskWoken); + return xTaskWoken; + } + + // In task + uhci_event_callbacks_t uhci_cbs = { + .on_rx_trans_event = s_uhci_rx_event_cbs, + }; + + // Register callback and start reception. + ESP_ERROR_CHECK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx)); + ESP_ERROR_CHECK(uhci_receive(uhci_ctrl, pdata, 100)); + + uhci_event_t evt; + while (1) { + // A queue in task for receiving event triggered by UHCI. + if (xQueueReceive(ctx->uhci_queue, &evt, portMAX_DELAY) == pdTRUE) { + if (evt == UHCI_EVT_EOF) { + printf("Received size: %d\n", ctx->receive_size); + break; + } + } + } + +In the API :cpp:func:`uhci_receive` interface, the parameter `read_buffer` is a buffer that must be provided by the user, and parameter `buffer_size` represents the size of the buffer supplied by the user. In the configuration structure of the UHCI controller, the parameter :cpp:member:`uhci_controller_config_t::max_receive_internal_mem` specifies the desired size of the internal DMA working space. The software allocates a certain number of DMA nodes based on this working space size. These nodes form a circular linked list. + +When a node is filled, but the reception has not yet completed, the event :cpp:member:`uhci_event_callbacks_t::on_rx_trans_event` will be triggered, accompanied by :cpp:member:`uhci_rx_event_data_t::flags::totally_received` set to 0. When all the data has been fully received, the :cpp:member:`uhci_event_callbacks_t::on_rx_trans_event` event will be triggered again with :cpp:member:`uhci_rx_event_data_t::flags::totally_received` set to 1. + +This mechanism allows the user to achieve continuous and fast reception using a relatively small buffer, without needing to allocate a buffer the same size as the total data being received. + +.. note:: + + The parameter `read_buffer` of :cpp:func:`uhci_receive` cannot be freed until receive finishes. + +Uninstall UHCI controller +^^^^^^^^^^^^^^^^^^^^^^^^^ + +If a previously installed UHCI controller is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`uhci_del_controller`, so that the underlying hardware is released. + +.. code:: c + + ESP_ERROR_CHECK(uhci_del_controller(uhci_ctrl)); + +Advanced Features +----------------- + +As the basic usage has been covered, it's time to explore more advanced features of the UHCI driver. + +Power Management +^^^^^^^^^^^^^^^^ + +When power management is enabled, i.e., :ref:`CONFIG_PM_ENABLE` is on, the system may adjust or disable the clock source before going to sleep. As a result, the FIFO inside the UHCI can't work as expected. + +The driver can prevent the above issue by creating a power management lock. The lock type is set based on different clock sources. The driver will acquire the lock in :cpp:func:`uhci_receive` or :cpp:func:`uhci_transmit`, and release it in the transaction-done interrupt. That means, any UHCI transactions between these two functions are guaranteed to work correctly and stably. + +Cache Safe +^^^^^^^^^^ + +By default, the interrupt on which UHCI relies is deferred when the Cache is disabled for reasons such as writing or erasing the main flash. Thus, the transaction-done interrupt fails to be handled in time, which is unacceptable in a real-time application. What is worse, when the UHCI transaction relies on **ping-pong** interrupt to successively encode or copy the UHCI buffer, a delayed interrupt can lead to an unpredictable result. + +There is a Kconfig option :ref:`CONFIG_UHCI_ISR_CACHE_SAFE` that has the following features: + +1. Enable the interrupt being serviced even when the cache is disabled +2. Place all functions used by the ISR into IRAM [1]_ +3. Place the driver object into DRAM in case it is mapped to PSRAM by accident + +This Kconfig option allows the interrupt handler to run while the cache is disabled but comes at the cost of increased IRAM consumption. + +Resource Consumption +^^^^^^^^^^^^^^^^^^^^ + +Check the code and data consumption of the UHCI driver. The following are the test results under 2 different conditions (using ESP32-C3 as an example): + +**Note that the following data are not exact values and are for reference only; they may differ on different chip models.** + +Resource consumption when :ref:`CONFIG_UHCI_ISR_CACHE_SAFE` is enabled: + +.. list-table:: Resource Consumption + :widths: 10 10 10 10 10 10 10 10 10 + :header-rows: 1 + + * - Component Layer + - Total Size + - DIRAM + - .bss + - .data + - .text + - Flash Code + - Flash Data + - .rodata + * - UHCI + - 5733 + - 680 + - 8 + - 34 + - 638 + - 4878 + - 175 + - 175 + +Resource consumption when :ref:`CONFIG_UHCI_ISR_CACHE_SAFE` is disabled: + +.. list-table:: Resource Consumption + :widths: 10 10 10 10 10 10 10 10 10 10 + :header-rows: 1 + + * - Component Layer + - Total Size + - DIRAM + - .bss + - .data + - .text + - Flash Code + - .text + - Flash Data + - .rodata + * - UHCI + - 5479 + - 42 + - 8 + - 34 + - 0 + - 5262 + - 5262 + - 175 + - 175 + +Performance +^^^^^^^^^^^ + +To improve the real-time response capability of interrupt handling, the UHCI driver provides the :ref:`CONFIG_UHCI_ISR_HANDLER_IN_IRAM` option. Enabling this option will place the interrupt handler in internal RAM, reducing the latency caused by cache misses when loading instructions from Flash. + +.. note:: + + However, user callback functions and context data called by the interrupt handler may still be located in Flash, and cache miss issues will still exist. Users need to place callback functions and data in internal RAM, for example, using :c:macro:`IRAM_ATTR` and :c:macro:`DRAM_ATTR`. + +Thread Safety +^^^^^^^^^^^^^ + +The factory function :cpp:func:`uhci_new_controller`, :cpp:func:`uhci_register_event_callbacks` and :cpp:func:`uhci_del_controller` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks. + +Other Kconfig Options +^^^^^^^^^^^^^^^^^^^^^ + +- :ref:`CONFIG_UHCI_ENABLE_DEBUG_LOG` is allowed for the forced enabling of all debug logs for the UHCI driver, regardless of the global log level setting. Enabling this option can help developers obtain more detailed log information during the debugging process, making it easier to locate and resolve issues, but it will increase the size of the firmware binary. + +Application Examples +-------------------- + +- :example:`peripherals/uart/uart_dma_ota` demonstrates how to use the uart dma for fast OTA the chip firmware with 1M baud rate speed. + +API Reference +------------- + +.. include-build-file:: inc/uhci.inc +.. include-build-file:: inc/components/esp_driver_uart/include/driver/uhci_types.inc +.. include-build-file:: inc/components/hal/include/hal/uhci_types.inc + +.. [1] + The callback function, e.g., :cpp:member:`uhci_event_callbacks_t::on_tx_trans_done`, :cpp:member:`uhci_event_callbacks_t::on_rx_trans_event` and the functions invoked by itself should also reside in IRAM, users need to take care of this by themselves. diff --git a/docs/zh_CN/api-guides/ble/ble-low-power-mode.rst b/docs/zh_CN/api-guides/ble/ble-low-power-mode.rst new file mode 100644 index 000000000000..0f2aabe1ef53 --- /dev/null +++ b/docs/zh_CN/api-guides/ble/ble-low-power-mode.rst @@ -0,0 +1,143 @@ +低功耗蓝牙\ :sup:`®` 场景下低功耗模式介绍 +======================================================================== + +:link_to_translation:`en:[English]` + +本节介绍低功耗蓝牙 (Bluetooth LE) 在低功耗模式下的时钟源选择,以及常见相关问题。 + +低功耗模式下的时钟源选择 +-------------------------------------------- + +在低功耗蓝牙应用场景中,由于协议要求休眠时钟精度需在 500 PPM 以内,light-sleep 和 modem-sleep 模式下所用的时钟源必须满足该要求。如果时钟精度不足,可能会出现 ACL 连接失败或超时断开等问题。**因此在使用前请确保所选时钟源及其精度满足要求。** + +选择主晶振 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +要选择主晶振作为低功耗蓝牙内部时钟源,请配置以下选项: + +.. only:: esp32 + + :ref:`CONFIG_BTDM_CTRL_LOW_POWER_CLOCK` = Main crystal (`CONFIG_BTDM_CTRL_LPCLK_SEL_MAIN_XTAL`) + +.. only:: esp32c3 or esp32s3 + + :ref:`CONFIG_BT_CTRL_LOW_POWER_CLOCK` = Main crystal (`CONFIG_BT_CTRL_LPCLK_SEL_MAIN_XTAL`) + +.. only:: esp32c2 or esp32c6 or esp32h2 or esp32c5 or esp32c61 + + :ref:`CONFIG_BT_LE_LP_CLK_SRC` = Use main XTAL as RTC clock source (`CONFIG_BT_LE_LP_CLK_SRC_MAIN_XTAL`) + +选择主晶振后,light-sleep 模式下主晶振电源不会关闭,因此电流消耗更高。有关使用主晶振与 32 kHz 外部晶振在 light-sleep 模式下的典型电流消耗,请参考 :example_file:`Power Save README ` 。 + +选择 32 kHz 外部晶振 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +要使用 32 kHz 外部晶振作为低功耗蓝牙内部时钟源,请配置以下选项: + +**配置路径 1:** + +.. only:: esp32 + + :ref:`CONFIG_BTDM_CTRL_LOW_POWER_CLOCK` = External 32 kHz crystal/oscillator (`CONFIG_BTDM_CTRL_LPCLK_SEL_EXT_32K_XTAL`) + +.. only:: esp32c3 or esp32s3 + + :ref:`CONFIG_BT_CTRL_LOW_POWER_CLOCK` = External 32 kHz crystal/oscillator (`CONFIG_BT_CTRL_LPCLK_SEL_EXT_32K_XTAL`) + +.. only:: esp32c2 or esp32c6 or esp32h2 or esp32c5 or esp32c61 + + :ref:`CONFIG_BT_LE_LP_CLK_SRC` = Use system RTC slow clock source (`CONFIG_BT_LE_LP_CLK_SRC_DEFAULT`) + +**配置路径 2:** + +:ref:`CONFIG_RTC_CLK_SRC` = External 32 kHz crystal (`CONFIG_RTC_CLK_SRC_EXT_CRYS`) + +**注意:** 即使在 menuconfig 中选择了 32 kHz 外部晶振,如果低功耗蓝牙初始化时未检测到外部晶振,系统会自动切换为主晶振,可能导致 light-sleep 电流高于预期。 + +选择 136 kHz RC 振荡器 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. only:: esp32c3 or esp32s3 + + 要使用 136 kHz 内部 RC 振荡器作为低功耗蓝牙内部时钟源,请配置以下选项: + + **配置路径 1:** + + :ref:`CONFIG_BT_CTRL_LOW_POWER_CLOCK` = Internal 136kHz RC oscillator (`CONFIG_BT_CTRL_LPCLK_SEL_RTC_SLOW`) + + 一般来说,136 kHz RC 振荡器难以满足低功耗蓝牙的精度要求,仅适用于对时钟精度要求不高的场景,如传统广播 (ADV) 或扫描 (SCAN)。它不支持以中心角色或外设角色建立连接。 + +.. only:: esp32 + + **注意:** ESP32 不支持 136 kHz RC 振荡器作为低功耗蓝牙时钟源。 + +.. only:: esp32c2 or esp32c6 or esp32h2 or esp32c5 or esp32c61 + + 要使用 136 kHz 内部 RC 振荡器作为低功耗蓝牙内部时钟源,请配置以下选项: + + **配置路径 1:** + + :ref:`CONFIG_BT_LE_LP_CLK_SRC` = Use system RTC slow clock source (`CONFIG_BT_LE_LP_CLK_SRC_DEFAULT`) + +.. only:: not esp32 + + **配置路径 2:** + + :ref:`CONFIG_RTC_CLK_SRC` = Internal 136 kHz RC oscillator (`CONFIG_RTC_CLK_SRC_INT_RC`) + +.. only:: esp32c2 or esp32c6 or esp32h2 or esp32c5 or esp32c61 + + 对于需要低功耗且没有 32 kHz 外部晶振的场景,可以选择 136 kHz RC 振荡器。然而,该时钟无法满足低功耗蓝牙的 500 PPM 的休眠时钟精度需求。如果对端设备使用 ESP 芯片,低功耗蓝牙功能仍可正常工作;但如果对端设备不是 ESP 芯片,则以下低功耗蓝牙行为将无法支持: + + 1. 作为连接的 Central 方 + 2. 作为 Periodic Advertising 的广播方 + + 如果对端设备也用 136 kHz RC 作为时钟源,需要如下配置: + + **配置路径:** + + - :ref:`CONFIG_BT_LE_LL_PEER_SCA_SET_ENABLE` = y + - :ref:`CONFIG_BT_LE_LL_PEER_SCA` = 3000 + + **注意:** 使用 136 kHz RC 振荡器可能偶发连接断开或连接失败。 + + +如何确认当前低功耗蓝牙使用的时钟源 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +可通过低功耗蓝牙初始化时的日志判断当前时钟源: + +.. list-table:: 低功耗蓝牙初始化日志与时钟源对应关系 + :widths: 50 50 + :header-rows: 1 + + * - 日志内容 + - 时钟源 + * - Using main XTAL as clock source + - 主晶振 (Main XTAL) + * - Using 136 kHz RC as clock source + - 内部 136 kHz RC 振荡器 (Internal 136 kHz RC oscillator) + * - Using external 32.768 kHz crystal as clock source + - 外部 32 kHz 晶振 (External 32 kHz crystal) + * - Using external 32.768 kHz oscillator at 32K_XP pin as clock source + - 外部 32 kHz 振荡器 (32K_XP 引脚) (External 32 kHz oscillator at 32K_XP pin) + + +常见问题 +-------------------------------------- + +1. 低功耗蓝牙 ACL 连接在低功耗模式下建立失败或断开 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +如时钟源选择部分所述,ACL 连接建立失败或断开时,请首先检查当前时钟源是否满足低功耗蓝牙精度要求。 + + +2. 实测 light-sleep 电流高于预期 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +如时钟源选择部分所述,若主晶振为时钟源,light-sleep 模式下主晶振持续供电,电流消耗高于其他时钟源。平均电流可能会因具体应用而异,并取决于低功耗蓝牙的配置以及处于 light-sleep 模式的时间周期。某些应用的平均电流可能会更大,这是因为低功耗蓝牙在其中花费了更高比例的时间进行发射和接收。 + +3. 无法进入 light-sleep 模式 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +启用 Auto light-sleep 后,若设备无法进入 light-sleep,通常是 IDLE 时间不足,未满足自动进入条件。这可能由日志过多或低功耗蓝牙配置导致 IDLE 时间过短(如连续扫描)引起。 diff --git a/docs/zh_CN/api-guides/ble/ble-qualification.rst b/docs/zh_CN/api-guides/ble/ble-qualification.rst index 4d8bef15ce0e..b7d0b80cd635 100644 --- a/docs/zh_CN/api-guides/ble/ble-qualification.rst +++ b/docs/zh_CN/api-guides/ble/ble-qualification.rst @@ -47,11 +47,11 @@ - .. centered:: Design Number / Qualified Design ID [1]_ - .. centered:: 协议版本 [2]_ * - ESP-Bluedroid - - .. centered:: `198312 `__ + - .. centered:: |198312| - .. centered:: 5.3 * - ESP-NimBLE - - .. centered:: `141499 `__ - - .. centered:: 5.1 + - .. centered:: |Q371597| + - .. centered:: 6.1 .. |141661| replace:: `141661 `__ .. |147845| replace:: `147845 `__ @@ -59,7 +59,7 @@ .. |194009| replace:: `194009 `__ .. |199258| replace:: `199258 `__ .. |198312| replace:: `198312 `__ -.. |141499| replace:: `141499 `__ +.. |Q371597| replace:: `Q371597 `__ .. |Q331318| replace:: `Q331318 `__ .. |Q335877| replace:: `Q335877 `__ diff --git a/docs/zh_CN/api-guides/ble/blufi.rst b/docs/zh_CN/api-guides/ble/blufi.rst index d747c1b4dd35..a8c697721b0c 100644 --- a/docs/zh_CN/api-guides/ble/blufi.rst +++ b/docs/zh_CN/api-guides/ble/blufi.rst @@ -223,9 +223,9 @@ ACK 帧格式 (8 bit): - * - 0x8 (b’001000) - - 断开 BLE GATT 连接。 + - 断开低功耗蓝牙 GATT 连接。 - - - ESP 设备收到该指令后主动断开 BLE GATT 连接。 + - ESP 设备收到该指令后主动断开低功耗蓝牙 GATT 连接。 * - 0x9 (b’001001) - 获取 Wi-Fi 列表。 diff --git a/docs/zh_CN/api-guides/ble/index.rst b/docs/zh_CN/api-guides/ble/index.rst index c785a73c7e6c..a3d14113ff11 100644 --- a/docs/zh_CN/api-guides/ble/index.rst +++ b/docs/zh_CN/api-guides/ble/index.rst @@ -13,6 +13,7 @@ overview ble-feature-support-status ble-qualification + 低功耗模式介绍 ********** 快速入门 diff --git a/docs/zh_CN/api-reference/peripherals/temp_sensor.rst b/docs/zh_CN/api-reference/peripherals/temp_sensor.rst index 743bcc7acff8..ce7ebe5c7d11 100644 --- a/docs/zh_CN/api-reference/peripherals/temp_sensor.rst +++ b/docs/zh_CN/api-reference/peripherals/temp_sensor.rst @@ -8,29 +8,9 @@ {IDF_TARGET_NAME} 内置传感器,用于测量芯片内部的温度。该温度传感器模组包含一个 8 位 Sigma-Delta 模拟-数字转换器 (ADC) 和一个数字-模拟转换器 (DAC),可以补偿测量结果,减少温度测量的误差。 -由于硬件限制,温度传感器存在预定义的测量范围及其对应误差,详见下表: - -.. list-table:: - :header-rows: 1 - :widths: 50 50 - :align: center - - * - 预定义测量范围 (°C) - - 测量误差 (°C) - * - 50 ~ 125 - - < 3 - * - 20 ~ 100 - - < 2 - * - -10 ~ 80 - - < 1 - * - -30 ~ 50 - - < 2 - * - -40 ~ 20 - - < 3 - .. note:: - 温度传感器主要用于测量芯片内部的温度变化。芯片内部温度通常高于环境温度,并且受到微控制器的时钟频率或 I/O 负载、外部散热环境等因素影响。 + 温度传感器主要用于测量芯片内部的温度变化。温度值可用作衡量温度变化趋势,但无法提供精确的温度数值,因此不建议用作精准测量温度。 功能概述 ------------------- diff --git a/docs/zh_CN/api-reference/peripherals/uart.rst b/docs/zh_CN/api-reference/peripherals/uart.rst index e9defcc98c7a..f8abfba556cf 100644 --- a/docs/zh_CN/api-reference/peripherals/uart.rst +++ b/docs/zh_CN/api-reference/peripherals/uart.rst @@ -18,6 +18,15 @@ 此外,{IDF_TARGET_NAME} 芯片还有一个满足低功耗需求的 LP UART 控制器。LP UART 是原 UART 的功能剪裁版本。它只支持基础 UART 功能,不支持 IrDA 或 RS485 协议,并且只有一块较小的 RAM 存储空间。想要全面了解的 UART 及 LP UART 功能区别,请参考 **{IDF_TARGET_NAME} 技术参考手册** > UART 控制器 (UART) > 主要特性 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]。 +.. only:: SOC_UHCI_SUPPORTED + + .. toctree:: + :hidden: + + uhci + + {IDF_TARGET_NAME} 芯片也支持 UART DMA 模式, 请参考 :doc:`uhci` 以获得更多信息. + 功能概述 ------------------- @@ -230,7 +239,7 @@ UART 控制器支持多种通信模式,使用函数 :cpp:func:`uart_set_mode` 使用中断 ^^^^^^^^^^^^^^^^^ -根据特定的 UART 状态或检测到的错误,可以生成许多不同的中断。**{IDF_TARGET_NAME} 技术参考手册** > UART 控制器 (UART) > UART 中断 和 UHCI 中断 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__] 中提供了可用中断的完整列表。调用 :cpp:func:`uart_enable_intr_mask` 或 :cpp:func:`uart_disable_intr_mask` 能够分别启用或禁用特定中断。 +根据特定的 UART 状态或检测到的错误,可以生成许多不同的中断。**{IDF_TARGET_NAME} 技术参考手册** > UART 控制器 (UART) > UART 中断 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__] 中提供了可用中断的完整列表。调用 :cpp:func:`uart_enable_intr_mask` 或 :cpp:func:`uart_disable_intr_mask` 能够分别启用或禁用特定中断。 UART 驱动提供了一种便利的方法来处理特定的中断,即将中断包装成相应的事件。这些事件定义在 :cpp:type:`uart_event_type_t` 中,FreeRTOS 队列功能可将这些事件报告给用户应用程序。 diff --git a/docs/zh_CN/api-reference/peripherals/uhci.rst b/docs/zh_CN/api-reference/peripherals/uhci.rst new file mode 100644 index 000000000000..6ec1cd451f3f --- /dev/null +++ b/docs/zh_CN/api-reference/peripherals/uhci.rst @@ -0,0 +1,299 @@ +UART DMA (UHCI) +=============== + +:link_to_translation:`en:[English]` + +本文档描述了 ESP-IDF 中 UART DMA(UHCI)驱动的功能。目录如下: + +.. contents:: + :local: + :depth: 2 + +概述 +------------ + +本文档将介绍如何将 UART 与 DMA 结合使用,以在高波特率下传输或接收大数据量。 {IDF_TARGET_NAME} 的 {IDF_TARGET_SOC_UART_HP_NUM} 个 UART 控制器通过主机控制接口(HCI)共享一组 DMA TX/RX 通道。在以下文档中,UHCI 是指控制 UART DMA 的实体。 + +.. note:: + + UART DMA 与 BT 共享 HCI 硬件,因此请勿同时使用 BT HCI 和 UART DMA,哪怕它们使用的是不同的 UART 端口。 + +快速入门 +----------- + +本节将快速指导您如何使用 UHCI 驱动。通过一个简单的传输和接收示例,展示了如何创建和启动 UHCI、启动传输和接收事务以及注册事件回调函数。一般使用流程如下: + +创建并启用 UHCI 控制器 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +UHCI 控制器需要通过 :cpp:type:`uhci_controller_config_t` 进行配置。 + +如果在 :cpp:type:`uhci_controller_config_t` 完成了配置,用户可以调用 :cpp:func:`uhci_new_controller` 来分配并初始化一个 UHCI 控制器。此函数如果运行正常,将返回一个 UHCI 控制器句柄。此外,UHCI 必须与已初始化的 UART 驱动程序一起工作。以下代码可供参考。 + +.. code:: c + + #define EX_UART_NUM 1 // 定义 UART 端口 + + // 关于 UART 端口配置项,请参考 UART 编程指南 + // 请注意波特率有可能受限于串口芯片 + uart_config_t uart_config = { + .baud_rate = 1 * 1000 * 1000, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + + // UART 参数配置 + ESP_ERROR_CHECK(uart_param_config(EX_UART_NUM, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_RX_IO, -1, -1)); + + uhci_controller_config_t uhci_cfg = { + .uart_port = EX_UART_NUM, // 将指定 UART 端口连接到 UHCI 硬件 + .tx_trans_queue_depth = 30, // 发送队列的队列深度 + .max_receive_internal_mem = 10 * 1024, // 内部接收内存大小,更多信息请参考 API 注释。 + .max_transmit_size = 10 * 1024, // 单次传输的最大传输量,单位是字节 + .dma_burst_size = 32, // 突发传输大小 + .rx_eof_flags.idle_eof = 1, // 结束帧的条件,用户可以选择 `idle_eof`, `rx_brk_eof` 和 `length_eof`, 关于更多信息请参考 API 注释. + }; + + uhci_controller_handle_t uhci_ctrl; + + ESP_ERROR_CHECK(uhci_new_controller(&uhci_cfg, &uhci_ctrl)); + +注册事件回调 +^^^^^^^^^^^^^^^^^^^^^^^^ + +当 UHCI 控制器上发生事件(例如传输或接收完成)时,CPU通过中断被通知此事件。如果在某些事件发生时需要调用特定函数,可以通过调用 :cpp:func:`uhci_register_event_callbacks` 为 TX 和 RX 方向分别注册回调。 由于注册的回调函数在中断上下文中调用,用户应确保回调函数不会阻塞,例如仅调用带有 `FromISR` 后缀的 FreeRTOS API。回调函数具有布尔返回值,指示回调是否解除了更高优先级任务的阻塞状态。 + +UHCI 事件回调在 :cpp:type:`uhci_event_callbacks_t` 中列出: + +- :cpp:member:`uhci_event_callbacks_t::on_tx_trans_done` 为“传输完成”事件设置回调函数。函数原型声明为 :cpp:type:`uhci_tx_done_callback_t`。 + +- :cpp:member:`uhci_event_callbacks_t::on_rx_trans_event` 为“接收事件”设置回调函数。函数原型声明为 :cpp:type:`uhci_rx_event_callback_t`。 + +.. note:: + + “rx-trans-event” 事件并不等同于“接收完成”。在一次接收事务中,该回调函数也可能在“部分接收”时被多次调用,此时可以通过 :cpp:member:`uhci_rx_event_data_t::flags::totally_received` 标志区分“部分接收”和“接收完成”。 + +用户还可以通过 :cpp:func:`uhci_register_event_callbacks` 中的参数 `user_data` 保存自己的上下文。用户数据会直接传递给每个回调函数。 + +在回调函数中,用户可以获取由驱动填充的事件特定数据,该数据保存在 ``edata`` 中。注意, ``edata`` 指针仅在回调期间有效,请勿尝试保存该指针并在回调函数外部使用。 + +TX 事件数据在 :cpp:type:`uhci_tx_done_event_data_t` 中定义: + +- :cpp:member:`uhci_tx_done_event_data_t::buffer` 表示 ``buffer`` 已经发送完成。 + +RX 事件数据在 :cpp:type:`uhci_rx_event_data_t` 中定义: + +- :cpp:member:`uhci_rx_event_data_t::data` 指向接收到的数据。数据保存在 :cpp:func:`uhci_receive` 函数的 ``buffer`` 参数中。用户在回调返回之前不应释放此接收缓冲区。 +- :cpp:member:`uhci_rx_event_data_t::recv_size` 表示接收到的数据大小。此值不会大于 :cpp:func:`uhci_receive` 函数的 ``buffer_size`` 参数。 +- :cpp:member:`uhci_rx_event_data_t::flags::totally_received` 指示当前接收缓冲区是否是事务中的最后一个。 + +启动 UHCI 传输 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:cpp:func:`uhci_transmit` 是一个非阻塞函数,这意味着在调用后会立即返回。您可以通过 :cpp:member:`uhci_event_callbacks_t::on_tx_trans_done` 相关回调指示事务完成。我们还提供了一个函数 :cpp:func:`uhci_wait_all_tx_transaction_done` 来阻塞线程,等待所有事务完成。 + +以下代码显示了如何通过 UHCI 接收数据: + +.. code:: c + + uint8_t data_wr[DATA_LENGTH]; + for (int i = 0; i < DATA_LENGTH; i++) { + data_wr[i] = i; + } + ESP_ERROR_CHECK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH)); + // 等待所有传输完成 + ESP_ERROR_CHECK(uhci_wait_all_tx_transaction_done(uhci_ctrl, -1)); + +启动 UHCI 接收 +^^^^^^^^^^^^^^^^^^^^^^^^^ + +:cpp:func:`uhci_receive` 是一个非阻塞函数,这意味着该函数在调用后会立即返回。用户可以通过 :cpp:member:`uhci_rx_event_data_t::recv_size` 获取相关的回调,以指示接收事件并判断事务是否完成。 + +以下代码展示了如何通过 UHCI 传输数据: + +.. code:: c + + // 全局变量:队列的句柄 + QueueHandle_t uhci_queue; + + IRAM_ATTR static bool s_uhci_rx_event_cbs(uhci_controller_handle_t uhci_ctrl, const uhci_rx_event_data_t *edata, void *user_ctx) + { + // 参数 `user_ctx` 是由函数 `uhci_register_event_callbacks` 的第三个参数传递的。 + uhci_context_t *ctx = (uhci_context_t *)user_ctx; + BaseType_t xTaskWoken = 0; + uhci_event_t evt = 0; + if (edata->flags.totally_received) { + evt = UHCI_EVT_EOF; + ctx->receive_size += edata->recv_size; + memcpy(ctx->p_receive_data, edata->data, edata->recv_size); + } else { + evt = UHCI_EVT_PARTIAL_DATA; + ctx->receive_size += edata->recv_size; + memcpy(ctx->p_receive_data, edata->data, edata->recv_size); + ctx->p_receive_data += edata->recv_size; + } + + xQueueSendFromISR(uhci_queue, &evt, &xTaskWoken); + return xTaskWoken; + } + + // 在任务中 + uhci_event_callbacks_t uhci_cbs = { + .on_rx_trans_event = s_uhci_rx_event_cbs, + }; + + // 注册回调,并开始启动回收 + ESP_ERROR_CHECK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx)); + ESP_ERROR_CHECK(uhci_receive(uhci_ctrl, pdata, 100)); + + uhci_event_t evt; + while (1) { + // 一个在任务中的队列用来接收 UHCI 抛出的事件 + if (xQueueReceive(uhci_queue, &evt, portMAX_DELAY) == pdTRUE) { + if (evt == UHCI_EVT_EOF) { + printf("Received size: %d\n", ctx->receive_size); + break; + } + } + } + +在 API :cpp:func:`uhci_receive` 接口中,参数 ``read_buffer`` 是用户必须提供的缓冲区,参数 ``buffer_size`` 表示用户提供的缓冲区大小。在 UHCI 控制器的配置结构中,参数 :cpp:member:`uhci_controller_config_t::max_receive_internal_mem` 指定了内部 DMA 工作空间的期望大小。软件将根据此工作空间大小分配一定数量的 DMA 节点,这些节点形成一个循环链表。 + +当一个节点被填满,但接收尚未完成时,将触发 :cpp:member:`uhci_event_callbacks_t::on_rx_trans_event` 事件,且 :cpp:member:`uhci_rx_event_data_t::flags::totally_received` 的值为 0。 当所有数据接收完成时,该事件将再次被触发,并且 :cpp:member:`uhci_rx_event_data_t::flags::totally_received` 的值为 1。 + +此机制允许用户使用相对较小的缓冲区实现连续且快速的接收,而无需分配与接收总数据量相等大小的缓冲区。 + +.. note:: + + 在接收完成之前,:cpp:func:`uhci_receive` 的参数 ``read_buffer`` 不可被释放。 + +卸载 UHCI 控制器 +^^^^^^^^^^^^^^^^^^^^^^^^^ + +如果不再需要已安装的 UHCI 控制器,建议通过调用 :cpp:func:`uhci_del_controller` 回收资源,以释放底层硬件。 + +.. code:: c + + ESP_ERROR_CHECK(uhci_del_controller(uhci_ctrl)); + +高级功能 +----------------- + +在理解了基本用法后,我们可以进一步探索 UHCI 驱动的高级功能。 + +关于低功耗 +^^^^^^^^^^^^^^^^ + +当启用电源管理时(即开启 :ref:`CONFIG_PM_ENABLE`),系统在进入睡眠前可能会调整或禁用时钟源。因此,UHCI 内部的 FIFO 可能无法正常工作。 + +通过创建电源管理锁,驱动程序可以避免上述问题. 驱动会根据不同的时钟源设置锁的类型. 驱动程序将在 :cpp:func:`uhci_receive` 或 :cpp:func:`uhci_transmit` 中获取锁,并在事务完成中断中释放锁。这意味着,这两个函数之间的任何 UHCI 事务都能保证正常稳定运行。 + +缓存安全 +^^^^^^^^^^ + +默认情况下,当由于写入或擦除主 Flash 导致缓存被禁用时,UHCI 所依赖的中断会被延迟. 因此,事务完成中断可能无法及时处理,这在实时应用中是不可接受的。更糟糕的是,当 UHCI 事务依赖 **乒乓** 中断来连续编码或复制 UHCI 缓冲区时,延迟的中断可能会导致不可预测的结果。 + +通过启用 Kconfig 选项 :ref:`CONFIG_UHCI_ISR_CACHE_SAFE`,可实现以下功能: + +1. 即使缓存被禁用,中断也能被服务。 +2. 将 ISR 使用的所有函数放入 IRAM [1]_ +3. 将驱动对象放入 DRAM,防止其意外映射到 PSRAM。 + +此选项允许中断处理程序在缓存禁用时运行,但代价是增加了 IRAM 的消耗。 + +资源消耗 +^^^^^^^^^^^^^^^^^^^^ + +检查 UHCI 驱动的代码和数据消耗。以下是基于 ESP32-C3 的测试结果(仅供参考,不同芯片型号可能会有所不同): + +**请注意以下数据仅供参考,不同芯片型号可能会有所不同.** + +启用 :ref:`CONFIG_UHCI_ISR_CACHE_SAFE` 时的资源消耗: + +.. list-table:: 资源消耗 + :widths: 10 10 10 10 10 10 10 10 10 + :header-rows: 1 + + * - Component Layer + - Total Size + - DIRAM + - .bss + - .data + - .text + - Flash Code + - Flash Data + - .rodata + * - UHCI + - 5733 + - 680 + - 8 + - 34 + - 638 + - 4878 + - 175 + - 175 + +禁用 :ref:`CONFIG_UHCI_ISR_CACHE_SAFE` 时的资源消耗: + +.. list-table:: 资源消耗 + :widths: 10 10 10 10 10 10 10 10 10 10 + :header-rows: 1 + + * - Component Layer + - Total Size + - DIRAM + - .bss + - .data + - .text + - Flash Code + - .text + - Flash Data + - .rodata + * - UHCI + - 5479 + - 42 + - 8 + - 34 + - 0 + - 5262 + - 5262 + - 175 + - 175 + +关于性能 +^^^^^^^^ + +为了提升中断处理的实时响应能力, UHCI 驱动提供了 :ref:`CONFIG_UHCI_ISR_HANDLER_IN_IRAM` 选项。启用该选项后,中断处理程序将被放置在内部 RAM 中运行,从而减少了从 Flash 加载指令时可能出现的缓存丢失带来的延迟。 + +.. note:: + + 但是,中断处理程序调用的用户回调函数和用户上下文数据仍然可能位于 Flash 中,缓存缺失的问题还是会存在,这需要用户自己将回调函数和数据放入内部 RAM 中,比如使用 :c:macro:`IRAM_ATTR` 和 :c:macro:`DRAM_ATTR`。 + +线程安全 +^^^^^^^^^^^^^ + +驱动程序保证工厂函数 :cpp:func:`uhci_new_controller`、:cpp:func:`uhci_register_event_callbacks` 和 :cpp:func:`uhci_del_controller` 的线程安全。这意味着用户可以从不同的 RTOS 任务中调用它们,而无需额外的锁保护。 + +其他 Kconfig 选项 +^^^^^^^^^^^^^^^^^^^^^ + +- :ref:`CONFIG_UHCI_ENABLE_DEBUG_LOG` 选项允许强制启用 UHCI 驱动的所有调试日志,无论全局日志级别设置如何。启用此选项可以帮助开发人员在调试过程中获取更详细的日志信息,从而更容易定位和解决问题,但会增加固件二进制文件的大小。 + +应用示例 +-------------------- + +- :example:`peripherals/uart/uart_dma_ota` 演示了如何使用 UART DMA 以 1Mbps 波特率快速 OTA 更新芯片固件。 + +API 参考 +------------- + +.. include-build-file:: inc/uhci.inc +.. include-build-file:: inc/components/esp_driver_uart/include/driver/uhci_types.inc +.. include-build-file:: inc/components/hal/include/hal/uhci_types.inc + +.. [1] + 回调函数(例如 :cpp:member:`uhci_event_callbacks_t::on_tx_trans_done` 、:cpp:member:`uhci_event_callbacks_t::on_rx_trans_event`)及其调用的函数也应驻留在 IRAM 中,用户需要自行注意这一点。 diff --git a/examples/bluetooth/.build-test-rules.yml b/examples/bluetooth/.build-test-rules.yml index d0e397416cc2..3ce3428e92a6 100644 --- a/examples/bluetooth/.build-test-rules.yml +++ b/examples/bluetooth/.build-test-rules.yml @@ -235,12 +235,12 @@ examples/bluetooth/nimble/ble_multi_conn: examples/bluetooth/nimble/ble_pawr_adv: <<: *bt_default_depends enable: - - if: IDF_TARGET == "esp32c6" + - if: SOC_ESP_NIMBLE_CONTROLLER == 1 and IDF_TARGET != "esp32c2" examples/bluetooth/nimble/ble_pawr_adv_conn: <<: *bt_default_depends enable: - - if: IDF_TARGET == "esp32c6" + - if: SOC_ESP_NIMBLE_CONTROLLER == 1 and IDF_TARGET != "esp32c2" examples/bluetooth/nimble/ble_periodic_adv: <<: *bt_default_depends diff --git a/examples/bluetooth/ble_get_started/nimble/NimBLE_Beacon/main/main.c b/examples/bluetooth/ble_get_started/nimble/NimBLE_Beacon/main/main.c index 2903a67c02fc..d4c871e96492 100644 --- a/examples/bluetooth/ble_get_started/nimble/NimBLE_Beacon/main/main.c +++ b/examples/bluetooth/ble_get_started/nimble/NimBLE_Beacon/main/main.c @@ -78,12 +78,14 @@ void app_main(void) { return; } +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* GAP service initialization */ rc = gap_init(); if (rc != 0) { ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc); return; } +#endif /* NimBLE host configuration initialization */ nimble_host_config_init(); diff --git a/examples/bluetooth/ble_get_started/nimble/NimBLE_Connection/main/main.c b/examples/bluetooth/ble_get_started/nimble/NimBLE_Connection/main/main.c index b3fa8a9012e5..6fc6c414a015 100644 --- a/examples/bluetooth/ble_get_started/nimble/NimBLE_Connection/main/main.c +++ b/examples/bluetooth/ble_get_started/nimble/NimBLE_Connection/main/main.c @@ -82,12 +82,14 @@ void app_main(void) { return; } +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* GAP service initialization */ rc = gap_init(); if (rc != 0) { ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc); return; } +#endif /* NimBLE host configuration initialization */ nimble_host_config_init(); diff --git a/examples/bluetooth/ble_get_started/nimble/NimBLE_Connection/main/src/gap.c b/examples/bluetooth/ble_get_started/nimble/NimBLE_Connection/main/src/gap.c index 8788b66f267e..d2689f9230fa 100644 --- a/examples/bluetooth/ble_get_started/nimble/NimBLE_Connection/main/src/gap.c +++ b/examples/bluetooth/ble_get_started/nimble/NimBLE_Connection/main/src/gap.c @@ -251,6 +251,7 @@ int gap_init(void) { /* Local variables */ int rc = 0; + /* Initialize GAP service */ ble_svc_gap_init(); diff --git a/examples/bluetooth/ble_get_started/nimble/NimBLE_GATT_Server/main/main.c b/examples/bluetooth/ble_get_started/nimble/NimBLE_GATT_Server/main/main.c index aa804b209951..5283e2e7856e 100644 --- a/examples/bluetooth/ble_get_started/nimble/NimBLE_GATT_Server/main/main.c +++ b/examples/bluetooth/ble_get_started/nimble/NimBLE_GATT_Server/main/main.c @@ -109,12 +109,14 @@ void app_main(void) { return; } +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* GAP service initialization */ rc = gap_init(); if (rc != 0) { ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc); return; } +#endif /* GATT server initialization */ rc = gatt_svc_init(); diff --git a/examples/bluetooth/ble_get_started/nimble/NimBLE_Security/main/main.c b/examples/bluetooth/ble_get_started/nimble/NimBLE_Security/main/main.c index 26541056a6c7..dfe997eb080c 100644 --- a/examples/bluetooth/ble_get_started/nimble/NimBLE_Security/main/main.c +++ b/examples/bluetooth/ble_get_started/nimble/NimBLE_Security/main/main.c @@ -118,12 +118,14 @@ void app_main(void) { return; } +#if CONFIG_BT_NIMBLE_GAP_SERVICE /* GAP service initialization */ rc = gap_init(); if (rc != 0) { ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc); return; } +#endif /* GATT server initialization */ rc = gatt_svc_init(); diff --git a/examples/bluetooth/nimble/ble_cts/cts_cent/main/main.c b/examples/bluetooth/nimble/ble_cts/cts_cent/main/main.c index 0a99776f2cba..128b6afd3ac8 100644 --- a/examples/bluetooth/nimble/ble_cts/cts_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_cts/cts_cent/main/main.c @@ -44,6 +44,7 @@ void printtime(struct ble_svc_cts_curr_time ctime) { ESP_LOGI(tag, "fractions : %d\n", ctime.et_256.fractions_256); } +#if MYNEWT_VAL(BLE_GATTC) /** * Application callback. Called when the read of the cts current time * characteristic has completed. @@ -137,6 +138,7 @@ ble_cts_cent_on_disc_complete(const struct peer *peer, int status, void *arg) */ ble_cts_cent_read_time(peer); } +#endif /** * Initiates the GAP general discovery procedure. @@ -434,6 +436,7 @@ ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg) return 0; } #else +#if MYNEWT_VAL(BLE_GATTC) /* Perform service discovery */ rc = peer_disc_all(event->connect.conn_handle, ble_cts_cent_on_disc_complete, NULL); @@ -441,6 +444,7 @@ ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif #endif // BLE_GATT_CACHING_ASSOC_ENABLE #endif } else { @@ -485,6 +489,7 @@ ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg) return 0; } #else +#if MYNEWT_VAL(BLE_GATTC) /*** Go for service discovery after encryption has been successfully enabled ***/ rc = peer_disc_all(event->connect.conn_handle, ble_cts_cent_on_disc_complete, NULL); @@ -492,6 +497,7 @@ ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif #endif // BLE_GATT_CACHING_ASSOC_ENABLE #endif return 0; diff --git a/examples/bluetooth/nimble/ble_cts/cts_prph/main/main.c b/examples/bluetooth/nimble/ble_cts/cts_prph/main/main.c index 2da3a66ad69f..04ee0c3eadd2 100644 --- a/examples/bluetooth/nimble/ble_cts/cts_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_cts/cts_prph/main/main.c @@ -28,7 +28,6 @@ static uint8_t ext_adv_pattern_1[] = { static const char *tag = "NimBLE_CTS_PRPH"; static const char *device_name = "ble_cts_prph"; - static int ble_cts_prph_gap_event(struct ble_gap_event *event, void *arg); static uint8_t ble_cts_prph_addr_type; @@ -274,8 +273,6 @@ void ble_cts_prph_host_task(void *param) void app_main(void) { - int rc; - /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -302,12 +299,15 @@ void app_main(void) ble_hs_cfg.sm_sc = 1; ble_hs_cfg.sm_mitm = 1; +#if MYNEWT_VAL(BLE_GATTS) + int rc; rc = gatt_svr_init(); assert(rc == 0); /* Set the default device name */ rc = ble_svc_gap_device_name_set(device_name); assert(rc == 0); +#endif /* Start the task */ nimble_port_freertos_init(ble_cts_prph_host_task); diff --git a/examples/bluetooth/nimble/ble_dynamic_service/main/main.c b/examples/bluetooth/nimble/ble_dynamic_service/main/main.c index d99326f81d3e..02a7ed07271e 100644 --- a/examples/bluetooth/nimble/ble_dynamic_service/main/main.c +++ b/examples/bluetooth/nimble/ble_dynamic_service/main/main.c @@ -280,12 +280,14 @@ app_main(void) ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(); assert(rc == 0); /* Set the default device name. */ rc = ble_svc_gap_device_name_set("ble-dynamic-service"); assert(rc == 0); +#endif nimble_port_freertos_init(dynamic_service_host_task); diff --git a/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent/main/main.c b/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent/main/main.c index 1b3e77574d69..a141d0f2e0fd 100644 --- a/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_cent/main/main.c @@ -22,7 +22,10 @@ static struct km_peer kmp[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1] = {0}; static const char *tag = "ENC_ADV_DATA_CENT"; static int enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg); + +#if MYNEWT_VAL(BLE_GATTC) static int mtu_def = 512; +#endif void ble_store_config_init(void); @@ -37,6 +40,7 @@ enc_adv_data_find_peer(const uint8_t *peer_addr) return -1; } +#if MYNEWT_VAL(BLE_GATTC) static int enc_adv_data_set_km_exist(const uint8_t *peer_addr) { @@ -47,6 +51,7 @@ enc_adv_data_set_km_exist(const uint8_t *peer_addr) kmp[ind].key_material_exist = true; return 0; } +#endif static bool enc_adv_data_check_km_exist(const uint8_t *peer_addr) @@ -60,6 +65,7 @@ enc_adv_data_check_km_exist(const uint8_t *peer_addr) return kmp[ind].key_material_exist; } +#if MYNEWT_VAL(BLE_GATTC) /** * Application callback. Called when the read has completed. */ @@ -175,6 +181,7 @@ enc_adv_data_cent_on_disc_complete(const struct peer *peer, int status, void *ar enc_adv_data_cent_read(peer); } } +#endif /** * Initiates the GAP general discovery procedure. @@ -444,6 +451,7 @@ enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg) print_conn_desc(&desc); MODLOG_DFLT(INFO, ""); +#if MYNEWT_VAL(BLE_GATTC) rc = ble_att_set_preferred_mtu(mtu_def); if (rc != 0) { ESP_LOGE(tag, "Failed to set preferred MTU; rc = %d", rc); @@ -453,6 +461,7 @@ enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg) if (rc != 0) { ESP_LOGE(tag, "Failed to negotiate MTU; rc = %d", rc); } +#endif /* Remember peer. */ rc = peer_add(event->connect.conn_handle); @@ -511,12 +520,14 @@ enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg) assert(rc == 0); print_conn_desc(&desc); +#if MYNEWT_VAL(BLE_GATTC) /* Perform service discovery */ rc = peer_disc_all(event->enc_change.conn_handle, enc_adv_data_cent_on_disc_complete, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); } +#endif return 0; case BLE_GAP_EVENT_NOTIFY_RX: diff --git a/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph/main/main.c b/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph/main/main.c index e0c87902583b..78a3159eb40e 100644 --- a/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_enc_adv_data/enc_adv_data_prph/main/main.c @@ -413,15 +413,16 @@ app_main(void) ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID; #endif +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(); assert(rc == 0); /* Set the default device name. */ rc = ble_svc_gap_device_name_set("enc_adv_data_prph"); assert(rc == 0); +#endif /* Set the session key and initialization vector */ - rc = ble_svc_gap_device_key_material_set(km.session_key, km.iv); assert(rc == 0); diff --git a/examples/bluetooth/nimble/ble_htp/htp_cent/main/main.c b/examples/bluetooth/nimble/ble_htp/htp_cent/main/main.c index 2e47880916b8..281dadc61030 100644 --- a/examples/bluetooth/nimble/ble_htp/htp_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_htp/htp_cent/main/main.c @@ -20,6 +20,8 @@ static int ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg); void ble_store_config_init(void); static void ble_htp_cent_scan(void); + +#if MYNEWT_VAL(BLE_GATTC) /** * Application callback. Called when the attempt to subscribe to notifications * for the HTP intermediate temperature characteristic has completed. @@ -255,7 +257,7 @@ ble_htp_cent_on_disc_complete(const struct peer *peer, int status, void *arg) */ ble_htp_cent_read_write_subscribe(peer); } - +#endif /** * Initiates the GAP general discovery procedure. */ @@ -548,6 +550,7 @@ ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg) return 0; } #else +#if MYNEWT_VAL(BLE_GATTC) /* Perform service discovery */ rc = peer_disc_all(event->connect.conn_handle, ble_htp_cent_on_disc_complete, NULL); @@ -555,6 +558,7 @@ ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif #endif // BLE_GATT_CACHING_ASSOC_ENABLE #endif } else { @@ -599,6 +603,7 @@ ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg) return 0; } #else +#if MYNEWT_VAL(BLE_GATTC) /*** Go for service discovery after encryption has been successfully enabled ***/ rc = peer_disc_all(event->connect.conn_handle, ble_htp_cent_on_disc_complete, NULL); @@ -606,6 +611,7 @@ ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif #endif // BLE_GATT_CACHING_ASSOC_ENABLE #endif return 0; diff --git a/examples/bluetooth/nimble/ble_htp/htp_prph/main/main.c b/examples/bluetooth/nimble/ble_htp/htp_prph/main/main.c index 01bece43a40e..896c1d6fd4e2 100644 --- a/examples/bluetooth/nimble/ble_htp/htp_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_htp/htp_prph/main/main.c @@ -201,6 +201,8 @@ ble_htp_prph_tx_htp_reset(void) static void ble_htp_prph_tx(TimerHandle_t ev) { + +#if CONFIG_BT_NIMBLE_HTP_SERVICE int rc; float temp; @@ -222,6 +224,7 @@ ble_htp_prph_tx(TimerHandle_t ev) } else { MODLOG_DFLT(INFO, "Error in sending notification"); } +#endif ble_htp_prph_tx_htp_reset(); } @@ -259,7 +262,9 @@ ble_htp_prph_gap_event(struct ble_gap_event *event, void *arg) #endif ble_htp_prph_tx_htp_stop(); +#if CONFIG_BT_NIMBLE_HTP_SERVICE ble_svc_htp_on_disconnect(event->disconnect.conn.conn_handle); +#endif break; case BLE_GAP_EVENT_ADV_COMPLETE: @@ -276,7 +281,9 @@ ble_htp_prph_gap_event(struct ble_gap_event *event, void *arg) "val_handle=%d\n", event->subscribe.cur_notify, event->subscribe.attr_handle); +#if CONFIG_BT_NIMBLE_HTP_SERVICE ble_svc_htp_subscribe(event->subscribe.conn_handle, event->subscribe.attr_handle); +#endif if (event->subscribe.cur_notify) { ble_htp_prph_tx_htp_reset(); @@ -336,8 +343,6 @@ void ble_htp_prph_host_task(void *param) void app_main(void) { - int rc; - /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -368,12 +373,15 @@ void app_main(void) ble_htp_prph_tx_timer = xTimerCreate("ble_htp_prph_tx_timer", pdMS_TO_TICKS(1000), pdTRUE, (void *)0, ble_htp_prph_tx); +#if MYNEWT_VAL(BLE_GATTS) + int rc; rc = gatt_svr_init(); assert(rc == 0); /* Set the default device name */ rc = ble_svc_gap_device_name_set(device_name); assert(rc == 0); +#endif /* Start the task */ nimble_port_freertos_init(ble_htp_prph_host_task); diff --git a/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/main/main.c b/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/main/main.c index 010ea85a1541..641a2f43bc21 100644 --- a/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/main/main.c +++ b/examples/bluetooth/nimble/ble_l2cap_coc/coc_bleprph/main/main.c @@ -133,7 +133,6 @@ bleprph_advertise(void) { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; - const char *name; int rc; /** @@ -160,6 +159,7 @@ bleprph_advertise(void) fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + const char *name; name = ble_svc_gap_device_name(); fields.name = (uint8_t *)name; fields.name_len = strlen(name); diff --git a/examples/bluetooth/nimble/ble_multi_adv/main/main.c b/examples/bluetooth/nimble/ble_multi_adv/main/main.c index b8393aa806f2..03f93210fd9d 100644 --- a/examples/bluetooth/nimble/ble_multi_adv/main/main.c +++ b/examples/bluetooth/nimble/ble_multi_adv/main/main.c @@ -459,8 +459,6 @@ void ble_multi_adv_host_task(void *param) void app_main(void) { - int rc; - /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -495,6 +493,8 @@ app_main(void) ble_instance_cb[i].cb = NULL; } +#if MYNEWT_VAL(BLE_GATTS) + int rc; rc = gatt_svr_init(); assert(rc == 0); @@ -502,6 +502,7 @@ app_main(void) /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-multi-adv"); assert(rc == 0); +#endif #endif /* XXX Need to have template for store */ diff --git a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/main/main.c b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/main/main.c index 6acd00750c52..c324092aee35 100644 --- a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_cent/main/main.c @@ -461,13 +461,17 @@ app_main(void) rc = peer_init(BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM, BLE_PEER_MAX_NUM); assert(rc == 0); #endif - /* Set the default device name. We will act as both central and peripheral. */ - rc = ble_svc_gap_device_name_set("esp-ble-role-coex"); - assert(rc == 0); + +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(); assert(rc == 0); + /* Set the default device name. We will act as both central and peripheral. */ + rc = ble_svc_gap_device_name_set("esp-ble-role-coex"); + assert(rc == 0); +#endif + /* XXX Need to have template for store */ ble_store_config_init(); diff --git a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/main/main.c b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/main/main.c index 47f6461572ae..e4b0f4686ae1 100644 --- a/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_multi_conn/ble_multi_conn_prph/main/main.c @@ -287,12 +287,14 @@ app_main(void) ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(); assert(rc == 0); /* Set the default device name. */ rc = ble_svc_gap_device_name_set("esp-multi-conn"); assert(rc == 0); +#endif /* XXX Need to have template for store */ ble_store_config_init(); diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/README.md b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/README.md index 70d5c1bce2bb..f079a5986f92 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/README.md +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/README.md @@ -1,9 +1,5 @@ -| Supported Targets | ESP32-C6 | -| ----------------- | -------- | - - -# Important Note -*This example currently requires an external Bluetooth controller supporting PAwR functionality, as the ESP chips listed above do not have native controller support for PAwR features and under development phase* +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | # BLE Periodic Advertiser With Response (PAwR) Advertiser Example @@ -14,7 +10,7 @@ This example starts PAwR advertising with configurable subevents and response slots. -It uses external Bluetooth controller and NimBLE stack based BLE host. +It uses Bluetooth controller and NimBLE stack based BLE host. This example aims at understanding PAwR advertisement and related NimBLE APIs. diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c index a9539b543263..06f104f8d4c0 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c @@ -11,6 +11,7 @@ #include "host/ble_hs.h" #define BLE_PAWR_EVENT_INTERVAL (600) +#define BLE_PAWR_PERIODIC_EVENT_INTERVAL_MS (3000) #define BLE_PAWR_NUM_SUBEVTS (10) #define BLE_PAWR_SUB_INTERVAL (44) /*!< Interval between subevents (N * 1.25 ms) */ #define BLE_PAWR_RSP_SLOT_DELAY (20) /*!< The first response slot delay (N * 1.25 ms)*/ @@ -135,8 +136,8 @@ start_periodic_adv(void) /* configure periodic advertising */ memset(&pparams, 0, sizeof(pparams)); pparams.include_tx_power = 0; - pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(3000); - pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(3000); + pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(BLE_PAWR_PERIODIC_EVENT_INTERVAL_MS); + pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(BLE_PAWR_PERIODIC_EVENT_INTERVAL_MS); /* Configure the parameters of PAwR. */ pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS; pparams.subevent_interval = BLE_PAWR_SUB_INTERVAL; diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/tutorial/BLE_pawr_adv_walkthrough.md b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/tutorial/BLE_pawr_adv_walkthrough.md index 146d981b6e12..e609cbd24a97 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/tutorial/BLE_pawr_adv_walkthrough.md +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/tutorial/BLE_pawr_adv_walkthrough.md @@ -2,7 +2,7 @@ ## Introduction -This tutorial examines the BLE Periodic Advertisement with Responses (PAwR) example code for ESP32 chipsets with BLE 5.0+ support. The code demonstrates how to implement PAwR functionality using NimBLE APIs, which enables bidirectional communication between advertiser and scanner devices in a power-efficient manner. +This tutorial examines the BLE Periodic Advertisement with Responses (PAwR) example code for ESP32 chipsets with BLE 5.0+ support. The code demonstrates how to implement PAwR functionality using NimBLE APIs, which enables bidirectional communication between advertiser and scanner devices. ## Includes @@ -123,7 +123,7 @@ esp_err_t esp_nimble_init(void) The example defines several PAwR parameters ```c -#define BLE_PAWR_EVENT_INTERVAL (600) +#define BLE_PAWR_PERIODIC_EVENT_INTERVAL_MS (3000) #define BLE_PAWR_NUM_SUBEVTS (10) #define BLE_PAWR_SUB_INTERVAL (44) /*!< Interval between subevents (N * 1.25 ms) */ #define BLE_PAWR_RSP_SLOT_DELAY (20) /*!< The first response slot delay (N * 1.25 ms) */ @@ -142,22 +142,6 @@ These parameters control: - Data length for subevent payloads - -## Periodic Advertising Configuration -```c -memset(&pparams, 0, sizeof(pparams)); -pparams.include_tx_power = 0; -pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(3000); -pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(3000); -pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS; -pparams.subevent_interval = BLE_PAWR_SUB_INTERVAL; -pparams.response_slot_delay = BLE_PAWR_RSP_SLOT_DELAY; -pparams.response_slot_spacing = BLE_PAWR_RSP_SLOT_SPACING; -pparams.num_response_slots = BLE_PAWR_NUM_RSP_SLOTS; - -rc = ble_gap_periodic_adv_configure(instance, &pparams); -assert(rc == 0); -``` ## Key PAwR Parameters: - num_subevents: Number of subevents per periodic interval (10) @@ -226,8 +210,8 @@ start_periodic_adv(void) /* configure periodic advertising */ memset(&pparams, 0, sizeof(pparams)); pparams.include_tx_power = 0; - pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(3000); - pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(3000); + pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(BLE_PAWR_PERIODIC_EVENT_INTERVAL_MS); + pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(BLE_PAWR_PERIODIC_EVENT_INTERVAL_MS); /* Configure the parameters of PAwR. */ pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS; pparams.subevent_interval = BLE_PAWR_SUB_INTERVAL; @@ -312,9 +296,7 @@ This PAwR example demonstrates: 2. Bidirectional communication between advertiser and scanners -3. Efficient power usage through scheduled communication windows - -4. Use of extended advertising to announce PAwR capabilities +3. Use of extended advertising to announce PAwR capabilities The implementation shows how to: @@ -326,4 +308,4 @@ The implementation shows how to: - Manage the advertising lifecycle -PAwR is particularly useful for applications requiring periodic, bidirectional communication with multiple devices while maintaining low power consumption. \ No newline at end of file +PAwR is particularly useful for applications requiring periodic, bidirectional communication with multiple devices \ No newline at end of file diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/README.md b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/README.md index 6b3336727464..c19b074ceb12 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/README.md +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/README.md @@ -1,8 +1,5 @@ -| Supported Targets | ESP32-C6 | -| ----------------- | -------- | - -# Important Note -*This example currently requires an external Bluetooth controller supporting PAwR functionality, as the ESP chips listed above do not have native controller support for PAwR features and under development phase* +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | # BLE Periodic Advertiser With Response (PAwR) Sync Example @@ -20,6 +17,7 @@ To test this demo, any BLE advertiser supporting PAwR can be used.(check /exampl - Configurable synchronization parameters (skip factor, timeout) - Detailed reporting of PAwR advertisement data - Handling of synchronization loss events +- Configuration of PAwR response data Note : diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c index dc9d1b83e2f4..12b517af515f 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c @@ -13,7 +13,9 @@ #define TAG "NimBLE_BLE_PAwR" #define TARGET_NAME "Nimble_PAwR" +#define BLE_PAWR_RSP_DATA_IDX (2) #define BLE_PAWR_RSP_DATA_LEN (16) + static uint8_t sub_data_pattern[BLE_PAWR_RSP_DATA_LEN] = {0}; static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc); @@ -60,7 +62,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg) .request_event = event->periodic_report.event_counter, .request_subevent = event->periodic_report.subevent, .response_subevent = event->periodic_report.subevent, - .response_slot = 2, + .response_slot = BLE_PAWR_RSP_DATA_IDX, }; struct os_mbuf *data = os_msys_get_pkthdr(BLE_PAWR_RSP_DATA_LEN, 0); diff --git a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/tutorial/BLE_pawr_sync_walkthrough.md b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/tutorial/BLE_pawr_sync_walkthrough.md index fade095b1f2f..5543acaa7ed1 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/tutorial/BLE_pawr_sync_walkthrough.md +++ b/examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/tutorial/BLE_pawr_sync_walkthrough.md @@ -140,6 +140,7 @@ The example defines several key parameters: ```c #define TAG "NimBLE_BLE_PAwR" #define TARGET_NAME "Nimble_PAwR" +#define BLE_PAWR_RSP_DATA_IDX (2) #define BLE_PAWR_RSP_DATA_LEN (16) static uint8_t sub_data_pattern[BLE_PAWR_RSP_DATA_LEN] = {0}; ``` @@ -147,6 +148,8 @@ These parameters control: - Target advertiser name to sync with +- Response slot index to send response data at + - Response data length - Data pattern for responses @@ -232,7 +235,7 @@ case BLE_GAP_EVENT_PERIODIC_REPORT: .request_event = event->periodic_report.event_counter, .request_subevent = event->periodic_report.subevent, .response_subevent = event->periodic_report.subevent, - .response_slot = rsp_slot_idx + .response_slot = BLE_PAWR_RSP_DATA_IDX }; // Prepare response data @@ -245,9 +248,12 @@ case BLE_GAP_EVENT_PERIODIC_REPORT: event->periodic_report.sync_handle, ¶m, data); break; ``` + +By default the response data will be sent within the same subevent where the periodic advertising report is received. + ## Subevent Configuration -After sync establishment: +After sync establishment, sync to configurable subevents: ```c // Choose subevents to listen to @@ -256,6 +262,8 @@ int result = ble_gap_periodic_adv_sync_subev( event->periodic_sync.sync_handle, 0, sizeof(subevents), subevents); ``` +The subevents sync selection depends on the subevent number of the Periodic Advertising device. + ## Error Handling When sync is lost: ```c @@ -270,4 +278,4 @@ case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: ## Conclusion -This implementation demonstrates a complete PAwR synchronization solution, showcasing advertiser discovery via extended scanning, periodic sync establishment with configurable subevents (0-4), and efficient bidirectional communication through managed response slots. The robust architecture handles sync loss recovery while maintaining low-power operation, making it ideal for IoT applications requiring scheduled, bidirectional communication with multiple endpoints. The solution leverages BLE 5.0's PAwR features to optimize power efficiency and reliability in dense RF environments. \ No newline at end of file +This implementation demonstrates a complete PAwR synchronization solution, showcasing advertiser discovery via extended scanning, periodic sync establishment with configurable subevents (0-4), and efficient bidirectional communication through managed response slots. The robust architecture handles sync loss recovery, making it ideal for IoT applications requiring scheduled, bidirectional communication with multiple endpoints. \ No newline at end of file diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/README.md b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/README.md index 648c6b8b66c3..e0ac0a16363e 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/README.md +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/README.md @@ -1,8 +1,5 @@ -| Supported Targets | ESP32-C6 | -| ----------------- | -------- | - -# Important Note -*This example currently requires an external Bluetooth controller supporting PAwR functionality, as the ESP chips listed above do not have native controller support for PAwR features and under development phase* +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | # BLE Periodic Advertiser With Response (PAwR) Advertiser Connection Example diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/main/main.c b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/main/main.c index bf034cf1422d..b88e82538dd6 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/main/main.c +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/main/main.c @@ -10,7 +10,7 @@ #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" -#define BLE_PAWR_EVENT_INTERVAL (520) +#define BLE_PAWR_EVENT_PERIODIC_INTERVAL_MS (3000) #define BLE_PAWR_NUM_SUBEVTS (10) #define BLE_PAWR_SUB_INTERVAL (52) /*!< Interval between subevents (N * 1.25 ms) */ #define BLE_PAWR_RSP_SLOT_DELAY (5) /*!< The first response slot delay (N * 1.25 ms)*/ @@ -217,8 +217,8 @@ start_periodic_adv(void) /* configure periodic advertising */ memset(&pparams, 0, sizeof(pparams)); pparams.include_tx_power = 0; - pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(3000); - pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(3000); + pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(BLE_PAWR_EVENT_PERIODIC_INTERVAL_MS); + pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(BLE_PAWR_EVENT_PERIODIC_INTERVAL_MS); /* Configure the parameters of PAwR. */ pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS; pparams.subevent_interval = BLE_PAWR_SUB_INTERVAL; diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/tutorial/BLE_pawr_adv_conn_walkthrough.md b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/tutorial/BLE_pawr_adv_conn_walkthrough.md index 94d8f0d2b014..0cd708f4a390 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/tutorial/BLE_pawr_adv_conn_walkthrough.md +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_adv_conn/tutorial/BLE_pawr_adv_conn_walkthrough.md @@ -123,6 +123,7 @@ esp_err_t esp_nimble_init(void) ## PAwR Configuration ```c #define BLE_PAWR_EVENT_INTERVAL (520) +#define BLE_PAWR_EVENT_PERIODIC_INTERVAL_MS (3000) #define BLE_PAWR_NUM_SUBEVTS (10) #define BLE_PAWR_SUB_INTERVAL (52) #define BLE_PAWR_RSP_SLOT_DELAY (5) @@ -130,33 +131,123 @@ esp_err_t esp_nimble_init(void) #define BLE_PAWR_NUM_RSP_SLOTS (25) #define BLE_PAWR_SUB_DATA_LEN (20) ``` -These parameters configure PAwR interval, subevents, response slot timing, and payload length. +These parameters control: -## Periodic Advertising Configuration +- The interval between periodic advertising events +- Number of subevents per periodic interval + +- Timing of response slots + +- Data length for subevent payloads + +## Key PAwR Parameters: + +- num_subevents: Number of subevents per periodic interval (10) + +- subevent_interval: Time between subevents (44 × 1.25ms = 55ms) + +- response_slot_delay: First response slot delay (20 × 1.25ms = 25ms) + +- response_slot_spacing: Time between slots (32 × 0.125ms = 4ms) + +- num_response_slots: Number of response slots per subevent (5) + +## PAwR Advertisement + +The start_periodic_adv() function configures and starts PAwR: ```c -memset(&pparams, 0, sizeof(pparams)); -pparams.include_tx_power = 0; -pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(3000); -pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(3000); -pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS; -pparams.subevent_interval = BLE_PAWR_SUB_INTERVAL; -pparams.response_slot_delay = BLE_PAWR_RSP_SLOT_DELAY; -pparams.response_slot_spacing = BLE_PAWR_RSP_SLOT_SPACING; -pparams.num_response_slots = BLE_PAWR_NUM_RSP_SLOTS; +static void +start_periodic_adv(void) +{ + int rc; + uint8_t addr[6]; + struct ble_gap_periodic_adv_params pparams; + struct ble_gap_ext_adv_params params; + struct ble_hs_adv_fields adv_fields; + struct os_mbuf *data; + uint8_t instance = 0; + +#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) + struct ble_gap_periodic_adv_enable_params eparams; + memset(&eparams, 0, sizeof(eparams)); +#endif + + /* Get the local public address. */ + rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL); + assert (rc == 0); + + ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3], + addr[2], addr[1], addr[0]); + + /* For periodic we use instance with non-connectable advertising */ + memset (¶ms, 0, sizeof(params)); + params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + params.primary_phy = BLE_HCI_LE_PHY_CODED; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.sid = 0; + params.itvl_min = BLE_GAP_ADV_ITVL_MS(50); + params.itvl_max = BLE_GAP_ADV_ITVL_MS(50); + + rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, gap_event_cb, NULL); + assert (rc == 0); + + memset(&adv_fields, 0, sizeof(adv_fields)); + adv_fields.name = (const uint8_t *)"Nimble_PAwR_CONN"; + adv_fields.name_len = strlen((char *)adv_fields.name); + + /* mbuf chain will be increased if needed */ + data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0); + assert(data); + + rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert(rc == 0); + + /* configure periodic advertising */ + memset(&pparams, 0, sizeof(pparams)); + pparams.include_tx_power = 0; + pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(BLE_PAWR_EVENT_PERIODIC_INTERVAL_MS); + pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(BLE_PAWR_EVENT_PERIODIC_INTERVAL_MS); + /* Configure the parameters of PAwR. */ + pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS; + pparams.subevent_interval = BLE_PAWR_SUB_INTERVAL; + pparams.response_slot_delay = BLE_PAWR_RSP_SLOT_DELAY; + pparams.response_slot_spacing = BLE_PAWR_RSP_SLOT_SPACING; + pparams.num_response_slots = BLE_PAWR_NUM_RSP_SLOTS; + + rc = ble_gap_periodic_adv_configure(instance, &pparams); + assert(rc == 0); + + /* start periodic advertising */ +#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH) + eparams.include_adi = 1; + rc = ble_gap_periodic_adv_start(instance, &eparams); +#else + rc = ble_gap_periodic_adv_start(instance); +#endif + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, 0, 0); + assert (rc == 0); + + ESP_LOGI(TAG, "instance %u started (periodic)\n", instance); +} ``` +Key steps: -These values are passed to ble_gap_periodic_adv_configure() to start PAwR. +- Configure extended advertising parameters -## PAwR Advertisement -The start_periodic_adv() function: -- Configures extended advertising parameters -- Sets up periodic advertising using subevent and slot parameters -- Starts extended + periodic advertising +- Set up periodic advertising with subevent and response slot parameters + +- Start both periodic and extended advertising ## Need of Extended Advertisement in Periodic Advertisement -Extended advertisements contain synchronization info that lets scanners align with periodic advertising. This enables precise subevent-based communicati +Extended advertisements contain synchronization info that lets scanners align with periodic advertising. This enables precise subevent-based communication. ## GAP Event Callback @@ -175,7 +266,7 @@ case BLE_GAP_EVENT_DISCONNECT: ## Using ble_gap_connect_with_synced() -The API ble_gap_connect_with_synced() is a NimBLE API used by a PAwR Advertiser to initiate a BLE connection with a synced scanner. This allows the advertiser to transition from scheduled subevent-based communication to a higher-throughput, lower-latency connection with a specific scanner. +The API ble_gap_connect_with_synced() is a NimBLE API used by a PAwR Advertiser to initiate a BLE connection with a synced scanner as central role. This allows the advertiser to transition from scheduled subevent-based communication to a higher-throughput, lower-latency connection with a specific scanner. This is especially useful in use cases where on-demand, peer-to-peer data exchange is needed. ```c @@ -207,41 +298,6 @@ void pawr_host_task(void *param) nimble_port_freertos_deinit(); } ``` -## Parameter Configuration - -The below snippets represent the parameter configuration for extended and periodic advertisement. - -### For Extended Advertisement - -```c - params.own_addr_type = BLE_OWN_ADDR_RANDOM; //Own address type is set to Random - params.primary_phy = BLE_HCI_LE_PHY_1M; // Primary advertising PHY is set to 1M - params.secondary_phy = BLE_HCI_LE_PHY_2M; // Secondary advertising PHY is set to 2M - params.sid = 2; // Advertising set Id is assigned with value 2. -``` - -### For Periodic Advertisement - -```c - memset(&pparams, 0, sizeof(pparams)); - pparams.include_tx_power = 0; // Indicates that TX power is not included in advertising PDU - pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120); // Minimum advertising interval of 240ms - pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240); //Maximum advertising interval of 480ms -``` - -Periodic advertisement is started for a particular advertisement instance by calling the API `ble_gap_periodic_adv_start(instance)`. This function takes instance-id as an input parameter. It defines the hci command by initializing the command parameters which are represented in the following lines. - -```c - struct ble_hci_le_set_periodic_adv_enable_cp cmd; - cmd.enable = 0x01; - cmd.adv_handle = instance; -``` - -Extended advertising is invoked for a particular instance using the API call `ble_gap_ext_adv_start(instance, 0, 0)`.Instance-id, duration, and max_events are input parameters for this API call respectively. - -Duration represents the time for which the adverteiment will take place. Upon expiration, the advertising procedure ends, and the BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no expiration. - -max_events Number of advertising events that should be sent before advertising ends and a BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no limit. ## Conclusion @@ -252,5 +308,5 @@ This PAwR with connection example demonstrates: - Periodic advertising with subevents and response slots - Dynamic connection initiation based on scanner responses - Use of extended advertisement for synchronization -- Efficient, scalable, low-power bidirectional communication +- Efficient, scalable, bidirectional communication diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/README.md b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/README.md index 86cbb85598a0..789ac1ee5931 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/README.md +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/README.md @@ -1,8 +1,5 @@ -| Supported Targets | ESP32-C6 | -| ----------------- | -------- | - -## Important Note -*This example currently requires an external Bluetooth controller supporting PAwR functionality, as the ESP chips listed above do not have native controller support for PAwR features and under development phase* +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | # BLE Periodic Advertiser With Response (PAwR) Sync Connection Example diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/main/main.c b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/main/main.c index e43a975ce43e..539d7b540a66 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/main/main.c +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/main/main.c @@ -13,6 +13,7 @@ #define TAG "NimBLE_BLE_PAwR_CONN" #define TARGET_NAME "Nimble_PAwR_CONN" +#define BLE_PAWR_RSP_SLOT_INDEX (2) #define BLE_PAWR_RSP_DATA_LEN (10) static uint8_t sub_data_pattern[BLE_PAWR_RSP_DATA_LEN] = {0}; @@ -114,7 +115,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg) .request_event = event->periodic_report.event_counter, .request_subevent = event->periodic_report.subevent, .response_subevent = event->periodic_report.subevent, - .response_slot = 2, + .response_slot = BLE_PAWR_RSP_SLOT_INDEX, }; struct os_mbuf *data = os_msys_get_pkthdr(BLE_PAWR_RSP_DATA_LEN, 0); diff --git a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/tutorial/BLE_pawr_sync_conn_walkthrough.md b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/tutorial/BLE_pawr_sync_conn_walkthrough.md index 2a476e3eb6f0..1f608d67ec14 100644 --- a/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/tutorial/BLE_pawr_sync_conn_walkthrough.md +++ b/examples/bluetooth/nimble/ble_pawr_adv_conn/ble_pawr_sync_conn/tutorial/BLE_pawr_sync_conn_walkthrough.md @@ -114,19 +114,14 @@ esp_err_t esp_nimble_init(void) Configures a passive extended scan to detect periodic advertisers: ```c -static void start_scan(void) { - struct ble_gap_ext_disc_params d = { - .itvl = BLE_GAP_SCAN_ITVL_MS(600), // Scan every 600ms - .window = BLE_GAP_SCAN_ITVL_MS(300), // Listen for 300ms - .passive= 1 // Do not send scan requests - }; - // Start discovery; gap_event_cb handles each advertisement - ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0, - NULL, &d, gap_event_cb, NULL); -} -``` + memset(&disc_params, 0, sizeof(disc_params)); + disc_params.itvl = BLE_GAP_SCAN_ITVL_MS(600); + disc_params.window = BLE_GAP_SCAN_ITVL_MS(300); + disc_params.passive = 1; -- BLE_OWN_ADDR_PUBLIC: Use the device’s public address. + rc = ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0, NULL, &disc_params, + gap_event_cb, NULL); +``` - gap_event_cb: Processes discovery events (EXT_DISC) to find our target.` @@ -151,60 +146,54 @@ static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc) { ``` - disc->addr / sid: Address and Sync ID identify the PAwR train. -- ble_gap_periodic_adv_sync_create: Starts low-power sync to periodic events. +- ble_gap_periodic_adv_sync_create: Starts sync to periodic events. -## Sending Response Data +## Subevent Synchronization -Once synchronized, respond during periodic reports: +After sync establishment, sync to configurable subevents: ```c +// Choose subevents to listen to +uint8_t subevents[] = {0, 1, 2, 3, 4}; +int result = ble_gap_periodic_adv_sync_subev( + event->periodic_sync.sync_handle, 0, sizeof(subevents), subevents); +``` -case BLE_GAPCreate Periodic Sync - -When a periodic advertiser is found, request synchronization: - -static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc) { - struct ble_gap_periodic_sync_params p = { - .skip = 0, // Do not skip any events - .sync_timeout = 4000, // Give 4000ms to establish sync - .reports_disabled= 0, // Keep reports enabled -#if CONFIG_EXAMPLE_PERIODIC_ADV_ENH - .filter_duplicates = 1, // Only receive when data-id changes -#endif - }; - // Initiate sync; callback will receive PERIODIC_SYNC - return ble_gap_periodic_adv_sync_create( - &disc->addr, disc->sid, &p, - gap_event_cb, NULL); -} +The subevents sync selection depends on the subevent number of the Periodic Advertising device. -disc->addr / sid: Address and Sync ID identify the PAwR train. +## Sending Response Data -ble_gap_periodic_adv_sync_create: Starts low-power sync to periodic events. +Respond after receiving periodic reports: -_EVENT_PERIODIC_REPORT: { - struct ble_gap_periodic_adv_response_params r = { - .request_event = event->periodic_report.event_counter, - .request_subevent = event->periodic_report.subevent, - .response_subevent= event->periodic_report.subevent, - .response_slot = 2, // Always use slot 2 - }; - // Allocate buffer for response payload - struct os_mbuf *m = os_msys_get_pkthdr(BLE_PAWR_RSP_DATA_LEN, 0); - // First byte: subevent index - sub_data_pattern[0] = event->periodic_report.subevent; - // Next 6 bytes: our public address - ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, device_addr, NULL); - memcpy(&sub_data_pattern[1], device_addr, BLE_DEV_ADDR_LEN); - // Fill remaining bytes with slot index - sub_data_pattern[7] = r.response_slot; - os_mbuf_append(m, sub_data_pattern, BLE_PAWR_RSP_DATA_LEN); - // Send response data back to advertiser - ble_gap_periodic_adv_set_response_data( - event->periodic_report.sync_handle, - &r, m); - break; -} +```c +case BLE_GAP_EVENT_PERIODIC_REPORT: + ESP_LOGI(TAG, "[Periodic Adv Report] handle:%d, event_counter(%d), subevent(%d)", + event->periodic_report.sync_handle, + event->periodic_report.event_counter, + event->periodic_report.subevent); + + struct ble_gap_periodic_adv_response_params param = { + .request_event = event->periodic_report.event_counter, + .request_subevent = event->periodic_report.subevent, + .response_subevent = event->periodic_report.subevent, + .response_slot = BLE_PAWR_RSP_SLOT_INDEX, + }; + + struct os_mbuf *data = os_msys_get_pkthdr(BLE_PAWR_RSP_DATA_LEN, 0); + if (!data) { + ESP_LOGE(TAG, "No memory"); + return 0; + } + // create a special data for checking manually in ADV side + + sub_data_pattern[0] = event->periodic_report.subevent; + rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, device_addr, NULL); + sub_data_pattern[1] = param.response_slot; + memcpy(&sub_data_pattern[2],device_addr,BLE_DEV_ADDR_LEN); + + os_mbuf_append(data, sub_data_pattern, BLE_PAWR_RSP_DATA_LEN); + + rc = ble_gap_periodic_adv_set_response_data(event->periodic_report.sync_handle, ¶m, data); ``` - os_msys_get_pkthdr: Allocates memory for the response. @@ -263,7 +252,7 @@ This PAwR Sync + Conn example demonstrates: - Passive discovery of periodic advertisers. -- Low-power synchronization to scheduled subevents. +- Synchronization to scheduled subevents. - Slot-based responses with custom payload. diff --git a/examples/bluetooth/nimble/ble_phy/phy_cent/main/main.c b/examples/bluetooth/nimble/ble_phy/phy_cent/main/main.c index 09eb5f6cc615..cec4bcaa3a77 100644 --- a/examples/bluetooth/nimble/ble_phy/phy_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_phy/phy_cent/main/main.c @@ -24,6 +24,7 @@ static void blecent_scan(void); static uint8_t s_current_phy; void ble_store_config_init(void); +#if MYNEWT_VAL(BLE_GATTC) /** * Performs GATT operation against the specified peer: * 1. Reads the Supported LE PHY characteristic. @@ -167,6 +168,7 @@ blecent_on_disc_complete(const struct peer *peer, int status, void *arg) blecent_read(peer); } +#endif /* Set default LE PHY before establishing connection */ void set_default_le_phy(uint8_t tx_phys_mask, uint8_t rx_phys_mask) @@ -388,6 +390,7 @@ blecent_gap_event(struct ble_gap_event *event, void *arg) return 0; } +#if MYNEWT_VAL(BLE_GATTC) /* Perform service discovery. */ rc = peer_disc_all(event->connect.conn_handle, blecent_on_disc_complete, NULL); @@ -395,6 +398,7 @@ blecent_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif } else { /* Connection attempt failed; resume scanning. */ MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n", diff --git a/examples/bluetooth/nimble/ble_phy/phy_prph/main/main.c b/examples/bluetooth/nimble/ble_phy/phy_prph/main/main.c index b43e8f649fd6..4fce3bedb36f 100644 --- a/examples/bluetooth/nimble/ble_phy/phy_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_phy/phy_prph/main/main.c @@ -342,8 +342,10 @@ app_main(void) ble_hs_cfg.sm_their_key_dist = 1; #endif +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init_le_phy(); assert(rc == 0); +#endif /* Set the default device name. */ rc = ble_svc_gap_device_name_set("bleprph-phy"); diff --git a/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent/main/main.c b/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent/main/main.c index 0f6f9b309d58..108988328003 100644 --- a/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent/main/main.c +++ b/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_cent/main/main.c @@ -33,6 +33,7 @@ void ble_store_config_init(void); static void ble_prox_cent_scan(void); static int ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg); +#if MYNEWT_VAL(BLE_GATTC) static int ble_prox_cent_on_read(uint16_t conn_handle, const struct ble_gatt_error *error, @@ -172,6 +173,7 @@ ble_prox_cent_on_disc_complete(const struct peer *peer, int status, void *arg) */ ble_prox_cent_read_write_subscribe(peer); } +#endif /** * Initiates the GAP general discovery procedure. @@ -480,6 +482,7 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg) return 0; } #else +#if MYNEWT_VAL(BLE_GATTC) /* Perform service discovery */ rc = peer_disc_all(event->connect.conn_handle, ble_prox_cent_on_disc_complete, NULL); @@ -487,6 +490,7 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif #endif // BLE_GATT_CACHING_ASSOC_ENABLE #endif } else { @@ -547,6 +551,7 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg) return 0; } #else +#if MYNEWT_VAL(BLE_GATTC) /*** Go for service discovery after encryption has been successfully enabled ***/ rc = peer_disc_all(event->connect.conn_handle, ble_prox_cent_on_disc_complete, NULL); @@ -554,6 +559,7 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif #endif // BLE_GATT_CACHING_ASSOC_ENABLE #endif return 0; @@ -656,6 +662,7 @@ ble_prox_cent_path_loss_task(void *pvParameters) path_loss = 0; } +#if MYNEWT_VAL(BLE_GATTC) rc = ble_gattc_write_no_rsp_flat(i, conn_peer[i].val_handle, &path_loss, sizeof(path_loss)); if (rc != 0) { @@ -664,6 +671,7 @@ ble_prox_cent_path_loss_task(void *pvParameters) } else { MODLOG_DFLT(INFO, "Write to alert level characteristis done"); } +#endif } } } @@ -743,7 +751,6 @@ app_main(void) /* Initialize a task to keep checking path loss of the link */ ble_prox_cent_init(); - for (int i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) { disconn_peer[i].addr = NULL; disconn_peer[i].link_lost = true; diff --git a/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_prph/main/main.c b/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_prph/main/main.c index 7eb301cdb25d..336f80c72297 100644 --- a/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_prph/main/main.c +++ b/examples/bluetooth/nimble/ble_proximity_sensor/proximity_sensor_prph/main/main.c @@ -270,8 +270,6 @@ void ble_prox_prph_host_task(void *param) void app_main(void) { - int rc; - /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -286,9 +284,10 @@ void app_main(void) return; } +#if CONFIG_BT_NIMBLE_PROX_SERVICE /* Initialize a task to keep checking path loss of the link */ ble_svc_prox_init(); - +#endif /* Initialize the NimBLE host configuration */ ble_hs_cfg.sync_cb = ble_prox_prph_on_sync; ble_hs_cfg.reset_cb = ble_prox_prph_on_reset; @@ -301,6 +300,7 @@ void app_main(void) ble_hs_cfg.sm_sc = 1; ble_hs_cfg.sm_mitm = 1; + int rc; /* Set the default device name */ rc = ble_svc_gap_device_name_set(device_name); assert(rc == 0); diff --git a/examples/bluetooth/nimble/ble_spp/spp_client/main/main.c b/examples/bluetooth/nimble/ble_spp/spp_client/main/main.c index 4b479214c394..5b5fb206e5f2 100644 --- a/examples/bluetooth/nimble/ble_spp/spp_client/main/main.c +++ b/examples/bluetooth/nimble/ble_spp/spp_client/main/main.c @@ -26,6 +26,7 @@ uint16_t attribute_handle[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1]; static void ble_spp_client_scan(void); static ble_addr_t connected_addr[CONFIG_BT_NIMBLE_MAX_CONNECTIONS + 1]; +#if MYNEWT_VAL(BLE_GATTC) static void ble_spp_client_write_subscribe(const struct peer *peer) { uint8_t value[2]; @@ -90,7 +91,6 @@ ble_spp_client_set_handle(const struct peer *peer) value, sizeof(value), NULL, NULL); } - /** * Called when service discovery of the specified peer has completed. */ @@ -119,6 +119,7 @@ ble_spp_client_on_disc_complete(const struct peer *peer, int status, void *arg) ble_spp_client_scan(); #endif } +#endif /** * Initiates the GAP general discovery procedure. @@ -306,6 +307,7 @@ ble_spp_client_gap_event(struct ble_gap_event *event, void *arg) return 0; } +#if MYNEWT_VAL(BLE_GATTC) /* Perform service discovery. */ rc = peer_disc_all(event->connect.conn_handle, ble_spp_client_on_disc_complete, NULL); @@ -313,6 +315,7 @@ ble_spp_client_gap_event(struct ble_gap_event *event, void *arg) MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc); return 0; } +#endif } else { /* Connection attempt failed; resume scanning. */ MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n", @@ -421,12 +424,14 @@ void ble_client_uart_task(void *pvParameters) uart_read_bytes(UART_NUM_0, temp, event.size, portMAX_DELAY); for ( i = 0; i <= CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { if (attribute_handle[i] != 0) { +#if MYNEWT_VAL(BLE_GATTC) rc = ble_gattc_write_flat(i, attribute_handle[i], temp, event.size, NULL, NULL); if (rc == 0) { ESP_LOGI(tag, "Write in uart task success!"); } else { ESP_LOGI(tag, "Error in writing characteristic rc=%d", rc); } +#endif vTaskDelay(10); } } diff --git a/examples/bluetooth/nimble/ble_spp/spp_server/main/main.c b/examples/bluetooth/nimble/ble_spp/spp_server/main/main.c index b61cc2247d90..b79521be6956 100644 --- a/examples/bluetooth/nimble/ble_spp/spp_server/main/main.c +++ b/examples/bluetooth/nimble/ble_spp/spp_server/main/main.c @@ -63,7 +63,6 @@ ble_spp_server_advertise(void) { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; - const char *name; int rc; /** @@ -90,6 +89,7 @@ ble_spp_server_advertise(void) fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + const char *name; name = ble_svc_gap_device_name(); fields.name = (uint8_t *)name; fields.name_len = strlen(name); @@ -412,8 +412,6 @@ static void ble_spp_uart_init(void) void app_main(void) { - int rc; - /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -459,6 +457,8 @@ app_main(void) ble_hs_cfg.sm_their_key_dist = 1; #endif +#if MYNEWT_VAL(BLE_GATTS) + int rc; /* Register custom service */ rc = gatt_svr_init(); assert(rc == 0); @@ -466,6 +466,7 @@ app_main(void) /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-ble-spp-svr"); assert(rc == 0); +#endif /* XXX Need to have template for store */ ble_store_config_init(); diff --git a/examples/bluetooth/nimble/blecent/sdkconfig.ci.esp32c3eco7 b/examples/bluetooth/nimble/blecent/sdkconfig.ci.esp32c3eco7 index bfdd97290d25..6cf7e827068e 100644 --- a/examples/bluetooth/nimble/blecent/sdkconfig.ci.esp32c3eco7 +++ b/examples/bluetooth/nimble/blecent/sdkconfig.ci.esp32c3eco7 @@ -10,3 +10,7 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n CONFIG_BTDM_CTRL_MODE_BTDM=n CONFIG_BT_BLUEDROID_ENABLED=n CONFIG_BT_NIMBLE_ENABLED=y + +# Test Config +CONFIG_EXAMPLE_USE_CI_ADDRESS=y +CONFIG_EXAMPLE_PEER_ADDR="${CI_PIPELINE_ID}" diff --git a/examples/bluetooth/nimble/blecsc/main/main.c b/examples/bluetooth/nimble/blecsc/main/main.c index 3b69a930dc17..73c30906d96c 100644 --- a/examples/bluetooth/nimble/blecsc/main/main.c +++ b/examples/bluetooth/nimble/blecsc/main/main.c @@ -324,12 +324,14 @@ app_main(void) rc = ble_npl_callout_reset(&blecsc_measure_timer, portTICK_PERIOD_MS * 100); assert(rc == 0); +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(&csc_measurement_state); assert(rc == 0); /* Set the default device name */ rc = ble_svc_gap_device_name_set(device_name); assert(rc == 0); +#endif nimble_port_freertos_init(blecsc_host_task); diff --git a/examples/bluetooth/nimble/blehr/main/main.c b/examples/bluetooth/nimble/blehr/main/main.c index b9c7ea991f91..94b7b495fd52 100644 --- a/examples/bluetooth/nimble/blehr/main/main.c +++ b/examples/bluetooth/nimble/blehr/main/main.c @@ -293,12 +293,14 @@ void app_main(void) /* name, period/time, auto reload, timer ID, callback */ blehr_tx_timer = xTimerCreate("blehr_tx_timer", pdMS_TO_TICKS(1000), pdTRUE, (void *)0, blehr_tx_hrate); +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(); assert(rc == 0); /* Set the default device name */ rc = ble_svc_gap_device_name_set(device_name); assert(rc == 0); +#endif /* Start the task */ nimble_port_freertos_init(blehr_host_task); diff --git a/examples/bluetooth/nimble/bleprph_host_only/main/main.c b/examples/bluetooth/nimble/bleprph_host_only/main/main.c index 756863565a90..a03b3157ead7 100644 --- a/examples/bluetooth/nimble/bleprph_host_only/main/main.c +++ b/examples/bluetooth/nimble/bleprph_host_only/main/main.c @@ -131,7 +131,6 @@ bleprph_advertise(void) { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; - const char *name; int rc; /** @@ -158,6 +157,7 @@ bleprph_advertise(void) fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + const char *name; name = ble_svc_gap_device_name(); fields.name = (uint8_t *)name; fields.name_len = strlen(name); @@ -528,12 +528,14 @@ app_main(void) ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID; #endif +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(); assert(rc == 0); /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-bleprph"); assert(rc == 0); +#endif /* XXX Need to have template for store */ ble_store_config_init(); diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c b/examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c index bd2af71daa53..4d31b9d3086b 100644 --- a/examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c @@ -311,7 +311,6 @@ bleprph_advertise(void) { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; - const char *name; int rc; /** @@ -338,6 +337,7 @@ bleprph_advertise(void) fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + const char *name; name = ble_svc_gap_device_name(); fields.name = (uint8_t *)name; fields.name_len = strlen(name); @@ -525,8 +525,6 @@ void bleprph_host_task(void *param) void app_main(void) { - int rc; - /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -550,12 +548,15 @@ app_main(void) ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; +#if MYNEWT_VAL(BLE_GATTS) + int rc; rc = gatt_svr_init(); assert(rc == 0); /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-bleprph"); assert(rc == 0); +#endif /* XXX Need to have template for store */ ble_store_config_init(); diff --git a/examples/bluetooth/nimble/power_save/main/main.c b/examples/bluetooth/nimble/power_save/main/main.c index e25bc52b9d63..330b803ce016 100644 --- a/examples/bluetooth/nimble/power_save/main/main.c +++ b/examples/bluetooth/nimble/power_save/main/main.c @@ -167,7 +167,6 @@ bleprph_advertise(void) { struct ble_gap_adv_params adv_params; struct ble_hs_adv_fields fields; - const char *name; int rc; /** @@ -194,6 +193,7 @@ bleprph_advertise(void) fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + const char *name; name = ble_svc_gap_device_name(); fields.name = (uint8_t *)name; fields.name_len = strlen(name); @@ -620,6 +620,7 @@ app_main(void) ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID; #endif +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(); assert(rc == 0); @@ -627,6 +628,7 @@ app_main(void) /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-bleprph"); assert(rc == 0); +#endif #endif /* XXX Need to have template for store */ diff --git a/examples/bluetooth/nimble/power_save/sdkconfig.ci.esp32c3eco7 b/examples/bluetooth/nimble/power_save/sdkconfig.ci.esp32c3eco7 index 2e432310113c..f170f0a35d12 100644 --- a/examples/bluetooth/nimble/power_save/sdkconfig.ci.esp32c3eco7 +++ b/examples/bluetooth/nimble/power_save/sdkconfig.ci.esp32c3eco7 @@ -12,3 +12,7 @@ CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP=y # Enable power down of MAC and baseband in light sleep mode CONFIG_ESP_PHY_MAC_BB_PD=y + +# Test Config +CONFIG_EXAMPLE_USE_CI_ADDRESS=y +CONFIG_EXAMPLE_CI_ADDRESS_OFFSET="${CI_PIPELINE_ID}" diff --git a/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/main.c b/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/main.c index 49d1007a94a8..1177a9fb0ea9 100644 --- a/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/main.c +++ b/examples/bluetooth/nimble/throughput_app/blecent_throughput/main/main.c @@ -945,6 +945,7 @@ app_main(void) rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); assert(rc == 0); #endif + /* Set the default device name. */ rc = ble_svc_gap_device_name_set("gattc-throughput"); assert(rc == 0); diff --git a/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/main.c b/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/main.c index 5ccfd6291459..e15042207fe3 100644 --- a/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/main.c +++ b/examples/bluetooth/nimble/throughput_app/bleprph_throughput/main/main.c @@ -26,10 +26,10 @@ static uint8_t ext_adv_pattern[] = { }; static uint8_t s_current_phy; -#else -static const char *device_name = "nimble_prph"; #endif +static const char *device_name = "nimble_prph"; + #define NOTIFY_THROUGHPUT_PAYLOAD 495 #define MIN_REQUIRED_MBUF 2 /* Assuming payload of 500Bytes and each mbuf can take 292Bytes. */ #define PREFERRED_MTU_VALUE 512 @@ -509,10 +509,10 @@ void app_main(void) /* Initialize Notify Task */ xTaskCreate(notify_task, "notify_task", 4096, NULL, 10, NULL); +#if MYNEWT_VAL(BLE_GATTS) rc = gatt_svr_init(); assert(rc == 0); -#if !(CONFIG_EXAMPLE_EXTENDED_ADV) /* Set the default device name */ rc = ble_svc_gap_device_name_set(device_name); assert(rc == 0); diff --git a/examples/network/sta2eth/main/idf_component.yml b/examples/network/sta2eth/main/idf_component.yml index 21db5940d057..9a80fa243f47 100644 --- a/examples/network/sta2eth/main/idf_component.yml +++ b/examples/network/sta2eth/main/idf_component.yml @@ -1,7 +1,7 @@ ## IDF Component Manager Manifest File dependencies: espressif/esp_tinyusb: - version: "^1.3.0" + version: "^2.0.0" rules: - if: "idf_version >=4.4" - if: "target in [esp32s2, esp32s3]" diff --git a/examples/network/sta2eth/main/usb_ncm_iface.c b/examples/network/sta2eth/main/usb_ncm_iface.c index 6f8234977a8a..18111db747f2 100644 --- a/examples/network/sta2eth/main/usb_ncm_iface.c +++ b/examples/network/sta2eth/main/usb_ncm_iface.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -12,6 +12,7 @@ #include "esp_netif.h" #include "esp_event.h" #include "tinyusb.h" +#include "tinyusb_default_config.h" #include "tinyusb_net.h" #include "wired_iface.h" #include "dhcpserver/dhcpserver_options.h" @@ -44,9 +45,7 @@ void mac_spoof(mac_spoof_direction_t direction, uint8_t *buffer, uint16_t len, u esp_err_t wired_bridge_init(wired_rx_cb_t rx_cb, wired_free_cb_t free_cb) { - const tinyusb_config_t tusb_cfg = { - .external_phy = false, - }; + const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); tinyusb_net_config_t net_config = { @@ -56,7 +55,7 @@ esp_err_t wired_bridge_init(wired_rx_cb_t rx_cb, wired_free_cb_t free_cb) esp_read_mac(net_config.mac_addr, ESP_MAC_WIFI_STA); - esp_err_t ret = tinyusb_net_init(TINYUSB_USBDEV_0, &net_config); + esp_err_t ret = tinyusb_net_init(&net_config); if (ret != ESP_OK) { ESP_LOGE(TAG, "USB net init but not connect wifi"); return ret; @@ -113,9 +112,7 @@ static esp_err_t netif_recv_callback(void *buffer, uint16_t len, void *ctx) */ esp_err_t wired_netif_init(void) { - const tinyusb_config_t tusb_cfg = { - .external_phy = false, - }; + const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); const tinyusb_net_config_t net_config = { @@ -125,7 +122,7 @@ esp_err_t wired_netif_init(void) .on_recv_callback = netif_recv_callback, }; - esp_err_t ret = tinyusb_net_init(TINYUSB_USBDEV_0, &net_config); + esp_err_t ret = tinyusb_net_init(&net_config); if (ret != ESP_OK) { ESP_LOGE(TAG, "Cannot initialize USB Net device"); return ret; diff --git a/examples/network/sta2eth/sdkconfig.defaults.esp32s3 b/examples/network/sta2eth/sdkconfig.defaults.esp32s3 index e0c0938990ec..f357f9133428 100644 --- a/examples/network/sta2eth/sdkconfig.defaults.esp32s3 +++ b/examples/network/sta2eth/sdkconfig.defaults.esp32s3 @@ -1,11 +1,4 @@ # ESP32S3 has USB-OTG, let's prefer virtual Ethernet (USB-NCM device) CONFIG_EXAMPLE_WIRED_INTERFACE_IS_USB=y CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET=n - -# TinyUSB needs to be initialized and run from one core -# that's why we pin the task to CPU0 and init tusb in the task -# on dual core devices (ESP32S3) -CONFIG_TINYUSB_TASK_AFFINITY_CPU0=y -CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=y - CONFIG_TINYUSB_NET_MODE_NCM=y diff --git a/examples/openthread/ot_br/README.md b/examples/openthread/ot_br/README.md index 871aa68ac700..29d13ae446a8 100644 --- a/examples/openthread/ot_br/README.md +++ b/examples/openthread/ot_br/README.md @@ -56,11 +56,11 @@ In order to run the example on single SoC which supports both Wi-Fi and Thread, Two ways are provided to setup the Thread Border Router in this example: - Auto Start -Enable `OPENTHREAD_BR_AUTO_START`, configure the `CONFIG_EXAMPLE_WIFI_SSID` and `CONFIG_EXAMPLE_WIFI_PASSWORD` with your access point's ssid and psk. +Enable `OPENTHREAD_BORDER_ROUTER_AUTO_START` and `OPENTHREAD_NETWORK_AUTO_START`, configure the `CONFIG_EXAMPLE_WIFI_SSID` and `CONFIG_EXAMPLE_WIFI_PASSWORD` with your access point's ssid and psk. The device will connect to Wi-Fi and form a Thread network automatically after bootup. - Manual mode -Disable `OPENTHREAD_BR_AUTO_START` and enable `OPENTHREAD_CLI_ESP_EXTENSION`. `wifi` command will be added for connecting the device to the Wi-Fi network. +Disable `OPENTHREAD_BORDER_ROUTER_AUTO_START` and `OPENTHREAD_NETWORK_AUTO_START` and enable `OPENTHREAD_CLI_ESP_EXTENSION`. `wifi` command will be added for connecting the device to the Wi-Fi network. If the `CONFIG_EXAMPLE_CONNECT_ETHERNET` option is enabled, the device will connect to `Ethernet`, form a Thread network and act as a Ethernet based Thread Border Router. @@ -71,14 +71,14 @@ Build the project and flash it to the board, then run monitor tool to view seria ``` idf.py -p PORT build flash monitor ``` -If the `OPENTHREAD_BR_AUTO_START` option is enabled, The device will be connected to the configured Wi-Fi and Thread network automatically then act as the border router. +If the `OPENTHREAD_NETWORK_AUTO_START` option is enabled, The device will be connected to the configured Wi-Fi and Thread network automatically then act as the border router. Otherwise, you need to manually configure the networks with CLI commands. `wifi` command can be used to configure the Wi-Fi network. ```bash -> wifi +esp32s3> ot wifi --wifi parameter--- connect -s : wifi ssid @@ -96,7 +96,7 @@ Done To join a Wi-Fi network, please use the `wifi connect` command: ```bash -> wifi connect -s threadcertAP -p threadcertAP +esp32s3> ot wifi connect -s threadcertAP -p threadcertAP ssid: threadcertAP psk: threadcertAP I (11331) wifi:wifi driver task: 3ffd06e4, prio:23, stack:6656, core=0 @@ -117,7 +117,7 @@ Done To get the state of the Wi-Fi network: ```bash -> wifi state +esp32s3> ot wifi state connected Done ``` @@ -165,7 +165,7 @@ For mobile devices, the route table rules will be automatically configured after Now in the Thread end device, check the IP addresses: ``` -> ipaddr +esp32h2> ot ipaddr fde6:75ff:def4:3bc3:9e9e:3ef:4245:28b5 fdde:ad00:beef:0:0:ff:fe00:c402 fdde:ad00:beef:0:ad4a:9a9a:3cd6:e423 @@ -192,13 +192,13 @@ The newly introduced service registration protocol([SRP](https://datatracker.iet Now we'll publish the service `my-service._test._udp` with hostname `test0` and port 12345 ``` -> srp client host name test0 +esp32h2> ot srp client host name test0 Done -> srp client host address fde6:75ff:def4:3bc3:9e9e:3ef:4245:28b5 +esp32h2> ot srp client host address fde6:75ff:def4:3bc3:9e9e:3ef:4245:28b5 Done -> srp client service add my-service _test._udp 12345 +esp32h2> ot srp client service add my-service _test._udp 12345 Done -> srp client autostart enable +esp32h2> ot srp client autostart enable Done ``` @@ -233,7 +233,7 @@ Then get the border router's OMR prefix global unicast address(or ML-EID), and c On the border router: ``` -> ipaddr +esp32s3> ot ipaddr fdde:ad00:beef:0:0:ff:fe00:fc10 fd9b:347f:93f7:1:1003:8f00:bcc1:3038 fdde:ad00:beef:0:0:ff:fe00:fc00 @@ -245,19 +245,19 @@ Done On the Thread end device: ``` -> dns config fd9b:347f:93f7:1:1003:8f00:bcc1:3038 +esp32h2> ot dns config fd9b:347f:93f7:1:1003:8f00:bcc1:3038 (or -> dns config fdde:ad00:beef:0:f891:287:866:776) +esp32h2> ot dns config fdde:ad00:beef:0:f891:287:866:776) Done ``` Now the service published on the Host can be discovered on the Thread end device. ``` -> dns resolve FA001208.default.service.arpa. +esp32h2> ot dns resolve FA001208.default.service.arpa. DNS response for FA001208.default.service.arpa. - fdde:ad00:beef:cafe:b939:26be:7516:b87e TTL:120 Done -> dns browse _test._udp.default.service.arpa. +esp32h2> ot dns browse _test._udp.default.service.arpa. DNS browse response for _test._udp.default.service.arpa. testhost Port:5683, Priority:0, Weight:0, TTL:120 @@ -266,7 +266,7 @@ testhost TXT:[test=31, dn=616162626262] TTL:120 Done -> dns service testhost _test._udp.default.service.arpa. +esp32h2> ot dns service testhost _test._udp.default.service.arpa. DNS service resolution response for testhost for service _test._udp.default.service.arpa. Port:5683, Priority:0, Weight:0, TTL:120 Host:FA001208.default.service.arpa. diff --git a/examples/openthread/ot_br/main/Kconfig.projbuild b/examples/openthread/ot_br/main/Kconfig.projbuild index 45e682cb925e..ca427f349845 100644 --- a/examples/openthread/ot_br/main/Kconfig.projbuild +++ b/examples/openthread/ot_br/main/Kconfig.projbuild @@ -1,13 +1,5 @@ menu "OpenThread Border Router Example" - config OPENTHREAD_BR_AUTO_START - bool 'Enable the automatic start mode in Thread Border Router.' - default n - help - If enabled, The Thread Border Router will connect to Wi-Fi with pre-configured - SSID and PSK, and then form a Thread network automatically. Otherwise, user need - to configure Wi-Fi and Thread manually. - config OPENTHREAD_SUPPORT_HW_RESET_RCP bool 'Enable hardware RCP resetting' default n @@ -20,33 +12,4 @@ menu "OpenThread Border Router Example" depends on OPENTHREAD_SUPPORT_HW_RESET_RCP default 7 - menu "External coexist wire type and pin config" - config EXTERNAL_COEX_WIRE_TYPE - int "The wire_type of external coexist" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE - default 3 - range 0 3 - help - Select wire_type for external coexist, the wire_type define in external_coex_wire_t. - - config EXTERNAL_COEX_REQUEST_PIN - int "The number of external coexist request pin" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 0) - default 0 - - config EXTERNAL_COEX_GRANT_PIN - int "The number of external coexist grant pin" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 1) - default 1 - - config EXTERNAL_COEX_PRIORITY_PIN - int "The number of external coexist priority pin" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 2) - default 2 - - config EXTERNAL_COEX_TX_LINE_PIN - int "The number of external coexist tx_line pin" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE = 3) - default 3 - endmenu # External coexist wire type and pin config endmenu diff --git a/examples/openthread/ot_br/main/esp_ot_br.c b/examples/openthread/ot_br/main/esp_ot_br.c index ffcfd3915791..7cfcca642bb7 100644 --- a/examples/openthread/ot_br/main/esp_ot_br.c +++ b/examples/openthread/ot_br/main/esp_ot_br.c @@ -17,50 +17,31 @@ #include "sdkconfig.h" #include "esp_check.h" +#include "esp_coexist.h" #include "esp_err.h" #include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_openthread.h" -#include "esp_openthread_border_router.h" -#include "esp_openthread_cli.h" #include "esp_openthread_lock.h" #include "esp_openthread_netif_glue.h" +#include "esp_openthread_spinel.h" #include "esp_openthread_types.h" #if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION #include "esp_ot_cli_extension.h" #endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION #include "esp_ot_config.h" -#include "esp_ot_wifi_cmd.h" #include "esp_vfs_dev.h" #include "esp_vfs_eventfd.h" -#include "esp_wifi.h" #include "mdns.h" #include "nvs_flash.h" -#include "protocol_examples_common.h" -#include "driver/gpio.h" -#include "driver/uart.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "hal/uart_types.h" -#include "openthread/error.h" -#include "openthread/logging.h" -#include "openthread/tasklet.h" +#include "ot_examples_br.h" +#include "ot_examples_common.h" #if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE #include "ot_led_strip.h" #endif -#if CONFIG_OPENTHREAD_BR_AUTO_START -#include "example_common_private.h" -#include "protocol_examples_common.h" -#endif - -#if !CONFIG_OPENTHREAD_BR_AUTO_START && CONFIG_EXAMPLE_CONNECT_ETHERNET -// TZ-1109: Add a menchanism for connecting ETH manually. -#error Currently we do not support a manual way to connect ETH, if you want to use ETH, please enable OPENTHREAD_BR_AUTO_START. -#endif - #define TAG "esp_ot_br" #if CONFIG_OPENTHREAD_SUPPORT_HW_RESET_RCP @@ -83,116 +64,6 @@ static void rcp_failure_hardware_reset_handler(void) } #endif -#if CONFIG_EXTERNAL_COEX_ENABLE -static void ot_br_external_coexist_init(void) -{ - esp_external_coex_gpio_set_t gpio_pin = ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG(); - esp_external_coex_set_work_mode(EXTERNAL_COEX_LEADER_ROLE); - ESP_ERROR_CHECK(esp_enable_extern_coex_gpio_pin(CONFIG_EXTERNAL_COEX_WIRE_TYPE, gpio_pin)); -} -#endif /* CONFIG_EXTERNAL_COEX_ENABLE */ - -static void ot_task_worker(void *aContext) -{ - esp_openthread_platform_config_t config = { - .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), - .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), - .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), - }; - - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); - esp_netif_t *openthread_netif = esp_netif_new(&cfg); - assert(openthread_netif != NULL); - - // Initialize the OpenThread stack - ESP_ERROR_CHECK(esp_openthread_init(&config)); - ESP_ERROR_CHECK(esp_netif_attach(openthread_netif, esp_openthread_netif_glue_init(&config))); - esp_openthread_lock_acquire(portMAX_DELAY); -#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC - // The OpenThread log level directly matches ESP log level - (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); -#endif // CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC -#if CONFIG_OPENTHREAD_CLI - esp_openthread_cli_init(); -#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION - esp_cli_custom_command_init(); -#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION - esp_openthread_cli_create_task(); -#endif // CONFIG_OPENTHREAD_CLI - esp_openthread_lock_release(); - - // Run the main loop - esp_openthread_launch_mainloop(); - - // Clean up - esp_openthread_netif_glue_deinit(); - esp_netif_destroy(openthread_netif); - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - -void ot_br_init(void *ctx) -{ -#if CONFIG_OPENTHREAD_CLI_WIFI - ESP_ERROR_CHECK(esp_ot_wifi_config_init()); -#endif -#if CONFIG_OPENTHREAD_BR_AUTO_START -#if CONFIG_EXAMPLE_CONNECT_WIFI || CONFIG_EXAMPLE_CONNECT_ETHERNET - bool wifi_or_ethernet_connected = false; -#else -#error No backbone netif! -#endif -#if CONFIG_EXAMPLE_CONNECT_WIFI - char wifi_ssid[32] = ""; - char wifi_password[64] = ""; - if (esp_ot_wifi_config_get_ssid(wifi_ssid) == ESP_OK) { - ESP_LOGI(TAG, "use the Wi-Fi config from NVS"); - esp_ot_wifi_config_get_password(wifi_password); - } else { - ESP_LOGI(TAG, "use the Wi-Fi config from Kconfig"); - strcpy(wifi_ssid, CONFIG_EXAMPLE_WIFI_SSID); - strcpy(wifi_password, CONFIG_EXAMPLE_WIFI_PASSWORD); - } - if (esp_ot_wifi_connect(wifi_ssid, wifi_password) == ESP_OK) { - wifi_or_ethernet_connected = true; - } else { - ESP_LOGE(TAG, "Fail to connect to Wi-Fi, please try again manually"); - } -#endif -#if CONFIG_EXAMPLE_CONNECT_ETHERNET - ESP_ERROR_CHECK(example_ethernet_connect()); - wifi_or_ethernet_connected = true; -#endif -#endif // CONFIG_OPENTHREAD_BR_AUTO_START - -#if CONFIG_EXTERNAL_COEX_ENABLE - ot_br_external_coexist_init(); -#endif // CONFIG_EXTERNAL_COEX_ENABLE - ESP_ERROR_CHECK(mdns_init()); - ESP_ERROR_CHECK(mdns_hostname_set("esp-ot-br")); - - esp_openthread_lock_acquire(portMAX_DELAY); -#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE - ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance())); -#endif -#if CONFIG_OPENTHREAD_BR_AUTO_START - if (wifi_or_ethernet_connected) { - esp_openthread_set_backbone_netif(get_example_netif()); - ESP_ERROR_CHECK(esp_openthread_border_router_init()); -#if CONFIG_EXAMPLE_CONNECT_WIFI - esp_ot_wifi_border_router_init_flag_set(true); -#endif - otOperationalDatasetTlvs dataset; - otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); - ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL)); - } else { - ESP_LOGE(TAG, "Auto-start mode failed, please try to start manually"); - } -#endif // CONFIG_OPENTHREAD_BR_AUTO_START - esp_openthread_lock_release(); - vTaskDelete(NULL); -} - void app_main(void) { // Used eventfds: @@ -218,9 +89,41 @@ void app_main(void) ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(mdns_init()); + ESP_ERROR_CHECK(mdns_hostname_set("esp-ot-br")); #if CONFIG_OPENTHREAD_SUPPORT_HW_RESET_RCP esp_openthread_register_rcp_failure_handler(rcp_failure_hardware_reset_handler); #endif - xTaskCreate(ot_task_worker, "ot_br_main", 8192, xTaskGetCurrentTaskHandle(), 5, NULL); - xTaskCreate(ot_br_init, "ot_br_init", 6144, NULL, 4, NULL); + +#if CONFIG_OPENTHREAD_CLI + ot_console_start(); +#endif + +#if CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE + ot_external_coexist_init(); +#endif + + static esp_openthread_config_t config = { + .netif_config = ESP_NETIF_DEFAULT_OPENTHREAD(), + .platform_config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }, + }; + + ESP_ERROR_CHECK(esp_openthread_start(&config)); + esp_netif_set_default_netif(esp_openthread_get_netif()); +#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION + esp_cli_custom_command_init(); +#endif +#if CONFIG_OPENTHREAD_BORDER_ROUTER_AUTO_START + ESP_ERROR_CHECK(esp_openthread_border_router_start()); +#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE && CONFIG_SOC_IEEE802154_SUPPORTED + ESP_ERROR_CHECK(esp_coex_wifi_i154_enable()); +#endif +#endif +#if CONFIG_OPENTHREAD_NETWORK_AUTO_START + ot_network_auto_start(); +#endif } diff --git a/examples/openthread/ot_br/main/esp_ot_config.h b/examples/openthread/ot_br/main/esp_ot_config.h index b7372f49439c..a1670b0c189c 100644 --- a/examples/openthread/ot_br/main/esp_ot_config.h +++ b/examples/openthread/ot_br/main/esp_ot_config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -68,39 +68,10 @@ } #endif // CONFIG_OPENTHREAD_RADIO_SPINEL_UART OR CONFIG_OPENTHREAD_RADIO_SPINEL_SPI -#if CONFIG_IDF_TARGET_ESP32C2 && CONFIG_XTAL_FREQ_26 -#define HOST_BAUD_RATE 74880 -#else -#define HOST_BAUD_RATE 115200 -#endif - -#if CONFIG_OPENTHREAD_CONSOLE_TYPE_UART -#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ - .host_uart_config = { \ - .port = 0, \ - .uart_config = \ - { \ - .baud_rate = HOST_BAUD_RATE, \ - .data_bits = UART_DATA_8_BITS, \ - .parity = UART_PARITY_DISABLE, \ - .stop_bits = UART_STOP_BITS_1, \ - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ - .rx_flow_ctrl_thresh = 0, \ - .source_clk = UART_SCLK_DEFAULT, \ - }, \ - .rx_pin = UART_PIN_NO_CHANGE, \ - .tx_pin = UART_PIN_NO_CHANGE, \ - }, \ - } -#elif CONFIG_OPENTHREAD_CONSOLE_TYPE_USB_SERIAL_JTAG #define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB, \ - .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), \ + .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ } -#endif #define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ { \ @@ -108,33 +79,3 @@ .netif_queue_size = 10, \ .task_queue_size = 10, \ } - -#if CONFIG_EXTERNAL_COEX_ENABLE -#if CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_1 -#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ - { \ - .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ - } -#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_2 -#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ - { \ - .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ - .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ - } -#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_3 -#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ - { \ - .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ - .priority = CONFIG_EXTERNAL_COEX_PRIORITY_PIN, \ - .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ - } -#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_4 -#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ - { \ - .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ - .priority = CONFIG_EXTERNAL_COEX_PRIORITY_PIN, \ - .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ - .tx_line = CONFIG_EXTERNAL_COEX_TX_LINE_PIN, \ - } -#endif -#endif // CONFIG_EXTERNAL_COEX_ENABLE diff --git a/examples/openthread/ot_br/main/idf_component.yml b/examples/openthread/ot_br/main/idf_component.yml index dff9291048a8..b00b26c7bb34 100644 --- a/examples/openthread/ot_br/main/idf_component.yml +++ b/examples/openthread/ot_br/main/idf_component.yml @@ -1,7 +1,7 @@ ## IDF Component Manager Manifest File dependencies: espressif/esp_ot_cli_extension: - version: "~1.3.0" + version: "~1.4.0" espressif/mdns: "^1.0.3" ## Required IDF version idf: @@ -10,3 +10,7 @@ dependencies: path: ${IDF_PATH}/examples/common_components/protocol_examples_common ot_led: path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_led + ot_examples_br: + path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_examples_br + ot_examples_common: + path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_examples_common diff --git a/examples/openthread/ot_br/partitions.csv b/examples/openthread/ot_br/partitions.csv index 46c7857278a7..376458ad8b8a 100644 --- a/examples/openthread/ot_br/partitions.csv +++ b/examples/openthread/ot_br/partitions.csv @@ -2,4 +2,4 @@ # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 1800K, +factory, app, factory, 0x10000, 1900K, diff --git a/examples/openthread/ot_br/sdkconfig.ci.ext_coex b/examples/openthread/ot_br/sdkconfig.ci.ext_coex index 10e03f180efd..aa3956957560 100644 --- a/examples/openthread/ot_br/sdkconfig.ci.ext_coex +++ b/examples/openthread/ot_br/sdkconfig.ci.ext_coex @@ -1,2 +1,2 @@ -CONFIG_EXTERNAL_COEX_ENABLE=y +CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE=y CONFIG_ESP_COEX_SW_COEXIST_ENABLE=n diff --git a/examples/openthread/ot_br/sdkconfig.ci.spinel_trel_ext_coex b/examples/openthread/ot_br/sdkconfig.ci.spinel_trel_ext_coex index 571881d7f6cc..8fd4485992b1 100644 --- a/examples/openthread/ot_br/sdkconfig.ci.spinel_trel_ext_coex +++ b/examples/openthread/ot_br/sdkconfig.ci.spinel_trel_ext_coex @@ -1,2 +1,2 @@ CONFIG_OPENTHREAD_RADIO_TREL=y -CONFIG_EXTERNAL_COEX_ENABLE=y +CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE=y diff --git a/examples/openthread/ot_br/sdkconfig.defaults b/examples/openthread/ot_br/sdkconfig.defaults index 2b17031c2bfb..7fe886d91f3f 100644 --- a/examples/openthread/ot_br/sdkconfig.defaults +++ b/examples/openthread/ot_br/sdkconfig.defaults @@ -25,6 +25,8 @@ CONFIG_MBEDTLS_ECJPAKE_C=y CONFIG_OPENTHREAD_ENABLED=y CONFIG_OPENTHREAD_BORDER_ROUTER=y CONFIG_OPENTHREAD_RADIO_SPINEL_UART=y +CONFIG_OPENTHREAD_TASK_SIZE=8192 +CONFIG_OPENTHREAD_CONSOLE_ENABLE=n # end of OpenThread # @@ -54,4 +56,5 @@ CONFIG_EXAMPLE_CONNECT_THREAD=n # ESP System Settings # CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144 # end of ESP System Settings diff --git a/examples/openthread/ot_br/sdkconfig.defaults.esp32p4 b/examples/openthread/ot_br/sdkconfig.defaults.esp32p4 index 0bf7855bcc00..3b161864c7f3 100644 --- a/examples/openthread/ot_br/sdkconfig.defaults.esp32p4 +++ b/examples/openthread/ot_br/sdkconfig.defaults.esp32p4 @@ -1,5 +1,6 @@ # Enable BR auto start for Ethernet builds -CONFIG_OPENTHREAD_BR_AUTO_START=y +CONFIG_OPENTHREAD_NETWORK_AUTO_START=y +CONFIG_OPENTHREAD_BORDER_ROUTER_AUTO_START=y # Enable PPP support as a workaround to ensure LWIP thread-lib compatibility for Ethernet builds CONFIG_LWIP_PPP_SUPPORT=y diff --git a/examples/openthread/ot_ci_function.py b/examples/openthread/ot_ci_function.py index 6b8e9567f56b..0c438e9f601c 100644 --- a/examples/openthread/ot_ci_function.py +++ b/examples/openthread/ot_ci_function.py @@ -168,7 +168,7 @@ def getDataset(dut: IdfDut) -> str: def init_thread(dut: IdfDut) -> None: - dut.expect('>', timeout=10) + dut.expect('OpenThread attached to netif', timeout=10) wait(dut, 3) reset_thread(dut) @@ -176,7 +176,6 @@ def init_thread(dut: IdfDut) -> None: def reset_thread(dut: IdfDut) -> None: execute_command(dut, 'factoryreset') dut.expect('OpenThread attached to netif', timeout=20) - dut.expect('>', timeout=10) wait(dut, 3) clean_buffer(dut) @@ -636,9 +635,9 @@ def get_nat64prefix(br: IdfDut) -> str: return str(nat64prefix) -def execute_command(dut: IdfDut, command: str) -> None: +def execute_command(dut: IdfDut, command: str, prefix: str = 'ot ') -> None: clean_buffer(dut) - dut.write(command) + dut.write(prefix + command) def get_ouput_string(dut: IdfDut, command: str, wait_time: int) -> str: diff --git a/examples/openthread/ot_cli/README.md b/examples/openthread/ot_cli/README.md index 853527695918..75bd3de76a29 100644 --- a/examples/openthread/ot_cli/README.md +++ b/examples/openthread/ot_cli/README.md @@ -37,7 +37,7 @@ Now you'll get an OpenThread command line shell. The `help` command will print all of the supported commands. ```bash -> help +esp32h2> ot help I(7058) OPENTHREAD:[INFO]-CLI-----: execute command: help bbr bufferinfo @@ -71,51 +71,51 @@ To run this example, at least two ESP32-H2 boards flashed with this ot_cli examp On the first device, run the following commands: ```bash -> factoryreset +esp32h2> ot factoryreset ... # the device will reboot -> dataset init new +esp32h2> ot dataset init new Done -> dataset commit active +esp32h2> ot dataset commit active Done -> ifconfig up +esp32h2> ot ifconfig up Done -> thread start +esp32h2> ot thread start Done # After some seconds -> state +esp32h2> ot state leader Done ``` Now the first device has formed a Thread network as a leader. Get some information which will be used in next steps: ```bash -> ipaddr +esp32h2> ot ipaddr fdde:ad00:beef:0:0:ff:fe00:fc00 fdde:ad00:beef:0:0:ff:fe00:8000 fdde:ad00:beef:0:a7c6:6311:9c8c:271b fe80:0:0:0:5c27:a723:7115:c8f8 # Get the Active Dataset -> dataset active -x +esp32h2> ot dataset active -x 0e080000000000010000000300001835060004001fffe00208fe7bb701f5f1125d0708fd75cbde7c6647bd0510b3914792d44f45b6c7d76eb9306eec94030f4f70656e5468726561642d35383332010258320410e35c581af5029b054fc904a24c2b27700c0402a0fff8 ``` On the second device, set the active dataset from leader, and start Thread interface: ```bash -> factoryreset +esp32h2> ot factoryreset ... # the device will reboot -> dataset set active 0e080000000000010000000300001835060004001fffe00208fe7bb701f5f1125d0708fd75cbde7c6647bd0510b3914792d44f45b6c7d76eb9306eec94030f4f70656e5468726561642d35383332010258320410e35c581af5029b054fc904a24c2b27700c0402a0fff8 -> ifconfig up +esp32h2> ot dataset set active 0e080000000000010000000300001835060004001fffe00208fe7bb701f5f1125d0708fd75cbde7c6647bd0510b3914792d44f45b6c7d76eb9306eec94030f4f70656e5468726561642d35383332010258320410e35c581af5029b054fc904a24c2b27700c0402a0fff8 +esp32h2> ot ifconfig up Done -> thread start +esp32h2> ot thread start Done # After some seconds -> state +esp32h2> ot state router # child is also a valid state Done ``` diff --git a/examples/openthread/ot_cli/main/Kconfig.projbuild b/examples/openthread/ot_cli/main/Kconfig.projbuild deleted file mode 100644 index 23aba5e8ceb0..000000000000 --- a/examples/openthread/ot_cli/main/Kconfig.projbuild +++ /dev/null @@ -1,9 +0,0 @@ -menu "OpenThread CLI Example" - - config OPENTHREAD_AUTO_START - bool 'Enable the automatic start mode.' - default n - help - If enabled, the Openthread Device will create or connect to thread network with pre-configured - network parameters automatically. Otherwise, user need to configure Thread via CLI command manually. -endmenu diff --git a/examples/openthread/ot_cli/main/esp_ot_cli.c b/examples/openthread/ot_cli/main/esp_ot_cli.c index 96a8d1e22ccd..ee83bcc22de7 100644 --- a/examples/openthread/ot_cli/main/esp_ot_cli.c +++ b/examples/openthread/ot_cli/main/esp_ot_cli.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -23,21 +23,13 @@ #include "esp_netif.h" #include "esp_netif_types.h" #include "esp_openthread.h" -#include "esp_openthread_cli.h" #include "esp_openthread_lock.h" #include "esp_openthread_netif_glue.h" #include "esp_openthread_types.h" #include "esp_ot_config.h" #include "esp_vfs_eventfd.h" -#include "driver/uart.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "hal/uart_types.h" #include "nvs_flash.h" -#include "openthread/cli.h" -#include "openthread/instance.h" -#include "openthread/logging.h" -#include "openthread/tasklet.h" +#include "ot_examples_common.h" #if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE #include "ot_led_strip.h" @@ -49,68 +41,6 @@ #define TAG "ot_esp_cli" -static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) -{ - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); - esp_netif_t *netif = esp_netif_new(&cfg); - assert(netif != NULL); - ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config))); - - return netif; -} - -static void ot_task_worker(void *aContext) -{ - esp_openthread_platform_config_t config = { - .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), - .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), - .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), - }; - - // Initialize the OpenThread stack - ESP_ERROR_CHECK(esp_openthread_init(&config)); - -#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE - ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance())); -#endif - -#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC - // The OpenThread log level directly matches ESP log level - (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); -#endif - // Initialize the OpenThread cli -#if CONFIG_OPENTHREAD_CLI - esp_openthread_cli_init(); -#endif - - esp_netif_t *openthread_netif; - // Initialize the esp_netif bindings - openthread_netif = init_openthread_netif(&config); - esp_netif_set_default_netif(openthread_netif); - -#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION - esp_cli_custom_command_init(); -#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION - - // Run the main loop -#if CONFIG_OPENTHREAD_CLI - esp_openthread_cli_create_task(); -#endif -#if CONFIG_OPENTHREAD_AUTO_START - otOperationalDatasetTlvs dataset; - otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); - ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL)); -#endif - esp_openthread_launch_mainloop(); - - // Clean up - esp_openthread_netif_glue_deinit(); - esp_netif_destroy(openthread_netif); - - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - void app_main(void) { // Used eventfds: @@ -125,5 +55,25 @@ void app_main(void) ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); - xTaskCreate(ot_task_worker, "ot_cli_main", 10240, xTaskGetCurrentTaskHandle(), 5, NULL); + +#if CONFIG_OPENTHREAD_CLI + ot_console_start(); +#endif + + static esp_openthread_config_t config = { + .netif_config = ESP_NETIF_DEFAULT_OPENTHREAD(), + .platform_config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }, + }; + + ESP_ERROR_CHECK(esp_openthread_start(&config)); +#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION + esp_cli_custom_command_init(); +#endif +#if CONFIG_OPENTHREAD_NETWORK_AUTO_START + ot_network_auto_start(); +#endif } diff --git a/examples/openthread/ot_cli/main/esp_ot_config.h b/examples/openthread/ot_cli/main/esp_ot_config.h index 0248a39af187..cd509e9e1707 100644 --- a/examples/openthread/ot_cli/main/esp_ot_config.h +++ b/examples/openthread/ot_cli/main/esp_ot_config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -44,33 +44,10 @@ } #endif -#if CONFIG_OPENTHREAD_CONSOLE_TYPE_UART -#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ - .host_uart_config = { \ - .port = 0, \ - .uart_config = \ - { \ - .baud_rate = 115200, \ - .data_bits = UART_DATA_8_BITS, \ - .parity = UART_PARITY_DISABLE, \ - .stop_bits = UART_STOP_BITS_1, \ - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ - .rx_flow_ctrl_thresh = 0, \ - .source_clk = UART_SCLK_DEFAULT, \ - }, \ - .rx_pin = UART_PIN_NO_CHANGE, \ - .tx_pin = UART_PIN_NO_CHANGE, \ - }, \ - } -#elif CONFIG_OPENTHREAD_CONSOLE_TYPE_USB_SERIAL_JTAG #define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB, \ - .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), \ + .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ } -#endif #define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ { \ diff --git a/examples/openthread/ot_cli/main/idf_component.yml b/examples/openthread/ot_cli/main/idf_component.yml index 06b75f0c81d6..079266490f06 100644 --- a/examples/openthread/ot_cli/main/idf_component.yml +++ b/examples/openthread/ot_cli/main/idf_component.yml @@ -1,8 +1,10 @@ ## IDF Component Manager Manifest File dependencies: espressif/esp_ot_cli_extension: - version: "~1.2.0" + version: "~1.4.0" idf: version: ">=4.1.0" ot_led: path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_led + ot_examples_common: + path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_examples_common diff --git a/examples/openthread/ot_cli/sdkconfig.ci.ext_coex b/examples/openthread/ot_cli/sdkconfig.ci.ext_coex index 55c9b2374fee..6b04e74e9d4d 100644 --- a/examples/openthread/ot_cli/sdkconfig.ci.ext_coex +++ b/examples/openthread/ot_cli/sdkconfig.ci.ext_coex @@ -1 +1 @@ -CONFIG_EXTERNAL_COEX_ENABLE=y +CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE=y diff --git a/examples/openthread/ot_cli/sdkconfig.defaults b/examples/openthread/ot_cli/sdkconfig.defaults index 7b724c1c89d1..f8fd9ed374ce 100644 --- a/examples/openthread/ot_cli/sdkconfig.defaults +++ b/examples/openthread/ot_cli/sdkconfig.defaults @@ -23,6 +23,8 @@ CONFIG_MBEDTLS_ECJPAKE_C=y CONFIG_OPENTHREAD_ENABLED=y CONFIG_OPENTHREAD_BORDER_ROUTER=n CONFIG_OPENTHREAD_DNS64_CLIENT=y +CONFIG_OPENTHREAD_TASK_SIZE=10240 +CONFIG_OPENTHREAD_CONSOLE_ENABLE=n # end of OpenThread # @@ -33,3 +35,9 @@ CONFIG_LWIP_IPV6_NUM_ADDRESSES=8 CONFIG_LWIP_MULTICAST_PING=y CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM=y # end of lwIP + +# +# ESP System Settings +# +CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144 +# end of ESP System Settings diff --git a/examples/openthread/ot_common_components/ot_examples_br/CMakeLists.txt b/examples/openthread/ot_common_components/ot_examples_br/CMakeLists.txt new file mode 100644 index 000000000000..fbda672d96de --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_br/CMakeLists.txt @@ -0,0 +1,9 @@ +set(srcs "") + +if(CONFIG_OPENTHREAD_BORDER_ROUTER_AUTO_START) + list(APPEND srcs "ot_examples_br.c") +endif() + +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "include" + PRIV_REQUIRES openthread protocol_examples_common) diff --git a/examples/openthread/ot_common_components/ot_examples_br/Kconfig.projbuild b/examples/openthread/ot_common_components/ot_examples_br/Kconfig.projbuild new file mode 100644 index 000000000000..2835110a1f53 --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_br/Kconfig.projbuild @@ -0,0 +1,12 @@ +menu "OpenThread Border Router Config" + depends on OPENTHREAD_BORDER_ROUTER + + config OPENTHREAD_BORDER_ROUTER_AUTO_START + depends on OPENTHREAD_BORDER_ROUTER + bool 'Enable the border router auto start' + default n + help + If enabled, the program will automatically connect to the backbone network and + initialize the border router at startup. + +endmenu diff --git a/examples/openthread/ot_common_components/ot_examples_br/include/ot_examples_br.h b/examples/openthread/ot_common_components/ot_examples_br/include/ot_examples_br.h new file mode 100644 index 000000000000..6851bfdbb744 --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_br/include/ot_examples_br.h @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Command Line Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "esp_err.h" +#include "esp_openthread.h" + +/** + * @brief Start the border router features of OpenThread. + * + * @note Calling this function will make the device connect to the Wi-Fi or Ethernet, + * and initialize the border router feature. + * + * @return + * - ESP_OK on success. + * - ESP_FAIL on failure. + * + */ +esp_err_t esp_openthread_border_router_start(void); + +/** + * @brief Stop the border router features of OpenThread. + * + * @note Calling this function will make the device deinitialize the border router feature. + * + */ +void esp_openthread_border_router_stop(void); diff --git a/examples/openthread/ot_common_components/ot_examples_br/ot_examples_br.c b/examples/openthread/ot_common_components/ot_examples_br/ot_examples_br.c new file mode 100644 index 000000000000..77ef1d1900af --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_br/ot_examples_br.c @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Command Line Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "ot_examples_br.h" +#include "esp_check.h" +#include "esp_err.h" +#include "esp_openthread.h" +#include "esp_openthread_lock.h" +#include "esp_openthread_border_router.h" +#include "protocol_examples_common.h" + +#define TAG "ot_examples_br" + +#if CONFIG_OPENTHREAD_CLI_WIFI +#error "CONFIG_OPENTHREAD_CLI_WIFI conflicts with the border router auto-initialization feature" +#endif + +static bool s_border_router_started = false; + +static void ot_br_init(void *ctx) +{ + ESP_ERROR_CHECK(example_connect()); + esp_openthread_lock_acquire(portMAX_DELAY); + esp_openthread_set_backbone_netif(get_example_netif()); + ESP_ERROR_CHECK(esp_openthread_border_router_init()); + esp_openthread_lock_release(); + s_border_router_started = true; + vTaskDelete(NULL); +} + +esp_err_t esp_openthread_border_router_start(void) +{ + return (xTaskCreate(ot_br_init, "ot_br_init", 6144, NULL, 4, NULL) == pdPASS) ? ESP_OK : ESP_FAIL; +} + +void esp_openthread_border_router_stop(void) +{ + if (s_border_router_started) { + esp_openthread_lock_acquire(portMAX_DELAY); + ESP_ERROR_CHECK(esp_openthread_border_router_deinit()); + esp_openthread_lock_release(); + s_border_router_started =false; + } +} diff --git a/examples/openthread/ot_common_components/ot_examples_common/CMakeLists.txt b/examples/openthread/ot_common_components/ot_examples_common/CMakeLists.txt new file mode 100644 index 000000000000..321063eb9489 --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_common/CMakeLists.txt @@ -0,0 +1,19 @@ +set(srcs "") + +if(CONFIG_OPENTHREAD_FTD OR CONFIG_OPENTHREAD_MTD) + list(APPEND srcs "ot_network.c") +endif() + +if(CONFIG_OPENTHREAD_CLI) + list(APPEND srcs "ot_console.c") +endif() + +if(CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE) + list(APPEND srcs "ot_external_coexist.c") +endif() + +idf_component_register( + SRCS "${srcs}" + INCLUDE_DIRS "include" + PRIV_REQUIRES console cmd_system esp_coex openthread +) diff --git a/examples/openthread/ot_common_components/ot_examples_common/Kconfig.projbuild b/examples/openthread/ot_common_components/ot_examples_common/Kconfig.projbuild new file mode 100644 index 000000000000..94e38cf6c103 --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_common/Kconfig.projbuild @@ -0,0 +1,67 @@ +menu "Config for OpenThread Examples" + depends on OPENTHREAD_ENABLED + + config OPENTHREAD_NETWORK_AUTO_START + bool 'Enable the automatic start mode of Thread network.' + depends on OPENTHREAD_FTD || OPENTHREAD_MTD + default n + help + If enabled, the Openthread Device will create or connect to Thread network with pre-configured + network parameters automatically. Otherwise, user need to configure Thread via CLI command manually. + + menu "External coexist wire type and pin config" + depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE + + choice EXTERNAL_COEX_WORK_MODE + prompt "The work mode of external coexist" + default EXTERNAL_COEX_WORK_MODE_FOLLOWER if SOC_IEEE802154_SUPPORTED + default EXTERNAL_COEX_WORK_MODE_LEADER if !SOC_IEEE802154_SUPPORTED && SOC_WIFI_SUPPORTED + help + Select work mode for external coexist, the work mode defined in esp_extern_coex_work_mode_t. + + config EXTERNAL_COEX_WORK_MODE_LEADER + bool "Leader mode" + help + Select this to set the external coexistence work mode to leader mode. + + config EXTERNAL_COEX_WORK_MODE_FOLLOWER + bool "Follower mode" + help + Select this to set the external coexistence work mode to follower mode. + + config EXTERNAL_COEX_WORK_MODE_UNKNOWN + bool "Unknown mode" + help + Select this to set the external coexistence work mode to unknown mode. + endchoice + + config EXTERNAL_COEX_WIRE_TYPE + int "The wire_type of external coexist" + depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE + default 3 + range 0 3 + help + Select wire_type for external coexist, the wire_type define in external_coex_wire_t. + + config EXTERNAL_COEX_REQUEST_PIN + int "The number of external coexist request pin" + depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 0) + default 0 + + config EXTERNAL_COEX_GRANT_PIN + int "The number of external coexist grant pin" + depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 1) + default 1 + + config EXTERNAL_COEX_PRIORITY_PIN + int "The number of external coexist priority pin" + depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 2) + default 2 + + config EXTERNAL_COEX_TX_LINE_PIN + int "The number of external coexist tx_line pin" + depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE = 3) + default 3 + endmenu # External coexist wire type and pin config + +endmenu diff --git a/examples/openthread/ot_common_components/ot_examples_common/idf_component.yml b/examples/openthread/ot_common_components/ot_examples_common/idf_component.yml new file mode 100644 index 000000000000..77b231f2a7fa --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_common/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + cmd_system: + path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_system diff --git a/examples/openthread/ot_common_components/ot_examples_common/include/ot_examples_common.h b/examples/openthread/ot_common_components/ot_examples_common/include/ot_examples_common.h new file mode 100644 index 000000000000..9c7ab229643c --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_common/include/ot_examples_common.h @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Command Line Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "sdkconfig.h" + +#if CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE +#if CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_1 +#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ + { \ + .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ + } +#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_2 +#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ + { \ + .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ + .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ + } +#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_3 +#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ + { \ + .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ + .priority = CONFIG_EXTERNAL_COEX_PRIORITY_PIN, \ + .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ + } +#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_4 +#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ + { \ + .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ + .priority = CONFIG_EXTERNAL_COEX_PRIORITY_PIN, \ + .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ + .tx_line = CONFIG_EXTERNAL_COEX_TX_LINE_PIN, \ + } +#endif +#endif // CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE + +/** + * @brief Initializes the external coexistence. + * + */ +void ot_external_coexist_init(void); + +/** + * @brief Initializes the console. + * + */ +void ot_console_start(void); + +/** + * @brief Form or join the Thread network automatically. + * + */ +void ot_network_auto_start(void); diff --git a/examples/openthread/ot_common_components/ot_examples_common/ot_console.c b/examples/openthread/ot_common_components/ot_examples_common/ot_console.c new file mode 100644 index 000000000000..a293adf38c49 --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_common/ot_console.c @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Command Line Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "ot_examples_common.h" +#include "esp_check.h" +#include "esp_err.h" +#include "esp_console.h" +#include "cmd_system.h" + +void ot_console_start(void) +{ + esp_console_repl_t *repl = NULL; + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + /* Prompt to be printed before each line. + * This can be customized, made dynamic, etc. + */ + repl_config.prompt = CONFIG_IDF_TARGET ">"; + repl_config.max_cmdline_length = 256; + repl_config.max_history_len = 10; + +#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) || defined(CONFIG_ESP_CONSOLE_UART_CUSTOM) + esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl)); +#elif defined(CONFIG_ESP_CONSOLE_USB_CDC) + esp_console_dev_usb_cdc_config_t hw_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&hw_config, &repl_config, &repl)); + +#elif defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) + esp_console_dev_usb_serial_jtag_config_t hw_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl)); +#else +#error Unsupported console type +#endif + ESP_ERROR_CHECK(esp_console_start_repl(repl)); + + register_system(); +} diff --git a/examples/openthread/ot_common_components/ot_examples_common/ot_external_coexist.c b/examples/openthread/ot_common_components/ot_examples_common/ot_external_coexist.c new file mode 100644 index 000000000000..463ee56d8f79 --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_common/ot_external_coexist.c @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Command Line Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "sdkconfig.h" +#include "esp_check.h" +#include "esp_err.h" +#include "esp_coexist.h" +#include "ot_examples_common.h" + +void ot_external_coexist_init(void) +{ + esp_extern_coex_work_mode_t mode = +#if CONFIG_EXTERNAL_COEX_WORK_MODE_LEADER + EXTERNAL_COEX_LEADER_ROLE; +#elif CONFIG_EXTERNAL_COEX_WORK_MODE_FOLLOWER + EXTERNAL_COEX_FOLLOWER_ROLE; +#else + EXTERNAL_COEX_UNKNOWN_ROLE; +#endif + + esp_external_coex_gpio_set_t gpio_pin = ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG(); + ESP_ERROR_CHECK(esp_external_coex_set_work_mode(mode)); + ESP_ERROR_CHECK(esp_enable_extern_coex_gpio_pin(CONFIG_EXTERNAL_COEX_WIRE_TYPE, gpio_pin)); +} diff --git a/examples/openthread/ot_common_components/ot_examples_common/ot_network.c b/examples/openthread/ot_common_components/ot_examples_common/ot_network.c new file mode 100644 index 000000000000..1b4fe9c872c8 --- /dev/null +++ b/examples/openthread/ot_common_components/ot_examples_common/ot_network.c @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + * + * OpenThread Command Line Example + * + * This example code is in the Public Domain (or CC0 licensed, at your option.) + * + * Unless required by applicable law or agreed to in writing, this + * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "ot_examples_common.h" +#include "esp_check.h" +#include "esp_err.h" +#include "esp_openthread.h" +#include "esp_openthread_lock.h" + +void ot_network_auto_start(void) +{ + otOperationalDatasetTlvs dataset; + esp_openthread_lock_acquire(portMAX_DELAY); + otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); + ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL)); + esp_openthread_lock_release(); +} diff --git a/examples/openthread/ot_rcp/main/Kconfig.projbuild b/examples/openthread/ot_rcp/main/Kconfig.projbuild index 2fa5dc99ddc7..3bff9f092003 100644 --- a/examples/openthread/ot_rcp/main/Kconfig.projbuild +++ b/examples/openthread/ot_rcp/main/Kconfig.projbuild @@ -18,33 +18,4 @@ menu "OpenThread RCP Example" default 5 range 0 25 - menu "External coexist wire type and pin config" - config EXTERNAL_COEX_WIRE_TYPE - int "The wire_type of external coexist" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE - default 3 - range 0 3 - help - Select wire_type for external coexist, the wire_type define in external_coex_wire_t. - - config EXTERNAL_COEX_REQUEST_PIN - int "The number of external coexist request pin" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 0) - default 0 - - config EXTERNAL_COEX_GRANT_PIN - int "The number of external coexist grant pin" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 1) - default 1 - - config EXTERNAL_COEX_PRIORITY_PIN - int "The number of external coexist priority pin" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE >= 2) - default 2 - - config EXTERNAL_COEX_TX_LINE_PIN - int "The number of external coexist tx_line pin" - depends on ESP_COEX_EXTERNAL_COEXIST_ENABLE && (EXTERNAL_COEX_WIRE_TYPE = 3) - default 3 - endmenu # External coexist wire type and pin config endmenu diff --git a/examples/openthread/ot_rcp/main/esp_ot_config.h b/examples/openthread/ot_rcp/main/esp_ot_config.h index d7a11503297b..69e1805e962c 100644 --- a/examples/openthread/ot_rcp/main/esp_ot_config.h +++ b/examples/openthread/ot_rcp/main/esp_ot_config.h @@ -14,10 +14,6 @@ #pragma once -#if CONFIG_EXTERNAL_COEX_ENABLE -#include "esp_coexist.h" -#endif - #include "esp_openthread_types.h" #define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \ { \ @@ -89,33 +85,3 @@ .netif_queue_size = 10, \ .task_queue_size = 10, \ } - -#if CONFIG_EXTERNAL_COEX_ENABLE -#if CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_1 -#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ - { \ - .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ - } -#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_2 -#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ - { \ - .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ - .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ - } -#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_3 -#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ - { \ - .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ - .priority = CONFIG_EXTERNAL_COEX_PRIORITY_PIN, \ - .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ - } -#elif CONFIG_EXTERNAL_COEX_WIRE_TYPE == EXTERNAL_COEXIST_WIRE_4 -#define ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG() \ - { \ - .request = CONFIG_EXTERNAL_COEX_REQUEST_PIN, \ - .priority = CONFIG_EXTERNAL_COEX_PRIORITY_PIN, \ - .grant = CONFIG_EXTERNAL_COEX_GRANT_PIN, \ - .tx_line = CONFIG_EXTERNAL_COEX_TX_LINE_PIN, \ - } -#endif -#endif // CONFIG_EXTERNAL_COEX_ENABLE diff --git a/examples/openthread/ot_rcp/main/esp_ot_rcp.c b/examples/openthread/ot_rcp/main/esp_ot_rcp.c index ccb9988f676a..fddd77668469 100644 --- a/examples/openthread/ot_rcp/main/esp_ot_rcp.c +++ b/examples/openthread/ot_rcp/main/esp_ot_rcp.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -20,10 +20,9 @@ #include "esp_openthread.h" #include "esp_ot_config.h" #include "esp_vfs_eventfd.h" -#include "driver/uart.h" -#if CONFIG_EXTERNAL_COEX_ENABLE -#include "esp_coexist.h" +#if CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE +#include "ot_examples_common.h" #endif #if !SOC_IEEE802154_SUPPORTED @@ -34,43 +33,6 @@ extern void otAppNcpInit(otInstance *instance); -#if CONFIG_EXTERNAL_COEX_ENABLE -#if SOC_EXTERNAL_COEX_ADVANCE -static void ot_external_coexist_init(void) -{ - esp_external_coex_gpio_set_t gpio_pin = ESP_OPENTHREAD_DEFAULT_EXTERNAL_COEX_CONFIG(); - esp_external_coex_set_work_mode(EXTERNAL_COEX_FOLLOWER_ROLE); - ESP_ERROR_CHECK(esp_enable_extern_coex_gpio_pin(CONFIG_EXTERNAL_COEX_WIRE_TYPE, gpio_pin)); -} -#endif // SOC_EXTERNAL_COEX_ADVANCE -#endif // CONFIG_EXTERNAL_COEX_ENABLE - -static void ot_task_worker(void *aContext) -{ - esp_openthread_platform_config_t config = { - .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), - .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), - .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), - }; - - // Initialize the OpenThread stack - ESP_ERROR_CHECK(esp_openthread_init(&config)); - -#if CONFIG_EXTERNAL_COEX_ENABLE - ot_external_coexist_init(); -#endif // CONFIG_EXTERNAL_COEX_ENABLE - - // Initialize the OpenThread ncp - otAppNcpInit(esp_openthread_get_instance()); - - // Run the main loop - esp_openthread_launch_mainloop(); - - // Clean up - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - void app_main(void) { // Used eventfds: @@ -83,5 +45,19 @@ void app_main(void) ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); - xTaskCreate(ot_task_worker, "ot_rcp_main", 3072, xTaskGetCurrentTaskHandle(), 5, NULL); + +#if CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE + ot_external_coexist_init(); +#endif + + static esp_openthread_config_t config = { + .netif_config = {0}, + .platform_config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }, + }; + + ESP_ERROR_CHECK(esp_openthread_start(&config)); } diff --git a/examples/openthread/ot_rcp/main/idf_component.yml b/examples/openthread/ot_rcp/main/idf_component.yml new file mode 100644 index 000000000000..92e0e5b649f4 --- /dev/null +++ b/examples/openthread/ot_rcp/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + ot_examples_common: + path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_examples_common diff --git a/examples/openthread/ot_rcp/sdkconfig.ci.ext_coex b/examples/openthread/ot_rcp/sdkconfig.ci.ext_coex index 10e03f180efd..fdd48dc0bc95 100644 --- a/examples/openthread/ot_rcp/sdkconfig.ci.ext_coex +++ b/examples/openthread/ot_rcp/sdkconfig.ci.ext_coex @@ -1,2 +1,3 @@ -CONFIG_EXTERNAL_COEX_ENABLE=y +CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE=y CONFIG_ESP_COEX_SW_COEXIST_ENABLE=n +CONFIG_EXTERNAL_COEX_WORK_MODE_FOLLOWER=y diff --git a/examples/openthread/ot_rcp/sdkconfig.defaults b/examples/openthread/ot_rcp/sdkconfig.defaults index 914395913458..e2612686e424 100644 --- a/examples/openthread/ot_rcp/sdkconfig.defaults +++ b/examples/openthread/ot_rcp/sdkconfig.defaults @@ -25,14 +25,16 @@ CONFIG_OPENTHREAD_BORDER_ROUTER=n CONFIG_OPENTHREAD_CLI=n CONFIG_OPENTHREAD_SRP_CLIENT=n CONFIG_OPENTHREAD_DNS_CLIENT=n +CONFIG_OPENTHREAD_TASK_SIZE=3072 +CONFIG_OPENTHREAD_CONSOLE_ENABLE=n # end of OpenThread # # Deprecated options for backward compatibility # -CONFIG_LOG_BOOTLOADER_LEVEL_ERROR=y -CONFIG_LOG_BOOTLOADER_LEVEL_INFO=n +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=n # End of deprecated options # @@ -48,3 +50,5 @@ CONFIG_LOG_DEFAULT_LEVEL_NONE=y CONFIG_NEWLIB_NANO_FORMAT=y CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC=n CONFIG_OPENTHREAD_LOG_LEVEL_NONE=y +CONFIG_OPENTHREAD_TIMING_OPTIMIZATION=y +CONFIG_FREERTOS_HZ=1000 diff --git a/examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device.c b/examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device.c index c77114060115..bfdf9fe0d97e 100644 --- a/examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device.c +++ b/examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -27,9 +27,6 @@ #include "esp_vfs_eventfd.h" #include "nvs_flash.h" #include "driver/rtc_io.h" -#include "driver/uart.h" -#include "openthread/logging.h" -#include "openthread/thread.h" #if !SOC_IEEE802154_SUPPORTED #error "Openthread sleepy device is only supported for the SoCs which have IEEE 802.15.4 module" @@ -60,16 +57,6 @@ static void create_config_network(otInstance *instance) ESP_ERROR_CHECK(esp_openthread_auto_start(NULL)); } -static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) -{ - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); - esp_netif_t *netif = esp_netif_new(&cfg); - assert(netif != NULL); - ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config))); - - return netif; -} - static void ot_state_change_callback(otChangedFlags changed_flags, void* ctx) { OT_UNUSED_VARIABLE(ctx); @@ -163,44 +150,6 @@ static void ot_deep_sleep_init(void) ESP_ERROR_CHECK(gpio_pulldown_dis(gpio_wakeup_pin)); } - -static void ot_task_worker(void *aContext) -{ - esp_openthread_platform_config_t config = { - .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), - .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), - .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), - }; - - // Initialize the OpenThread stack - ESP_ERROR_CHECK(esp_openthread_init(&config)); - - ot_deep_sleep_init(); - -#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC - // The OpenThread log level directly matches ESP log level - (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); -#endif - esp_netif_t *openthread_netif; - // Initialize the esp_netif bindings - openthread_netif = init_openthread_netif(&config); - esp_netif_set_default_netif(openthread_netif); - otSetStateChangedCallback(esp_openthread_get_instance(), ot_state_change_callback, NULL); - - create_config_network(esp_openthread_get_instance()); - - // Run the main loop - esp_openthread_launch_mainloop(); - - // Clean up - esp_openthread_netif_glue_deinit(); - esp_netif_destroy(openthread_netif); - - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - - void app_main(void) { // Used eventfds: @@ -215,6 +164,19 @@ void app_main(void) ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); + ot_deep_sleep_init(); + + static esp_openthread_config_t config = { + .netif_config = ESP_NETIF_DEFAULT_OPENTHREAD(), + .platform_config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }, + }; + ESP_ERROR_CHECK(esp_openthread_start(&config)); + esp_netif_set_default_netif(esp_openthread_get_netif()); - xTaskCreate(ot_task_worker, "ot_power_save_main", 4096, NULL, 5, NULL); + otSetStateChangedCallback(esp_openthread_get_instance(), ot_state_change_callback, NULL); + create_config_network(esp_openthread_get_instance()); } diff --git a/examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device_config.h b/examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device_config.h index 145bb245ccc2..5f82ae0ed61e 100644 --- a/examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device_config.h +++ b/examples/openthread/ot_sleepy_device/deep_sleep/main/esp_ot_sleepy_device_config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -25,24 +25,9 @@ } #endif -#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ - .host_uart_config = { \ - .port = 0, \ - .uart_config = \ - { \ - .baud_rate = 115200, \ - .data_bits = UART_DATA_8_BITS, \ - .parity = UART_PARITY_DISABLE, \ - .stop_bits = UART_STOP_BITS_1, \ - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ - .rx_flow_ctrl_thresh = 0, \ - .source_clk = UART_SCLK_DEFAULT, \ - }, \ - .rx_pin = UART_PIN_NO_CHANGE, \ - .tx_pin = UART_PIN_NO_CHANGE, \ - }, \ +#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ + { \ + .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ } #define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ diff --git a/examples/openthread/ot_sleepy_device/deep_sleep/sdkconfig.defaults b/examples/openthread/ot_sleepy_device/deep_sleep/sdkconfig.defaults index b76a1dcd330b..3edc9e3663e1 100644 --- a/examples/openthread/ot_sleepy_device/deep_sleep/sdkconfig.defaults +++ b/examples/openthread/ot_sleepy_device/deep_sleep/sdkconfig.defaults @@ -21,6 +21,7 @@ CONFIG_MBEDTLS_ECJPAKE_C=y CONFIG_OPENTHREAD_ENABLED=y CONFIG_OPENTHREAD_BORDER_ROUTER=n CONFIG_OPENTHREAD_DNS64_CLIENT=y +CONFIG_OPENTHREAD_CONSOLE_ENABLE=n # end of OpenThread # diff --git a/examples/openthread/ot_sleepy_device/light_sleep/README.md b/examples/openthread/ot_sleepy_device/light_sleep/README.md index 01e8259d2bbd..24ed4aa1925f 100644 --- a/examples/openthread/ot_sleepy_device/light_sleep/README.md +++ b/examples/openthread/ot_sleepy_device/light_sleep/README.md @@ -15,8 +15,8 @@ Set the chip target: `idf.py set-target `, then configure the project There are two options to configure Openthread Dataset: -* Auto start mode: Enable `OPENTHREAD_AUTO_START` under `OpenThread Sleepy Example---> Enable the automatic start mode`, and configure the dataset under `Component config ---> Openthread ---> Thread Operation Dataset`. -* Manual mode: Disable `OPENTHREAD_AUTO_START`, use the CLI command to configure the dataset and start network. +* Auto start mode: Enable `OPENTHREAD_NETWORK_AUTO_START` under `OpenThread Sleepy Example---> Enable the automatic start mode`, and configure the dataset under `Component config ---> Openthread ---> Thread Operation Dataset`. +* Manual mode: Disable `OPENTHREAD_NETWORK_AUTO_START`, use the CLI command to configure the dataset and start network. ### Build and Flash @@ -24,11 +24,11 @@ Build the project and flash it to the board. Use the following command: `idf.py ### Configure the Openthread sleepy device ``` -> mode - -> pollperiod 3000 -> dataset set active -> ifconfig up -> thread start +esp32h2> ot mode - +esp32h2> ot pollperiod 3000 +esp32h2> ot dataset set active +esp32h2> ot ifconfig up +esp32h2> ot thread start ``` ### Example Output @@ -62,23 +62,23 @@ I (652) gdma: GDMA pair (0, 0) retention initialization I(660) OPENTHREAD:[I] ChildSupervsn-: Timeout: 0 -> 190 > I (664) OPENTHREAD: OpenThread attached to netif I (635) main_task: Returned from app_main() -> mode - +esp32h2> ot mode - I(2250683) OPENTHREAD:[N] Mle-----------: Mode 0x0f -> 0x04 [rx-on:no ftd:no full-net:no] Done -> pollperiod 3000 +esp32h2> ot pollperiod 3000 Done -> dataset set active 0e080000000000010000000300001a35060004001fffe00208dead00beef00cafe0708fd000db800a00000051000112233445566778899aabbccdd0000030e4f70656e5468726561642d455350010212340410104810e2315100afd6bc9215a6bfac530c0402a0f7f8 +esp32h2> ot dataset set active 0e080000000000010000000300001a35060004001fffe00208dead00beef00cafe0708fd000db800a00000051000112233445566778899aabbccdd0000030e4f70656e5468726561642d455350010212340410104810e2315100afd6bc9215a6bfac530c0402a0f7f8 Done -> ifconfig up +esp32h2> ot ifconfig up Done I (2274801) OT_STATE: netif up -> thread start +esp32h2> ot thread start I(2279917) OPENTHREAD:[N] Mle-----------: Role disabled -> detached Done diff --git a/examples/openthread/ot_sleepy_device/light_sleep/main/Kconfig.projbuild b/examples/openthread/ot_sleepy_device/light_sleep/main/Kconfig.projbuild deleted file mode 100644 index 27aa83ff0c7f..000000000000 --- a/examples/openthread/ot_sleepy_device/light_sleep/main/Kconfig.projbuild +++ /dev/null @@ -1,9 +0,0 @@ -menu "OpenThread Sleepy Example" - - config OPENTHREAD_AUTO_START - bool 'Enable the automatic start mode.' - default n - help - If enabled, the Openthread Device will create or connect to thread network with pre-configured - network parameters automatically. Otherwise, user need to configure Thread via CLI command manually. -endmenu diff --git a/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device.c b/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device.c index d4e1892273d8..b54b2680424e 100644 --- a/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device.c +++ b/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device.c @@ -19,17 +19,15 @@ #include "esp_err.h" #include "esp_event.h" #include "esp_log.h" +#include "esp_netif.h" #include "esp_openthread.h" -#include "esp_openthread_cli.h" #include "esp_openthread_lock.h" #include "esp_openthread_netif_glue.h" #include "esp_ot_sleepy_device_config.h" #include "esp_vfs_eventfd.h" #include "esp_private/esp_clk.h" -#include "driver/uart.h" #include "nvs_flash.h" -#include "openthread/logging.h" -#include "openthread/thread.h" +#include "ot_examples_common.h" #if CONFIG_ESP_SLEEP_DEBUG #include "esp_private/esp_pmu.h" #include "esp_private/esp_sleep_internal.h" @@ -50,7 +48,7 @@ static esp_pm_lock_handle_t s_cli_pm_lock = NULL; TimerHandle_t xTimer; -#if CONFIG_OPENTHREAD_AUTO_START +#if CONFIG_OPENTHREAD_NETWORK_AUTO_START static void create_config_network(otInstance *instance) { otLinkModeConfig linkMode = { 0 }; @@ -73,7 +71,7 @@ static void create_config_network(otInstance *instance) otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL)); } -#endif // CONFIG_OPENTHREAD_AUTO_START +#endif // CONFIG_OPENTHREAD_NETWORK_AUTO_START static esp_err_t esp_openthread_sleep_device_init(void) { @@ -105,16 +103,6 @@ static void process_state_change(otChangedFlags flags, void* context) } } -static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) -{ - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); - esp_netif_t *netif = esp_netif_new(&cfg); - assert(netif != NULL); - ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config))); - - return netif; -} - #if CONFIG_ESP_SLEEP_DEBUG static esp_sleep_context_t s_sleep_ctx; @@ -128,63 +116,6 @@ void vTimerCallback( TimerHandle_t xTimer ) } #endif -static void ot_task_worker(void *aContext) -{ - otError ret; - esp_openthread_platform_config_t config = { - .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), - .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), - .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), - }; - - // Initialize the OpenThread stack - ESP_ERROR_CHECK(esp_openthread_init(&config)); - - esp_openthread_lock_acquire(portMAX_DELAY); - ret = otSetStateChangedCallback(esp_openthread_get_instance(), process_state_change, esp_openthread_get_instance()); - esp_openthread_lock_release(); - if(ret != OT_ERROR_NONE) { - ESP_LOGE(TAG, "Failed to set state changed callback"); - } -#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC - // The OpenThread log level directly matches ESP log level - (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); -#endif - // Initialize the OpenThread cli -#if CONFIG_OPENTHREAD_CLI - esp_openthread_cli_init(); -#endif - esp_netif_t *openthread_netif; - // Initialize the esp_netif bindings - openthread_netif = init_openthread_netif(&config); - esp_netif_set_default_netif(openthread_netif); -#if CONFIG_OPENTHREAD_AUTO_START - create_config_network(esp_openthread_get_instance()); -#endif // CONFIG_OPENTHREAD_AUTO_START - -#if CONFIG_OPENTHREAD_CLI - esp_openthread_cli_create_task(); -#endif -#if CONFIG_ESP_SLEEP_DEBUG - esp_sleep_set_sleep_context(&s_sleep_ctx); - esp_log_level_set(TAG, ESP_LOG_DEBUG); - - // Use freeRTOS timer so that it is lower priority than OpenThread - xTimer = xTimerCreate("print_sleep_flag", pdMS_TO_TICKS(2000), pdTRUE, NULL, vTimerCallback); - xTimerStart( xTimer, 0 ); -#endif - - // Run the main loop - esp_openthread_launch_mainloop(); - - // Clean up - esp_openthread_netif_glue_deinit(); - esp_netif_destroy(openthread_netif); - - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - static esp_err_t ot_power_save_init(void) { esp_err_t rc = ESP_OK; @@ -227,5 +158,34 @@ void app_main(void) ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); ESP_ERROR_CHECK(ot_power_save_init()); ESP_ERROR_CHECK(esp_openthread_sleep_device_init()); - xTaskCreate(ot_task_worker, "ot_power_save_main", 4096, NULL, 5, NULL); + +#if CONFIG_OPENTHREAD_CLI + ot_console_start(); +#endif + + static esp_openthread_config_t config = { + .netif_config = ESP_NETIF_DEFAULT_OPENTHREAD(), + .platform_config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }, + }; + ESP_ERROR_CHECK(esp_openthread_start(&config)); + esp_netif_set_default_netif(esp_openthread_get_netif()); + + otSetStateChangedCallback(esp_openthread_get_instance(), process_state_change, esp_openthread_get_instance()); + +#if CONFIG_OPENTHREAD_NETWORK_AUTO_START + create_config_network(esp_openthread_get_instance()); +#endif + +#if CONFIG_ESP_SLEEP_DEBUG + esp_sleep_set_sleep_context(&s_sleep_ctx); + esp_log_level_set(TAG, ESP_LOG_DEBUG); + + // Use freeRTOS timer so that it is lower priority than OpenThread + xTimer = xTimerCreate("print_sleep_flag", pdMS_TO_TICKS(2000), pdTRUE, NULL, vTimerCallback); + xTimerStart( xTimer, 0 ); +#endif } diff --git a/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device_config.h b/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device_config.h index 73bd029b275e..716433c60545 100644 --- a/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device_config.h +++ b/examples/openthread/ot_sleepy_device/light_sleep/main/esp_ot_sleepy_device_config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -25,25 +25,9 @@ } #endif -// When JIRA PM-3 is fixed, the UART clock will automatically switch. -#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ - .host_uart_config = { \ - .port = 0, \ - .uart_config = \ - { \ - .baud_rate = 115200, \ - .data_bits = UART_DATA_8_BITS, \ - .parity = UART_PARITY_DISABLE, \ - .stop_bits = UART_STOP_BITS_1, \ - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ - .rx_flow_ctrl_thresh = 0, \ - .source_clk = UART_SCLK_XTAL, \ - }, \ - .rx_pin = UART_PIN_NO_CHANGE, \ - .tx_pin = UART_PIN_NO_CHANGE, \ - }, \ +#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ + { \ + .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ } #define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ diff --git a/examples/openthread/ot_sleepy_device/light_sleep/main/idf_component.yml b/examples/openthread/ot_sleepy_device/light_sleep/main/idf_component.yml new file mode 100644 index 000000000000..72f76924fe03 --- /dev/null +++ b/examples/openthread/ot_sleepy_device/light_sleep/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: + version: ">=4.1.0" + ot_examples_common: + path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_examples_common diff --git a/examples/openthread/ot_sleepy_device/light_sleep/sdkconfig.defaults b/examples/openthread/ot_sleepy_device/light_sleep/sdkconfig.defaults index 1b7c25c723be..355e9dd3aa41 100644 --- a/examples/openthread/ot_sleepy_device/light_sleep/sdkconfig.defaults +++ b/examples/openthread/ot_sleepy_device/light_sleep/sdkconfig.defaults @@ -23,6 +23,8 @@ CONFIG_OPENTHREAD_BORDER_ROUTER=n CONFIG_OPENTHREAD_MTD=y CONFIG_OPENTHREAD_DNS64_CLIENT=y CONFIG_OPENTHREAD_CLI=y +CONFIG_OPENTHREAD_TASK_SIZE=4096 +CONFIG_OPENTHREAD_CONSOLE_ENABLE=n # end of OpenThread # @@ -51,3 +53,9 @@ CONFIG_IEEE802154_SLEEP_ENABLE=y CONFIG_FREERTOS_HZ=1000 CONFIG_ESP_SLEEP_POWER_DOWN_FLASH=y # end of light sleep + +# +# ESP System Settings +# +CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144 +# end of ESP System Settings diff --git a/examples/openthread/ot_trel/README.md b/examples/openthread/ot_trel/README.md index 63af9ea2bd2b..0c5562315770 100644 --- a/examples/openthread/ot_trel/README.md +++ b/examples/openthread/ot_trel/README.md @@ -44,7 +44,7 @@ Now you'll get an OpenThread command line shell. The `help` command will print all of the supported commands. ```bash -> help +esp32s3> ot help I(7058) OPENTHREAD:[INFO]-CLI-----: execute command: help bbr bufferinfo @@ -78,51 +78,51 @@ To run this example, at least two ESP32-S3 boards flashed with this ot_trel exam On the first device, run the following commands: ```bash -> factoryreset +esp32s3> ot factoryreset ... # the device will reboot -> dataset init new +esp32s3> ot dataset init new Done -> dataset commit active +esp32s3> ot dataset commit active Done -> ifconfig up +esp32s3> ot ifconfig up Done -> thread start +esp32s3> ot thread start Done # After some seconds -> state +esp32s3> ot state leader Done ``` Now the first device has formed a Thread network as a leader. Get some information which will be used in next steps: ```bash -> ipaddr +esp32s3> ot ipaddr fdde:ad00:beef:0:0:ff:fe00:fc00 fdde:ad00:beef:0:0:ff:fe00:8000 fdde:ad00:beef:0:a7c6:6311:9c8c:271b fe80:0:0:0:5c27:a723:7115:c8f8 # Get the Active Dataset -> dataset active -x +esp32s3> ot dataset active -x 0e080000000000010000000300001835060004001fffe00208fe7bb701f5f1125d0708fd75cbde7c6647bd0510b3914792d44f45b6c7d76eb9306eec94030f4f70656e5468726561642d35383332010258320410e35c581af5029b054fc904a24c2b27700c0402a0fff8 ``` On the second device, set the active dataset from leader, and start Thread interface: ```bash -> factoryreset +esp32s3> ot factoryreset ... # the device will reboot -> dataset set active 0e080000000000010000000300001835060004001fffe00208fe7bb701f5f1125d0708fd75cbde7c6647bd0510b3914792d44f45b6c7d76eb9306eec94030f4f70656e5468726561642d35383332010258320410e35c581af5029b054fc904a24c2b27700c0402a0fff8 -> ifconfig up +esp32s3> ot dataset set active 0e080000000000010000000300001835060004001fffe00208fe7bb701f5f1125d0708fd75cbde7c6647bd0510b3914792d44f45b6c7d76eb9306eec94030f4f70656e5468726561642d35383332010258320410e35c581af5029b054fc904a24c2b27700c0402a0fff8 +esp32s3> ot ifconfig up Done -> thread start +esp32s3> ot thread start Done # After some seconds -> state +esp32s3> ot state router # child is also a valid state Done ``` diff --git a/examples/openthread/ot_trel/main/Kconfig.projbuild b/examples/openthread/ot_trel/main/Kconfig.projbuild deleted file mode 100644 index a00fd8759180..000000000000 --- a/examples/openthread/ot_trel/main/Kconfig.projbuild +++ /dev/null @@ -1,9 +0,0 @@ -menu "OpenThread TREL Example" - - config OPENTHREAD_AUTO_START - bool 'Enable the automatic start mode.' - default n - help - If enabled, the Openthread Device will create or connect to thread network with pre-configured - network parameters automatically. Otherwise, user need to configure Thread via CLI command manually. -endmenu diff --git a/examples/openthread/ot_trel/main/esp_ot_config.h b/examples/openthread/ot_trel/main/esp_ot_config.h index be8eff0e21cb..f72a321ee6c0 100644 --- a/examples/openthread/ot_trel/main/esp_ot_config.h +++ b/examples/openthread/ot_trel/main/esp_ot_config.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -22,33 +22,10 @@ .radio_mode = RADIO_MODE_TREL, \ } -#if CONFIG_OPENTHREAD_CONSOLE_TYPE_UART -#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ - { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART, \ - .host_uart_config = { \ - .port = 0, \ - .uart_config = \ - { \ - .baud_rate = 115200, \ - .data_bits = UART_DATA_8_BITS, \ - .parity = UART_PARITY_DISABLE, \ - .stop_bits = UART_STOP_BITS_1, \ - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ - .rx_flow_ctrl_thresh = 0, \ - .source_clk = UART_SCLK_DEFAULT, \ - }, \ - .rx_pin = UART_PIN_NO_CHANGE, \ - .tx_pin = UART_PIN_NO_CHANGE, \ - }, \ - } -#elif CONFIG_OPENTHREAD_CONSOLE_TYPE_USB_SERIAL_JTAG #define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \ { \ - .host_connection_mode = HOST_CONNECTION_MODE_CLI_USB, \ - .host_usb_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(), \ + .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ } -#endif #define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \ { \ diff --git a/examples/openthread/ot_trel/main/esp_ot_trel.c b/examples/openthread/ot_trel/main/esp_ot_trel.c index a333eface5f5..d45b211b7b6b 100644 --- a/examples/openthread/ot_trel/main/esp_ot_trel.c +++ b/examples/openthread/ot_trel/main/esp_ot_trel.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 * @@ -23,23 +23,14 @@ #include "esp_netif.h" #include "esp_netif_types.h" #include "esp_openthread.h" -#include "esp_openthread_cli.h" #include "esp_openthread_lock.h" #include "esp_openthread_netif_glue.h" -#include "esp_openthread_types.h" #include "esp_ot_config.h" #include "esp_vfs_eventfd.h" -#include "driver/uart.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "hal/uart_types.h" #include "nvs_flash.h" -#include "openthread/cli.h" -#include "openthread/instance.h" -#include "openthread/logging.h" -#include "openthread/tasklet.h" #include "protocol_examples_common.h" #include "mdns.h" +#include "ot_examples_common.h" #if !CONFIG_EXAMPLE_CONNECT_WIFI && !CONFIG_EXAMPLE_CONNECT_ETHERNET #error No netif for TREL! @@ -56,70 +47,6 @@ #define TAG "ot_esp_trel" -static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config) -{ - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); - esp_netif_t *netif = esp_netif_new(&cfg); - assert(netif != NULL); - ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config))); - - return netif; -} - -static void ot_task_worker(void *aContext) -{ - esp_openthread_platform_config_t config = { - .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), - .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), - .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), - }; - - ESP_ERROR_CHECK(example_connect()); - - // Initialize the OpenThread stack - ESP_ERROR_CHECK(esp_openthread_init(&config)); - -#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE - ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance())); -#endif - -#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC - // The OpenThread log level directly matches ESP log level - (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL); -#endif - // Initialize the OpenThread cli -#if CONFIG_OPENTHREAD_CLI - esp_openthread_cli_init(); -#endif - - esp_netif_t *openthread_netif; - // Initialize the esp_netif bindings - openthread_netif = init_openthread_netif(&config); - esp_netif_set_default_netif(openthread_netif); - -#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION - esp_cli_custom_command_init(); -#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION - - // Run the main loop -#if CONFIG_OPENTHREAD_CLI - esp_openthread_cli_create_task(); -#endif -#if CONFIG_OPENTHREAD_AUTO_START - otOperationalDatasetTlvs dataset; - otError error = otDatasetGetActiveTlvs(esp_openthread_get_instance(), &dataset); - ESP_ERROR_CHECK(esp_openthread_auto_start((error == OT_ERROR_NONE) ? &dataset : NULL)); -#endif - esp_openthread_launch_mainloop(); - - // Clean up - esp_openthread_netif_glue_deinit(); - esp_netif_destroy(openthread_netif); - - esp_vfs_eventfd_unregister(); - vTaskDelete(NULL); -} - void app_main(void) { // Used eventfds: @@ -136,5 +63,27 @@ void app_main(void) ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); ESP_ERROR_CHECK(mdns_init()); ESP_ERROR_CHECK(mdns_hostname_set("esp-ot-trel")); - xTaskCreate(ot_task_worker, "ot_trel_main", 8192, xTaskGetCurrentTaskHandle(), 5, NULL); + + ESP_ERROR_CHECK(example_connect()); + +#if CONFIG_OPENTHREAD_CLI + ot_console_start(); +#endif + + static esp_openthread_config_t config = { + .netif_config = ESP_NETIF_DEFAULT_OPENTHREAD(), + .platform_config = { + .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(), + .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), + .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), + }, + }; + ESP_ERROR_CHECK(esp_openthread_start(&config)); + esp_netif_set_default_netif(esp_openthread_get_netif()); +#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION + esp_cli_custom_command_init(); +#endif +#if CONFIG_OPENTHREAD_NETWORK_AUTO_START + ot_network_auto_start(); +#endif } diff --git a/examples/openthread/ot_trel/main/idf_component.yml b/examples/openthread/ot_trel/main/idf_component.yml index a52a17085f02..4318a416301b 100644 --- a/examples/openthread/ot_trel/main/idf_component.yml +++ b/examples/openthread/ot_trel/main/idf_component.yml @@ -1,7 +1,7 @@ ## IDF Component Manager Manifest File dependencies: espressif/esp_ot_cli_extension: - version: "~1.2.0" + version: "~1.4.0" espressif/mdns: "^1.0.3" idf: version: ">=4.1.0" @@ -9,3 +9,5 @@ dependencies: path: ${IDF_PATH}/examples/common_components/protocol_examples_common ot_led: path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_led + ot_examples_common: + path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_examples_common diff --git a/examples/openthread/ot_trel/sdkconfig.defaults b/examples/openthread/ot_trel/sdkconfig.defaults index 0e5eb34891f5..92fc2c7df0df 100644 --- a/examples/openthread/ot_trel/sdkconfig.defaults +++ b/examples/openthread/ot_trel/sdkconfig.defaults @@ -25,6 +25,8 @@ CONFIG_OPENTHREAD_BORDER_ROUTER=n CONFIG_OPENTHREAD_DNS64_CLIENT=y CONFIG_OPENTHREAD_RADIO_154_NONE=y CONFIG_OPENTHREAD_RADIO_TREL=y +CONFIG_OPENTHREAD_TASK_SIZE=10240 +CONFIG_OPENTHREAD_CONSOLE_ENABLE=n # end of OpenThread # @@ -49,4 +51,10 @@ CONFIG_EXAMPLE_CONNECT_THREAD=n # Wireless Coexistence # CONFIG_ESP_COEX_SW_COEXIST_ENABLE=n -CONFIG_EXTERNAL_COEX_ENABLE=n +CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE=n + +# +# ESP System Settings +# +CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144 +# end of ESP System Settings diff --git a/examples/peripherals/temperature_sensor/temp_sensor/README.md b/examples/peripherals/temperature_sensor/temp_sensor/README.md index e798628a4bb4..fe363c9cadd7 100644 --- a/examples/peripherals/temperature_sensor/temp_sensor/README.md +++ b/examples/peripherals/temperature_sensor/temp_sensor/README.md @@ -7,13 +7,13 @@ The ESP32-S2/C3/S3/C2 has a built-in temperature sensor. The temperature sensor The conversion relationship is the first two columns of the table below. Among them, `offset = 0`(default) is the main measurement option, and other values are extended measurement options. -| DAC level | offset | measure range(℃) | measure error(℃) | -| :-------: | :----: | :--------------: | :--------------: | -| 0 | -2 | 50 ~ 125 | < 3 | -| 1 | -1 | 20 ~ 100 | < 2 | -| 2 | 0 | -10 ~ 80 | < 1 | -| 3 | 1 | -30 ~ 50 | < 2 | -| 4 | 2 | -40 ~ 20 | < 3 | +| DAC level | offset | measure range(℃) | +| :-------: | :----: | :--------------: | +| 0 | -2 | 50 ~ 125 | +| 1 | -1 | 20 ~ 100 | +| 2 | 0 | -10 ~ 80 | +| 3 | 1 | -30 ~ 50 | +| 4 | 2 | -40 ~ 20 | ## How to use example diff --git a/examples/peripherals/timer_group/gptimer/pytest_gptimer_example.py b/examples/peripherals/timer_group/gptimer/pytest_gptimer_example.py index 29a534d6110a..db52faffe3b7 100644 --- a/examples/peripherals/timer_group/gptimer/pytest_gptimer_example.py +++ b/examples/peripherals/timer_group/gptimer/pytest_gptimer_example.py @@ -1,6 +1,5 @@ # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 - import pytest from pytest_embedded import Dut @@ -10,25 +9,25 @@ def test_gptimer_example(dut: Dut) -> None: dut.expect_exact('Create timer handle', timeout=5) dut.expect_exact('Start timer, stop it at alarm event', timeout=5) - res = dut.expect(r'Timer stopped, count=(\d+)', timeout=30) + res = dut.expect(r'Timer stopped, count=(\d+)\D', timeout=30) stopped_count = res.group(1).decode('utf8') assert (1000000 - 20) < int(stopped_count) < (1000000 + 20) dut.expect_exact('Set count value') dut.expect_exact('Get count value') - res = dut.expect(r'Timer count value=(\d+)', timeout=5) + res = dut.expect(r'Timer count value=(\d+)\D', timeout=5) count_val = res.group(1).decode('utf8') assert int(count_val) == 100 dut.expect_exact('Start timer, auto-reload at alarm event', timeout=5) - res = dut.expect(r'Timer reloaded, count=(\d+)', timeout=5) + res = dut.expect(r'Timer reloaded, count=(\d+)\D', timeout=5) reloaded_count = res.group(1).decode('utf8') assert 0 <= int(reloaded_count) < 20 dut.expect_exact('Stop timer') dut.expect_exact('Start timer, update alarm value dynamically') - for i in range(1,5): - res = dut.expect(r'Timer alarmed, count=(\d+)', timeout=5) + for i in range(1, 5): + res = dut.expect(r'Timer alarmed, count=(\d+)\D', timeout=5) alarm_count = res.group(1).decode('utf8') assert (i * 1000000 - 20) < int(alarm_count) < (i * 1000000 + 20) diff --git a/examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/idf_component.yml b/examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/idf_component.yml index 06b047d6a2de..f15f3aa5cb22 100644 --- a/examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/idf_component.yml +++ b/examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/idf_component.yml @@ -1,4 +1,5 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_tinyusb: "^1.2" + espressif/esp_tinyusb: + version: "^2.0.0" idf: "^5.0" diff --git a/examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/tusb_composite_main.c b/examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/tusb_composite_main.c index f1c8b4723ed0..cc2028ab14a1 100644 --- a/examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/tusb_composite_main.c +++ b/examples/peripherals/usb/device/tusb_composite_msc_serialdevice/main/tusb_composite_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -10,8 +10,9 @@ #include "esp_partition.h" #include "esp_check.h" #include "tinyusb.h" -#include "tusb_msc_storage.h" -#include "tusb_cdc_acm.h" +#include "tinyusb_default_config.h" +#include "tinyusb_msc.h" +#include "tinyusb_cdc_acm.h" #define BASE_PATH "/usb" // base path to mount the partition @@ -110,42 +111,36 @@ void app_main(void) ESP_LOGI(TAG, "Initializing storage..."); static wl_handle_t wl_handle = WL_INVALID_HANDLE; + ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle)); - const tinyusb_msc_spiflash_config_t config_spi = { - .wl_handle = wl_handle + tinyusb_msc_storage_handle_t storage_hdl = NULL; + + const tinyusb_msc_storage_config_t storage_cfg = { + .mount_point = TINYUSB_MSC_STORAGE_MOUNT_APP, // Initial mount point to APP + .medium.wl_handle = wl_handle, + .fat_fs = { + .base_path = BASE_PATH, // User specific base path + }, }; - ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi)); - ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH)); + + ESP_ERROR_CHECK(tinyusb_msc_new_storage_spiflash(&storage_cfg, &storage_hdl)); + file_operations(); ESP_LOGI(TAG, "USB Composite initialization"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = NULL, - .string_descriptor = NULL, - .string_descriptor_count = 0, - .external_phy = false, -#if (TUD_OPT_HIGH_SPEED) - .fs_configuration_descriptor = NULL, - .hs_configuration_descriptor = NULL, - .qualifier_descriptor = NULL, -#else - .configuration_descriptor = NULL, -#endif // TUD_OPT_HIGH_SPEED - }; + const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); tinyusb_config_cdcacm_t acm_cfg = { - .usb_dev = TINYUSB_USBDEV_0, .cdc_port = TINYUSB_CDC_ACM_0, - .rx_unread_buf_sz = 64, .callback_rx = &tinyusb_cdc_rx_callback, // the first way to register a callback .callback_rx_wanted_char = NULL, .callback_line_state_changed = NULL, .callback_line_coding_changed = NULL }; - ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + ESP_ERROR_CHECK(tinyusb_cdcacm_init(&acm_cfg)); /* the second way to register a callback */ ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( TINYUSB_CDC_ACM_0, diff --git a/examples/peripherals/usb/device/tusb_console/main/idf_component.yml b/examples/peripherals/usb/device/tusb_console/main/idf_component.yml index ff67b5da1ec2..f15f3aa5cb22 100644 --- a/examples/peripherals/usb/device/tusb_console/main/idf_component.yml +++ b/examples/peripherals/usb/device/tusb_console/main/idf_component.yml @@ -1,4 +1,5 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_tinyusb: "^1" + espressif/esp_tinyusb: + version: "^2.0.0" idf: "^5.0" diff --git a/examples/peripherals/usb/device/tusb_console/main/tusb_console_main.c b/examples/peripherals/usb/device/tusb_console/main/tusb_console_main.c index 5b16591c6589..8a16e1b558d5 100644 --- a/examples/peripherals/usb/device/tusb_console/main/tusb_console_main.c +++ b/examples/peripherals/usb/device/tusb_console/main/tusb_console_main.c @@ -1,13 +1,12 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ // DESCRIPTION: -// This example contains minimal code to make ESP32-S2 based device -// recognizable by USB-host devices as a USB Serial Device printing output from -// the application. +// This example contains minimal code to make a USB device, recognizable by USB-host as +// a USB Serial Device printing output from the application. #include #include @@ -16,8 +15,9 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "tinyusb.h" -#include "tusb_cdc_acm.h" -#include "tusb_console.h" +#include "tinyusb_default_config.h" +#include "tinyusb_cdc_acm.h" +#include "tinyusb_console.h" #include "sdkconfig.h" static const char *TAG = "example"; @@ -27,23 +27,11 @@ void app_main(void) /* Setting TinyUSB up */ ESP_LOGI(TAG, "USB initialization"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = NULL, - .string_descriptor = NULL, - .external_phy = false, // In the most cases you need to use a `false` value -#if (TUD_OPT_HIGH_SPEED) - .fs_configuration_descriptor = NULL, - .hs_configuration_descriptor = NULL, - .qualifier_descriptor = NULL, -#else - .configuration_descriptor = NULL, -#endif // TUD_OPT_HIGH_SPEED - }; - + const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); tinyusb_config_cdcacm_t acm_cfg = { 0 }; // the configuration uses default values - ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + ESP_ERROR_CHECK(tinyusb_cdcacm_init(&acm_cfg)); ESP_LOGI(TAG, "USB initialization DONE"); while (1) { @@ -54,13 +42,13 @@ void app_main(void) fprintf(stderr, "example: print -> stderr\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); - esp_tusb_init_console(TINYUSB_CDC_ACM_0); // log to usb + ESP_ERROR_CHECK(tinyusb_console_init(TINYUSB_CDC_ACM_0)); // log to usb ESP_LOGI(TAG, "log -> USB"); vTaskDelay(1000 / portTICK_PERIOD_MS); fprintf(stdout, "example: print -> stdout\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); fprintf(stderr, "example: print -> stderr\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); - esp_tusb_deinit_console(TINYUSB_CDC_ACM_0); // log to uart + ESP_ERROR_CHECK(tinyusb_console_deinit(TINYUSB_CDC_ACM_0)); // log to uart } } diff --git a/examples/peripherals/usb/device/tusb_hid/main/idf_component.yml b/examples/peripherals/usb/device/tusb_hid/main/idf_component.yml index b60893c81e6e..f15f3aa5cb22 100644 --- a/examples/peripherals/usb/device/tusb_hid/main/idf_component.yml +++ b/examples/peripherals/usb/device/tusb_hid/main/idf_component.yml @@ -1,4 +1,5 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_tinyusb: "^1.1" + espressif/esp_tinyusb: + version: "^2.0.0" idf: "^5.0" diff --git a/examples/peripherals/usb/device/tusb_hid/main/tusb_hid_example_main.c b/examples/peripherals/usb/device/tusb_hid/main/tusb_hid_example_main.c index 6ac29716142d..a099c803149e 100644 --- a/examples/peripherals/usb/device/tusb_hid/main/tusb_hid_example_main.c +++ b/examples/peripherals/usb/device/tusb_hid/main/tusb_hid_example_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -9,6 +9,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "tinyusb.h" +#include "tinyusb_default_config.h" #include "class/hid/hid_device.h" #include "driver/gpio.h" @@ -164,19 +165,15 @@ void app_main(void) ESP_ERROR_CHECK(gpio_config(&boot_button_config)); ESP_LOGI(TAG, "USB initialization"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = NULL, - .string_descriptor = hid_string_descriptor, - .string_descriptor_count = sizeof(hid_string_descriptor) / sizeof(hid_string_descriptor[0]), - .external_phy = false, + tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); + + tusb_cfg.descriptor.device = NULL; + tusb_cfg.descriptor.full_speed_config = hid_configuration_descriptor; + tusb_cfg.descriptor.string = hid_string_descriptor; + tusb_cfg.descriptor.string_count = sizeof(hid_string_descriptor) / sizeof(hid_string_descriptor[0]); #if (TUD_OPT_HIGH_SPEED) - .fs_configuration_descriptor = hid_configuration_descriptor, // HID configuration descriptor for full-speed and high-speed are the same - .hs_configuration_descriptor = hid_configuration_descriptor, - .qualifier_descriptor = NULL, -#else - .configuration_descriptor = hid_configuration_descriptor, + tusb_cfg.descriptor.high_speed_config = hid_configuration_descriptor; #endif // TUD_OPT_HIGH_SPEED - }; ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); ESP_LOGI(TAG, "USB initialization DONE"); diff --git a/examples/peripherals/usb/device/tusb_midi/main/idf_component.yml b/examples/peripherals/usb/device/tusb_midi/main/idf_component.yml index b60893c81e6e..f15f3aa5cb22 100644 --- a/examples/peripherals/usb/device/tusb_midi/main/idf_component.yml +++ b/examples/peripherals/usb/device/tusb_midi/main/idf_component.yml @@ -1,4 +1,5 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_tinyusb: "^1.1" + espressif/esp_tinyusb: + version: "^2.0.0" idf: "^5.0" diff --git a/examples/peripherals/usb/device/tusb_midi/main/tusb_midi_main.c b/examples/peripherals/usb/device/tusb_midi/main/tusb_midi_main.c index 04fe850b364a..f8a154d9e7da 100644 --- a/examples/peripherals/usb/device/tusb_midi/main/tusb_midi_main.c +++ b/examples/peripherals/usb/device/tusb_midi/main/tusb_midi_main.c @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: MIT * - * SPDX-FileContributor: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2022-2025 Espressif Systems (Shanghai) CO LTD */ #include @@ -11,6 +11,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "tinyusb.h" +#include "tinyusb_default_config.h" #include "esp_timer.h" static const char *TAG = "example"; @@ -149,19 +150,16 @@ void app_main(void) { ESP_LOGI(TAG, "USB initialization"); - tinyusb_config_t const tusb_cfg = { - .device_descriptor = NULL, // If device_descriptor is NULL, tinyusb_driver_install() will use Kconfig - .string_descriptor = s_str_desc, - .string_descriptor_count = sizeof(s_str_desc) / sizeof(s_str_desc[0]), - .external_phy = false, + tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); + + tusb_cfg.descriptor.string = s_str_desc; + tusb_cfg.descriptor.string_count = sizeof(s_str_desc) / sizeof(s_str_desc[0]); + tusb_cfg.descriptor.full_speed_config = s_midi_cfg_desc; #if (TUD_OPT_HIGH_SPEED) - .fs_configuration_descriptor = s_midi_cfg_desc, // HID configuration descriptor for full-speed and high-speed are the same - .hs_configuration_descriptor = s_midi_hs_cfg_desc, - .qualifier_descriptor = NULL, -#else - .configuration_descriptor = s_midi_cfg_desc, + tusb_cfg.descriptor.high_speed_config = s_midi_hs_cfg_desc; + tusb_cfg.descriptor.qualifier = NULL; #endif // TUD_OPT_HIGH_SPEED - }; + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); ESP_LOGI(TAG, "USB initialization DONE"); diff --git a/examples/peripherals/usb/device/tusb_msc/main/idf_component.yml b/examples/peripherals/usb/device/tusb_msc/main/idf_component.yml index 1659a1ebe3ed..f15f3aa5cb22 100644 --- a/examples/peripherals/usb/device/tusb_msc/main/idf_component.yml +++ b/examples/peripherals/usb/device/tusb_msc/main/idf_component.yml @@ -1,4 +1,5 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_tinyusb: "^1.4.2" + espressif/esp_tinyusb: + version: "^2.0.0" idf: "^5.0" diff --git a/examples/peripherals/usb/device/tusb_msc/main/tusb_msc_main.c b/examples/peripherals/usb/device/tusb_msc/main/tusb_msc_main.c index 3f9c954ed9cf..2b6dfe9958eb 100644 --- a/examples/peripherals/usb/device/tusb_msc/main/tusb_msc_main.c +++ b/examples/peripherals/usb/device/tusb_msc/main/tusb_msc_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -19,7 +19,8 @@ #include "esp_partition.h" #include "driver/gpio.h" #include "tinyusb.h" -#include "tusb_msc_storage.h" +#include "tinyusb_default_config.h" +#include "tinyusb_msc.h" #ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMC #include "diskio_impl.h" #include "diskio_sdmmc.h" @@ -39,6 +40,10 @@ static const char *TAG = "example_main"; static esp_console_repl_t *repl = NULL; +/* Storage global variables */ +tinyusb_msc_storage_handle_t storage_hdl = NULL; +tinyusb_msc_mount_point_t mp; + /* TinyUSB descriptors ********************************************************************* */ #define EPNUM_MSC 1 @@ -161,11 +166,11 @@ const esp_console_cmd_t cmds[] = { } }; -// mount the partition and show all the files in BASE_PATH +// Set mount point to the application and list files in BASE_PATH by filesystem API static void _mount(void) { ESP_LOGI(TAG, "Mount storage..."); - ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH)); + ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_APP)); // List all the files in this directory ESP_LOGI(TAG, "\nls command output:"); @@ -173,15 +178,15 @@ static void _mount(void) DIR *dh = opendir(BASE_PATH); if (!dh) { if (errno == ENOENT) { - //If the directory is not found + // If the directory is not found ESP_LOGE(TAG, "Directory doesn't exist %s", BASE_PATH); } else { - //If the directory is not readable then throw error and exit + // If the directory is not readable then throw error and exit ESP_LOGE(TAG, "Unable to read directory %s", BASE_PATH); } return; } - //While the next entry is not readable we will print directory files + // While the next entry is not readable we will print directory files while ((d = readdir(dh)) != NULL) { printf("%s\n", d->d_name); } @@ -191,20 +196,22 @@ static void _mount(void) // unmount storage static int console_unmount(int argc, char **argv) { - if (tinyusb_msc_storage_in_use_by_usb_host()) { - ESP_LOGE(TAG, "storage is already exposed"); + ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp)); + if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) { + ESP_LOGE(TAG, "Storage is already exposed"); return -1; } ESP_LOGI(TAG, "Unmount storage..."); - ESP_ERROR_CHECK(tinyusb_msc_storage_unmount()); + ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_USB)); return 0; } // read BASE_PATH/README.MD and print its contents static int console_read(int argc, char **argv) { - if (tinyusb_msc_storage_in_use_by_usb_host()) { - ESP_LOGE(TAG, "storage exposed over USB. Application can't read from storage."); + ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp)); + if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) { + ESP_LOGE(TAG, "Storage exposed over USB. Application can't read from storage."); return -1; } ESP_LOGD(TAG, "read from storage:"); @@ -225,7 +232,8 @@ static int console_read(int argc, char **argv) // create file BASE_PATH/README.MD if it does not exist static int console_write(int argc, char **argv) { - if (tinyusb_msc_storage_in_use_by_usb_host()) { + ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp)); + if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) { ESP_LOGE(TAG, "storage exposed over USB. Application can't write to storage."); return -1; } @@ -246,38 +254,72 @@ static int console_write(int argc, char **argv) // Show storage size and sector size static int console_size(int argc, char **argv) { - if (tinyusb_msc_storage_in_use_by_usb_host()) { + ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp)); + if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) { ESP_LOGE(TAG, "storage exposed over USB. Application can't access storage"); return -1; } - uint32_t sec_count = tinyusb_msc_storage_get_sector_count(); - uint32_t sec_size = tinyusb_msc_storage_get_sector_size(); - printf("Storage Capacity %lluMB\n", ((uint64_t) sec_count) * sec_size / (1024 * 1024)); + + uint32_t sec_count; + uint32_t sec_size; + + ESP_ERROR_CHECK(tinyusb_msc_get_storage_sector_size(storage_hdl, &sec_size)); + ESP_ERROR_CHECK(tinyusb_msc_get_storage_capacity(storage_hdl, &sec_count)); + + // Calculate size in MB or KB + uint64_t total_bytes = (uint64_t)sec_size * sec_count; + if (total_bytes >= (1024 * 1024)) { + uint64_t total_mb = total_bytes / (1024 * 1024); + printf("Storage Capacity %lluMB\n", total_mb); + } else { + uint64_t total_kb = total_bytes / 1024; + printf("Storage Capacity %lluKB\n", total_kb); + } return 0; } // exit from application static int console_status(int argc, char **argv) { - printf("storage exposed over USB: %s\n", tinyusb_msc_storage_in_use_by_usb_host() ? "Yes" : "No"); + ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp)); + printf("storage exposed over USB: %s\n", (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) ? "Yes" : "No"); return 0; } // exit from application static int console_exit(int argc, char **argv) { - tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED); - tinyusb_msc_storage_deinit(); - tinyusb_driver_uninstall(); + ESP_ERROR_CHECK(tinyusb_msc_delete_storage(storage_hdl)); + ESP_ERROR_CHECK(tinyusb_driver_uninstall()); + printf("Application Exit\n"); repl->del(repl); return 0; } -// callback that is delivered when storage is mounted/unmounted by application. -static void storage_mount_changed_cb(tinyusb_msc_event_t *event) +/** + * @brief Storage mount changed callback + * + * @param handle Storage handle + * @param event Event information + * @param arg User argument, provided during callback registration + */ +static void storage_mount_changed_cb(tinyusb_msc_storage_handle_t handle, tinyusb_msc_event_t *event, void *arg) { - ESP_LOGI(TAG, "Storage mounted to application: %s", event->mount_changed_data.is_mounted ? "Yes" : "No"); + switch (event->id) { + case TINYUSB_MSC_EVENT_MOUNT_START: + // Verify that all the files are closed before unmounting + break; + case TINYUSB_MSC_EVENT_MOUNT_COMPLETE: + ESP_LOGI(TAG, "Storage mounted to application: %s", (event->mount_point == TINYUSB_MSC_STORAGE_MOUNT_APP) ? "Yes" : "No"); + break; + case TINYUSB_MSC_EVENT_MOUNT_FAILED: + case TINYUSB_MSC_EVENT_FORMAT_REQUIRED: + ESP_LOGE(TAG, "Storage mount failed or format required"); + break; + default: + break; + } } #ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH @@ -377,48 +419,50 @@ void app_main(void) { ESP_LOGI(TAG, "Initializing storage..."); + tinyusb_msc_storage_config_t storage_cfg = { + .mount_point = TINYUSB_MSC_STORAGE_MOUNT_USB, // Initial mount point to USB + .fat_fs = { + .base_path = NULL, // Use default base path + .config.max_files = 5, // Maximum number of files that can be opened simultaneously + .format_flags = 0, // No special format flags + }, + }; + #ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH static wl_handle_t wl_handle = WL_INVALID_HANDLE; ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle)); + // Set the storage medium to the wear leveling handle + storage_cfg.medium.wl_handle = wl_handle; - const tinyusb_msc_spiflash_config_t config_spi = { - .wl_handle = wl_handle, - .callback_mount_changed = storage_mount_changed_cb, /* First way to register the callback. This is while initializing the storage. */ - .mount_config.max_files = 5, - }; - ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi)); - ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); /* Other way to register the callback i.e. registering using separate API. If the callback had been already registered, it will be overwritten. */ + ESP_ERROR_CHECK(tinyusb_msc_new_storage_spiflash(&storage_cfg, &storage_hdl)); #else // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH static sdmmc_card_t *card = NULL; ESP_ERROR_CHECK(storage_init_sdmmc(&card)); - - const tinyusb_msc_sdmmc_config_t config_sdmmc = { - .card = card, - .callback_mount_changed = storage_mount_changed_cb, /* First way to register the callback. This is while initializing the storage. */ - .mount_config.max_files = 5, - }; - ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); - ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); /* Other way to register the callback i.e. registering using separate API. If the callback had been already registered, it will be overwritten. */ + // Set the storage medium to the SD/MMC card + storage_cfg.medium.card = card; + ESP_ERROR_CHECK(tinyusb_msc_new_storage_sdmmc(&storage_cfg, &storage_hdl)); #endif // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH - //mounted in the app by default + // Configure the callback for mount changed events + ESP_ERROR_CHECK(tinyusb_msc_set_storage_callback(storage_mount_changed_cb, NULL)); + // Re-mount to the APP _mount(); ESP_LOGI(TAG, "USB MSC initialization"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = &descriptor_config, - .string_descriptor = string_desc_arr, - .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), - .external_phy = false, + + tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); + + tusb_cfg.descriptor.device = &descriptor_config; + tusb_cfg.descriptor.full_speed_config = msc_fs_configuration_desc; + tusb_cfg.descriptor.string = string_desc_arr; + tusb_cfg.descriptor.string_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]); #if (TUD_OPT_HIGH_SPEED) - .fs_configuration_descriptor = msc_fs_configuration_desc, - .hs_configuration_descriptor = msc_hs_configuration_desc, - .qualifier_descriptor = &device_qualifier, -#else - .configuration_descriptor = msc_fs_configuration_desc, + tusb_cfg.descriptor.high_speed_config = msc_hs_configuration_desc; + tusb_cfg.descriptor.qualifier = &device_qualifier; #endif // TUD_OPT_HIGH_SPEED - }; + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + ESP_LOGI(TAG, "USB MSC initialization DONE"); esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); diff --git a/examples/peripherals/usb/device/tusb_ncm/main/idf_component.yml b/examples/peripherals/usb/device/tusb_ncm/main/idf_component.yml index 2576181c1c0c..f15f3aa5cb22 100644 --- a/examples/peripherals/usb/device/tusb_ncm/main/idf_component.yml +++ b/examples/peripherals/usb/device/tusb_ncm/main/idf_component.yml @@ -1,5 +1,5 @@ ## IDF Component Manager Manifest File dependencies: espressif/esp_tinyusb: - version: "^1.3.0" + version: "^2.0.0" idf: "^5.0" diff --git a/examples/peripherals/usb/device/tusb_ncm/main/tusb_ncm_main.c b/examples/peripherals/usb/device/tusb_ncm/main/tusb_ncm_main.c index 79efe53a9b3d..be3a695f5709 100644 --- a/examples/peripherals/usb/device/tusb_ncm/main/tusb_ncm_main.c +++ b/examples/peripherals/usb/device/tusb_ncm/main/tusb_ncm_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -22,6 +22,7 @@ #include "esp_private/wifi.h" #include "tinyusb.h" +#include "tinyusb_default_config.h" #include "tinyusb_net.h" static const char *TAG = "USB_NCM"; @@ -100,9 +101,7 @@ void app_main(void) ESP_ERROR_CHECK(ret); ESP_LOGI(TAG, "USB NCM device initialization"); - const tinyusb_config_t tusb_cfg = { - .external_phy = false, - }; + const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); ESP_GOTO_ON_ERROR(tinyusb_driver_install(&tusb_cfg), err, TAG, "Failed to install TinyUSB driver"); tinyusb_net_config_t net_config = { @@ -113,7 +112,7 @@ void app_main(void) esp_read_mac(net_config.mac_addr, ESP_MAC_WIFI_STA); uint8_t *mac = net_config.mac_addr; ESP_LOGI(TAG, "Network interface HW address: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - ESP_GOTO_ON_ERROR(tinyusb_net_init(TINYUSB_USBDEV_0, &net_config), err, TAG, "Failed to initialize TinyUSB NCM device class"); + ESP_GOTO_ON_ERROR(tinyusb_net_init(&net_config), err, TAG, "Failed to initialize TinyUSB NCM device class"); ESP_LOGI(TAG, "WiFi initialization"); ESP_GOTO_ON_ERROR(start_wifi(&s_is_wifi_connected), err, TAG, "Failed to init and start WiFi"); diff --git a/examples/peripherals/usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 b/examples/peripherals/usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 index 0776bebde038..65730e3edb7f 100644 --- a/examples/peripherals/usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 +++ b/examples/peripherals/usb/device/tusb_ncm/sdkconfig.defaults.esp32s3 @@ -1,3 +1 @@ -CONFIG_TINYUSB_TASK_AFFINITY_CPU0=y -CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=y CONFIG_TINYUSB_NET_MODE_NCM=y diff --git a/examples/peripherals/usb/device/tusb_serial_device/main/idf_component.yml b/examples/peripherals/usb/device/tusb_serial_device/main/idf_component.yml index ff67b5da1ec2..f15f3aa5cb22 100644 --- a/examples/peripherals/usb/device/tusb_serial_device/main/idf_component.yml +++ b/examples/peripherals/usb/device/tusb_serial_device/main/idf_component.yml @@ -1,4 +1,5 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_tinyusb: "^1" + espressif/esp_tinyusb: + version: "^2.0.0" idf: "^5.0" diff --git a/examples/peripherals/usb/device/tusb_serial_device/main/tusb_serial_device_main.c b/examples/peripherals/usb/device/tusb_serial_device/main/tusb_serial_device_main.c index 93fc49f086c5..1b0c25f81ee9 100644 --- a/examples/peripherals/usb/device/tusb_serial_device/main/tusb_serial_device_main.c +++ b/examples/peripherals/usb/device/tusb_serial_device/main/tusb_serial_device_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -9,7 +9,8 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "tinyusb.h" -#include "tusb_cdc_acm.h" +#include "tinyusb_default_config.h" +#include "tinyusb_cdc_acm.h" #include "sdkconfig.h" static const char *TAG = "example"; @@ -44,32 +45,18 @@ void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) void app_main(void) { ESP_LOGI(TAG, "USB initialization"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = NULL, - .string_descriptor = NULL, - .external_phy = false, -#if (TUD_OPT_HIGH_SPEED) - .fs_configuration_descriptor = NULL, - .hs_configuration_descriptor = NULL, - .qualifier_descriptor = NULL, -#else - .configuration_descriptor = NULL, -#endif // TUD_OPT_HIGH_SPEED - }; - + const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); tinyusb_config_cdcacm_t acm_cfg = { - .usb_dev = TINYUSB_USBDEV_0, .cdc_port = TINYUSB_CDC_ACM_0, - .rx_unread_buf_sz = 64, .callback_rx = &tinyusb_cdc_rx_callback, // the first way to register a callback .callback_rx_wanted_char = NULL, .callback_line_state_changed = NULL, .callback_line_coding_changed = NULL }; - ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + ESP_ERROR_CHECK(tinyusb_cdcacm_init(&acm_cfg)); /* the second way to register a callback */ ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( TINYUSB_CDC_ACM_0, @@ -78,7 +65,7 @@ void app_main(void) #if (CONFIG_TINYUSB_CDC_COUNT > 1) acm_cfg.cdc_port = TINYUSB_CDC_ACM_1; - ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + ESP_ERROR_CHECK(tinyusb_cdcacm_init(&acm_cfg)); ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( TINYUSB_CDC_ACM_1, CDC_EVENT_LINE_STATE_CHANGED, diff --git a/examples/wifi/ftm/main/ftm_main.c b/examples/wifi/ftm/main/ftm_main.c index b6eb413ab769..98c02c98dbd0 100644 --- a/examples/wifi/ftm/main/ftm_main.c +++ b/examples/wifi/ftm/main/ftm_main.c @@ -82,7 +82,6 @@ static const char *TAG_AP = "ftm_ap"; static EventGroupHandle_t s_wifi_event_group; static const int CONNECTED_BIT = BIT0; -static const int DISCONNECTED_BIT = BIT1; static EventGroupHandle_t s_ftm_event_group; static const int FTM_REPORT_BIT = BIT0; @@ -90,8 +89,6 @@ static const int FTM_FAILURE_BIT = BIT1; static uint8_t s_ftm_report_num_entries; static uint32_t s_rtt_est, s_dist_est; static bool s_ap_started; -static uint8_t s_ap_channel; -static uint8_t s_ap_bssid[ETH_ALEN]; const int g_report_lvl = #ifdef CONFIG_ESP_FTM_REPORT_SHOW_DIAG @@ -120,9 +117,6 @@ static void event_handler(void *arg, esp_event_base_t event_base, ESP_LOGI(TAG_STA, "Connected to %s (BSSID: "MACSTR", Channel: %d)", event->ssid, MAC2STR(event->bssid), event->channel); - memcpy(s_ap_bssid, event->bssid, ETH_ALEN); - s_ap_channel = event->channel; - xEventGroupClearBits(s_wifi_event_group, DISCONNECTED_BIT); xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT); } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_reconnect && ++s_retry_num < MAX_CONNECT_RETRY_ATTEMPTS) { @@ -132,7 +126,6 @@ static void event_handler(void *arg, esp_event_base_t event_base, ESP_LOGI(TAG_STA, "sta disconnected"); } xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT); - xEventGroupSetBits(s_wifi_event_group, DISCONNECTED_BIT); } else if (event_id == WIFI_EVENT_FTM_REPORT) { wifi_event_ftm_report_t *event = (wifi_event_ftm_report_t *) event_data; @@ -269,12 +262,12 @@ esp_err_t wifi_add_mode(wifi_mode_t mode) if (mode == WIFI_MODE_STA) { if (cur_mode == WIFI_MODE_STA || cur_mode == WIFI_MODE_APSTA) { - int bits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT, 0, 1, 0); - if (bits & CONNECTED_BIT) { + wifi_ap_record_t ap_info; + esp_err_t ret = esp_wifi_sta_get_ap_info(&ap_info); + if (ret == ESP_OK) { s_reconnect = false; xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT); ESP_ERROR_CHECK( esp_wifi_disconnect() ); - xEventGroupWaitBits(s_wifi_event_group, DISCONNECTED_BIT, 0, 1, portMAX_DELAY); } return ESP_OK; } else if (cur_mode == WIFI_MODE_AP) { @@ -314,7 +307,7 @@ static bool wifi_cmd_sta_join(const char *ssid, const char *pass) s_reconnect = true; s_retry_num = 0; - xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT, 0, 1, 5000 / portTICK_PERIOD_MS); + xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT, pdTRUE, pdTRUE, 5000 / portTICK_PERIOD_MS); return true; } @@ -329,9 +322,14 @@ static int wifi_cmd_sta(int argc, char **argv) } if (sta_args.disconnect->count) { s_reconnect = false; + wifi_ap_record_t ap_info; + esp_err_t ret = esp_wifi_sta_get_ap_info(&ap_info); + if (ret != ESP_OK) { + ESP_LOGE(TAG_STA, "Sta Already disconnected"); + return 0; + } xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT); esp_wifi_disconnect(); - xEventGroupWaitBits(s_wifi_event_group, DISCONNECTED_BIT, 0, 1, portMAX_DELAY); return 0; } @@ -469,19 +467,30 @@ static int wifi_cmd_query(int argc, char **argv) { wifi_config_t cfg; wifi_mode_t mode; + wifi_ap_record_t ap_info; esp_wifi_get_mode(&mode); if (WIFI_MODE_AP == mode) { esp_wifi_get_config(WIFI_IF_AP, &cfg); ESP_LOGI(TAG_AP, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password); } else if (WIFI_MODE_STA == mode) { - int bits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT, 0, 1, 0); - if (bits & CONNECTED_BIT) { - esp_wifi_get_config(WIFI_IF_STA, &cfg); - ESP_LOGI(TAG_STA, "sta mode, connected %s", cfg.ap.ssid); + esp_err_t ret = esp_wifi_sta_get_ap_info(&ap_info); + if (ret == ESP_OK) { + ESP_LOGI(TAG_STA, "sta mode, connected %s", ap_info.ssid); + } else { + ESP_LOGI(TAG_STA, "sta mode, disconnected"); + } + } else if (WIFI_MODE_APSTA == mode) { + ESP_LOGI(TAG_STA, "AP-Sta mode"); + esp_wifi_get_config(WIFI_IF_AP, &cfg); + ESP_LOGI(TAG_AP, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password); + esp_err_t ret = esp_wifi_sta_get_ap_info(&ap_info); + if (ret == ESP_OK) { + ESP_LOGI(TAG_STA, "sta mode, connected %s", ap_info.ssid); } else { ESP_LOGI(TAG_STA, "sta mode, disconnected"); } + } else { ESP_LOGI(TAG_STA, "NULL mode"); return 0; @@ -528,7 +537,7 @@ wifi_ap_record_t *find_ftm_responder_ap(const char *ssid) static int wifi_cmd_ftm(int argc, char **argv) { int nerrors = arg_parse(argc, argv, (void **) &ftm_args); - wifi_ap_record_t *ap_record; + wifi_ap_record_t *ap_record, ap_info; uint32_t wait_time_ms = DEFAULT_WAIT_TIME_MS; EventBits_t bits; @@ -551,10 +560,10 @@ static int wifi_cmd_ftm(int argc, char **argv) if (ftm_args.responder->count != 0) goto ftm_responder; - bits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT, 0, 1, 0); - if (bits & CONNECTED_BIT && !ftm_args.ssid->count) { - memcpy(ftmi_cfg.resp_mac, s_ap_bssid, ETH_ALEN); - ftmi_cfg.channel = s_ap_channel; + esp_err_t ret = esp_wifi_sta_get_ap_info(&ap_info); + if (ret == ESP_OK && !ftm_args.ssid->count) { + memcpy(ftmi_cfg.resp_mac, ap_info.bssid, ETH_ALEN); + ftmi_cfg.channel = ap_info.primary; } else if (ftm_args.ssid->count == 1) { ap_record = find_ftm_responder_ap(ftm_args.ssid->sval[0]); if (ap_record) { diff --git a/tools/cmake/kconfig.cmake b/tools/cmake/kconfig.cmake index 22bb1b9063a4..fe96443e65c3 100644 --- a/tools/cmake/kconfig.cmake +++ b/tools/cmake/kconfig.cmake @@ -253,6 +253,9 @@ function(__kconfig_generate_config sdkconfig sdkconfig_defaults) endif() # Generate the menuconfig target + # WARNING: If you change anything here, please ensure that only the necessary files are touched! + # If unnecessary files (those not affected by the change from menuconfig) are touched + # (their timestamp changed), it will cause unnecessary rebuilds of the whole project! add_custom_target(menuconfig ${menuconfig_depends} # create any missing config file, with defaults if necessary @@ -263,7 +266,7 @@ function(__kconfig_generate_config sdkconfig sdkconfig_defaults) --env "IDF_ENV_FPGA=${idf_env_fpga}" --env "IDF_INIT_VERSION=${idf_init_version}" --dont-write-deprecated - ${kconfgen_output_options} + --output config ${sdkconfig} # Do NOT regenerate the rest of the config files! COMMAND ${TERM_CHECK_CMD} COMMAND ${CMAKE_COMMAND} -E env "COMPONENT_KCONFIGS_SOURCE_FILE=${kconfigs_path}" @@ -275,8 +278,8 @@ function(__kconfig_generate_config sdkconfig sdkconfig_defaults) "IDF_INIT_VERSION=${idf_init_version}" ${MENUCONFIG_CMD} ${root_kconfig} USES_TERMINAL - # additional run of kconfgen esures that the deprecated options will be inserted into sdkconfig (for backward - # compatibility) + # additional run of kconfgen ensures that the deprecated options will be inserted into config files + # (for backward compatibility) COMMAND ${kconfgen_basecommand} --env "IDF_TARGET=${idf_target}" --env "IDF_TOOLCHAIN=${idf_toolchain}"