diff --git a/CHANGELOG.md b/CHANGELOG.md index 409e46aae..d93df3507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,12 @@ - Extensive overhaul of UART and port drivers on all hubs. This affects all official LEGO sensors on all hubs. +### Fixed +- Reduced hanging when broadcasting and observing at the same time with Technic + Hub ([support#2206]). + [support#220]: https://github.com/pybricks/support/issues/220 +[support#2206]: https://github.com/pybricks/support/issues/2206 [pybricks-micropython#208]: https://github.com/pybricks/pybricks-micropython/pull/208 ## [3.6.1] - 2025-03-11 diff --git a/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c b/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c index 4d972654f..326948096 100644 --- a/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c +++ b/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c @@ -160,8 +160,8 @@ static const pbdrv_bluetooth_stm32_cc2640_platform_data_t *pdata = &pbdrv_blueto #define SCAN_RESTART_INTERVAL 3000 #define OBSERVE_RESTART_INTERVAL 10000 static struct etimer scan_restart_timer; -static struct etimer observe_restart_timer; -static bool observe_restart_enabled; +static bool observe_restart_requested; +static uint32_t observe_restart_time; /** * Converts a ble error code to the most appropriate pbio error code. @@ -500,7 +500,6 @@ static PT_THREAD(peripheral_scan_and_connect_task(struct pt *pt, pbio_task_t *ta // temporarily stop observing so we can active scan if (is_observing) { - observe_restart_enabled = false; PT_WAIT_WHILE(pt, write_xfer_size); GAP_DeviceDiscoveryCancel(); @@ -747,10 +746,8 @@ static PT_THREAD(peripheral_scan_and_connect_task(struct pt *pt, pbio_task_t *ta device_discovery_done = false; - PROCESS_CONTEXT_BEGIN(&pbdrv_bluetooth_spi_process); - etimer_set(&observe_restart_timer, OBSERVE_RESTART_INTERVAL); - PROCESS_CONTEXT_END(&pbdrv_bluetooth_spi_process); - observe_restart_enabled = true; + observe_restart_time = pbdrv_clock_get_ms(); + observe_restart_requested = false; } PT_END(pt); @@ -1073,6 +1070,11 @@ void pbdrv_bluetooth_peripheral_disconnect(pbio_task_t *task) { static PT_THREAD(broadcast_task(struct pt *pt, pbio_task_t *task)) { pbdrv_bluetooth_value_t *value = task->context; + static struct { + pbdrv_bluetooth_value_t v; + uint8_t d[31]; + } previous_value; + PT_BEGIN(pt); if (value->size > B_MAX_ADV_LEN) { @@ -1080,6 +1082,14 @@ static PT_THREAD(broadcast_task(struct pt *pt, pbio_task_t *task)) { PT_EXIT(pt); } + // Avoid I/O operations if the user tries to broadcast the same data + // over and over in a tight loop. + if (is_broadcasting && !memcmp((void *)&previous_value, (void *)value, sizeof(pbdrv_bluetooth_value_t) + value->size)) { + task->status = PBIO_SUCCESS; + PT_EXIT(pt); + } + memcpy((void *)&previous_value, (void *)value, sizeof(pbdrv_bluetooth_value_t) + value->size); + // HACK: calling GAP_updateAdvertisingData() repeatedly will cause the // Bluetooth chips on Technic and City hubs to eventually lock up. So we // call the standard Bluetooth command instead. We still get the vendor- @@ -1162,10 +1172,8 @@ static PT_THREAD(observe_task(struct pt *pt, pbio_task_t *task)) { device_discovery_done = false; is_observing = true; - PROCESS_CONTEXT_BEGIN(&pbdrv_bluetooth_spi_process); - etimer_set(&observe_restart_timer, OBSERVE_RESTART_INTERVAL); - PROCESS_CONTEXT_END(&pbdrv_bluetooth_spi_process); - observe_restart_enabled = true; + observe_restart_time = pbdrv_clock_get_ms(); + observe_restart_requested = false; } task->status = PBIO_SUCCESS; @@ -1201,11 +1209,16 @@ static PT_THREAD(stop_observe_task(struct pt *pt, pbio_task_t *task)) { void pbdrv_bluetooth_stop_observing(pbio_task_t *task) { observe_callback = NULL; // avoid restarting observing even if this task get queued - observe_restart_enabled = false; + observe_restart_requested = false; start_task(task, stop_observe_task, NULL); } +void pbdrv_bluetooth_restart_observing_request(void) { + observe_restart_requested = true; + process_poll(&pbdrv_bluetooth_spi_process); +} + // Driver interrupt callbacks void pbdrv_bluetooth_stm32_cc2640_srdy_irq(bool srdy) { @@ -2250,7 +2263,7 @@ PROCESS_THREAD(pbdrv_bluetooth_spi_process, ev, data) { spi_set_mrdy(false); bluetooth_reset(RESET_STATE_OUT_LOW); bluetooth_ready = pybricks_notify_en = uart_tx_notify_en = - is_broadcasting = is_observing = observe_restart_enabled = false; + is_broadcasting = is_observing = observe_restart_requested = false; conn_handle = peripheral_singleton.con_handle = NO_CONNECTION; pbio_task_t *task; @@ -2309,11 +2322,14 @@ PROCESS_THREAD(pbdrv_bluetooth_spi_process, ev, data) { for (;;) { PROCESS_WAIT_UNTIL({ for (;;) { - if (observe_restart_enabled && etimer_expired(&observe_restart_timer)) { + if (observe_restart_requested && pbdrv_clock_get_ms() - observe_restart_time > OBSERVE_RESTART_INTERVAL) { static pbio_task_t observe_restart_stop_task; static pbio_task_t observe_restart_start_task; pbdrv_bluetooth_start_observing_callback_t callback = observe_callback; + // Avoid queueing this more than once. + observe_restart_time = pbdrv_clock_get_ms(); + pbdrv_bluetooth_stop_observing(&observe_restart_stop_task); pbdrv_bluetooth_start_observing(&observe_restart_start_task, callback); } diff --git a/pybricks/common/pb_type_ble.c b/pybricks/common/pb_type_ble.c index 8b762c6b1..cd3105cbb 100644 --- a/pybricks/common/pb_type_ble.c +++ b/pybricks/common/pb_type_ble.c @@ -440,6 +440,12 @@ static mp_obj_t pb_module_ble_observe(mp_obj_t self_in, mp_obj_t channel_in) { // Have not received data yet or timed out. if (ch_data.rssi == INT8_MIN) { + + #if PBDRV_CONFIG_BLUETOOTH_STM32_CC2640 + extern void pbdrv_bluetooth_restart_observing_request(void); + pbdrv_bluetooth_restart_observing_request(); + #endif + return mp_const_none; }