Skip to content

Commit 1d9a8bf

Browse files
committed
feat(split): allow central to connect to multiple peripherals
1 parent d7bd81e commit 1d9a8bf

File tree

4 files changed

+122
-66
lines changed

4 files changed

+122
-66
lines changed

app/include/zmk/ble.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL))
1515

1616
#if ZMK_BLE_IS_CENTRAL
17-
#define ZMK_BLE_PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
18-
#define ZMK_BLE_SPLIT_PERIPHERAL_COUNT 1
17+
#define ZMK_BLE_PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS)
18+
#define ZMK_BLE_SPLIT_PERIPHERAL_COUNT CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS
1919
#else
2020
#define ZMK_BLE_PROFILE_COUNT CONFIG_BT_MAX_PAIRED
2121
#endif
@@ -34,5 +34,5 @@ char *zmk_ble_active_profile_name();
3434
int zmk_ble_unpair_all();
3535

3636
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
37-
void zmk_ble_set_peripheral_addr(bt_addr_le_t *addr);
37+
int zmk_ble_put_peripheral_addr(const bt_addr_le_t *addr);
3838
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */

app/src/ble.c

+39-15
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,6 @@ static uint8_t passkey_digit = 0;
4747

4848
#endif /* IS_ENABLED(CONFIG_ZMK_BLE_PASSKEY_ENTRY) */
4949

50-
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
51-
#define PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
52-
#else
53-
#define PROFILE_COUNT CONFIG_BT_MAX_PAIRED
54-
#endif
55-
5650
enum advertising_type {
5751
ZMK_ADV_NONE,
5852
ZMK_ADV_DIR,
@@ -84,7 +78,7 @@ static const struct bt_data zmk_ble_ad[] = {
8478

8579
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
8680

87-
static bt_addr_le_t peripheral_addr;
81+
static bt_addr_le_t peripheral_addrs[ZMK_BLE_SPLIT_PERIPHERAL_COUNT];
8882

8983
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
9084

@@ -280,9 +274,34 @@ char *zmk_ble_active_profile_name() { return profiles[active_profile].name; }
280274

281275
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
282276

283-
void zmk_ble_set_peripheral_addr(bt_addr_le_t *addr) {
284-
memcpy(&peripheral_addr, addr, sizeof(bt_addr_le_t));
285-
settings_save_one("ble/peripheral_address", addr, sizeof(bt_addr_le_t));
277+
int zmk_ble_put_peripheral_addr(const bt_addr_le_t *addr) {
278+
for (int i = 0; i < ZMK_BLE_SPLIT_PERIPHERAL_COUNT; i++) {
279+
// If the address is recognized and already stored in settings, return
280+
// index and no additional action is necessary.
281+
if (!bt_addr_le_cmp(&peripheral_addrs[i], addr)) {
282+
return i;
283+
}
284+
285+
// If the peripheral address slot is open, store new peripheral in the
286+
// slot and return index. This compares against BT_ADDR_LE_ANY as that
287+
// is the zero value.
288+
if (!bt_addr_le_cmp(&peripheral_addrs[i], BT_ADDR_LE_ANY)) {
289+
char addr_str[BT_ADDR_LE_STR_LEN];
290+
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
291+
LOG_DBG("Storing peripheral %s in slot %d", log_strdup(addr_str), i);
292+
bt_addr_le_copy(&peripheral_addrs[i], addr);
293+
294+
char setting_name[32];
295+
sprintf(setting_name, "ble/peripheral_addresses/%d", i);
296+
settings_save_one(setting_name, addr, sizeof(bt_addr_le_t));
297+
298+
return i;
299+
}
300+
}
301+
302+
// The peripheral does not match a known peripheral and there is no
303+
// available slot.
304+
return -ENOMEM;
286305
}
287306

288307
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
@@ -337,15 +356,20 @@ static int ble_profiles_handle_set(const char *name, size_t len, settings_read_c
337356
}
338357
}
339358
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
340-
else if (settings_name_steq(name, "peripheral_address", &next) && !next) {
359+
else if (settings_name_steq(name, "peripheral_addresses", &next) && next) {
341360
if (len != sizeof(bt_addr_le_t)) {
342361
return -EINVAL;
343362
}
344363

345-
int err = read_cb(cb_arg, &peripheral_addr, sizeof(bt_addr_le_t));
346-
if (err <= 0) {
347-
LOG_ERR("Failed to handle peripheral address from settings (err %d)", err);
348-
return err;
364+
int i = atoi(next);
365+
if (i >= ZMK_BLE_SPLIT_PERIPHERAL_COUNT) {
366+
LOG_ERR("Failed to store peripheral address in memory");
367+
} else {
368+
int err = read_cb(cb_arg, &peripheral_addrs[i], sizeof(bt_addr_le_t));
369+
if (err <= 0) {
370+
LOG_ERR("Failed to handle peripheral address from settings (err %d)", err);
371+
return err;
372+
}
349373
}
350374
}
351375
#endif

app/src/split/bluetooth/Kconfig

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ config ZMK_SPLIT_ROLE_CENTRAL
1717

1818
if ZMK_SPLIT_ROLE_CENTRAL
1919

20+
config ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS
21+
int "Number of peripherals that will connect to the central."
22+
default 1
23+
2024
config ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE
2125
int "Max number of key position state events to queue when received from peripherals"
2226
default 5

app/src/split/bluetooth/central.c

+76-48
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ struct peripheral_slot {
4949

5050
static struct peripheral_slot peripherals[ZMK_BLE_SPLIT_PERIPHERAL_COUNT];
5151

52+
static bool is_scanning = false;
53+
5254
static const struct bt_uuid_128 split_service_uuid = BT_UUID_INIT_128(ZMK_SPLIT_BT_SERVICE_UUID);
5355

5456
K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed),
@@ -130,8 +132,9 @@ int release_peripheral_slot(int index) {
130132
return 0;
131133
}
132134

133-
int reserve_peripheral_slot() {
134-
for (int i = 0; i < ZMK_BLE_SPLIT_PERIPHERAL_COUNT; i++) {
135+
int reserve_peripheral_slot(const bt_addr_le_t *addr) {
136+
int i = zmk_ble_put_peripheral_addr(addr);
137+
if (i >= 0) {
135138
if (peripherals[i].state == PERIPHERAL_SLOT_STATE_OPEN) {
136139
// Be sure the slot is fully reinitialized.
137140
release_peripheral_slot(i);
@@ -344,9 +347,56 @@ static void split_central_process_connection(struct bt_conn *conn) {
344347

345348
LOG_DBG("New connection params: Interval: %d, Latency: %d, PHY: %d", info.le.interval,
346349
info.le.latency, info.le.phy->rx_phy);
350+
351+
// Restart scanning if necessary.
352+
start_scan();
347353
}
348354

349-
static bool split_central_eir_found(struct bt_data *data, void *user_data) {
355+
static bool split_central_eir_found(const bt_addr_le_t *addr) {
356+
LOG_DBG("Found the split service");
357+
358+
int slot_idx = reserve_peripheral_slot(addr);
359+
if (slot_idx < 0) {
360+
LOG_ERR("Failed to reserve peripheral slot (err %d)", slot_idx);
361+
return false;
362+
}
363+
364+
// Stop scanning so we can connect to the peripheral device.
365+
LOG_DBG("Stopping peripheral scanning");
366+
is_scanning = false;
367+
int err = bt_le_scan_stop();
368+
if (err) {
369+
LOG_ERR("Stop LE scan failed (err %d)", err);
370+
return false;
371+
}
372+
373+
struct peripheral_slot *slot = &peripherals[slot_idx];
374+
375+
slot->conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr);
376+
if (slot->conn) {
377+
LOG_DBG("Found existing connection");
378+
split_central_process_connection(slot->conn);
379+
err = bt_conn_le_phy_update(slot->conn, BT_CONN_LE_PHY_PARAM_2M);
380+
if (err) {
381+
LOG_ERR("Update phy conn failed (err %d)", err);
382+
}
383+
} else {
384+
struct bt_le_conn_param *param = BT_LE_CONN_PARAM(0x0006, 0x0006, 30, 400);
385+
386+
LOG_DBG("Initiating new connnection");
387+
388+
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &slot->conn);
389+
if (err) {
390+
LOG_ERR("Create conn failed (err %d) (create conn? 0x%04x)", err,
391+
BT_HCI_OP_LE_CREATE_CONN);
392+
start_scan();
393+
}
394+
}
395+
396+
return false;
397+
}
398+
399+
static bool split_central_eir_parse(struct bt_data *data, void *user_data) {
350400
bt_addr_le_t *addr = user_data;
351401
int i;
352402

@@ -361,9 +411,7 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data) {
361411
}
362412

363413
for (i = 0; i < data->data_len; i += 16) {
364-
struct bt_le_conn_param *param;
365414
struct bt_uuid_128 uuid;
366-
int err;
367415

368416
if (!bt_uuid_create(&uuid.uuid, &data->data[i], 16)) {
369417
LOG_ERR("Unable to load UUID");
@@ -382,46 +430,7 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data) {
382430
continue;
383431
}
384432

385-
LOG_DBG("Found the split service");
386-
387-
zmk_ble_set_peripheral_addr(addr);
388-
389-
err = bt_le_scan_stop();
390-
if (err) {
391-
LOG_ERR("Stop LE scan failed (err %d)", err);
392-
continue;
393-
}
394-
395-
uint8_t slot_idx = reserve_peripheral_slot();
396-
if (slot_idx < 0) {
397-
LOG_ERR("Faild to reserve peripheral slot (err %d)", slot_idx);
398-
continue;
399-
}
400-
401-
struct peripheral_slot *slot = &peripherals[slot_idx];
402-
403-
slot->conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr);
404-
if (slot->conn) {
405-
LOG_DBG("Found existing connection");
406-
split_central_process_connection(slot->conn);
407-
err = bt_conn_le_phy_update(slot->conn, BT_CONN_LE_PHY_PARAM_2M);
408-
if (err) {
409-
LOG_ERR("Update phy conn failed (err %d)", err);
410-
}
411-
} else {
412-
param = BT_LE_CONN_PARAM(0x0006, 0x0006, 30, 400);
413-
414-
LOG_DBG("Initiating new connnection");
415-
416-
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &slot->conn);
417-
if (err) {
418-
LOG_ERR("Create conn failed (err %d) (create conn? 0x%04x)", err,
419-
BT_HCI_OP_LE_CREATE_CONN);
420-
start_scan();
421-
}
422-
}
423-
424-
return false;
433+
return split_central_eir_found(addr);
425434
}
426435
}
427436

@@ -438,14 +447,33 @@ static void split_central_device_found(const bt_addr_le_t *addr, int8_t rssi, ui
438447

439448
/* We're only interested in connectable events */
440449
if (type == BT_GAP_ADV_TYPE_ADV_IND || type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
441-
bt_data_parse(ad, split_central_eir_found, (void *)addr);
450+
bt_data_parse(ad, split_central_eir_parse, (void *)addr);
442451
}
443452
}
444453

445454
static int start_scan(void) {
446-
int err;
455+
// No action is necessary if central is already scanning.
456+
if (is_scanning) {
457+
LOG_DBG("Scanning already running");
458+
return 0;
459+
}
460+
461+
// If all the devices are connected, there is no need to scan.
462+
bool has_unconnected = false;
463+
for (int i = 0; i < CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS; i++) {
464+
if (peripherals[i].conn == NULL) {
465+
has_unconnected = true;
466+
break;
467+
}
468+
}
469+
if (!has_unconnected) {
470+
LOG_DBG("All devices are connected, scanning is unnecessary");
471+
return 0;
472+
}
447473

448-
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, split_central_device_found);
474+
// Start scanning otherwise.
475+
is_scanning = true;
476+
int err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, split_central_device_found);
449477
if (err) {
450478
LOG_ERR("Scanning failed to start (err %d)", err);
451479
return err;

0 commit comments

Comments
 (0)