Skip to content

Commit 5d9ae8f

Browse files
xudongzhengpetejohanson
authored andcommitted
feat(split): allow central to connect to multiple peripherals
1 parent f08802e commit 5d9ae8f

File tree

4 files changed

+130
-72
lines changed

4 files changed

+130
-72
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_SPLIT_BLE_PERIPHERAL_COUNT 1
17+
#define ZMK_BLE_PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS)
18+
#define ZMK_SPLIT_BLE_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 @@ RING_BUF_DECLARE(passkey_entries, PASSKEY_DIGITS);
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_SPLIT_BLE_PERIPHERAL_COUNT];
8882

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

@@ -283,9 +277,34 @@ char *zmk_ble_active_profile_name() { return profiles[active_profile].name; }
283277

284278
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
285279

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

291310
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
@@ -340,15 +359,20 @@ static int ble_profiles_handle_set(const char *name, size_t len, settings_read_c
340359
}
341360
}
342361
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
343-
else if (settings_name_steq(name, "peripheral_address", &next) && !next) {
362+
else if (settings_name_steq(name, "peripheral_addresses", &next) && next) {
344363
if (len != sizeof(bt_addr_le_t)) {
345364
return -EINVAL;
346365
}
347366

348-
int err = read_cb(cb_arg, &peripheral_addr, sizeof(bt_addr_le_t));
349-
if (err <= 0) {
350-
LOG_ERR("Failed to handle peripheral address from settings (err %d)", err);
351-
return err;
367+
int i = atoi(next);
368+
if (i < 0 || i >= ZMK_SPLIT_BLE_PERIPHERAL_COUNT) {
369+
LOG_ERR("Failed to store peripheral address in memory");
370+
} else {
371+
int err = read_cb(cb_arg, &peripheral_addrs[i], sizeof(bt_addr_le_t));
372+
if (err <= 0) {
373+
LOG_ERR("Failed to handle peripheral address from settings (err %d)", err);
374+
return err;
375+
}
352376
}
353377
}
354378
#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

+84-54
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
2626
#include <zmk/event_manager.h>
2727
#include <zmk/events/position_state_changed.h>
2828

29-
static int start_scan(void);
29+
static int start_scanning(void);
3030

3131
#define POSITION_STATE_DATA_LEN 16
3232

@@ -49,6 +49,8 @@ struct peripheral_slot {
4949

5050
static struct peripheral_slot peripherals[ZMK_SPLIT_BLE_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_SPLIT_BLE_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,54 @@ 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_scanning();
353+
}
354+
355+
static int stop_scanning() {
356+
LOG_DBG("Stopping peripheral scanning");
357+
is_scanning = false;
358+
359+
int err = bt_le_scan_stop();
360+
if (err < 0) {
361+
LOG_ERR("Stop LE scan failed (err %d)", err);
362+
return err;
363+
}
364+
365+
return 0;
347366
}
348367

349-
static bool split_central_eir_found(struct bt_data *data, void *user_data) {
368+
static bool split_central_eir_found(const bt_addr_le_t *addr) {
369+
LOG_DBG("Found the split service");
370+
371+
// Stop scanning so we can connect to the peripheral device.
372+
int err = stop_scanning();
373+
if (err < 0) {
374+
return false;
375+
}
376+
377+
int slot_idx = reserve_peripheral_slot(addr);
378+
if (slot_idx < 0) {
379+
LOG_ERR("Failed to reserve peripheral slot (err %d)", slot_idx);
380+
return false;
381+
}
382+
383+
struct peripheral_slot *slot = &peripherals[slot_idx];
384+
385+
LOG_DBG("Initiating new connnection");
386+
struct bt_le_conn_param *param = BT_LE_CONN_PARAM(0x0006, 0x0006, 30, 400);
387+
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &slot->conn);
388+
if (err < 0) {
389+
LOG_ERR("Create conn failed (err %d) (create conn? 0x%04x)", err, BT_HCI_OP_LE_CREATE_CONN);
390+
release_peripheral_slot(slot_idx);
391+
start_scanning();
392+
}
393+
394+
return false;
395+
}
396+
397+
static bool split_central_eir_parse(struct bt_data *data, void *user_data) {
350398
bt_addr_le_t *addr = user_data;
351399
int i;
352400

@@ -361,9 +409,7 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data) {
361409
}
362410

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

368414
if (!bt_uuid_create(&uuid.uuid, &data->data[i], 16)) {
369415
LOG_ERR("Unable to load UUID");
@@ -381,46 +427,7 @@ static bool split_central_eir_found(struct bt_data *data, void *user_data) {
381427
continue;
382428
}
383429

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

@@ -436,15 +443,34 @@ static void split_central_device_found(const bt_addr_le_t *addr, int8_t rssi, ui
436443

437444
/* We're only interested in connectable events */
438445
if (type == BT_GAP_ADV_TYPE_ADV_IND || type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
439-
bt_data_parse(ad, split_central_eir_found, (void *)addr);
446+
bt_data_parse(ad, split_central_eir_parse, (void *)addr);
440447
}
441448
}
442449

443-
static int start_scan(void) {
444-
int err;
450+
static int start_scanning(void) {
451+
// No action is necessary if central is already scanning.
452+
if (is_scanning) {
453+
LOG_DBG("Scanning already running");
454+
return 0;
455+
}
445456

446-
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, split_central_device_found);
447-
if (err) {
457+
// If all the devices are connected, there is no need to scan.
458+
bool has_unconnected = false;
459+
for (int i = 0; i < CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS; i++) {
460+
if (peripherals[i].conn == NULL) {
461+
has_unconnected = true;
462+
break;
463+
}
464+
}
465+
if (!has_unconnected) {
466+
LOG_DBG("All devices are connected, scanning is unnecessary");
467+
return 0;
468+
}
469+
470+
// Start scanning otherwise.
471+
is_scanning = true;
472+
int err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, split_central_device_found);
473+
if (err < 0) {
448474
LOG_ERR("Scanning failed to start (err %d)", err);
449475
return err;
450476
}
@@ -471,7 +497,7 @@ static void split_central_connected(struct bt_conn *conn, uint8_t conn_err) {
471497

472498
release_peripheral_slot_for_conn(conn);
473499

474-
start_scan();
500+
start_scanning();
475501
return;
476502
}
477503

@@ -495,7 +521,7 @@ static void split_central_disconnected(struct bt_conn *conn, uint8_t reason) {
495521
return;
496522
}
497523

498-
start_scan();
524+
start_scanning();
499525
}
500526

501527
static struct bt_conn_cb conn_callbacks = {
@@ -527,6 +553,10 @@ void split_central_split_run_callback(struct k_work *work) {
527553
LOG_ERR("Source not connected");
528554
continue;
529555
}
556+
if (!peripherals[payload_wrapper.source].run_behavior_handle) {
557+
LOG_ERR("Run behavior handle not found");
558+
continue;
559+
}
530560

531561
int err = bt_gatt_write_without_response(
532562
peripherals[payload_wrapper.source].conn,
@@ -590,7 +620,7 @@ int zmk_split_bt_central_init(const struct device *_arg) {
590620
CONFIG_ZMK_BLE_THREAD_PRIORITY, NULL);
591621
bt_conn_cb_register(&conn_callbacks);
592622

593-
return start_scan();
623+
return start_scanning();
594624
}
595625

596626
SYS_INIT(zmk_split_bt_central_init, APPLICATION, CONFIG_ZMK_BLE_INIT_PRIORITY);

0 commit comments

Comments
 (0)