From 367731249fd660de91dc3a25ca3ba0efe5d23c74 Mon Sep 17 00:00:00 2001 From: carrefinho Date: Wed, 31 Dec 2025 10:55:13 -0500 Subject: [PATCH 1/2] feat(split): Increase split interval during idle Slows down BLE connection interval between split halves when keyboard goes idle, reducing central half battery consumption significantly during idle periods. When the keyboard becomes active again, connection parameters are restored to their normal values. There may be brief latency (0.6-1s) when waking from idle. Based on: https://github.com/zmkfirmware/zmk/pull/682 Co-authored-by: Nick Winans --- app/src/split/bluetooth/CMakeLists.txt | 1 + app/src/split/bluetooth/Kconfig | 20 ++++++ app/src/split/bluetooth/central_idle.c | 86 ++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 app/src/split/bluetooth/central_idle.c diff --git a/app/src/split/bluetooth/CMakeLists.txt b/app/src/split/bluetooth/CMakeLists.txt index f4e12a9d097..5a81515380d 100644 --- a/app/src/split/bluetooth/CMakeLists.txt +++ b/app/src/split/bluetooth/CMakeLists.txt @@ -7,6 +7,7 @@ if (NOT CONFIG_ZMK_SPLIT_ROLE_CENTRAL) endif() if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE central.c) + target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_PREF_IDLE app PRIVATE central_idle.c) endif() if (CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY) diff --git a/app/src/split/bluetooth/Kconfig b/app/src/split/bluetooth/Kconfig index 5f4a782f772..930a12fc920 100644 --- a/app/src/split/bluetooth/Kconfig +++ b/app/src/split/bluetooth/Kconfig @@ -74,6 +74,26 @@ config ZMK_SPLIT_BLE_PREF_TIMEOUT int "Supervision timeout to use for split central/peripheral connection" default 400 +config ZMK_SPLIT_BLE_PREF_IDLE + bool "Set slower split peripheral BLE params on idle to save power" + default y + +if ZMK_SPLIT_BLE_PREF_IDLE + +config ZMK_SPLIT_BLE_PREF_IDLE_INT + int "Idle connection interval to use for split central/peripheral connection" + default 18 + +config ZMK_SPLIT_BLE_PREF_IDLE_LATENCY + int "Idle latency to use for split central/peripheral connection" + default 10 + +config ZMK_SPLIT_BLE_PREF_IDLE_TIMEOUT + int "Idle supervision timeout to use for split central/peripheral connection" + default 400 + +endif # ZMK_SPLIT_BLE_PREF_IDLE + endif # ZMK_SPLIT_ROLE_CENTRAL if !ZMK_SPLIT_ROLE_CENTRAL diff --git a/app/src/split/bluetooth/central_idle.c b/app/src/split/bluetooth/central_idle.c new file mode 100644 index 00000000000..536e1b4589f --- /dev/null +++ b/app/src/split/bluetooth/central_idle.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2025 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 && info.state == BT_CONN_STATE_CONNECTED) { + int err = + bt_conn_le_param_update(conn, BT_LE_CONN_PARAM(CONFIG_ZMK_SPLIT_BLE_PREF_IDLE_INT, + CONFIG_ZMK_SPLIT_BLE_PREF_IDLE_INT, + CONFIG_ZMK_SPLIT_BLE_PREF_IDLE_LATENCY, + CONFIG_ZMK_SPLIT_BLE_PREF_IDLE_TIMEOUT)); + + if (err) { + LOG_DBG("Failed to set idle params on 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 && info.state == BT_CONN_STATE_CONNECTED) { + int err = bt_conn_le_param_update( + conn, + BT_LE_CONN_PARAM(CONFIG_ZMK_SPLIT_BLE_PREF_INT, CONFIG_ZMK_SPLIT_BLE_PREF_INT, + CONFIG_ZMK_SPLIT_BLE_PREF_LATENCY, CONFIG_ZMK_SPLIT_BLE_PREF_TIMEOUT)); + + if (err) { + LOG_DBG("Failed to set active params on split connection: %d", err); + } + } +} + +static void sleep_all(void) { + LOG_DBG("Setting idle connection parameters on peripherals"); + + bt_conn_foreach(BT_CONN_TYPE_LE, set_sleep_params, NULL); +} + +static void wake_all(void) { + LOG_DBG("Waking up from idle connection parameters on peripherals"); + + bt_conn_foreach(BT_CONN_TYPE_LE, set_wake_params, NULL); +} + +static int central_idle_listener(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_idle, central_idle_listener); +ZMK_SUBSCRIPTION(central_idle, zmk_activity_state_changed); From dcedcc237cb82e2a7c519b08e19566b728270104 Mon Sep 17 00:00:00 2001 From: carrefinho Date: Wed, 31 Dec 2025 11:10:57 -0500 Subject: [PATCH 2/2] REMOVEME: Temporarily move to Zephyr branch with peripheral conn update fix --- app/west.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/west.yml b/app/west.yml index d177f2bd1c0..6e3cbe06728 100644 --- a/app/west.yml +++ b/app/west.yml @@ -4,10 +4,12 @@ manifest: url-base: https://github.com/zephyrproject-rtos - name: zmkfirmware url-base: https://github.com/zmkfirmware + - name: carrefinho + url-base: https://github.com/carrefinho projects: - name: zephyr - remote: zmkfirmware - revision: v4.1.0+zmk-fixes + remote: carrefinho + revision: v4.1.0+zmk-fixes+split-conn-fix clone-depth: 1 import: name-blocklist: