Merge branch 'ble_dev/added_ble_power_save_pytest_v5.0' into 'release/v5.0'

Added pytest case for BLE power save example v5.0

See merge request espressif/esp-idf!28594
This commit is contained in:
Island
2024-01-31 18:33:14 +08:00
14 changed files with 169 additions and 151 deletions

View File

@@ -164,10 +164,19 @@ examples/bluetooth/nimble/blecent:
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3" ] - if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3" ]
temporary: true temporary: true
reason: the other targets are not tested yet reason: the other targets are not tested yet
disable_test: disable:
- if: IDF_TARGET in ["esp32c2", "esp32c3", "esp32s3"] - if: SOC_BLE_SUPPORTED != 1
temporary: true depends_components:
reason: The runner doesn't support yet - 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: examples/bluetooth/nimble/blehr:
enable: enable:
@@ -207,6 +216,19 @@ examples/bluetooth/nimble/hci:
examples/bluetooth/nimble/power_save: examples/bluetooth/nimble/power_save:
enable: enable:
- if: IDF_TARGET in ["esp32", "esp32c3", "esp32s3"] - if: IDF_TARGET in ["esp32", "esp32c2", "esp32c3", "esp32s3"]
temporary: true temporary: true
reason: the other targets are not tested yet 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

View File

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

View File

@@ -28,4 +28,12 @@ menu "Example Configuration"
prompt "Enable Link Encryption" prompt "Enable Link Encryption"
help help
This enables bonding and encryption after connection has been established. 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 endmenu

View File

@@ -455,17 +455,26 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
{ {
int offset = 0; int offset = 0;
int ad_struct_len = 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 && if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
} }
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 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); ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */ /* Convert string to address */
sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&peer_addr[5], &peer_addr[4], &peer_addr[3], &peer_addr[5], &peer_addr[4], &peer_addr[3],
&peer_addr[2], &peer_addr[1], &peer_addr[0]); &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) { if (memcmp(peer_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0; return 0;
} }
@@ -501,6 +510,9 @@ blecent_should_connect(const struct ble_gap_disc_desc *disc)
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
int rc; int rc;
int i; 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. */ /* The device has to be advertising connectability. */
if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && 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)) { 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); ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
#if !CONFIG_EXAMPLE_USE_CI_ADDRESS
/* Convert string to address */ /* Convert string to address */
sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&peer_addr[5], &peer_addr[4], &peer_addr[3], &peer_addr[5], &peer_addr[4], &peer_addr[3],
&peer_addr[2], &peer_addr[1], &peer_addr[0]); &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) { if (memcmp(peer_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0; return 0;
} }

View File

@@ -0,0 +1,2 @@
CONFIG_EXAMPLE_USE_CI_ADDRESS=y
CONFIG_EXAMPLE_PEER_ADDR="${CI_JOB_ID}"

View File

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

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- |
Bluetooth Power Save Example Bluetooth Power Save Example
================================= =================================

View File

@@ -145,4 +145,19 @@ menu "Example Configuration"
help help
Use this option to enable resolving peer's address. 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 endmenu

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * 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 const char *tag = "NimBLE_BLE_PRPH";
static int bleprph_gap_event(struct ble_gap_event *event, void *arg); 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; static uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM;
#else #else
static uint8_t own_addr_type; static uint8_t own_addr_type;
@@ -93,7 +93,7 @@ ext_bleprph_advertise(void)
params.connectable = 1; params.connectable = 1;
/* advertise using random addr */ /* 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.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
@@ -462,14 +462,26 @@ bleprph_on_sync(void)
ble_app_set_addr(); ble_app_set_addr();
#endif #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) */ /* 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); rc = ble_hs_util_ensure_addr(1);
#else #else
rc = ble_hs_util_ensure_addr(0); rc = ble_hs_util_ensure_addr(0);
#endif #endif
assert(rc == 0); assert(rc == 0);
/* Figure out address to use while advertising (no privacy for now) */ /* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {

View File

@@ -0,0 +1,5 @@
#
# Test Config
#
CONFIG_EXAMPLE_USE_CI_ADDRESS=y
CONFIG_EXAMPLE_CI_ADDRESS_OFFSET="${CI_JOB_ID}"

View File

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

View File

@@ -5,6 +5,8 @@ CONFIG_BT_CTRL_MODEM_SLEEP=y
CONFIG_BT_CTRL_MODEM_SLEEP_MODE_1=y CONFIG_BT_CTRL_MODEM_SLEEP_MODE_1=y
# Bluetooth low power clock # Bluetooth low power clock
CONFIG_BT_CTRL_LPCLK_SEL_EXT_32K_XTAL=y 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 # RTC clock source
CONFIG_RTC_CLK_SRC_EXT_CRYS=y CONFIG_RTC_CLK_SRC_EXT_CRYS=y

View File

@@ -5,6 +5,8 @@ CONFIG_BT_CTRL_MODEM_SLEEP=y
CONFIG_BT_CTRL_MODEM_SLEEP_MODE_1=y CONFIG_BT_CTRL_MODEM_SLEEP_MODE_1=y
# Bluetooth low power clock # Bluetooth low power clock
CONFIG_BT_CTRL_LPCLK_SEL_EXT_32K_XTAL=y 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 # RTC clock source
CONFIG_RTC_CLK_SRC_EXT_CRYS=y CONFIG_RTC_CLK_SRC_EXT_CRYS=y

View File

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