bluetooth: BAS: add battery level status char to bas service
Added the battery level status char to bas service as per bas_1.1 spec. Added BSIM test for BAS service to test the NTF/INDs of BAS characteristics. Signed-off-by: Nithin Ramesh Myliattil <niym@demant.com>
This commit is contained in:
parent
b3ae323add
commit
baa5683e59
19 changed files with 1643 additions and 110 deletions
29
tests/bsim/bluetooth/samples/battery_service/CMakeLists.txt
Normal file
29
tests/bsim/bluetooth/samples/battery_service/CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(battery_service_test)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/central_test.c
|
||||
src/peripheral_test.c
|
||||
)
|
||||
|
||||
# This contains a variety of helper functions that abstract away common tasks,
|
||||
# like scanning, setting up a connection, querying the peer for a given
|
||||
# characteristic, etc..
|
||||
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib)
|
||||
target_link_libraries(app PRIVATE testlib)
|
||||
|
||||
# This contains babblesim-specific helpers, e.g. device synchronization.
|
||||
add_subdirectory(${ZEPHYR_BASE}/tests/bsim/babblekit babblekit)
|
||||
target_link_libraries(app PRIVATE babblekit)
|
||||
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
|
||||
|
||||
zephyr_include_directories(
|
||||
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
14
tests/bsim/bluetooth/samples/battery_service/prj.conf
Normal file
14
tests/bsim/bluetooth/samples/battery_service/prj.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_BAS=y
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
CONFIG_BT_DEVICE_NAME="bsim_bas"
|
||||
CONFIG_BT_ATT_TX_COUNT=5
|
||||
|
||||
CONFIG_BT_BAS_BLS=y
|
||||
CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT=y
|
||||
CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT=y
|
||||
CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT=y
|
562
tests/bsim/bluetooth/samples/battery_service/src/central_test.c
Normal file
562
tests/bsim/bluetooth/samples/battery_service/src/central_test.c
Normal file
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Demant A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "bs_types.h"
|
||||
#include "bs_tracing.h"
|
||||
#include "time_machine.h"
|
||||
#include "bstests.h"
|
||||
#include <argparse.h>
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/bluetooth/services/bas.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include "testlib/conn.h"
|
||||
#include "testlib/scan.h"
|
||||
#include "testlib/log_utils.h"
|
||||
|
||||
#include "babblekit/flags.h"
|
||||
#include "babblekit/sync.h"
|
||||
#include "babblekit/testcase.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(bt_bsim_bas, CONFIG_BT_BAS_LOG_LEVEL);
|
||||
|
||||
static struct bt_conn *default_conn;
|
||||
static bt_addr_le_t peer = {};
|
||||
|
||||
static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0);
|
||||
static struct bt_gatt_discover_params discover_params;
|
||||
|
||||
static struct bt_gatt_subscribe_params battery_level_notify_params;
|
||||
static struct bt_gatt_subscribe_params battery_level_status_sub_params;
|
||||
|
||||
/*
|
||||
* Battery Service test:
|
||||
* We expect to find a connectable peripheral to which we will
|
||||
* connect and discover Battery Service
|
||||
*
|
||||
* Test the Read/Notify/Indicate Characteristics of BAS
|
||||
*/
|
||||
|
||||
#define WAIT_TIME 10 /*seconds*/
|
||||
#define BAS_BLS_IND_RECEIVED_COUNT 20
|
||||
#define BAS_BLS_NTF_RECEIVED_COUNT 20
|
||||
|
||||
static DEFINE_FLAG(notification_count_reached);
|
||||
static DEFINE_FLAG(indication_count_reached);
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
static void test_bas_central_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(WAIT_TIME * 1e6);
|
||||
bst_result = In_progress;
|
||||
}
|
||||
|
||||
static void test_bas_central_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
/*
|
||||
* If in WAIT_TIME seconds the testcase did not already pass
|
||||
* (and finish) we consider it failed
|
||||
*/
|
||||
if (bst_result != Passed) {
|
||||
TEST_FAIL("test_bas_central failed (not passed after %i seconds)\n", WAIT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback for handling Battery Level Notifications */
|
||||
static uint8_t battery_level_notify_cb(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params, const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
if (data) {
|
||||
LOG_INF("[NOTIFICATION] BAS Battery Level: %d%%", *(const uint8_t *)data);
|
||||
} else {
|
||||
LOG_INF("Battery Level Notifications disabled");
|
||||
}
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
/* Callback for handling Battery Level Read Response */
|
||||
static uint8_t battery_level_read_cb(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_read_params *params, const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
TEST_ASSERT(err == 0, "Failed to read Battery Level (err %d)", err);
|
||||
if (data) {
|
||||
LOG_DBG("[READ] BAS Battery Level: %d%%\n", *(const uint8_t *)data);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static bool parse_battery_level_status(const uint8_t *data, uint16_t length)
|
||||
{
|
||||
/* Check minimum length for parsing flags and power state */
|
||||
if (length < 3) {
|
||||
TEST_FAIL("Invalid data length: %d", length);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Parse flags (first byte) */
|
||||
uint8_t flags = data[0];
|
||||
|
||||
LOG_INF("Parsed Flags: 0x%02x", flags);
|
||||
|
||||
if (flags & BT_BAS_BLS_FLAG_IDENTIFIER_PRESENT) {
|
||||
LOG_INF(" Identifier Present");
|
||||
} else {
|
||||
LOG_INF(" Identifier Not Present");
|
||||
}
|
||||
|
||||
if (flags & BT_BAS_BLS_FLAG_BATTERY_LEVEL_PRESENT) {
|
||||
LOG_INF(" Battery Level Present");
|
||||
} else {
|
||||
LOG_INF(" Battery Level Not Present");
|
||||
}
|
||||
|
||||
if (flags & BT_BAS_BLS_FLAG_ADDITIONAL_STATUS_PRESENT) {
|
||||
LOG_INF(" Additional Status Present");
|
||||
} else {
|
||||
LOG_INF(" Additional Status Not Present");
|
||||
}
|
||||
|
||||
/* Parse power state (next 2 bytes) */
|
||||
uint16_t power_state = sys_get_le16(&data[1]);
|
||||
|
||||
LOG_INF("Parsed Power State: 0x%04x", power_state);
|
||||
/* Print out each power state value */
|
||||
LOG_INF(" Battery Present: %s", (power_state & BIT(0)) ? "Yes" : "No");
|
||||
|
||||
uint8_t wired_power = (power_state >> 1) & 0x03;
|
||||
|
||||
switch (wired_power) {
|
||||
case 0:
|
||||
LOG_INF(" Wired Power Source: No");
|
||||
break;
|
||||
case 1:
|
||||
LOG_INF(" Wired Power Source: Yes");
|
||||
break;
|
||||
case 2:
|
||||
LOG_INF(" Wired Power Source: Unknown");
|
||||
break;
|
||||
default:
|
||||
LOG_INF(" Wired Power Source: RFU");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t wireless_power = (power_state >> 3) & 0x03;
|
||||
|
||||
switch (wireless_power) {
|
||||
case 0:
|
||||
LOG_INF(" Wireless Power Source: No");
|
||||
break;
|
||||
case 1:
|
||||
LOG_INF(" Wireless Power Source: Yes");
|
||||
break;
|
||||
case 2:
|
||||
LOG_INF(" Wireless Power Source: Unknown");
|
||||
break;
|
||||
default:
|
||||
LOG_INF(" Wireless Power Source: RFU");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t charge_state = (power_state >> 5) & 0x03;
|
||||
|
||||
switch (charge_state) {
|
||||
case 0:
|
||||
LOG_INF(" Battery Charge State: Unknown");
|
||||
break;
|
||||
case 1:
|
||||
LOG_INF(" Battery Charge State: Charging");
|
||||
break;
|
||||
case 2:
|
||||
LOG_INF(" Battery Charge State: Discharging (Active)");
|
||||
break;
|
||||
case 3:
|
||||
LOG_INF(" Battery Charge State: Discharging (Inactive)");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t charge_level = (power_state >> 7) & 0x03;
|
||||
|
||||
switch (charge_level) {
|
||||
case 0:
|
||||
LOG_INF(" Battery Charge Level: Unknown");
|
||||
break;
|
||||
case 1:
|
||||
LOG_INF(" Battery Charge Level: Good");
|
||||
break;
|
||||
case 2:
|
||||
LOG_INF(" Battery Charge Level: Low");
|
||||
break;
|
||||
case 3:
|
||||
LOG_INF(" Battery Charge Level: Critical");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t charging_type = (power_state >> 9) & 0x07;
|
||||
|
||||
switch (charging_type) {
|
||||
case 0:
|
||||
LOG_INF(" Charging Type: Unknown or Not Charging");
|
||||
break;
|
||||
case 1:
|
||||
LOG_INF(" Charging Type: Constant Current");
|
||||
break;
|
||||
case 2:
|
||||
LOG_INF(" Charging Type: Constant Voltage");
|
||||
break;
|
||||
case 3:
|
||||
LOG_INF(" Charging Type: Trickle");
|
||||
break;
|
||||
case 4:
|
||||
LOG_INF(" Charging Type: Float");
|
||||
break;
|
||||
default:
|
||||
LOG_INF(" Charging Type: RFU");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t charging_fault = (power_state >> 12) & 0x07;
|
||||
|
||||
if (charging_fault) {
|
||||
LOG_INF(" Charging Fault Reason: %s%s%s",
|
||||
(charging_fault & BIT(0)) ? "Battery " : "",
|
||||
(charging_fault & BIT(1)) ? "External Power Source " : "",
|
||||
(charging_fault & BIT(2)) ? "Other " : "");
|
||||
} else {
|
||||
LOG_INF(" Charging Fault Reason: None");
|
||||
}
|
||||
|
||||
/* Optional: Check if identifier is present */
|
||||
if (IS_ENABLED(CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT)) {
|
||||
/* Check if length is sufficient for identifier */
|
||||
if (length < 5) {
|
||||
TEST_FAIL("Invalid data length for identifier");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Parse identifier (next 2 bytes) */
|
||||
uint16_t identifier = sys_get_le16(&data[3]);
|
||||
|
||||
LOG_INF("Parsed Identifier: 0x%04x", identifier);
|
||||
}
|
||||
|
||||
/* Optional: Check if battery level is present */
|
||||
if (IS_ENABLED(CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT)) {
|
||||
/* Check if length is sufficient for battery level */
|
||||
if (length < 6) {
|
||||
TEST_FAIL("Invalid data length for battery level");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Parse battery level (next byte) */
|
||||
uint8_t battery_level = data[5];
|
||||
|
||||
LOG_INF("Parsed Battery Level: %d%%", battery_level);
|
||||
}
|
||||
|
||||
/* Optional: Check if additional status is present */
|
||||
if (IS_ENABLED(CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT)) {
|
||||
/* Check if length is sufficient for additional status */
|
||||
if (length < 7) {
|
||||
TEST_FAIL("Invalid data length for additional status");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Parse additional status (next byte) */
|
||||
uint8_t additional_status = data[6];
|
||||
|
||||
LOG_INF("Parsed Additional Status: 0x%02x", additional_status);
|
||||
|
||||
/* Print out additional status values */
|
||||
uint8_t service_required = additional_status & 0x03;
|
||||
|
||||
switch (service_required) {
|
||||
case 0:
|
||||
LOG_INF(" Service Required: False");
|
||||
break;
|
||||
case 1:
|
||||
LOG_INF(" Service Required: True");
|
||||
break;
|
||||
case 2:
|
||||
LOG_INF(" Service Required: Unknown");
|
||||
break;
|
||||
default:
|
||||
LOG_INF(" Service Required: RFU");
|
||||
break;
|
||||
}
|
||||
|
||||
bool battery_fault = (additional_status & BIT(2)) ? true : false;
|
||||
|
||||
LOG_INF(" Battery Fault: %s", battery_fault ? "Yes" : "No");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned char battery_level_status_indicate_cb(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
if (!data) {
|
||||
LOG_INF("bas level status indication disabled\n");
|
||||
} else {
|
||||
static int ind_received;
|
||||
|
||||
printk("[INDICATION] BAS Battery Level Status: ");
|
||||
for (int i = 0; i < length; i++) {
|
||||
printk("%02x ", ((uint8_t *)data)[i]);
|
||||
}
|
||||
printk("\n");
|
||||
|
||||
if (parse_battery_level_status(data, length)) {
|
||||
LOG_INF("Notification parsed successfully");
|
||||
} else {
|
||||
LOG_ERR("Notification parsing failed");
|
||||
}
|
||||
|
||||
if (ind_received++ > BAS_BLS_IND_RECEIVED_COUNT) {
|
||||
SET_FLAG(indication_count_reached);
|
||||
}
|
||||
}
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static uint8_t battery_level_status_notify_cb(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
if (!data) {
|
||||
LOG_INF("bas level status notification disabled\n");
|
||||
} else {
|
||||
static int notify_count;
|
||||
|
||||
printk("[NOTIFICATION] BAS Battery Level Status: ");
|
||||
for (int i = 0; i < length; i++) {
|
||||
printk("%02x ", ((uint8_t *)data)[i]);
|
||||
}
|
||||
printk("\n");
|
||||
|
||||
if (parse_battery_level_status(data, length)) {
|
||||
LOG_INF("Notification parsed successfully");
|
||||
} else {
|
||||
LOG_ERR("Notification parsing failed");
|
||||
}
|
||||
|
||||
if (notify_count++ > BAS_BLS_NTF_RECEIVED_COUNT) {
|
||||
SET_FLAG(notification_count_reached);
|
||||
}
|
||||
}
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void read_battery_level(const struct bt_gatt_attr *attr)
|
||||
{
|
||||
/* Read the battery level after subscribing */
|
||||
static struct bt_gatt_read_params read_params;
|
||||
|
||||
read_params.func = battery_level_read_cb;
|
||||
read_params.handle_count = 1;
|
||||
read_params.single.handle = bt_gatt_attr_get_handle(attr);
|
||||
read_params.single.offset = 0;
|
||||
bt_gatt_read(default_conn, &read_params);
|
||||
}
|
||||
|
||||
static void subscribe_battery_level(const struct bt_gatt_attr *attr)
|
||||
{
|
||||
int err;
|
||||
|
||||
battery_level_notify_params = (struct bt_gatt_subscribe_params){
|
||||
/* In Zephyr, it is common practice for the CCC handle
|
||||
* to be positioned two handles after the characteristic handle.
|
||||
*/
|
||||
.ccc_handle = bt_gatt_attr_get_handle(attr) + 2,
|
||||
.value_handle = bt_gatt_attr_value_handle(attr),
|
||||
.value = BT_GATT_CCC_NOTIFY,
|
||||
.notify = battery_level_notify_cb,
|
||||
};
|
||||
|
||||
err = bt_gatt_subscribe(default_conn, &battery_level_notify_params);
|
||||
if (err && err != -EALREADY) {
|
||||
TEST_FAIL("Subscribe failed (err %d)\n", err);
|
||||
} else {
|
||||
LOG_DBG("Battery level [SUBSCRIBED]\n");
|
||||
}
|
||||
read_battery_level(attr);
|
||||
}
|
||||
|
||||
static void subscribe_battery_level_status(const struct bt_gatt_attr *attr)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (get_device_nbr() == 1) { /* One device for Indication */
|
||||
battery_level_status_sub_params = (struct bt_gatt_subscribe_params){
|
||||
/* In Zephyr, it is common practice for the CCC handle
|
||||
* to be positioned two handles after the characteristic handle.
|
||||
*/
|
||||
.ccc_handle = bt_gatt_attr_get_handle(attr) + 2,
|
||||
.value_handle = bt_gatt_attr_value_handle(attr),
|
||||
.value = BT_GATT_CCC_INDICATE,
|
||||
.notify = battery_level_status_indicate_cb,
|
||||
};
|
||||
} else { /* Other device for Notification */
|
||||
battery_level_status_sub_params = (struct bt_gatt_subscribe_params){
|
||||
.ccc_handle = bt_gatt_attr_get_handle(attr) + 2,
|
||||
.value_handle = bt_gatt_attr_value_handle(attr),
|
||||
.value = BT_GATT_CCC_NOTIFY,
|
||||
.notify = battery_level_status_notify_cb,
|
||||
};
|
||||
}
|
||||
|
||||
err = bt_gatt_subscribe(default_conn, &battery_level_status_sub_params);
|
||||
if (err && err != -EALREADY) {
|
||||
TEST_FAIL("Subscribe failed (err %d)\n", err);
|
||||
} else {
|
||||
LOG_DBG("Battery level status [SUBSCRIBED]\n");
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
struct bt_gatt_discover_params *params)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!attr) {
|
||||
LOG_DBG("Discover complete\n");
|
||||
memset(params, 0, sizeof(*params));
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
LOG_DBG("[ATTRIBUTE] handle %u\n", attr->handle);
|
||||
|
||||
if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_BAS)) {
|
||||
LOG_DBG("Battery Service\n");
|
||||
memcpy(&uuid, BT_UUID_BAS_BATTERY_LEVEL, sizeof(uuid));
|
||||
discover_params.uuid = &uuid.uuid;
|
||||
discover_params.start_handle = attr->handle + 1;
|
||||
discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(conn, &discover_params);
|
||||
if (err) {
|
||||
TEST_FAIL("Discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
} else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_BAS_BATTERY_LEVEL)) {
|
||||
LOG_DBG("Subscribe Battery Level Char\n");
|
||||
subscribe_battery_level(attr);
|
||||
|
||||
memcpy(&uuid, BT_UUID_BAS_BATTERY_LEVEL_STATUS, sizeof(uuid));
|
||||
discover_params.uuid = &uuid.uuid;
|
||||
discover_params.start_handle = attr->handle + 1;
|
||||
discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(conn, &discover_params);
|
||||
if (err) {
|
||||
TEST_FAIL("Discover failed (err %d)\n", err);
|
||||
}
|
||||
} else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_BAS_BATTERY_LEVEL_STATUS)) {
|
||||
LOG_DBG("Subscribe Batterry Level Status Char\n");
|
||||
subscribe_battery_level_status(attr);
|
||||
}
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static void discover_bas_service(struct bt_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("%s\n", __func__);
|
||||
|
||||
memcpy(&uuid, BT_UUID_BAS, sizeof(uuid));
|
||||
discover_params.uuid = &uuid.uuid;
|
||||
discover_params.func = discover_func;
|
||||
discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
|
||||
discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
||||
discover_params.type = BT_GATT_DISCOVER_PRIMARY;
|
||||
err = bt_gatt_discover(conn, &discover_params);
|
||||
if (err) {
|
||||
TEST_FAIL("Discover failed(err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_bas_central_main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Mark test as in progress. */
|
||||
TEST_START("central");
|
||||
/* bk_sync_init only works between two devices in a simulation, with IDs 0 and 1. */
|
||||
if (get_device_nbr() == 1) {
|
||||
/* Initialize device sync library */
|
||||
bk_sync_init();
|
||||
}
|
||||
|
||||
err = bt_enable(NULL);
|
||||
TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
|
||||
|
||||
LOG_DBG("Bluetooth initialized\n");
|
||||
|
||||
err = bt_testlib_scan_find_name(&peer, CONFIG_BT_DEVICE_NAME);
|
||||
TEST_ASSERT(!err, "Failed to start scan (err %d)", err);
|
||||
|
||||
/* Create a connection using that address */
|
||||
err = bt_testlib_connect(&peer, &default_conn);
|
||||
TEST_ASSERT(!err, "Failed to initiate connection (err %d)", err);
|
||||
|
||||
LOG_DBG("Connected");
|
||||
discover_bas_service(default_conn);
|
||||
|
||||
if (get_device_nbr() == 1) {
|
||||
WAIT_FOR_FLAG(indication_count_reached);
|
||||
LOG_INF("Indication Count Reached!");
|
||||
} else {
|
||||
WAIT_FOR_FLAG(notification_count_reached);
|
||||
LOG_INF("Notification Count Reached!");
|
||||
}
|
||||
|
||||
/* bk_sync_send only works between two devices in a simulation, with IDs 0 and 1. */
|
||||
if (get_device_nbr() == 1) {
|
||||
bk_sync_send();
|
||||
}
|
||||
|
||||
bst_result = Passed;
|
||||
TEST_PASS("Central Test Passed");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_bas_central[] = {
|
||||
{
|
||||
.test_id = "central",
|
||||
.test_descr =
|
||||
"Battery Service test. It expects that a peripheral device can be found. "
|
||||
"The test will pass if it can receive notifications and indications more "
|
||||
"than the threshold set within 15 sec. ",
|
||||
.test_pre_init_f = test_bas_central_init,
|
||||
.test_tick_f = test_bas_central_tick,
|
||||
.test_main_f = test_bas_central_main,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_bas_central_install(struct bst_test_list *tests)
|
||||
{
|
||||
tests = bst_add_tests(tests, test_bas_central);
|
||||
return tests;
|
||||
}
|
22
tests/bsim/bluetooth/samples/battery_service/src/main.c
Normal file
22
tests/bsim/bluetooth/samples/battery_service/src/main.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Demant A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "bstests.h"
|
||||
|
||||
extern struct bst_test_list *test_bas_central_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_bas_peripheral_install(struct bst_test_list *tests);
|
||||
|
||||
bst_test_install_t test_installers[] = {
|
||||
test_bas_central_install,
|
||||
test_bas_peripheral_install,
|
||||
NULL,
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bst_main();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Demant A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "bs_types.h"
|
||||
#include "bs_tracing.h"
|
||||
#include "time_machine.h"
|
||||
#include "bstests.h"
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/bluetooth/services/bas.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include "testlib/conn.h"
|
||||
#include "testlib/scan.h"
|
||||
#include "testlib/log_utils.h"
|
||||
|
||||
#include "babblekit/flags.h"
|
||||
#include "babblekit/sync.h"
|
||||
#include "babblekit/testcase.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(bt_bsim_bas, CONFIG_BT_BAS_LOG_LEVEL);
|
||||
|
||||
static struct bt_conn *default_conn;
|
||||
|
||||
static struct k_work_delayable update_bas_char_work;
|
||||
|
||||
/*
|
||||
* Battery Service test:
|
||||
* We expect a central to connect to us.
|
||||
*/
|
||||
|
||||
#define WAIT_TIME 10 /*seconds*/
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
static void test_bas_peripheral_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(WAIT_TIME * 1e6);
|
||||
bst_result = In_progress;
|
||||
}
|
||||
|
||||
static void test_bas_peripheral_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
/*
|
||||
* If in WAIT_TIME seconds the testcase did not already pass
|
||||
* (and finish) we consider it failed
|
||||
*/
|
||||
if (bst_result != Passed) {
|
||||
TEST_FAIL("test_bas_peripheral failed (not passed after %i seconds)\n", WAIT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
|
||||
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
|
||||
};
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
if (err) {
|
||||
TEST_FAIL("Connection failed (err 0x%02x)\n", err);
|
||||
} else {
|
||||
default_conn = bt_conn_ref(conn);
|
||||
|
||||
LOG_DBG("Peripheral Connected\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
LOG_DBG("Peripheral %s (reason 0x%02x)\n", __func__, reason);
|
||||
|
||||
if (default_conn) {
|
||||
bt_conn_unref(default_conn);
|
||||
default_conn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_conn_cb conn_callbacks = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
static void bt_ready(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("Peripheral Bluetooth initialized\n");
|
||||
|
||||
err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (err) {
|
||||
TEST_FAIL("Advertising failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Advertising successfully started");
|
||||
}
|
||||
|
||||
static void update_bas_char(void)
|
||||
{
|
||||
LOG_DBG("[PERIPHERAL] setting battery level");
|
||||
bt_bas_set_battery_level(90);
|
||||
LOG_DBG("[PERIPHERAL] setting battery present");
|
||||
bt_bas_bls_set_battery_present(BT_BAS_BLS_BATTERY_PRESENT);
|
||||
LOG_DBG("[PERIPHERAL] setting battery charge level");
|
||||
bt_bas_bls_set_battery_charge_level(BT_BAS_BLS_CHARGE_LEVEL_CRITICAL);
|
||||
LOG_DBG("[PERIPHERAL] setting battery service required true");
|
||||
bt_bas_bls_set_service_required(BT_BAS_BLS_SERVICE_REQUIRED_TRUE);
|
||||
LOG_DBG("[PERIPHERAL] setting battery service charge type ");
|
||||
bt_bas_bls_set_battery_charge_type(BT_BAS_BLS_CHARGE_TYPE_FLOAT);
|
||||
}
|
||||
|
||||
/* Work handler function */
|
||||
void update_bas_char_work_handler(struct k_work *work)
|
||||
{
|
||||
update_bas_char();
|
||||
k_work_reschedule(&update_bas_char_work, K_SECONDS(1));
|
||||
}
|
||||
|
||||
static void test_bas_peripheral_main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
bt_conn_cb_register(&conn_callbacks);
|
||||
|
||||
/* Mark test as in progress. */
|
||||
TEST_START("peripheral");
|
||||
|
||||
/* Initialize device sync library */
|
||||
bk_sync_init();
|
||||
|
||||
/* Initialize Bluetooth */
|
||||
err = bt_enable(NULL);
|
||||
TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
|
||||
|
||||
LOG_DBG("Bluetooth initialized");
|
||||
|
||||
bt_ready();
|
||||
|
||||
/* Initialize the update bas char work handler */
|
||||
k_work_init_delayable(&update_bas_char_work, update_bas_char_work_handler);
|
||||
|
||||
/* Schedule the update bas char work for delayed execution */
|
||||
k_work_schedule(&update_bas_char_work, K_SECONDS(1));
|
||||
|
||||
/* Main thread waits for the sync signal from other device */
|
||||
bk_sync_wait();
|
||||
|
||||
bst_result = Passed;
|
||||
TEST_PASS_AND_EXIT("Peripheral Test Passed");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_bas_peripheral[] = {
|
||||
{
|
||||
.test_id = "peripheral",
|
||||
.test_descr = "Battery Service test. It expects that a central device can be found "
|
||||
"The test will pass if ind/ntf can be sent without crash. ",
|
||||
.test_pre_init_f = test_bas_peripheral_init,
|
||||
.test_tick_f = test_bas_peripheral_tick,
|
||||
.test_main_f = test_bas_peripheral_main,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_bas_peripheral_install(struct bst_test_list *tests)
|
||||
{
|
||||
tests = bst_add_tests(tests, test_bas_peripheral);
|
||||
return tests;
|
||||
}
|
29
tests/bsim/bluetooth/samples/battery_service/tests_scripts/bas.sh
Executable file
29
tests/bsim/bluetooth/samples/battery_service/tests_scripts/bas.sh
Executable file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2024 Demant A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
|
||||
|
||||
# Battery service test: a central connects to a peripheral and expects a
|
||||
# indication/notification of BAS chars from peripheral
|
||||
simulation_id="battery_service_test"
|
||||
verbosity_level=2
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_samples_battery_service_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=0 \
|
||||
-testid=peripheral -rs=23
|
||||
|
||||
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_samples_battery_service_prj_conf\
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=1 \
|
||||
-testid=central -rs=6
|
||||
|
||||
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_samples_battery_service_prj_conf\
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=2 \
|
||||
-testid=central -rs=6
|
||||
|
||||
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
|
||||
-D=3 -sim_length=10e6 $@
|
||||
|
||||
wait_for_background_jobs
|
|
@ -37,5 +37,10 @@ app=tests/bsim/bluetooth/samples/central_hr_peripheral_hr \
|
|||
extra_conf_file=${ZEPHYR_BASE}/samples/bluetooth/central_hr/prj.conf \
|
||||
conf_overlay=${ZEPHYR_BASE}/samples/bluetooth/central_hr/overlay-phy_coded.conf \
|
||||
compile
|
||||
if [ ${BOARD} == "nrf52_bsim" ]; then
|
||||
app=tests/bsim/bluetooth/samples/battery_service \
|
||||
conf_file=prj.conf \
|
||||
compile
|
||||
fi
|
||||
|
||||
wait_for_background_jobs
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
# This file is used in CI to select which tests are run
|
||||
tests/bsim/bluetooth/ll/conn/tests_scripts/basic_conn_encrypted_split_privacy.sh
|
||||
tests/bsim/bluetooth/ll/bis/tests_scripts/broadcast_iso.sh
|
||||
tests/bsim/bluetooth/samples/
|
||||
tests/bsim/bluetooth/samples/central_hr_peripheral_hr/
|
||||
tests/bsim/bluetooth/audio_samples/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue