Skip to content

Commit b4395c3

Browse files
carrefinhoNicell
andcommitted
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: zmkfirmware#682 Co-authored-by: Nick Winans <nick@winans.codes>
1 parent fee2404 commit b4395c3

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed

app/src/split/bluetooth/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ if (NOT CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
77
endif()
88
if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
99
target_sources(app PRIVATE central.c)
10+
target_sources_ifdef(CONFIG_ZMK_SPLIT_BLE_PREF_IDLE app PRIVATE central_idle.c)
1011
endif()
1112

1213
if (CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY)

app/src/split/bluetooth/Kconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,26 @@ config ZMK_SPLIT_BLE_PREF_TIMEOUT
7474
int "Supervision timeout to use for split central/peripheral connection"
7575
default 400
7676

77+
config ZMK_SPLIT_BLE_PREF_IDLE
78+
bool "Set slower split peripheral BLE params on idle to save power"
79+
default y
80+
81+
if ZMK_SPLIT_BLE_PREF_IDLE
82+
83+
config ZMK_SPLIT_BLE_PREF_IDLE_INT
84+
int "Idle connection interval to use for split central/peripheral connection"
85+
default 18
86+
87+
config ZMK_SPLIT_BLE_PREF_IDLE_LATENCY
88+
int "Idle latency to use for split central/peripheral connection"
89+
default 10
90+
91+
config ZMK_SPLIT_BLE_PREF_IDLE_TIMEOUT
92+
int "Idle supervision timeout to use for split central/peripheral connection"
93+
default 400
94+
95+
endif # ZMK_SPLIT_BLE_PREF_IDLE
96+
7797
endif # ZMK_SPLIT_ROLE_CENTRAL
7898

7999
if !ZMK_SPLIT_ROLE_CENTRAL
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2025 The ZMK Contributors
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#include <zephyr/logging/log.h>
8+
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
9+
10+
#include <zephyr/bluetooth/bluetooth.h>
11+
#include <zephyr/bluetooth/conn.h>
12+
13+
#include <zmk/event_manager.h>
14+
#include <zmk/events/activity_state_changed.h>
15+
16+
static void set_sleep_params(struct bt_conn *conn, void *data) {
17+
struct bt_conn_info info;
18+
19+
bt_conn_get_info(conn, &info);
20+
21+
if (info.role == BT_CONN_ROLE_CENTRAL && info.state == BT_CONN_STATE_CONNECTED) {
22+
int err =
23+
bt_conn_le_param_update(conn, BT_LE_CONN_PARAM(CONFIG_ZMK_SPLIT_BLE_PREF_IDLE_INT,
24+
CONFIG_ZMK_SPLIT_BLE_PREF_IDLE_INT,
25+
CONFIG_ZMK_SPLIT_BLE_PREF_IDLE_LATENCY,
26+
CONFIG_ZMK_SPLIT_BLE_PREF_IDLE_TIMEOUT));
27+
28+
if (err) {
29+
LOG_DBG("Failed to set idle params on split connection: %d", err);
30+
}
31+
}
32+
}
33+
34+
static void set_wake_params(struct bt_conn *conn, void *data) {
35+
struct bt_conn_info info;
36+
37+
bt_conn_get_info(conn, &info);
38+
39+
if (info.role == BT_CONN_ROLE_CENTRAL && info.state == BT_CONN_STATE_CONNECTED) {
40+
int err = bt_conn_le_param_update(
41+
conn,
42+
BT_LE_CONN_PARAM(CONFIG_ZMK_SPLIT_BLE_PREF_INT, CONFIG_ZMK_SPLIT_BLE_PREF_INT,
43+
CONFIG_ZMK_SPLIT_BLE_PREF_LATENCY, CONFIG_ZMK_SPLIT_BLE_PREF_TIMEOUT));
44+
45+
if (err) {
46+
LOG_DBG("Failed to set active params on split connection: %d", err);
47+
}
48+
}
49+
}
50+
51+
static void sleep_all(void) {
52+
LOG_DBG("Setting idle connection parameters on peripherals");
53+
54+
bt_conn_foreach(BT_CONN_TYPE_LE, set_sleep_params, NULL);
55+
}
56+
57+
static void wake_all(void) {
58+
LOG_DBG("Waking up from idle connection parameters on peripherals");
59+
60+
bt_conn_foreach(BT_CONN_TYPE_LE, set_wake_params, NULL);
61+
}
62+
63+
static int central_idle_listener(const zmk_event_t *eh) {
64+
struct zmk_activity_state_changed *ev = as_zmk_activity_state_changed(eh);
65+
if (ev == NULL) {
66+
return -ENOTSUP;
67+
}
68+
69+
switch (ev->state) {
70+
case ZMK_ACTIVITY_ACTIVE:
71+
wake_all();
72+
break;
73+
case ZMK_ACTIVITY_IDLE:
74+
sleep_all();
75+
break;
76+
case ZMK_ACTIVITY_SLEEP:
77+
break;
78+
default:
79+
LOG_WRN("Unhandled activity state: %d", ev->state);
80+
return -EINVAL;
81+
}
82+
return 0;
83+
}
84+
85+
ZMK_LISTENER(central_idle, central_idle_listener);
86+
ZMK_SUBSCRIPTION(central_idle, zmk_activity_state_changed);

0 commit comments

Comments
 (0)