Skip to content

Commit

Permalink
bluetooth: BAS: add battery critical status char to bas service
Browse files Browse the repository at this point in the history
Added the battery critical status char to bas service
as per bas_1.1 spec. Updated BSIM test for BAS service
to test the INDs of BAS critical characteristic.

Signed-off-by: Nithin Ramesh Myliattil <niym@demant.com>
  • Loading branch information
niym-ot committed Oct 17, 2024
1 parent 82a192c commit dd9f664
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 1 deletion.
5 changes: 5 additions & 0 deletions subsys/bluetooth/services/bas/Kconfig.bas
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ config BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT
bool "Additional Battery Status Present"
help
Enable this option if Additional Battery Status information is present.

config BT_BAS_BCS
bool "Battery Critical Status"
help
Enable this option to include Battery Critical Status Characteristic.
endif
100 changes: 100 additions & 0 deletions subsys/bluetooth/services/bas/bas.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,38 @@ LOG_MODULE_REGISTER(bas, CONFIG_BT_BAS_LOG_LEVEL);

static uint8_t battery_level = 100U;

#if defined(CONFIG_BT_BAS_BCS)

#define BT_BAS_BATTERY_CRITICAL_STATUS_CHAR_HANDLE 9

/* Indicate battery critical status to all connections */
static struct bt_gatt_indicate_params bcs_ind_params;

static uint8_t battery_critical_status;
/** Battery Critical Status Bit 0: Critical Power State */
#define BCS_BATTERY_CRITICAL_STATE (1 << 0)
/** Battery Critical Status Bit 1: Immediate Service Required */
#define BCS_IMMEDIATE_SERVICE_REQUIRED (1 << 1)

static void bcs_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
ARG_UNUSED(attr);

bool ind_enabled = (value == BT_GATT_CCC_INDICATE);

LOG_INF("BAS Critical status Indication %s", ind_enabled ? "enabled" : "disabled");
}

static ssize_t read_battery_critical_status(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
uint8_t bcs = battery_critical_status;

return bt_gatt_attr_read(conn, attr, buf, len, offset, &bcs, sizeof(bcs));
}

#endif /* CONFIG_BT_BAS_BCS */

static void blvl_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
ARG_UNUSED(attr);
Expand Down Expand Up @@ -84,6 +116,12 @@ BT_GATT_SERVICE_DEFINE(
BT_GATT_PERM_READ, bt_bas_bls_read_blvl_status, NULL, NULL),
BT_GATT_CCC(blvl_status_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
#endif
#if defined(CONFIG_BT_BAS_BCS)
BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_CRIT_STATUS,
BT_GATT_CHRC_READ | BT_GATT_CHRC_INDICATE, BT_GATT_PERM_READ,
read_battery_critical_status, NULL, NULL),
BT_GATT_CCC(bcs_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
#endif /* CONFIG_BT_BAS_BCS */
);

static int bas_init(void)
Expand Down Expand Up @@ -131,4 +169,66 @@ const struct bt_gatt_attr *bt_bas_get_bas_attr(uint16_t index)
return NULL;
}

#if defined(CONFIG_BT_BAS_BCS)

static void bcs_indicate_cb(struct bt_conn *conn, struct bt_gatt_indicate_params *params,
uint8_t err)
{
if (err != 0) {
LOG_DBG("BCS Indication failed with error %d\n", err);
} else {
LOG_DBG("BCS Indication sent successfully\n");
}
}

static void bt_bas_bcs_update_battery_critical_status(void)
{
uint8_t err;

/* Indicate all connections */
bcs_ind_params.attr = &bas.attrs[BT_BAS_BATTERY_CRITICAL_STATUS_CHAR_HANDLE];
bcs_ind_params.data = &battery_critical_status;
bcs_ind_params.len = sizeof(battery_critical_status);
bcs_ind_params.func = bcs_indicate_cb;
err = bt_gatt_indicate(NULL, &bcs_ind_params);
if (err) {
LOG_DBG("Failed to send ind to all connections (err %d)\n", err);
}
}

void bt_bas_bcs_set_battery_critical_state(bool critical_state)
{
bool current_state = (battery_critical_status & BCS_BATTERY_CRITICAL_STATE) != 0;

if (current_state == critical_state) {
LOG_INF("Already battery_critical_state is %d\n", critical_state);
return;
}

if (critical_state) {
battery_critical_status |= BCS_BATTERY_CRITICAL_STATE;
} else {
battery_critical_status &= ~BCS_BATTERY_CRITICAL_STATE;
}
bt_bas_bcs_update_battery_critical_status();
}

void bt_bas_bcs_set_immediate_service_required(bool service_required)
{
bool current_state = (battery_critical_status & BCS_IMMEDIATE_SERVICE_REQUIRED) != 0;

if (current_state == service_required) {
LOG_INF("Already immediate_service_required is %d\n", service_required);
return;
}

if (service_required) {
battery_critical_status |= BCS_IMMEDIATE_SERVICE_REQUIRED;
} else {
battery_critical_status &= ~BCS_IMMEDIATE_SERVICE_REQUIRED;
}
bt_bas_bcs_update_battery_critical_status();
}
#endif /* CONFIG_BT_BAS_BCS */

SYS_INIT(bas_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
18 changes: 18 additions & 0 deletions subsys/bluetooth/services/bas/bas_bls.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ void bt_bas_bls_set_battery_charge_level(enum bt_bas_bls_battery_charge_level le
bls.power_state &= ~BATTERY_CHARGE_LEVEL_MASK;
bls.power_state |= (level << BATTERY_CHARGE_LEVEL_SHIFT) & BATTERY_CHARGE_LEVEL_MASK;
bt_bas_bls_update_battery_level_status();

#if defined(CONFIG_BT_BAS_BCS)
if (level == BT_BAS_BLS_CHARGE_LEVEL_CRITICAL) {
bt_bas_bcs_set_battery_critical_state(true);
return;
}

bt_bas_bcs_set_battery_critical_state(false);
#endif /* CONFIG_BT_BAS_BCS */
}

void bt_bas_bls_set_battery_charge_type(enum bt_bas_bls_battery_charge_type type)
Expand Down Expand Up @@ -232,6 +241,15 @@ void bt_bas_bls_set_service_required(enum bt_bas_bls_service_required value)
bls.additional_status &= ~SERVICE_REQUIRED_MASK;
bls.additional_status |= (value << SERVICE_REQUIRED_SHIFT) & SERVICE_REQUIRED_MASK;
bt_bas_bls_update_battery_level_status();

#if defined(CONFIG_BT_BAS_BCS)
if (value == BT_BAS_BLS_SERVICE_REQUIRED_TRUE) {
bt_bas_bcs_set_immediate_service_required(true);
return;
}

bt_bas_bcs_set_immediate_service_required(false);
#endif /* CONFIG_BT_BAS_BCS */
}

void bt_bas_bls_set_battery_fault(enum bt_bas_bls_battery_fault value)
Expand Down
14 changes: 14 additions & 0 deletions subsys/bluetooth/services/bas/bas_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@ void bt_bas_bls_init(void);
*/
void bt_bas_bls_set_battery_level(uint8_t battery_level);

/**
* @brief Set the battery critical state flag.
*
* @param critical_state The battery critical state to set (true for critical, false otherwise).
*/
void bt_bas_bcs_set_battery_critical_state(bool critical_state);

/**
* @brief Set the immediate service required flag.
*
* @param service_required The immediate service required status to set.
*/
void bt_bas_bcs_set_immediate_service_required(bool service_required);

/**
* @brief Read the Battery Level Status characteristic.
*
Expand Down
3 changes: 2 additions & 1 deletion tests/bsim/bluetooth/samples/battery_service/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ 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_ATT_TX_COUNT=10

CONFIG_BT_BAS_BLS=y
CONFIG_BT_BAS_BCS=y
CONFIG_BT_BAS_BLS_IDENTIFIER_PRESENT=y
CONFIG_BT_BAS_BLS_BATTERY_LEVEL_PRESENT=y
CONFIG_BT_BAS_BLS_ADDITIONAL_STATUS_PRESENT=y
53 changes: 53 additions & 0 deletions tests/bsim/bluetooth/samples/battery_service/src/central_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ 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;
static struct bt_gatt_subscribe_params battery_critical_status_sub_params;

/*
* Battery Service test:
Expand Down Expand Up @@ -311,6 +312,23 @@ static bool parse_battery_level_status(const uint8_t *data, uint16_t length)
return true;
}

static unsigned char battery_critical_status_indicate_cb(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params,
const void *data, uint16_t length)
{
if (!data) {
LOG_INF("BAS critical status indication disabled\n");
} else {
uint8_t status_byte = ((uint8_t *)data)[0];

printk("[INDICATION] BAS Critical Level Status:\n");
printk("Battery state: %s\n", (status_byte & 0x01) ? "Critical" : "Normal");
printk("Immediate service: %s\n",
(status_byte & 0x02) ? "Required" : "Not Required");
}
return BT_GATT_ITER_CONTINUE;
}

static unsigned char battery_level_status_indicate_cb(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params,
const void *data, uint16_t length)
Expand Down Expand Up @@ -402,6 +420,28 @@ static void subscribe_battery_level(const struct bt_gatt_attr *attr)
read_battery_level(attr);
}

static void subscribe_battery_critical_status(const struct bt_gatt_attr *attr)
{
int err;

battery_critical_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_critical_status_indicate_cb,
};

err = bt_gatt_subscribe(default_conn, &battery_critical_status_sub_params);
if (err && err != -EALREADY) {
TEST_FAIL("Subscribe failed (err %d)\n", err);
} else {
LOG_DBG("Battery critical status [SUBSCRIBED]\n");
}
}

static void subscribe_battery_level_status(const struct bt_gatt_attr *attr)
{
int err;
Expand Down Expand Up @@ -474,6 +514,19 @@ static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *at
} 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);

memcpy(&uuid, BT_UUID_BAS_BATTERY_CRIT_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_CRIT_STATUS)) {
LOG_DBG("Subscribe Batterry Critical Status Char\n");
subscribe_battery_critical_status(attr);
}
return BT_GATT_ITER_STOP;
}
Expand Down

0 comments on commit dd9f664

Please sign in to comment.