diff --git a/app/Kconfig b/app/Kconfig index 5aedd9d90309..a3750461cbd5 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -179,6 +179,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 aaad4dc5b8b1..10a1d94131df 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 773323c1cf93..feee3f983a39 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 03bb7d8c8988..79dc37e8c388 100644 --- a/app/src/behaviors/behavior_bt.c +++ b/app/src/behaviors/behavior_bt.c @@ -37,6 +37,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 7e1ae7d4949c..d4c1eb7244bb 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(); @@ -407,6 +409,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/behaviors/bluetooth.md b/docs/docs/behaviors/bluetooth.md index dc2dfbbd2d51..4b95e2cfa34e 100644 --- a/docs/docs/behaviors/bluetooth.md +++ b/docs/docs/behaviors/bluetooth.md @@ -38,18 +38,21 @@ 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 diff --git a/docs/docs/config/bluetooth.md b/docs/docs/config/bluetooth.md index 02d203510dd8..73c983261f54 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. This includes changes to timing on BLE pairing initiation, restores use of the updated/new LLCP implementation, and 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. This includes changes to timing on BLE pairing initiation, restores use of the updated/new LLCP implementation, and 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_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 | +| `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 |