diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 1492be16..f0fad2ef 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -76,6 +76,7 @@ if (CONFIG_ZMK_SPLIT_BLE AND (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)) endif() if (CONFIG_ZMK_SPLIT_BLE AND CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/split/bluetooth/central.c) + target_sources_ifdef(CONFIG_ZMK_BLE_PERIPHERAL_IDLE app PRIVATE src/split/bluetooth/central_listener.c) endif() target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.c) target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c) diff --git a/app/Kconfig b/app/Kconfig index 9b47c4c2..cfe5a0a7 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -202,6 +202,40 @@ config ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE int "Max number of behavior run events to queue to send to the peripheral(s)" default 5 +config ZMK_BLE_PERIPHERAL_INT + int "Peripheral connection interval in 1.25ms units" + default 6 + +config ZMK_BLE_PERIPHERAL_LATENCY + int "Peripheral latency in Connection Intervals" + default 30 + +config ZMK_BLE_PERIPHERAL_TIMEOUT + int "Peripheral supervision timeout in 10ms units" + default 400 + +config ZMK_BLE_PERIPHERAL_IDLE + bool "Set slower split peripheral BLE params on idle to save power" + depends on ZMK_SPLIT_BLE_ROLE_CENTRAL + default y + +if ZMK_BLE_PERIPHERAL_IDLE + +config ZMK_BLE_PERIPHERAL_IDLE_INT + int "Peripheral idle connection interval in 1.25ms units" + default 18 + +config ZMK_BLE_PERIPHERAL_IDLE_LATENCY + int "Peripheral idle latency in Connection Intervals" + default 10 + +config ZMK_BLE_PERIPHERAL_IDLE_TIMEOUT + int "Peripheral idle supervision timeout in 10ms units" + default 400 + +# ZMK_BLE_PERIPHERAL_IDLE +endif + endif if !ZMK_SPLIT_BLE_ROLE_CENTRAL diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index 2f02faaf..d812dd35 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -393,7 +393,9 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data) { LOG_ERR("Update phy conn failed (err %d)", err); } } else { - param = BT_LE_CONN_PARAM(0x0006, 0x0006, 30, 400); + param = BT_LE_CONN_PARAM( + CONFIG_ZMK_BLE_PERIPHERAL_INT, CONFIG_ZMK_BLE_PERIPHERAL_INT, + CONFIG_ZMK_BLE_PERIPHERAL_LATENCY, CONFIG_ZMK_BLE_PERIPHERAL_TIMEOUT); LOG_DBG("Initiating new connnection"); diff --git a/app/src/split/bluetooth/central_listener.c b/app/src/split/bluetooth/central_listener.c new file mode 100644 index 00000000..b9e9f7ce --- /dev/null +++ b/app/src/split/bluetooth/central_listener.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include + +#include +#include + +static void set_sleep_params(struct bt_conn *conn, void *data) { + struct bt_conn_info info; + + bt_conn_get_info(conn, &info); + + if (info.role == BT_CONN_ROLE_CENTRAL) { + int err = + bt_conn_le_param_update(conn, BT_LE_CONN_PARAM(CONFIG_ZMK_BLE_PERIPHERAL_IDLE_INT, + CONFIG_ZMK_BLE_PERIPHERAL_IDLE_INT, + CONFIG_ZMK_BLE_PERIPHERAL_IDLE_LATENCY, + CONFIG_ZMK_BLE_PERIPHERAL_IDLE_TIMEOUT)); + + if (err) { + LOG_DBG("Failed to sleep split connection: %d", err); + } + } +} + +static void set_wake_params(struct bt_conn *conn, void *data) { + struct bt_conn_info info; + + bt_conn_get_info(conn, &info); + + if (info.role == BT_CONN_ROLE_CENTRAL) { + int err = bt_conn_le_param_update( + conn, + BT_LE_CONN_PARAM(CONFIG_ZMK_BLE_PERIPHERAL_INT, CONFIG_ZMK_BLE_PERIPHERAL_INT, + CONFIG_ZMK_BLE_PERIPHERAL_LATENCY, CONFIG_ZMK_BLE_PERIPHERAL_TIMEOUT)); + + if (err) { + LOG_DBG("Failed to wake up split connection: %d", err); + } + } +} + +static void sleep_all() { + LOG_DBG("Setting idle connection parameters on peripherals"); + + bt_conn_foreach(BT_CONN_TYPE_LE, set_sleep_params, NULL); +} + +static void wake_all() { + LOG_DBG("Waking up from idle connection parameters on peripherals"); + + bt_conn_foreach(BT_CONN_TYPE_LE, set_wake_params, NULL); +} + +int central_event_handler(const zmk_event_t *eh) { + struct zmk_activity_state_changed *ev = as_zmk_activity_state_changed(eh); + if (ev == NULL) { + return -ENOTSUP; + } + + switch (ev->state) { + case ZMK_ACTIVITY_ACTIVE: + wake_all(); + break; + case ZMK_ACTIVITY_IDLE: + sleep_all(); + break; + case ZMK_ACTIVITY_SLEEP: + break; + default: + LOG_WRN("Unhandled activity state: %d", ev->state); + return -EINVAL; + } + return 0; +} + +ZMK_LISTENER(central, central_event_handler); +ZMK_SUBSCRIPTION(central, zmk_activity_state_changed);