Skip to content

Commit acbbae9

Browse files
committed
pbio/main: Wait for Bluetooth end at pbio.
Other than safely freeing allocated memory, application code shouldn't have to worry about deinitializing the Bluetooth driver, so move it towards the pbio layer. Disconnecting from peripherals at the end is not unlike stopping motors etc.
1 parent 0b30a63 commit acbbae9

File tree

12 files changed

+103
-84
lines changed

12 files changed

+103
-84
lines changed

bricks/_common/micropython.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ static void run_user_program(void) {
314314
// The global scope is preserved to facilitate debugging, but we
315315
// stop active resources like motors and sounds. They are stopped
316316
// but not reset so the user can restart them in the REPL.
317-
pbio_stop_all(false);
317+
pbio_main_soft_stop();
318318

319319
// Enter REPL.
320320
run_repl();
@@ -420,9 +420,10 @@ void pbsys_main_run_program(pbsys_main_program_t *program) {
420420
run_user_program();
421421
break;
422422
}
423+
}
424+
425+
void pbsys_main_run_program_cleanup(void) {
423426

424-
// De-init bluetooth resources (including flushing stdout) that may use
425-
// memory allocated by MicroPython before we wipe it.
426427
pb_package_pybricks_deinit();
427428

428429
gc_sweep_all();

lib/pbio/drv/bluetooth/bluetooth.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,12 @@ pbio_error_t pbdrv_bluetooth_await_peripheral_command(pbio_os_state_t *state, vo
296296
// of interest and will be cancelled if the active function supports it.
297297
pbio_os_timer_set(&peri->watchdog, 10);
298298

299-
return peripheral_singleton.func ? peripheral_singleton.err : PBIO_SUCCESS;
299+
return peri->func ? peri->err : PBIO_SUCCESS;
300+
}
301+
302+
void pbdrv_bluetooth_cancel_operation_request(void) {
303+
// Only some peripheral actions support cancellation.
304+
peripheral_singleton.cancel = true;
300305
}
301306

302307
//

lib/pbio/include/pbdrv/bluetooth.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,15 @@ pbio_error_t pbdrv_bluetooth_peripheral_write_characteristic(uint16_t handle, co
421421
*/
422422
pbio_error_t pbdrv_bluetooth_await_peripheral_command(pbio_os_state_t *state, void *context);
423423

424+
/**
425+
* Requests active Bluetooth tasks to be cancelled. It is up to the task
426+
* implementation to respect or ignore it. The task should still be awaited
427+
* with ::pbdrv_bluetooth_await_advertise_or_scan_command or
428+
* with ::pbdrv_bluetooth_await_peripheral_command. Cancelling just allows
429+
* some commands to exit earlier.
430+
*/
431+
void pbdrv_bluetooth_cancel_operation_request(void);
432+
424433
//
425434
// Functions related to advertising and scanning.
426435
//
@@ -565,6 +574,9 @@ static inline pbio_error_t pbdrv_bluetooth_await_peripheral_command(pbio_os_stat
565574
return PBIO_ERROR_NOT_SUPPORTED;
566575
}
567576

577+
static inline void pbdrv_bluetooth_cancel_operation_request(void) {
578+
}
579+
568580
static inline pbio_error_t pbdrv_bluetooth_start_advertising(bool start) {
569581
return PBIO_ERROR_NOT_SUPPORTED;
570582
}

lib/pbio/include/pbio/main.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
void pbio_init(bool start_processes);
1212
void pbio_deinit(void);
13-
void pbio_stop_all(bool reset);
13+
void pbio_main_stop_application_resources();
14+
void pbio_main_soft_stop(void);
1415

1516
#endif // _PBIO_MAIN_H_

lib/pbio/include/pbsys/main.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ pbio_error_t pbsys_main_program_validate(pbsys_main_program_t *program);
110110
*/
111111
void pbsys_main_run_program(pbsys_main_program_t *program);
112112

113+
/**
114+
* Cleans up after running main application program, such as wiping application
115+
* heap.
116+
*
117+
* This is separate from ::pbsys_main_run_program so that the system can
118+
* safely close resources and unset callbacks before this cleanup runs.
119+
*/
120+
void pbsys_main_run_program_cleanup(void);
121+
113122
/**
114123
* Stops (cancels) the main application program.
115124
*

lib/pbio/src/main.c

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88

99
#include <stdbool.h>
1010

11+
#include <pbdrv/bluetooth.h>
12+
#include <pbdrv/display.h>
1113
#include <pbdrv/sound.h>
1214

1315
#include <pbio/battery.h>
16+
#include <pbio/image.h>
1417
#include <pbio/imu.h>
1518
#include <pbio/motor_process.h>
1619
#include <pbio/port_interface.h>
@@ -51,26 +54,62 @@ void pbio_deinit(void) {
5154
}
5255

5356
/**
54-
* Stops all user-level background processes. Called when the user application
55-
* completes to get these modules back into their default state. Drivers and
56-
* OS-level processes continue running.
57+
* Stops resources like motors or sounds or peripheral procedures that take a
58+
* long time.
5759
*
58-
* @param [in] reset Whether to reset all user-level processes to a clean
59-
* state (true), or whether to only stop active outputs
60-
* like sound or motors (false). The latter is useful to
61-
* preserve the state for debugging, without sound or
62-
* movement getting in the way, or out of control.
60+
* Useful to get the system in a safe state for the user without doing a full
61+
* reset. Applications can all this to enter a user debug mode like the
62+
* MicroPython REPL.
6363
*/
64-
void pbio_stop_all(bool reset) {
65-
#if PBIO_CONFIG_LIGHT
66-
if (reset) {
67-
pbio_light_animation_stop_all();
68-
}
69-
#endif
64+
void pbio_main_soft_stop(void) {
7065

71-
pbio_port_stop_user_actions(reset);
66+
pbio_port_stop_user_actions(false);
7267

7368
pbdrv_sound_stop();
69+
70+
pbdrv_bluetooth_cancel_operation_request();
71+
}
72+
73+
static void wait_for_bluetooth(void) {
74+
pbio_os_state_t unused;
75+
while (pbdrv_bluetooth_await_advertise_or_scan_command(&unused, NULL) == PBIO_ERROR_AGAIN ||
76+
pbdrv_bluetooth_await_peripheral_command(&unused, NULL) == PBIO_ERROR_AGAIN) {
77+
78+
// Run event loop until Bluetooth is idle.
79+
pbio_os_run_processes_and_wait_for_event();
80+
}
81+
}
82+
83+
/**
84+
* Stops all application-level background processes. Called when the user
85+
* application completes to get these modules back into their default state.
86+
* Drivers and OS-level processes continue running.
87+
*/
88+
void pbio_main_stop_application_resources() {
89+
90+
pbio_main_soft_stop();
91+
92+
// Let ongoing task finish first.
93+
wait_for_bluetooth();
94+
95+
// Stop broadcasting, observing and disconnect peripheral.
96+
pbdrv_bluetooth_start_broadcasting(NULL, 0);
97+
wait_for_bluetooth();
98+
99+
pbdrv_bluetooth_start_observing(NULL);
100+
wait_for_bluetooth();
101+
102+
pbdrv_bluetooth_peripheral_disconnect();
103+
wait_for_bluetooth();
104+
105+
#if PBIO_CONFIG_LIGHT
106+
pbio_light_animation_stop_all();
107+
#endif
108+
109+
#if PBDRV_CONFIG_DISPLAY
110+
pbio_image_fill(pbdrv_display_get_image(), 0);
111+
pbdrv_display_update();
112+
#endif
74113
}
75114

76115
/** @} */

lib/pbio/sys/light_matrix.c

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -153,28 +153,21 @@ static uint32_t pbsys_hub_light_matrix_user_program_animation_next(pbio_light_an
153153
}
154154

155155
/**
156-
* Updates light matrix behavior when program is started or stopped.
157-
*
158-
* @param start @c true for start or @c false for stop.
156+
* Updates light matrix behavior when program is started.
159157
*/
160-
void pbsys_hub_light_matrix_handle_user_program_start(bool start) {
158+
void pbsys_hub_light_matrix_handle_user_program_start(void) {
161159

162160
#if PBSYS_CONFIG_HUB_LIGHT_MATRIX_DISPLAY
163161
pbio_image_fill(pbdrv_display_get_image(), 0);
164162
pbdrv_display_update();
165163
return;
166164
#endif
167165

168-
if (start) {
169-
// The user animation updates only a subset of pixels to save time,
170-
// so the rest must be cleared before it starts.
171-
pbsys_hub_light_matrix_user_program_animation_clear();
172-
pbio_light_animation_init(&pbsys_hub_light_matrix->animation, pbsys_hub_light_matrix_user_program_animation_next);
173-
pbio_light_animation_start(&pbsys_hub_light_matrix->animation);
174-
} else {
175-
// If the user program has ended, show stop sign and selected slot.
176-
pbsys_hub_light_matrix_show_idle_ui(100);
177-
}
166+
// The user animation updates only a subset of pixels to save time,
167+
// so the rest must be cleared before it starts.
168+
pbsys_hub_light_matrix_user_program_animation_clear();
169+
pbio_light_animation_init(&pbsys_hub_light_matrix->animation, pbsys_hub_light_matrix_user_program_animation_next);
170+
pbio_light_animation_start(&pbsys_hub_light_matrix->animation);
178171
}
179172

180173
#endif // PBSYS_CONFIG_HUB_LIGHT_MATRIX

lib/pbio/sys/light_matrix.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
#if PBSYS_CONFIG_HUB_LIGHT_MATRIX
1212
void pbsys_hub_light_matrix_init(void);
1313
void pbsys_hub_light_matrix_deinit(void);
14-
void pbsys_hub_light_matrix_handle_user_program_start(bool start);
14+
void pbsys_hub_light_matrix_handle_user_program_start(void);
1515
void pbsys_hub_light_matrix_update_program_slot(void);
1616
#else
1717
#define pbsys_hub_light_matrix_init()
1818
#define pbsys_hub_light_matrix_deinit()
19-
#define pbsys_hub_light_matrix_handle_user_program_start(start)
19+
#define pbsys_hub_light_matrix_handle_user_program_start()
2020
#define pbsys_hub_light_matrix_update_program_slot()
2121
#endif
2222

lib/pbio/sys/main.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ int main(int argc, char **argv) {
102102
pbsys_status_set_program_id(program.id);
103103
pbsys_status_set(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING);
104104
pbsys_host_stdin_set_callback(pbsys_main_stdin_event);
105-
pbsys_hub_light_matrix_handle_user_program_start(true);
105+
pbsys_hub_light_matrix_handle_user_program_start();
106106

107107
#if PBSYS_CONFIG_STATUS_LIGHT
108108
#if PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS
@@ -123,18 +123,22 @@ int main(int argc, char **argv) {
123123
// Run the main application.
124124
pbsys_main_run_program(&program);
125125

126+
// Stop motors, user animations, user bluetooth activity, etc.
127+
pbio_main_stop_application_resources();
128+
126129
// Get system back in idle state.
127130
pbsys_status_clear(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING);
128131
pbsys_host_stdin_set_callback(NULL);
129132
pbsys_program_stop_set_buttons(PBIO_BUTTON_CENTER);
130-
pbsys_hub_light_matrix_handle_user_program_start(false);
131-
pbio_stop_all(true);
132133
program.start_request_type = PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE;
133134

134135
// Handle pending events triggered by the status change, such as
135136
// stopping status light animation.
136137
while (pbio_os_run_processes_once()) {
137138
}
139+
140+
// Finalize application now that system resources are safely closed.
141+
pbsys_main_run_program_cleanup();
138142
}
139143

140144
// Stop system processes and selected drivers in reverse order. This will

pybricks/common/pb_type_ble.c

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -577,26 +577,8 @@ mp_obj_t pb_type_BLE_new(mp_obj_t broadcast_channel_in, mp_obj_t observe_channel
577577
return MP_OBJ_FROM_PTR(self);
578578
}
579579

580-
static void wait_for_bluetooth(void) {
581-
pbio_os_state_t state;
582-
while (pbdrv_bluetooth_await_advertise_or_scan_command(&state, NULL) == PBIO_ERROR_AGAIN) {
583-
MICROPY_VM_HOOK_LOOP;
584-
585-
// Stop waiting (and potentially blocking) in case of forced shutdown.
586-
if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) {
587-
break;
588-
}
589-
}
590-
}
591-
592580
void pb_type_ble_start_cleanup(void) {
593581

594-
wait_for_bluetooth();
595-
pbdrv_bluetooth_start_broadcasting(NULL, 0);
596-
wait_for_bluetooth();
597-
pbdrv_bluetooth_start_observing(NULL);
598-
wait_for_bluetooth();
599-
600582
observed_data = NULL;
601583
num_observed_data = 0;
602584
}

0 commit comments

Comments
 (0)