diff --git a/app/include/dt-bindings/zmk/bt.h b/app/include/dt-bindings/zmk/bt.h index aaad4dc5b8b..a1e012699b6 100644 --- a/app/include/dt-bindings/zmk/bt.h +++ b/app/include/dt-bindings/zmk/bt.h @@ -10,6 +10,9 @@ #define BT_SEL_CMD 3 #define BT_CLR_ALL_CMD 4 #define BT_DISC_CMD 5 +#define BT_ADV_OFF_CMD 6 +#define BT_ADV_ON_CMD 7 +#define BT_ADV_TOG_CMD 8 /* Note: Some future commands will include additional parameters, so we @@ -22,3 +25,6 @@ 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_ADV_OFF BT_ADV_OFF_CMD 0 +#define BT_ADV_ON BT_ADV_ON_CMD 0 +#define BT_ADV_TOG BT_ADV_TOG_CMD 0 \ No newline at end of file diff --git a/app/include/zmk/ble.h b/app/include/zmk/ble.h index c44c5e16869..c3e393fac3a 100644 --- a/app/include/zmk/ble.h +++ b/app/include/zmk/ble.h @@ -20,6 +20,12 @@ #define ZMK_BLE_PROFILE_COUNT CONFIG_BT_MAX_PAIRED #endif +enum advertising_type { + ZMK_ADV_NONE, + ZMK_ADV_DIR, + ZMK_ADV_CONN, +}; + void zmk_ble_clear_bonds(void); int zmk_ble_prof_next(void); int zmk_ble_prof_prev(void); @@ -41,6 +47,9 @@ int zmk_ble_unpair_all(void); int zmk_ble_set_device_name(char *name); +void zmk_ble_adv_enabled_set(bool adv_enabled); +bool zmk_ble_adv_enabled_get(void); + #if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) int zmk_ble_put_peripheral_addr(const bt_addr_le_t *addr); #endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */ diff --git a/app/src/behaviors/behavior_bt.c b/app/src/behaviors/behavior_bt.c index f439e49b1cf..6e9909cf989 100644 --- a/app/src/behaviors/behavior_bt.c +++ b/app/src/behaviors/behavior_bt.c @@ -43,6 +43,21 @@ static const struct behavior_parameter_value_metadata no_arg_values[] = { .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, .value = BT_CLR_CMD, }, + { + .display_name = "Disconnect All and Stop Advertising", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_ADV_OFF_CMD, + }, + { + .display_name = "Start Advertising", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_ADV_ON_CMD, + }, + { + .display_name = "Toggle Advertising", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_ADV_TOG_CMD, + }, }; static const struct behavior_parameter_metadata_set no_args_set = { @@ -105,6 +120,15 @@ 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_ADV_ON_CMD: + zmk_ble_adv_enabled_set(true); + return 0; + case BT_ADV_OFF_CMD: + zmk_ble_adv_enabled_set(false); + return 0; + case BT_ADV_TOG_CMD: + zmk_ble_adv_enabled_set(zmk_ble_adv_enabled_get() ? false : 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 e63c63b0cca..be3cda1c423 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -47,11 +47,7 @@ RING_BUF_DECLARE(passkey_entries, PASSKEY_DIGITS); #endif /* IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) */ -enum advertising_type { - ZMK_ADV_NONE, - ZMK_ADV_DIR, - ZMK_ADV_CONN, -} advertising_status; +enum advertising_type advertising_status; #define CURR_ADV(adv) (adv << 4) @@ -63,6 +59,8 @@ enum advertising_type { static struct zmk_ble_profile profiles[ZMK_BLE_PROFILE_COUNT]; static uint8_t active_profile; +static bool permit_adv = true; + #define DEVICE_NAME CONFIG_BT_DEVICE_NAME #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) @@ -167,9 +165,9 @@ int update_advertising(void) { struct bt_conn *conn; enum advertising_type desired_adv = ZMK_ADV_NONE; - if (zmk_ble_active_profile_is_open()) { + if (permit_adv && zmk_ble_active_profile_is_open()) { desired_adv = ZMK_ADV_CONN; - } else if (!zmk_ble_active_profile_is_connected()) { + } else if (permit_adv && !zmk_ble_active_profile_is_connected()) { desired_adv = ZMK_ADV_CONN; // Need to fix directed advertising for privacy centrals. See // https://github.com/zephyrproject-rtos/zephyr/pull/14984 char @@ -210,6 +208,28 @@ static void update_advertising_callback(struct k_work *work) { update_advertisin K_WORK_DEFINE(update_advertising_work, update_advertising_callback); +void zmk_ble_adv_enabled_set(bool adv_enabled) { + if (adv_enabled) { + if (advertising_status != ZMK_ADV_CONN) { + permit_adv = true; + LOG_DBG("Enabling adv"); + } + update_advertising(); + } else { + permit_adv = false; + LOG_DBG("Disabling adv and disconnecting"); + for (int i = 0; i < ZMK_BLE_PROFILE_COUNT; i++) { + int err = zmk_ble_prof_disconnect(i); + if (err) { + LOG_DBG("Failed to disconnect profile %d : %d", i, err); + } + } + update_advertising(); + } +} + +bool zmk_ble_adv_enabled_get(void) { return permit_adv; } + static void clear_profile_bond(uint8_t profile) { if (bt_addr_le_cmp(&profiles[profile].peer, BT_ADDR_LE_ANY)) { bt_unpair(BT_ID_DEFAULT, &profiles[profile].peer); diff --git a/docs/docs/keymaps/behaviors/bluetooth.md b/docs/docs/keymaps/behaviors/bluetooth.md index 93d0842814a..c2c7c714862 100644 --- a/docs/docs/keymaps/behaviors/bluetooth.md +++ b/docs/docs/keymaps/behaviors/bluetooth.md @@ -46,12 +46,19 @@ Here is a table describing the command for each define: | `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_ADV_OFF` | Disable advertising and disconnect from all profiles | +| `BT_ADV_ON` | Enable advertising and attempt to reconnect to profiles | +| `BT_ADV_TOG` | Toggle advertising on and off | :::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. ::: +:::note[Advertising] +Bluetooth advertising is enabled in all situations by default, and doesn't have to be explicitly enabled via the bindings above. +::: + ## Bluetooth Behavior The bluetooth behavior completes an bluetooth action given on press.