Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(split): allow central to connect to multiple peripherals #836

Merged
merged 1 commit into from
Jun 5, 2023

Conversation

xudongzheng
Copy link
Contributor

This addresses #314 and #609.

This might also affect #281 and #718.

@innovaker innovaker added the enhancement New feature or request label Jun 12, 2021
@@ -337,15 +362,20 @@ static int ble_profiles_handle_set(const char *name, size_t len, settings_read_c
}
}
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
else if (settings_name_steq(name, "peripheral_address", &next) && !next) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this line will solve multiple problems i have currently with my split setup

@xudongzheng
Copy link
Contributor Author

Most likely some additional changes would be needed for settings reset. It's not clear how that should be implemented, perhaps just loop and delete ble/peripheral_addresses/[0,7] or just call nvs_clear() to handle peripheral addresses and everything else.

// index and no additional action is necessary.
if (!bt_addr_le_cmp(&peripheral_addrs[i], addr)) {
return i;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the recorded peripheral appears after slot i+1, and takes ith slot?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand what i+1 you are referring to.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the addr is actually recorded at some slot j, where j > i, but somehow the ith slot is open, so the addr takes away the ith slot as well.

This may be a rare case, and jth slot anyhow could be recycled for the addr should trigger disconnected function in normal cases.

Copy link
Contributor Author

@xudongzheng xudongzheng Sep 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slot 0 is filled, then slot 1 is filled, and so on. There is no code to delete just a single ble/peripheral_addresses/%d setting so it's not possible to have i open and j occupied where i < j.

Copy link
Contributor

@petejohanson petejohanson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple minor items, but on a whole, LGTM.

err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, split_central_device_found);
// Start scanning otherwise.
is_scanning = true;
int err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, split_central_device_found);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for now, but ideally a future enhancement will add a backoff on scanning, since multi-peripheral setups are more likely to have one of them only on some of the time.

Copy link

@bezmi bezmi Nov 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm exploring this,

bt_le_scan_start(&test_scan_params, split_central_device_found)

where,

static struct bt_le_scan_param test_scan_params = {
    .type = BT_LE_SCAN_TYPE_PASSIVE,
    .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
    .interval = BT_GAP_SCAN_FAST_INTERVAL,
    .window = BT_GAP_SCAN_FAST_WINDOW,
    .timeout = 1500,
    .interval_coded = 0,
    .window_coded = 0,
};

Setting any timeout > 0 will cause zmk to hang. .timeout = 0 will work just fine,

[00:00:00.770,935] <dbg> zmk: zmk_usb_get_conn_state: state: 1
[00:00:00.770,965] <dbg> zmk: get_selected_endpoint: No endpoints are ready.
[00:00:01.081,146] <inf> usb_hid: Device configured
[00:00:01.081,207] <dbg> zmk: zmk_usb_get_conn_state: state: 3
[00:00:01.081,237] <dbg> zmk: zmk_usb_get_conn_state: state: 3
[00:00:01.081,237] <dbg> zmk: get_selected_endpoint: Only USB is ready.
[00:00:01.081,237] <dbg> zmk: zmk_endpoints_send_report: usage page 0x07
[00:00:01.081,268] <dbg> zmk: zmk_endpoints_send_report: usage page 0x0C
[00:00:01.081,298] <inf> zmk: Endpoint changed: 0
[00:00:01.081,359] <dbg> zmk: destination_connection: Address pointer 0x20008fdf
[00:00:01.081,359] <wrn> zmk: Not sending, no active address for current profile
[00:00:01.081,390] <dbg> zmk: destination_connection: Address pointer 0x20008fdf
[00:00:01.081,390] <wrn> zmk: Not sending, no active address for current profile

Any ideas? I was hoping to register scan callbacks where after the 15s timeout, scanning is restarted with new params with a longer interval and window and an indefinite timeout.

@xudongzheng
Copy link
Contributor Author

@petejohanson

Is there a reason for this section of code https://github.com/zmkfirmware/zmk/blob/main/app/src/split/bluetooth/central.c#L403-L411? It's not clear to me why split_central_process_connection() would be called there as it's already called from the connect event handler.

I would consider getting rid of the lookup and having slicemk@44e0e7b#diff-18de66983adf7197deaf9e826c4087b486beb147e0ab19445d41a85ba7be9fe0R374-R384.

@petejohanson
Copy link
Contributor

@petejohanson

Is there a reason for this section of code https://github.com/zmkfirmware/zmk/blob/main/app/src/split/bluetooth/central.c#L403-L411? It's not clear to me why split_central_process_connection() would be called there as it's already called from the connect event handler.

I would consider getting rid of the lookup and having slicemk@44e0e7b#diff-18de66983adf7197deaf9e826c4087b486beb147e0ab19445d41a85ba7be9fe0R374-R384.

IIRC, at least with Zephyr from several versions ago when I first wrote this, some times we would end up having an existing connection, after the scanning first discovers the advertising info, we and we needed to handle both scenarios:

  1. No connect, create it.
  2. Already connected, process the connection.

If we can test this thoroughly and see this no longer happening with the stack, we should simplify this, for sure.

Copy link
Contributor

@petejohanson petejohanson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few final code nitpicks. I will try to get to testing this soon.

app/src/ble.c Outdated
LOG_ERR("Failed to handle peripheral address from settings (err %d)", err);
return err;
int i = atoi(next);
if (i >= ZMK_BLE_SPLIT_PERIPHERAL_COUNT) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be bullet proof here, should check for i < 0 as well.

@xudongzheng
Copy link
Contributor Author

My suggestion is to only handle a new connection when the connected callback happens. This should happen exactly once per new connection.

If there is an existing connection, reserve_peripheral_slot() should fail because each peripheral will resolve to the same index each time - that's in zmk_ble_put_peripheral_addr().

Will have an updated PR shortly.

@xudongzheng xudongzheng force-pushed the dongle-pr branch 2 times, most recently from 3aa5b79 to 42d83f5 Compare November 9, 2022 16:25
@bezmi
Copy link

bezmi commented Nov 10, 2022

I have been testing this (on the latest one as of this comment) and it's working well. @xudongzheng would you please consider pushing on top of the current commit instead of re-writing history with the force pushes? Makes it a little easier to explore the changes and can always be squashed when merging. Also, see my comment above RE scan timeout.

@xudongzheng
Copy link
Contributor Author

xudongzheng commented Nov 10, 2022

@bezmi good point, you can see today's changes in d26b0ff on top of the earlier commit

I'm not familiar with the scan timeout. I'd consider explicitly stopping the scanning using a custom timer, which might be necessary regardless for maximum flexibility.

@bezmi
Copy link

bezmi commented May 4, 2023

@xudongzheng, I have been testing this PR on my daily driver split keyboard for pretty much as long as it has existed. I often run into a particular latency issue where keypresses from each half will arrive out of order, making it difficult to type quickly. It is very similar in description to #1633. Have you had any such issues and any success in troubleshooting them?

@xudongzheng
Copy link
Contributor Author

@bezmi The first thing to try is placing the dongle right between the two keyboard halves. If it works fine there, it's likely a signal issue and not a firmware issue. You can bump the Bluetooth TX power as is typically recommended for ZMK.

@petejohanson
Copy link
Contributor

@xudongzheng Can you please rebase now that your refactor #1815 is in? Code looks good w/ your latest push, will test here then merge, barring any problems appearing. Thanks!

@petejohanson
Copy link
Contributor

@xudongzheng See the pre-commit check issue please on this.

@xudongzheng
Copy link
Contributor Author

@petejohanson Oops, somehow missed that. The new commit seems to pass all the commit checks.

@petejohanson petejohanson self-assigned this Jun 5, 2023
@petejohanson petejohanson added bluetooth Bluetooth related items split labels Jun 5, 2023
Copy link
Contributor

@petejohanson petejohanson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on a pair of ZMK Uno/DK kits set up as splits. Works fine, and I know others have leveraged for a while for "dongle mode" as well.

This is a great first step toward fully supported BLE dongle mode, which will need still more docs, and build fixes to make doing so more seamless.

Thanks for sticking w/ this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bluetooth Bluetooth related items enhancement New feature or request split
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants