diff --git a/app/Kconfig b/app/Kconfig index 1189c6547b0..9419615cd3c 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -185,6 +185,13 @@ config ZMK_BLE_PASSKEY_ENTRY bool "Require passkey entry on the keyboard to complete pairing" select RING_BUFFER +config ZMK_BLE_PERSIST_PROFILE_ON_CHANGE + bool "Persist the active BLE profile on change" + default y + help + Enables persisting the active BLE profile on change (after ZMK_SETTINGS_SAVE_DEBOUNCE ms), + ensuring it is active on startup. + config BT_SMP_ALLOW_UNAUTH_OVERWRITE imply ZMK_BLE_PASSKEY_ENTRY diff --git a/app/include/dt-bindings/zmk/bt.h b/app/include/dt-bindings/zmk/bt.h index aaad4dc5b8b..10a1d94131d 100644 --- a/app/include/dt-bindings/zmk/bt.h +++ b/app/include/dt-bindings/zmk/bt.h @@ -10,6 +10,7 @@ #define BT_SEL_CMD 3 #define BT_CLR_ALL_CMD 4 #define BT_DISC_CMD 5 +#define BT_SAVE_CMD 6 /* Note: Some future commands will include additional parameters, so we @@ -22,3 +23,4 @@ defines these aliases up front. #define BT_SEL BT_SEL_CMD #define BT_CLR_ALL BT_CLR_ALL_CMD 0 #define BT_DISC BT_DISC_CMD +#define BT_SAVE BT_SAVE_CMD 0 diff --git a/app/include/zmk/ble.h b/app/include/zmk/ble.h index cc55a6ce142..56aba918ea7 100644 --- a/app/include/zmk/ble.h +++ b/app/include/zmk/ble.h @@ -26,6 +26,7 @@ int zmk_ble_prof_prev(void); int zmk_ble_prof_select(uint8_t index); void zmk_ble_clear_all_bonds(void); int zmk_ble_prof_disconnect(uint8_t index); +void zmk_ble_save_profile(bool immediate); int zmk_ble_active_profile_index(void); int zmk_ble_profile_index(const bt_addr_le_t *addr); diff --git a/app/src/behaviors/behavior_bt.c b/app/src/behaviors/behavior_bt.c index f439e49b1cf..dca83b3cbbb 100644 --- a/app/src/behaviors/behavior_bt.c +++ b/app/src/behaviors/behavior_bt.c @@ -105,6 +105,9 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, return 0; case BT_DISC_CMD: return zmk_ble_prof_disconnect(binding->param2); + case BT_SAVE_CMD: + zmk_ble_save_profile(true); + return 0; default: LOG_ERR("Unknown BT command: %d", binding->param1); } diff --git a/app/src/ble.c b/app/src/ble.c index 776730fe5c3..a6a2b09ced1 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -251,16 +251,16 @@ int zmk_ble_profile_index(const bt_addr_le_t *addr) { #if IS_ENABLED(CONFIG_SETTINGS) static void ble_save_profile_work(struct k_work *work) { settings_save_one("ble/active_profile", &active_profile, sizeof(active_profile)); + LOG_DBG("Saved active profile %d.", active_profile); } static struct k_work_delayable ble_save_work; #endif -static int ble_save_profile(void) { +void zmk_ble_save_profile(bool immediate) { #if IS_ENABLED(CONFIG_SETTINGS) - return k_work_reschedule(&ble_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE)); -#else - return 0; + int delay = immediate ? 0 : CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE; + k_work_reschedule(&ble_save_work, K_MSEC(delay)); #endif } @@ -275,7 +275,9 @@ int zmk_ble_prof_select(uint8_t index) { } active_profile = index; - ble_save_profile(); +#if IS_ENABLED(CONFIG_ZMK_BLE_PERSIST_PROFILE_ON_CHANGE) + zmk_ble_save_profile(false); +#endif update_advertising(); @@ -422,6 +424,7 @@ static int ble_profiles_handle_set(const char *name, size_t len, settings_read_c LOG_ERR("Failed to handle active profile from settings (err %d)", err); return err; } + LOG_DBG("Loaded active profile %d", active_profile); } #if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) else if (settings_name_steq(name, "peripheral_addresses", &next) && next) { diff --git a/docs/docs/config/bluetooth.md b/docs/docs/config/bluetooth.md index 83fb9ec09d8..51a66c0173b 100644 --- a/docs/docs/config/bluetooth.md +++ b/docs/docs/config/bluetooth.md @@ -9,10 +9,11 @@ See [Configuration Overview](index.md) for instructions on how to change these s ## Kconfig -| Option | Type | Description | Default | -| -------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| `CONFIG_ZMK_BLE_EXPERIMENTAL_CONN` | bool | Enables a combination of settings that are planned to be default in future versions of ZMK to improve connection stability. Currently this only disables 2M PHY support. | n | -| `CONFIG_ZMK_BLE_EXPERIMENTAL_SEC` | bool | Enables a combination of settings that are planned to be officially supported in the future. This includes enabling BT Secure Connection passkey entry, and allows overwrite of keys from previously paired hosts. | n | -| `CONFIG_ZMK_BLE_EXPERIMENTAL_FEATURES` | bool | Aggregate config that enables both `CONFIG_ZMK_BLE_EXPERIMENTAL_CONN` and `CONFIG_ZMK_BLE_EXPERIMENTAL_SEC`. | n | -| `CONFIG_ZMK_BLE_PASSKEY_ENTRY` | bool | Enable passkey entry during pairing for enhanced security. (Note: After enabling this, you will need to re-pair all previously paired hosts.) | n | -| `CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION` | bool | Low level setting for GATT subscriptions. Set to `n` to work around an annoying Windows bug with battery notifications. | y | +| Option | Type | Description | Default | +| ------------------------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| `CONFIG_ZMK_BLE_EXPERIMENTAL_CONN` | bool | Enables a combination of settings that are planned to be default in future versions of ZMK to improve connection stability. Currently this only disables 2M PHY support. | n | +| `CONFIG_ZMK_BLE_EXPERIMENTAL_SEC` | bool | Enables a combination of settings that are planned to be officially supported in the future. This includes enabling BT Secure Connection passkey entry, and allows overwrite of keys from previously paired hosts. | n | +| `CONFIG_ZMK_BLE_EXPERIMENTAL_FEATURES` | bool | Aggregate config that enables both `CONFIG_ZMK_BLE_EXPERIMENTAL_CONN` and `CONFIG_ZMK_BLE_EXPERIMENTAL_SEC`. | n | +| `CONFIG_ZMK_BLE_PASSKEY_ENTRY` | bool | Enable passkey entry during pairing for enhanced security. (Note: After enabling this, you will need to re-pair all previously paired hosts.) | n | +| `CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION` | bool | Low level setting for GATT subscriptions. Set to `n` to work around an annoying Windows bug with battery notifications. | y | +| `CONFIG_ZMK_BLE_PERSIST_PROFILE_ON_CHANGE` | bool | Enables the new profile to be saved upon selection, ensuring it is active at startup. (Note: The profile is only saved after a delay of `CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE` milliseconds, to minimize potential wear on the flash memory.) | y | diff --git a/docs/docs/keymaps/behaviors/bluetooth.md b/docs/docs/keymaps/behaviors/bluetooth.md index 93d0842814a..f7c46ac43d3 100644 --- a/docs/docs/keymaps/behaviors/bluetooth.md +++ b/docs/docs/keymaps/behaviors/bluetooth.md @@ -38,18 +38,22 @@ This will allow you to reference the actions defined in this header such as `BT_ Here is a table describing the command for each define: -| Define | Action | -| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `BT_CLR` | Clear bond information between the keyboard and host for the selected profile. | -| `BT_CLR_ALL` | Clear bond information between the keyboard and host for all profiles. | -| `BT_NXT` | Switch to the next profile, cycling through to the first one when the end is reached. | -| `BT_PRV` | Switch to the previous profile, cycling through to the last one when the beginning is reached. | -| `BT_SEL` | Select the 0-indexed profile by number; must include a number as an argument in the keymap to work correctly, e.g. `BT_SEL 0`. | -| `BT_DISC` | Disconnect from the 0-indexed profile by number, if it's currently connected and inactive; must include a number as an argument in the keymap to work correctly, e.g. `BT_DISC 0`. | +| Define | Action | +| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `BT_CLR` | Clear bond information between the keyboard and host for the selected profile. | +| `BT_CLR_ALL` | Clear bond information between the keyboard and host for all profiles. | +| `BT_NXT` | Switch to the next profile, cycling through to the first one when the end is reached. | +| `BT_PRV` | Switch to the previous profile, cycling through to the last one when the beginning is reached. | +| `BT_SEL` | Select the 0-indexed profile by number; must include a number as an argument in the keymap to work correctly, e.g. `BT_SEL 0`. | +| `BT_DISC` | Disconnect from the 0-indexed profile by number, if it's currently connected and inactive; must include a number as an argument in the keymap to work correctly, e.g. `BT_DISC 0`. | +| `BT_SAVE` | Saves the currently selected profile, ensuring it is active at startup.

Note: This is only useful if `ZMK_CONFIG_BLE_PERSIST_PROFILE_ON_CHANGE` is disabled. Per default the active profile will be persisted on change automatically. | :::note[Selected profile persistence] The profile that is selected by the `BT_SEL`/`BT_PRV`/`BT_NXT` actions will be saved to flash storage and hence persist across restarts and firmware flashes. + However it will only be saved after [`CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE`](../../config/system.md#general) milliseconds in order to reduce potential wear on the flash memory. + +[`CONFIG_ZMK_BLE_PERSIST_PROFILE_ON_CHANGE`](../../config/bluetooth.md) can be used to disable automatic saving of the selected profile to flash storage. In this case `BT_SAVE` can be used to manually persist the currently selected profile to change the active profile at startup. ::: ## Bluetooth Behavior