From 9c50e0c8096042a162f49334faa71e0a6a678632 Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Wed, 10 Jan 2024 15:16:55 +0800 Subject: [PATCH 1/3] fix(ble): Fixed the issue of being unable to enter light sleep - Fixed the issue where light sleep cannot be entered when external 32k XTAL does not exist --- examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32c3 | 2 ++ examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32s3 | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32c3 b/examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32c3 index 6fe39ffb73..fff614bf97 100644 --- a/examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32c3 +++ b/examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32c3 @@ -5,6 +5,8 @@ CONFIG_BT_CTRL_MODEM_SLEEP=y CONFIG_BT_CTRL_MODEM_SLEEP_MODE_1=y # Bluetooth low power clock CONFIG_BT_CTRL_LPCLK_SEL_EXT_32K_XTAL=y +# Power up main XTAL during light sleep +CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP=y # RTC clock source CONFIG_RTC_CLK_SRC_EXT_CRYS=y diff --git a/examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32s3 b/examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32s3 index 4e9d2f3edf..306bc4ab2d 100644 --- a/examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32s3 +++ b/examples/bluetooth/nimble/power_save/sdkconfig.defaults.esp32s3 @@ -5,6 +5,8 @@ CONFIG_BT_CTRL_MODEM_SLEEP=y CONFIG_BT_CTRL_MODEM_SLEEP_MODE_1=y # Bluetooth low power clock CONFIG_BT_CTRL_LPCLK_SEL_EXT_32K_XTAL=y +# Power up main XTAL during light sleep +CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP=y # RTC clock source CONFIG_RTC_CLK_SRC_EXT_CRYS=y From 227ad67a09a716a596b439eed2e8749b86f0ea9f Mon Sep 17 00:00:00 2001 From: cjin Date: Tue, 9 Jan 2024 21:01:59 +0800 Subject: [PATCH 2/3] feat(ble): added pytest for ble power save example --- examples/bluetooth/.build-test-rules.yml | 32 ++++++++-- .../nimble/blecent/main/Kconfig.projbuild | 8 +++ examples/bluetooth/nimble/blecent/main/main.c | 18 ++++++ .../bluetooth/nimble/blecent/sdkconfig.ci | 2 + .../blecent/sdkconfig.ci.esp32c2_xtal26m | 4 ++ .../bluetooth/nimble/power_save/README.md | 4 +- .../nimble/power_save/main/Kconfig.projbuild | 15 +++++ .../bluetooth/nimble/power_save/main/main.c | 20 ++++-- .../bluetooth/nimble/power_save/sdkconfig.ci | 5 ++ .../power_save/sdkconfig.ci.esp32c2_xtal26m | 7 +++ .../bluetooth/nimble/pytest_nimble_test.py | 61 +++++++++++++++++++ 11 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 examples/bluetooth/nimble/blecent/sdkconfig.ci create mode 100644 examples/bluetooth/nimble/blecent/sdkconfig.ci.esp32c2_xtal26m create mode 100644 examples/bluetooth/nimble/power_save/sdkconfig.ci create mode 100644 examples/bluetooth/nimble/power_save/sdkconfig.ci.esp32c2_xtal26m create mode 100644 examples/bluetooth/nimble/pytest_nimble_test.py diff --git a/examples/bluetooth/.build-test-rules.yml b/examples/bluetooth/.build-test-rules.yml index 0b96830999..b2bc42ac18 100644 --- a/examples/bluetooth/.build-test-rules.yml +++ b/examples/bluetooth/.build-test-rules.yml @@ -164,10 +164,19 @@ examples/bluetooth/nimble/blecent: - if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3" ] temporary: true reason: the other targets are not tested yet - disable_test: - - if: IDF_TARGET in ["esp32c2", "esp32c3", "esp32s3"] - temporary: true - reason: The runner doesn't support yet + disable: + - if: SOC_BLE_SUPPORTED != 1 + depends_components: + - bt + - esp_phy + - esp_event + - esp_coex + - esp_pm + depends_filepatterns: + - examples/bluetooth/nimble/common/**/* + - examples/bluetooth/nimble/blecent/**/* + - examples/bluetooth/nimble/power_save/**/* + - examples/bluetooth/nimble/pytest_nimble_test.py examples/bluetooth/nimble/blehr: enable: @@ -207,6 +216,19 @@ examples/bluetooth/nimble/hci: examples/bluetooth/nimble/power_save: enable: - - if: IDF_TARGET in ["esp32", "esp32c3", "esp32s3"] + - if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3"] temporary: true reason: the other targets are not tested yet + disable: + - if: SOC_BLE_SUPPORTED != 1 + depends_components: + - bt + - esp_phy + - esp_event + - esp_coex + - esp_pm + depends_filepatterns: + - examples/bluetooth/nimble/common/**/* + - examples/bluetooth/nimble/blecent/**/* + - examples/bluetooth/nimble/power_save/**/* + - examples/bluetooth/nimble/pytest_nimble_test.py diff --git a/examples/bluetooth/nimble/blecent/main/Kconfig.projbuild b/examples/bluetooth/nimble/blecent/main/Kconfig.projbuild index b5f044b14a..23e88bae48 100644 --- a/examples/bluetooth/nimble/blecent/main/Kconfig.projbuild +++ b/examples/bluetooth/nimble/blecent/main/Kconfig.projbuild @@ -28,4 +28,12 @@ menu "Example Configuration" prompt "Enable Link Encryption" help This enables bonding and encryption after connection has been established. + + config EXAMPLE_USE_CI_ADDRESS + bool + default n + prompt "Advertise using Test address(Internal Test ONLY)" + help + Used for internal test ONLY. + Use this option to advertise in a specific random address. endmenu diff --git a/examples/bluetooth/nimble/blecent/main/main.c b/examples/bluetooth/nimble/blecent/main/main.c index b2818f2631..075ced1170 100644 --- a/examples/bluetooth/nimble/blecent/main/main.c +++ b/examples/bluetooth/nimble/blecent/main/main.c @@ -455,17 +455,26 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc) { int offset = 0; int ad_struct_len = 0; +#if CONFIG_EXAMPLE_USE_CI_ADDRESS + uint32_t *addr_offset; +#endif // CONFIG_EXAMPLE_USE_CI_ADDRESS if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { return 0; } if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 0)) { +#if !CONFIG_EXAMPLE_USE_CI_ADDRESS ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); /* Convert string to address */ sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &peer_addr[5], &peer_addr[4], &peer_addr[3], &peer_addr[2], &peer_addr[1], &peer_addr[0]); +#else + addr_offset = (uint32_t *)&peer_addr[1]; + *addr_offset = atoi(CONFIG_EXAMPLE_PEER_ADDR); + peer_addr[5] = 0xC3; +#endif // !CONFIG_EXAMPLE_USE_CI_ADDRESS if (memcmp(peer_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { return 0; } @@ -501,6 +510,9 @@ blecent_should_connect(const struct ble_gap_disc_desc *disc) struct ble_hs_adv_fields fields; int rc; int i; +#if CONFIG_EXAMPLE_USE_CI_ADDRESS + uint32_t *addr_offset; +#endif // CONFIG_EXAMPLE_USE_CI_ADDRESS /* The device has to be advertising connectability. */ if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && @@ -516,10 +528,16 @@ blecent_should_connect(const struct ble_gap_disc_desc *disc) if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) { ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); +#if !CONFIG_EXAMPLE_USE_CI_ADDRESS /* Convert string to address */ sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &peer_addr[5], &peer_addr[4], &peer_addr[3], &peer_addr[2], &peer_addr[1], &peer_addr[0]); +#else + addr_offset = (uint32_t *)&peer_addr[1]; + *addr_offset = atoi(CONFIG_EXAMPLE_PEER_ADDR); + peer_addr[5] = 0xC3; +#endif // !CONFIG_EXAMPLE_USE_CI_ADDRESS if (memcmp(peer_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { return 0; } diff --git a/examples/bluetooth/nimble/blecent/sdkconfig.ci b/examples/bluetooth/nimble/blecent/sdkconfig.ci new file mode 100644 index 0000000000..ac6390187d --- /dev/null +++ b/examples/bluetooth/nimble/blecent/sdkconfig.ci @@ -0,0 +1,2 @@ +CONFIG_EXAMPLE_USE_CI_ADDRESS=y +CONFIG_EXAMPLE_PEER_ADDR="${CI_JOB_ID}" diff --git a/examples/bluetooth/nimble/blecent/sdkconfig.ci.esp32c2_xtal26m b/examples/bluetooth/nimble/blecent/sdkconfig.ci.esp32c2_xtal26m new file mode 100644 index 0000000000..c82bb24692 --- /dev/null +++ b/examples/bluetooth/nimble/blecent/sdkconfig.ci.esp32c2_xtal26m @@ -0,0 +1,4 @@ +CONFIG_IDF_TARGET="esp32c2" +CONFIG_XTAL_FREQ_26=y +CONFIG_EXAMPLE_USE_CI_ADDRESS=y +CONFIG_EXAMPLE_PEER_ADDR="${CI_JOB_ID}" diff --git a/examples/bluetooth/nimble/power_save/README.md b/examples/bluetooth/nimble/power_save/README.md index 8b338c12d4..821cdfb55c 100644 --- a/examples/bluetooth/nimble/power_save/README.md +++ b/examples/bluetooth/nimble/power_save/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C3 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | Bluetooth Power Save Example ================================= diff --git a/examples/bluetooth/nimble/power_save/main/Kconfig.projbuild b/examples/bluetooth/nimble/power_save/main/Kconfig.projbuild index 912236c456..b582f5612c 100644 --- a/examples/bluetooth/nimble/power_save/main/Kconfig.projbuild +++ b/examples/bluetooth/nimble/power_save/main/Kconfig.projbuild @@ -145,4 +145,19 @@ menu "Example Configuration" help Use this option to enable resolving peer's address. + config EXAMPLE_USE_CI_ADDRESS + bool + default n + prompt "Advertise using Test address(Internal Test ONLY)" + help + Used for internal test ONLY. + Use this option to advertise in a specific random address. + + config EXAMPLE_CI_ADDRESS_OFFSET + string + prompt "Advertise using Test address(Internal Test ONLY)" + depends on EXAMPLE_USE_CI_ADDRESS + help + Used for internal test ONLY. + Use this option to advertise in a specific random address. endmenu diff --git a/examples/bluetooth/nimble/power_save/main/main.c b/examples/bluetooth/nimble/power_save/main/main.c index 605651fd06..f168fabaa8 100644 --- a/examples/bluetooth/nimble/power_save/main/main.c +++ b/examples/bluetooth/nimble/power_save/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -28,7 +28,7 @@ static uint8_t ext_adv_pattern_1[] = { static const char *tag = "NimBLE_BLE_PRPH"; static int bleprph_gap_event(struct ble_gap_event *event, void *arg); -#if CONFIG_EXAMPLE_RANDOM_ADDR +#if CONFIG_EXAMPLE_RANDOM_ADDR || CONFIG_EXAMPLE_USE_CI_ADDRESS static uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM; #else static uint8_t own_addr_type; @@ -93,7 +93,7 @@ ext_bleprph_advertise(void) params.connectable = 1; /* advertise using random addr */ - params.own_addr_type = BLE_OWN_ADDR_PUBLIC; + params.own_addr_type = own_addr_type; params.primary_phy = BLE_HCI_LE_PHY_1M; params.secondary_phy = BLE_HCI_LE_PHY_2M; @@ -462,14 +462,26 @@ bleprph_on_sync(void) ble_app_set_addr(); #endif +#if CONFIG_EXAMPLE_USE_CI_ADDRESS + if (strlen(CONFIG_EXAMPLE_CI_ADDRESS_OFFSET)) { + uint8_t addr[6] = {0}; + uint32_t *offset = (uint32_t *)&addr[1]; + *offset = atoi(CONFIG_EXAMPLE_CI_ADDRESS_OFFSET); + addr[5] = 0xC3; + rc = ble_hs_id_set_rnd(addr); + assert(rc == 0); + } +#endif // CONFIG_EXAMPLE_USE_CI_ADDRESS + /* Make sure we have proper identity address set (public preferred) */ -#if CONFIG_EXAMPLE_RANDOM_ADDR +#if CONFIG_EXAMPLE_RANDOM_ADDR || CONFIG_EXAMPLE_USE_CI_ADDRESS rc = ble_hs_util_ensure_addr(1); #else rc = ble_hs_util_ensure_addr(0); #endif assert(rc == 0); + /* Figure out address to use while advertising (no privacy for now) */ rc = ble_hs_id_infer_auto(0, &own_addr_type); if (rc != 0) { diff --git a/examples/bluetooth/nimble/power_save/sdkconfig.ci b/examples/bluetooth/nimble/power_save/sdkconfig.ci new file mode 100644 index 0000000000..a466b75320 --- /dev/null +++ b/examples/bluetooth/nimble/power_save/sdkconfig.ci @@ -0,0 +1,5 @@ +# +# Test Config +# +CONFIG_EXAMPLE_USE_CI_ADDRESS=y +CONFIG_EXAMPLE_CI_ADDRESS_OFFSET="${CI_JOB_ID}" diff --git a/examples/bluetooth/nimble/power_save/sdkconfig.ci.esp32c2_xtal26m b/examples/bluetooth/nimble/power_save/sdkconfig.ci.esp32c2_xtal26m new file mode 100644 index 0000000000..69cdde9cd5 --- /dev/null +++ b/examples/bluetooth/nimble/power_save/sdkconfig.ci.esp32c2_xtal26m @@ -0,0 +1,7 @@ +CONFIG_IDF_TARGET="esp32c2" +CONFIG_XTAL_FREQ_26=y +# +# Test Config +# +CONFIG_EXAMPLE_USE_CI_ADDRESS=y +CONFIG_EXAMPLE_CI_ADDRESS_OFFSET="${CI_JOB_ID}" diff --git a/examples/bluetooth/nimble/pytest_nimble_test.py b/examples/bluetooth/nimble/pytest_nimble_test.py new file mode 100644 index 0000000000..378d6cff43 --- /dev/null +++ b/examples/bluetooth/nimble/pytest_nimble_test.py @@ -0,0 +1,61 @@ +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import os.path +from typing import Tuple + +import pexpect +import pytest +from pytest_embedded_idf.dut import IdfDut + + +# Case 1: BLE power save test +@pytest.mark.esp32c3 +@pytest.mark.esp32s3 +@pytest.mark.esp32 +@pytest.mark.wifi_two_dut +@pytest.mark.parametrize( + 'count, app_path', [ + (2, + f'{os.path.join(os.path.dirname(__file__), "power_save")}|{os.path.join(os.path.dirname(__file__), "blecent")}'), + ], + indirect=True, +) +def test_power_save_conn(app_path: str, dut: Tuple[IdfDut, IdfDut]) -> None: + peripheral = dut[0] + central = dut[1] + + peripheral.expect('NimBLE_BLE_PRPH: BLE Host Task Started', timeout=30) + central.expect('NimBLE_BLE_CENT: BLE Host Task Started', timeout=30) + peripheral.expect('Returned from app_main()', timeout=30) + central.expect('Returned from app_main()', timeout=30) + central.expect('Connection established', timeout=30) + peripheral.expect('connection established; status=0', timeout=30) + output = peripheral.expect(pexpect.TIMEOUT, timeout=30) + assert 'rst:' not in str(output) and 'boot:' not in str(output) + + +# Case 2: BLE power save test for ESP32C2 +@pytest.mark.esp32c2 +@pytest.mark.wifi_two_dut +@pytest.mark.xtal_26mhz +@pytest.mark.parametrize( + 'config, count, app_path, baud', [ + ('esp32c2_xtal26m', 2, + f'{os.path.join(os.path.dirname(__file__), "power_save")}|{os.path.join(os.path.dirname(__file__), "blecent")}', + '74880'), + ], + indirect=True, +) +def test_power_save_conn_esp32c2_26mhz(dut: Tuple[IdfDut, IdfDut]) -> None: + peripheral = dut[0] + central = dut[1] + + peripheral.expect('NimBLE_BLE_PRPH: BLE Host Task Started', timeout=5) + central.expect('NimBLE_BLE_CENT: BLE Host Task Started', timeout=5) + peripheral.expect('Returned from app_main()', timeout=5) + central.expect('Returned from app_main()', timeout=5) + central.expect('Connection established', timeout=30) + peripheral.expect('connection established; status=0', timeout=30) + output = peripheral.expect(pexpect.TIMEOUT, timeout=30) + assert 'rst:' not in str(output) and 'boot:' not in str(output) From 5e44ecb938cff5fa5eda9510d83b9d0164ecdf4a Mon Sep 17 00:00:00 2001 From: cjin Date: Tue, 23 Jan 2024 11:08:47 +0800 Subject: [PATCH 3/3] fix(ble): delete unused example ci test script --- .../bluetooth/nimble/blecent/blecent_test.py | 140 ------------------ 1 file changed, 140 deletions(-) delete mode 100644 examples/bluetooth/nimble/blecent/blecent_test.py diff --git a/examples/bluetooth/nimble/blecent/blecent_test.py b/examples/bluetooth/nimble/blecent/blecent_test.py deleted file mode 100644 index 2e09579b52..0000000000 --- a/examples/bluetooth/nimble/blecent/blecent_test.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 Espressif Systems (Shanghai) PTE LTD -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import print_function - -import os -import subprocess -import threading -import traceback - -try: - import Queue -except ImportError: - import queue as Queue - -import ttfw_idf -from ble import lib_ble_client -from tiny_test_fw import Utility - -# When running on local machine execute the following before running this script -# > make app bootloader -# > make print_flash_cmd | tail -n 1 > build/download.config - - -def blecent_client_task(dut): - interface = 'hci0' - adv_host_name = 'BleCentTestApp' - adv_type = 'peripheral' - adv_uuid = '1811' - - # Get BLE client module - ble_client_obj = lib_ble_client.BLE_Bluez_Client(iface=interface) - - # Discover Bluetooth Adapter and power on - is_adapter_set = ble_client_obj.set_adapter() - if not is_adapter_set: - return - - ''' - Blecent application run: - Create GATT data - Register GATT Application - Create Advertising data - Register advertisement - Start advertising - ''' - # Create Gatt Application - # Register GATT Application - ble_client_obj.register_gatt_app() - - # Register Advertisement - # Check read/write/subscribe is received from device - ble_client_obj.register_adv(adv_host_name, adv_type, adv_uuid) - - # Check dut responses - dut.expect('Connection established', timeout=30) - dut.expect('Service discovery complete; status=0', timeout=30) - dut.expect('GATT procedure initiated: read;', timeout=30) - dut.expect('Read complete; status=0', timeout=30) - dut.expect('GATT procedure initiated: write;', timeout=30) - dut.expect('Write complete; status=0', timeout=30) - dut.expect('GATT procedure initiated: write;', timeout=30) - dut.expect('Subscribe complete; status=0', timeout=30) - dut.expect('received notification;', timeout=30) - - -class BleCentThread(threading.Thread): - def __init__(self, dut, exceptions_queue): - threading.Thread.__init__(self) - self.dut = dut - self.exceptions_queue = exceptions_queue - - def run(self): - try: - blecent_client_task(self.dut) - except RuntimeError: - self.exceptions_queue.put(traceback.format_exc(), block=False) - - -@ttfw_idf.idf_example_test(env_tag='Example_WIFI_BT') -def test_example_app_ble_central(env, extra_data): - """ - Steps: - 1. Discover Bluetooth Adapter and Power On - 2. Connect BLE Device - 3. Start Notifications - 4. Updated value is retrieved - 5. Stop Notifications - """ - # Remove cached bluetooth devices of any previous connections - subprocess.check_output(['rm', '-rf', '/var/lib/bluetooth/*']) - subprocess.check_output(['hciconfig', 'hci0', 'reset']) - - # Acquire DUT - dut = env.get_dut('blecent', 'examples/bluetooth/nimble/blecent', dut_class=ttfw_idf.ESP32DUT) - - # Get binary file - binary_file = os.path.join(dut.app.binary_path, 'blecent.bin') - bin_size = os.path.getsize(binary_file) - ttfw_idf.log_performance('blecent_bin_size', '{}KB'.format(bin_size // 1024)) - - # Upload binary and start testing - Utility.console_log('Starting blecent example test app') - dut.start_app() - dut.reset() - - exceptions_queue = Queue.Queue() - # Starting a py-client in a separate thread - blehr_thread_obj = BleCentThread(dut, exceptions_queue) - blehr_thread_obj.start() - blehr_thread_obj.join() - - exception_msg = None - while True: - try: - exception_msg = exceptions_queue.get(block=False) - except Queue.Empty: - break - else: - Utility.console_log('\n' + exception_msg) - - if exception_msg: - raise Exception('Blecent thread did not run successfully') - - -if __name__ == '__main__': - test_example_app_ble_central()