feat(ble): Support perhipheral battery levels.
* Add ability to fetch and report peripheral battery levels on split centrals. * Add additional support for adding a new Battery Level service to split centrals that exposes fetched peripheral battery levels to connected hosts. Co-authored-by: Peter Johanson <peter@peterjohanson.com>
This commit is contained in:
parent
d35311af97
commit
0e2f94b73b
9 changed files with 299 additions and 21 deletions
|
@ -15,3 +15,11 @@ struct zmk_battery_state_changed {
|
||||||
};
|
};
|
||||||
|
|
||||||
ZMK_EVENT_DECLARE(zmk_battery_state_changed);
|
ZMK_EVENT_DECLARE(zmk_battery_state_changed);
|
||||||
|
|
||||||
|
struct zmk_peripheral_battery_state_changed {
|
||||||
|
uint8_t source;
|
||||||
|
// TODO: Other battery channels
|
||||||
|
uint8_t state_of_charge;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZMK_EVENT_DECLARE(zmk_peripheral_battery_state_changed);
|
|
@ -16,3 +16,9 @@ int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *bi
|
||||||
int zmk_split_bt_update_hid_indicator(zmk_hid_indicators_t indicators);
|
int zmk_split_bt_update_hid_indicator(zmk_hid_indicators_t indicators);
|
||||||
|
|
||||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||||
|
|
||||||
|
int zmk_split_get_peripheral_battery_level(uint8_t source, uint8_t *level);
|
||||||
|
|
||||||
|
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
|
@ -63,8 +63,9 @@ void battery_status_update_cb(struct battery_status_state state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct battery_status_state battery_status_get_state(const zmk_event_t *eh) {
|
static struct battery_status_state battery_status_get_state(const zmk_event_t *eh) {
|
||||||
|
const struct zmk_battery_state_changed *ev = as_zmk_battery_state_changed(eh);
|
||||||
return (struct battery_status_state) {
|
return (struct battery_status_state) {
|
||||||
.level = bt_bas_get_battery_level(),
|
.level = ev->state_of_charge,
|
||||||
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
|
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
|
||||||
.usb_present = zmk_usb_is_powered(),
|
.usb_present = zmk_usb_is_powered(),
|
||||||
#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */
|
#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */
|
||||||
|
|
|
@ -8,3 +8,5 @@
|
||||||
#include <zmk/events/battery_state_changed.h>
|
#include <zmk/events/battery_state_changed.h>
|
||||||
|
|
||||||
ZMK_EVENT_IMPL(zmk_battery_state_changed);
|
ZMK_EVENT_IMPL(zmk_battery_state_changed);
|
||||||
|
|
||||||
|
ZMK_EVENT_IMPL(zmk_peripheral_battery_state_changed);
|
|
@ -9,3 +9,7 @@ endif()
|
||||||
if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||||
target_sources(app PRIVATE central.c)
|
target_sources(app PRIVATE central.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY)
|
||||||
|
target_sources(app PRIVATE central_bas_proxy.c)
|
||||||
|
endif()
|
|
@ -16,12 +16,36 @@ config ZMK_SPLIT_ROLE_CENTRAL
|
||||||
select BT_GATT_AUTO_DISCOVER_CCC
|
select BT_GATT_AUTO_DISCOVER_CCC
|
||||||
select BT_SCAN_WITH_IDENTITY
|
select BT_SCAN_WITH_IDENTITY
|
||||||
|
|
||||||
|
# Bump this value needed for concurrent GATT discovery of splits
|
||||||
|
config BT_L2CAP_TX_BUF_COUNT
|
||||||
|
default 5 if ZMK_SPLIT_ROLE_CENTRAL
|
||||||
|
|
||||||
if ZMK_SPLIT_ROLE_CENTRAL
|
if ZMK_SPLIT_ROLE_CENTRAL
|
||||||
|
|
||||||
config ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS
|
config ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS
|
||||||
int "Number of peripherals that will connect to the central."
|
int "Number of peripherals that will connect to the central."
|
||||||
default 1
|
default 1
|
||||||
|
|
||||||
|
menuconfig ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING
|
||||||
|
bool "Fetch Peripheral Battery Level Info"
|
||||||
|
help
|
||||||
|
Adds internal support for fetching the battery levels from peripherals
|
||||||
|
and generating events in the ZMK eventing system.
|
||||||
|
|
||||||
|
if ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING
|
||||||
|
|
||||||
|
config ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_QUEUE_SIZE
|
||||||
|
int "Max number of battery level events to queue when received from peripherals"
|
||||||
|
default ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS
|
||||||
|
|
||||||
|
config ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY
|
||||||
|
bool "Proxy Peripheral Battery Level Info"
|
||||||
|
help
|
||||||
|
Adds support for reporting the battery levels of connected split
|
||||||
|
peripherals through an additional Battery Level service.
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
config ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE
|
config ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE
|
||||||
int "Max number of key position state events to queue when received from peripherals"
|
int "Max number of key position state events to queue when received from peripherals"
|
||||||
default 5
|
default 5
|
||||||
|
|
|
@ -27,6 +27,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
#include <zmk/event_manager.h>
|
#include <zmk/event_manager.h>
|
||||||
#include <zmk/events/position_state_changed.h>
|
#include <zmk/events/position_state_changed.h>
|
||||||
#include <zmk/events/sensor_event.h>
|
#include <zmk/events/sensor_event.h>
|
||||||
|
#include <zmk/events/battery_state_changed.h>
|
||||||
#include <zmk/hid_indicators_types.h>
|
#include <zmk/hid_indicators_types.h>
|
||||||
|
|
||||||
static int start_scanning(void);
|
static int start_scanning(void);
|
||||||
|
@ -47,6 +48,10 @@ struct peripheral_slot {
|
||||||
struct bt_gatt_subscribe_params sensor_subscribe_params;
|
struct bt_gatt_subscribe_params sensor_subscribe_params;
|
||||||
struct bt_gatt_discover_params sub_discover_params;
|
struct bt_gatt_discover_params sub_discover_params;
|
||||||
uint16_t run_behavior_handle;
|
uint16_t run_behavior_handle;
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||||
|
struct bt_gatt_subscribe_params batt_lvl_subscribe_params;
|
||||||
|
struct bt_gatt_read_params batt_lvl_read_params;
|
||||||
|
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||||
uint16_t update_hid_indicators;
|
uint16_t update_hid_indicators;
|
||||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||||
|
@ -265,6 +270,110 @@ static uint8_t split_central_notify_func(struct bt_conn *conn,
|
||||||
return BT_GATT_ITER_CONTINUE;
|
return BT_GATT_ITER_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||||
|
|
||||||
|
static uint8_t peripheral_battery_levels[ZMK_SPLIT_BLE_PERIPHERAL_COUNT] = {0};
|
||||||
|
|
||||||
|
int zmk_split_get_peripheral_battery_level(uint8_t source, uint8_t *level) {
|
||||||
|
if (source >= ARRAY_SIZE(peripheral_battery_levels)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peripherals[source].state != PERIPHERAL_SLOT_STATE_CONNECTED) {
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
*level = peripheral_battery_levels[source];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
K_MSGQ_DEFINE(peripheral_batt_lvl_msgq, sizeof(struct zmk_peripheral_battery_state_changed),
|
||||||
|
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_QUEUE_SIZE, 4);
|
||||||
|
|
||||||
|
void peripheral_batt_lvl_change_callback(struct k_work *work) {
|
||||||
|
struct zmk_peripheral_battery_state_changed ev;
|
||||||
|
while (k_msgq_get(&peripheral_batt_lvl_msgq, &ev, K_NO_WAIT) == 0) {
|
||||||
|
LOG_DBG("Triggering peripheral battery level change %u", ev.state_of_charge);
|
||||||
|
peripheral_battery_levels[ev.source] = ev.state_of_charge;
|
||||||
|
ZMK_EVENT_RAISE(new_zmk_peripheral_battery_state_changed(ev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
K_WORK_DEFINE(peripheral_batt_lvl_work, peripheral_batt_lvl_change_callback);
|
||||||
|
|
||||||
|
static uint8_t split_central_battery_level_notify_func(struct bt_conn *conn,
|
||||||
|
struct bt_gatt_subscribe_params *params,
|
||||||
|
const void *data, uint16_t length) {
|
||||||
|
struct peripheral_slot *slot = peripheral_slot_for_conn(conn);
|
||||||
|
|
||||||
|
if (!slot) {
|
||||||
|
LOG_ERR("No peripheral state found for connection");
|
||||||
|
return BT_GATT_ITER_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
LOG_DBG("[UNSUBSCRIBED]");
|
||||||
|
params->value_handle = 0U;
|
||||||
|
return BT_GATT_ITER_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
LOG_ERR("Zero length battery notification received");
|
||||||
|
return BT_GATT_ITER_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("[BATTERY LEVEL NOTIFICATION] data %p length %u", data, length);
|
||||||
|
uint8_t battery_level = ((uint8_t *)data)[0];
|
||||||
|
LOG_DBG("Battery level: %u", battery_level);
|
||||||
|
struct zmk_peripheral_battery_state_changed ev = {
|
||||||
|
.source = peripheral_slot_index_for_conn(conn), .state_of_charge = battery_level};
|
||||||
|
k_msgq_put(&peripheral_batt_lvl_msgq, &ev, K_NO_WAIT);
|
||||||
|
k_work_submit(&peripheral_batt_lvl_work);
|
||||||
|
|
||||||
|
return BT_GATT_ITER_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t split_central_battery_level_read_func(struct bt_conn *conn, uint8_t err,
|
||||||
|
struct bt_gatt_read_params *params,
|
||||||
|
const void *data, uint16_t length) {
|
||||||
|
if (err > 0) {
|
||||||
|
LOG_ERR("Error during reading peripheral battery level: %u", err);
|
||||||
|
return BT_GATT_ITER_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct peripheral_slot *slot = peripheral_slot_for_conn(conn);
|
||||||
|
|
||||||
|
if (!slot) {
|
||||||
|
LOG_ERR("No peripheral state found for connection");
|
||||||
|
return BT_GATT_ITER_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
LOG_DBG("[READ COMPLETED]");
|
||||||
|
return BT_GATT_ITER_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("[BATTERY LEVEL READ] data %p length %u", data, length);
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
LOG_ERR("Zero length battery notification received");
|
||||||
|
return BT_GATT_ITER_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t battery_level = ((uint8_t *)data)[0];
|
||||||
|
|
||||||
|
LOG_DBG("Battery level: %u", battery_level);
|
||||||
|
|
||||||
|
struct zmk_peripheral_battery_state_changed ev = {
|
||||||
|
.source = peripheral_slot_index_for_conn(conn), .state_of_charge = battery_level};
|
||||||
|
k_msgq_put(&peripheral_batt_lvl_msgq, &ev, K_NO_WAIT);
|
||||||
|
k_work_submit(&peripheral_batt_lvl_work);
|
||||||
|
|
||||||
|
return BT_GATT_ITER_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||||
|
|
||||||
static int split_central_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *params) {
|
static int split_central_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *params) {
|
||||||
int err = bt_gatt_subscribe(conn, params);
|
int err = bt_gatt_subscribe(conn, params);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
|
@ -306,10 +415,6 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
||||||
|
|
||||||
if (bt_uuid_cmp(chrc_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID)) == 0) {
|
if (bt_uuid_cmp(chrc_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID)) == 0) {
|
||||||
LOG_DBG("Found position state characteristic");
|
LOG_DBG("Found position state characteristic");
|
||||||
slot->discover_params.uuid = NULL;
|
|
||||||
slot->discover_params.start_handle = attr->handle + 2;
|
|
||||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
|
||||||
|
|
||||||
slot->subscribe_params.disc_params = &slot->sub_discover_params;
|
slot->subscribe_params.disc_params = &slot->sub_discover_params;
|
||||||
slot->subscribe_params.end_handle = slot->discover_params.end_handle;
|
slot->subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||||
slot->subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
slot->subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||||
|
@ -342,9 +447,27 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
||||||
LOG_DBG("Found update HID indicators handle");
|
LOG_DBG("Found update HID indicators handle");
|
||||||
slot->update_hid_indicators = bt_gatt_attr_value_handle(attr);
|
slot->update_hid_indicators = bt_gatt_attr_value_handle(attr);
|
||||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||||
|
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||||
|
BT_UUID_BAS_BATTERY_LEVEL)) {
|
||||||
|
LOG_DBG("Found battery level characteristics");
|
||||||
|
slot->batt_lvl_subscribe_params.disc_params = &slot->sub_discover_params;
|
||||||
|
slot->batt_lvl_subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||||
|
slot->batt_lvl_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||||
|
slot->batt_lvl_subscribe_params.notify = split_central_battery_level_notify_func;
|
||||||
|
slot->batt_lvl_subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||||
|
split_central_subscribe(conn, &slot->batt_lvl_subscribe_params);
|
||||||
|
|
||||||
|
slot->batt_lvl_read_params.func = split_central_battery_level_read_func;
|
||||||
|
slot->batt_lvl_read_params.handle_count = 1;
|
||||||
|
slot->batt_lvl_read_params.single.handle = bt_gatt_attr_value_handle(attr);
|
||||||
|
slot->batt_lvl_read_params.single.offset = 0;
|
||||||
|
bt_gatt_read(conn, &slot->batt_lvl_read_params);
|
||||||
|
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||||
}
|
}
|
||||||
|
|
||||||
bool subscribed = (slot->run_behavior_handle && slot->subscribe_params.value_handle);
|
bool subscribed = slot->run_behavior_handle && slot->subscribe_params.value_handle;
|
||||||
|
|
||||||
#if ZMK_KEYMAP_HAS_SENSORS
|
#if ZMK_KEYMAP_HAS_SENSORS
|
||||||
subscribed = subscribed && slot->sensor_subscribe_params.value_handle;
|
subscribed = subscribed && slot->sensor_subscribe_params.value_handle;
|
||||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||||
|
@ -352,6 +475,9 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
||||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||||
subscribed = subscribed && slot->update_hid_indicators;
|
subscribed = subscribed && slot->update_hid_indicators;
|
||||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||||
|
subscribed = subscribed && slot->batt_lvl_subscribe_params.value_handle;
|
||||||
|
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||||
|
|
||||||
return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
|
return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
|
||||||
}
|
}
|
||||||
|
@ -382,7 +508,6 @@ static uint8_t split_central_service_discovery_func(struct bt_conn *conn,
|
||||||
LOG_DBG("Found split service");
|
LOG_DBG("Found split service");
|
||||||
slot->discover_params.uuid = NULL;
|
slot->discover_params.uuid = NULL;
|
||||||
slot->discover_params.func = split_central_chrc_discovery_func;
|
slot->discover_params.func = split_central_chrc_discovery_func;
|
||||||
slot->discover_params.start_handle = attr->handle + 1;
|
|
||||||
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||||
|
|
||||||
int err = bt_gatt_discover(conn, &slot->discover_params);
|
int err = bt_gatt_discover(conn, &slot->discover_params);
|
||||||
|
@ -605,6 +730,13 @@ static void split_central_disconnected(struct bt_conn *conn, uint8_t reason) {
|
||||||
|
|
||||||
LOG_DBG("Disconnected: %s (reason %d)", addr, reason);
|
LOG_DBG("Disconnected: %s (reason %d)", addr, reason);
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||||
|
struct zmk_peripheral_battery_state_changed ev = {
|
||||||
|
.source = peripheral_slot_index_for_conn(conn), .state_of_charge = 0};
|
||||||
|
k_msgq_put(&peripheral_batt_lvl_msgq, &ev, K_NO_WAIT);
|
||||||
|
k_work_submit(&peripheral_batt_lvl_work);
|
||||||
|
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||||
|
|
||||||
err = release_peripheral_slot_for_conn(conn);
|
err = release_peripheral_slot_for_conn(conn);
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|
98
app/src/split/bluetooth/central_bas_proxy.c
Normal file
98
app/src/split/bluetooth/central_bas_proxy.c
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/init.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/sensor.h>
|
||||||
|
#include <zephyr/bluetooth/gatt.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/battery.h>
|
||||||
|
#include <zmk/events/battery_state_changed.h>
|
||||||
|
#include <zmk/split/bluetooth/central.h>
|
||||||
|
|
||||||
|
static void blvl_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) {
|
||||||
|
ARG_UNUSED(attr);
|
||||||
|
|
||||||
|
bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
|
||||||
|
|
||||||
|
LOG_INF("BAS Notifications %s", notif_enabled ? "enabled" : "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t read_blvl(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
|
||||||
|
uint16_t len, uint16_t offset) {
|
||||||
|
const uint8_t source = (uint8_t)(uint32_t)attr->user_data;
|
||||||
|
uint8_t level = 0;
|
||||||
|
int rc = zmk_split_get_peripheral_battery_level(source, &level);
|
||||||
|
|
||||||
|
if (rc == -EINVAL) {
|
||||||
|
LOG_ERR("Invalid peripheral index requested for battery level read: %d", source);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &level, sizeof(uint8_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bt_gatt_cpf aux_level_cpf = {
|
||||||
|
.format = 0x04, // uint8
|
||||||
|
.exponent = 0x0,
|
||||||
|
.unit = 0x27AD, // Percentage
|
||||||
|
.name_space = 0x01, // Bluetooth SIG
|
||||||
|
.description = 0x0108, // "auxiliary"
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PERIPH_CUD_(x) "Peripheral " #x
|
||||||
|
#define PERIPH_CUD(x) PERIPH_CUD_(x)
|
||||||
|
|
||||||
|
// How many GATT attributes each battery level adds to our service
|
||||||
|
#define PERIPH_BATT_LEVEL_ATTR_COUNT 5
|
||||||
|
// The second generated attribute is the one used to send GATT notifications
|
||||||
|
#define PERIPH_BATT_LEVEL_ATTR_NOTIFY_IDX 1
|
||||||
|
|
||||||
|
#define PERIPH_BATT_LEVEL_ATTRS(i, _) \
|
||||||
|
BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_LEVEL, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
|
||||||
|
BT_GATT_PERM_READ, read_blvl, NULL, i), \
|
||||||
|
BT_GATT_CCC(blvl_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), \
|
||||||
|
BT_GATT_CPF(&aux_level_cpf), BT_GATT_CUD(PERIPH_CUD(i), BT_GATT_PERM_READ),
|
||||||
|
|
||||||
|
BT_GATT_SERVICE_DEFINE(bas_aux, BT_GATT_PRIMARY_SERVICE(BT_UUID_BAS),
|
||||||
|
LISTIFY(CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS, PERIPH_BATT_LEVEL_ATTRS,
|
||||||
|
()));
|
||||||
|
|
||||||
|
int peripheral_batt_lvl_listener(const zmk_event_t *eh) {
|
||||||
|
const struct zmk_peripheral_battery_state_changed *ev =
|
||||||
|
as_zmk_peripheral_battery_state_changed(eh);
|
||||||
|
if (ev == NULL) {
|
||||||
|
return ZMK_EV_EVENT_BUBBLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ev->source >= CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS) {
|
||||||
|
LOG_WRN("Got battery level event for an out of range peripheral index");
|
||||||
|
return ZMK_EV_EVENT_BUBBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Peripheral battery level event: %u", ev->state_of_charge);
|
||||||
|
|
||||||
|
// Offset by the index of the source plus the specific offset to find the attribute to notify
|
||||||
|
// on.
|
||||||
|
int index = (PERIPH_BATT_LEVEL_ATTR_COUNT * ev->source) + PERIPH_BATT_LEVEL_ATTR_NOTIFY_IDX;
|
||||||
|
|
||||||
|
int rc = bt_gatt_notify(NULL, &bas_aux.attrs[index], &ev->state_of_charge, sizeof(uint8_t));
|
||||||
|
if (rc < 0 && rc != -ENOTCONN) {
|
||||||
|
LOG_WRN("Failed to notify hosts of peripheral battery level: %d", rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ZMK_EV_EVENT_BUBBLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZMK_LISTENER(peripheral_batt_lvl_listener, peripheral_batt_lvl_listener);
|
||||||
|
ZMK_SUBSCRIPTION(peripheral_batt_lvl_listener, zmk_peripheral_battery_state_changed);
|
|
@ -104,15 +104,18 @@ Note that `CONFIG_BT_MAX_CONN` and `CONFIG_BT_MAX_PAIRED` should be set to the s
|
||||||
|
|
||||||
Following split keyboard settings are defined in [zmk/app/src/split/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/src/split/Kconfig) (generic) and [zmk/app/src/split/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/src/split/bluetooth/Kconfig) (bluetooth).
|
Following split keyboard settings are defined in [zmk/app/src/split/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/src/split/Kconfig) (generic) and [zmk/app/src/split/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/src/split/bluetooth/Kconfig) (bluetooth).
|
||||||
|
|
||||||
| Config | Type | Description | Default |
|
| Config | Type | Description | Default |
|
||||||
| ----------------------------------------------------- | ---- | ------------------------------------------------------------------------ | ------- |
|
| ------------------------------------------------------- | ---- | -------------------------------------------------------------------------- | ------------------------------------------ |
|
||||||
| `CONFIG_ZMK_SPLIT` | bool | Enable split keyboard support | n |
|
| `CONFIG_ZMK_SPLIT` | bool | Enable split keyboard support | n |
|
||||||
| `CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS` | bool | Enable split keyboard support for passing indicator state to peripherals | n |
|
| `CONFIG_ZMK_SPLIT_ROLE_CENTRAL` | bool | `y` for central device, `n` for peripheral | |
|
||||||
| `CONFIG_ZMK_SPLIT_BLE` | bool | Use BLE to communicate between split keyboard halves | y |
|
| `CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS` | bool | Enable split keyboard support for passing indicator state to peripherals | n |
|
||||||
| `CONFIG_ZMK_SPLIT_ROLE_CENTRAL` | bool | `y` for central device, `n` for peripheral | |
|
| `CONFIG_ZMK_SPLIT_BLE` | bool | Use BLE to communicate between split keyboard halves | y |
|
||||||
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue when received from peripherals | 5 |
|
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING` | bool | Enable fetching split peripheral battery levels to the central side | n |
|
||||||
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE` | int | Stack size of the BLE split central write thread | 512 |
|
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY` | bool | Enable central reporting of split battery levels to hosts | n |
|
||||||
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE` | int | Max number of behavior run events to queue to send to the peripheral(s) | 5 |
|
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_QUEUE_SIZE` | int | Max number of battery level events to queue when received from peripherals | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS` |
|
||||||
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE` | int | Stack size of the BLE split peripheral notify thread | 650 |
|
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue when received from peripherals | 5 |
|
||||||
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY` | int | Priority of the BLE split peripheral notify thread | 5 |
|
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE` | int | Stack size of the BLE split central write thread | 512 |
|
||||||
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue to send to the central | 10 |
|
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE` | int | Max number of behavior run events to queue to send to the peripheral(s) | 5 |
|
||||||
|
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE` | int | Stack size of the BLE split peripheral notify thread | 650 |
|
||||||
|
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY` | int | Priority of the BLE split peripheral notify thread | 5 |
|
||||||
|
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue to send to the central | 10 |
|
||||||
|
|
Loading…
Add table
Reference in a new issue