Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
- Fixed slow broadcasting while observing at the same time ([support#2822]).
- Fixed slow observing while connected to a computer or app ([support#2822]).
- Fixed connected remote preventing Move Hub from advertising.
- Fixed Move Hub not broadcasting while connected to a PC ([support#2826]).

[support#2822]: https://github.com/orgs/pybricks/discussions/2822
[support#2826]: https://github.com/pybricks/support/issues/2826

[Unreleased]: https://github.com/pybricks/pybricks-micropython/compare/v4.1.0b3...HEAD

Expand Down
119 changes: 86 additions & 33 deletions lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,25 @@ const char *pbdrv_bluetooth_get_fw_version(void) {
return pbdrv_bluetooth_fw_version;
}

/**
* Sends a link layer command with constant parameters.
*
* The BlueNRG library's hci_le_*() functions for these commands wait for the
* response, which this driver cannot do, so the command is packed here.
*
* @param [in] ocf The opcode command field.
* @param [in] params The command parameters.
* @param [in] plen The size of @p params in bytes.
*/
static void hci_send_le_command(uint16_t ocf, const uint8_t *params, uint8_t plen) {
struct hci_request rq = {
.opcode = cmd_opcode_pack(OGF_LE_CTL, ocf),
.cparam = (void *)params,
.clen = plen,
};
hci_send_req(&rq);
}

/**
* Sets advertising data and enables advertisements.
*/
Expand Down Expand Up @@ -292,10 +311,21 @@ pbio_error_t pbdrv_bluetooth_stop_advertising_func(pbio_os_state_t *state, void

// REVISIT: might need to delete advertising data here

PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
aci_gap_set_non_discoverable_begin();
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
// aci_gap_set_non_discoverable_end();
if (pbdrv_bluetooth_advertising_state == PBDRV_BLUETOOTH_ADVERTISING_STATE_BROADCASTING) {
// The GAP layer does not know about advertising that was enabled with
// a link layer command, so it has to be stopped the same way.
PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
{
static const uint8_t disable = 0;
hci_send_le_command(OCF_LE_SET_ADVERTISE_ENABLE, &disable, sizeof(disable));
}
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
} else {
PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
aci_gap_set_non_discoverable_begin();
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
// aci_gap_set_non_discoverable_end();
}

// This protothread is also shared with stop broadcasting. Either way,
// nothing is advertising or broadcasting after this, so reset that state.
Expand Down Expand Up @@ -599,49 +629,72 @@ pbio_error_t pbdrv_bluetooth_start_broadcasting_func(pbio_os_state_t *state, voi

PBIO_OS_ASYNC_BEGIN(state);

// The GAP layer refuses to enter any advertising mode while a computer is
// connected, so the link layer is driven directly instead.
if (pbdrv_bluetooth_advertising_state != PBDRV_BLUETOOTH_ADVERTISING_STATE_BROADCASTING) {
PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
// This chip cannot broadcast faster than every 100ms, so other hubs
// receive from it more slowly than they do from each other.
aci_gap_set_non_connectable_begin(ADV_NONCONN_IND, STATIC_RANDOM_ADDR);
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
status = aci_gap_set_non_connectable_end();

if (status != BLE_STATUS_SUCCESS) {
pbio_error_t err = ble_error_to_pbio_error(status);
// Broadcasting does not work while connected to the computer. But
// returning an error means that Move Hub programs with
// broadcasting can never run while connected, which makes it very
// impractical to test any program. So mark as success.
if (err == PBIO_ERROR_INVALID_OP) {
return PBIO_SUCCESS;
}
return err;
// Advertising parameters can only be set while advertising is stopped,
// so leave the GAP discoverable mode if it is still active.
if (pbdrv_bluetooth_advertising_state == PBDRV_BLUETOOTH_ADVERTISING_STATE_ADVERTISING_PYBRICKS) {
PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
aci_gap_set_non_discoverable_begin();
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
// aci_gap_set_non_discoverable_end();
}

// These AD types are left over from connectable discovery and need
// to be deleted _after_ starting non-connectable advertising.

PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
aci_gap_delete_ad_type_begin(AD_TYPE_128_BIT_SERV_UUID);
{
static const uint8_t params[] = {
// 100ms is the fastest this controller allows. The range has
// to be wide, because the chip fits a new activity to the
// anchor period it is already running for the connection and
// any scanning, and rejects an interval that is not a multiple
// of it.
0xa0, 0x00, // Advertising_Interval_Min, 100ms
0x40, 0x01, // Advertising_Interval_Max, 200ms
ADV_NONCONN_IND,
STATIC_RANDOM_ADDR,
0, // Peer_Address_Type
0, 0, 0, 0, 0, 0, // Peer_Address
0x07, // Advertising_Channel_Map
NO_WHITE_LIST_USE,
};
hci_send_le_command(OCF_LE_SET_ADV_PARAMETERS, params, sizeof(params));
}
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
status = hci_le_command_end();

PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
aci_gap_delete_ad_type_begin(AD_TYPE_TX_POWER_LEVEL);
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
if (status != BLE_STATUS_SUCCESS) {
return ble_error_to_pbio_error(status);
}
}

// Errors from deleting are ignored since we should only get an error
// if the AD does not exist, which is OK.
// The link layer advertises exactly this payload.
PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
hci_le_set_advertising_data_begin(pbdrv_bluetooth_broadcast_data_size, pbdrv_bluetooth_broadcast_data);
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
status = hci_le_set_advertising_data_end();

pbdrv_bluetooth_advertising_state = PBDRV_BLUETOOTH_ADVERTISING_STATE_BROADCASTING;
if (status != BLE_STATUS_SUCCESS) {
return ble_error_to_pbio_error(status);
}

// This has to be done _after_ other data is delete to make sure it fits.
if (pbdrv_bluetooth_advertising_state == PBDRV_BLUETOOTH_ADVERTISING_STATE_BROADCASTING) {
// Already advertising, so updating the data was all that was needed.
return PBIO_SUCCESS;
}

PBIO_OS_AWAIT_WHILE(state, write_xfer_size);
aci_gap_update_adv_data_begin(pbdrv_bluetooth_broadcast_data_size, pbdrv_bluetooth_broadcast_data);
{
static const uint8_t enable = 1;
hci_send_le_command(OCF_LE_SET_ADVERTISE_ENABLE, &enable, sizeof(enable));
}
PBIO_OS_AWAIT_UNTIL(state, hci_command_complete);
status = aci_gap_update_adv_data_end();
status = hci_le_command_end();

if (status == BLE_STATUS_SUCCESS) {
pbdrv_bluetooth_advertising_state = PBDRV_BLUETOOTH_ADVERTISING_STATE_BROADCASTING;
}

PBIO_OS_ASYNC_END(ble_error_to_pbio_error(status));
}
Expand Down
3 changes: 0 additions & 3 deletions pybricks/messaging/pb_type_ble_radio.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,6 @@ static mp_obj_t pb_module_ble_broadcast(size_t n_args, const mp_obj_t *pos_args,
PB_PARSE_ARGS_METHOD(n_args, pos_args, kw_args,
pb_obj_BLE_t, self,
PB_ARG_REQUIRED(data));
// On Move Hub, nothing is broadcast if it is called while the
// move hub is connected to Pybricks Code. Also, broadcasting interferes
// with observing even when not connected to Pybricks Code.

mp_obj_t self_in = MP_OBJ_FROM_PTR(self);

Expand Down