Skip to content

Commit

Permalink
pbio/sys/main: Expose how program was started.
Browse files Browse the repository at this point in the history
This gives the user a way to optionally run certain code only when it is first downloaded to the robot. Examples include one-off motor initialization or one-off wiping of user data.

Fixes pybricks/support#1496
  • Loading branch information
laurensvalk committed Oct 15, 2024
1 parent 26f7701 commit 2a30d93
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 15 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
coding ([support#1661]).
- Added `observe_enable` to the hub `BLE` class to selectively turn observing
on and off, just like you can with broadcasting ([support#1806]).
- Added `hub.system.info()` method with hub status flags ([support#1496]).
- Added `hub.system.info()` method with hub status flags ([support#1496]) and
value representing how the program was started.

### Changed

Expand Down
38 changes: 34 additions & 4 deletions lib/pbio/include/pbsys/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,30 @@
#include <stdbool.h>
#include <stdint.h>

/**
* Whether and how the program was requested to start.
*
* Values are returned to the user, so the numeric values must not change.
*/
typedef enum {
/**
* The program was not requested to start.
*/
PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE = 0,
/**
* The program was requested to start automatically on boot.
*/
PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_BOOT = 1,
/**
* The program was requested to start using the hub button/display UI.
*/
PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_HUB_UI = 2,
/**
* The program was requested to start remotely, such as with an IDE.
*/
PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_REMOTE = 3,
} pbsys_main_program_start_request_type_t;

/**
* Main application program data information.
*/
Expand All @@ -41,14 +65,16 @@ typedef struct _pbsys_main_program_t {
*/
pbio_pybricks_user_program_id_t id;
/**
* Whether a request was made to start the program.
* Whether a request was made to start the program, and how.
*/
bool start_requested;
pbsys_main_program_start_request_type_t start_request_type;
} pbsys_main_program_t;

#if PBSYS_CONFIG_MAIN

pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id);
pbsys_main_program_start_request_type_t pbsys_main_program_get_start_request_type(void);

pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id, pbsys_main_program_start_request_type_t start_request_type);

/**
* Validates the program that is being requested to start.
Expand Down Expand Up @@ -99,7 +125,11 @@ const char *pbsys_main_get_application_version_hash(void);

#else // PBSYS_CONFIG_MAIN

static inline pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id) {
static inline pbsys_main_program_start_request_type_t pbsys_main_program_get_start_request_type(void) {
return PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE;
}

static inline pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id, pbsys_main_program_start_request_type_t start_request_type) {
return PBIO_ERROR_NOT_SUPPORTED;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/pbio/sys/command.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ pbio_pybricks_error_t pbsys_command(const uint8_t *data, uint32_t size) {
}
// Use payload as program ID, otherwise use active user slot.
return pbio_pybricks_error_from_pbio_error(
pbsys_main_program_request_start((size == 2 ? data[1] : pbsys_hmi_get_selected_program_slot())));
pbsys_main_program_request_start((size == 2 ? data[1] : pbsys_hmi_get_selected_program_slot()), PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_REMOTE));
}
#if PBSYS_CONFIG_FEATURE_BUILTIN_USER_PROGRAM_REPL
case PBIO_PYBRICKS_COMMAND_START_REPL:
// Deprecated. For backwards compatibility with Pybricks
// Profile < v1.4.0, make it work anyway.
return pbio_pybricks_error_from_pbio_error(
pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_REPL));
pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_REPL, PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_REMOTE));
#endif // PBSYS_CONFIG_FEATURE_BUILTIN_USER_PROGRAM_REPL

case PBIO_PYBRICKS_COMMAND_WRITE_USER_PROGRAM_META:
Expand Down
4 changes: 2 additions & 2 deletions lib/pbio/sys/hmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static PT_THREAD(update_program_run_button_wait_state(bool button_pressed)) {

// If we made it through a full press and release, without the user
// program running, then start the currently selected user program.
pbsys_main_program_request_start(selected_slot);
pbsys_main_program_request_start(selected_slot, PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_HUB_UI);
}

PT_END(pt);
Expand Down Expand Up @@ -152,7 +152,7 @@ static PT_THREAD(update_left_right_button_wait_state(bool left_button_pressed, b
if (left_button_pressed && pbdrv_clock_get_ms() - first_press_time < 100) {
selected_slot = previous_slot;
pbsys_hub_light_matrix_update_program_slot();
pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_PORT_VIEW);
pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_PORT_VIEW, PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_HUB_UI);
} else {
// Successful switch. And UI was already updated.
previous_slot = selected_slot;
Expand Down
33 changes: 27 additions & 6 deletions lib/pbio/sys/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,29 @@
#include <pbsys/program_stop.h>
#include <pbsys/bluetooth.h>

// Singleton with information about the currently (or soon) active program.
static pbsys_main_program_t program;

/**
* Checks if a start request has been made for the main program.
*
* @param [in] program A pointer to the main program structure.
* @returns true if a start request has been made, false otherwise.
*/
static bool pbsys_main_program_start_requested() {
return program.start_request_type != PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE;
}

/**
* Gets the type of start request for the main program.
*
* @param [in] program A pointer to the main program structure.
* @returns The type of start request.
*/
pbsys_main_program_start_request_type_t pbsys_main_program_get_start_request_type(void) {
return program.start_request_type;
}

/**
* Requests to start the main user application program.
*
Expand All @@ -31,10 +52,10 @@ static pbsys_main_program_t program;
* ::PBIO_ERROR_NOT_SUPPORTED if the program is not available.
* Otherwise ::PBIO_SUCCESS.
*/
pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id) {
pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id, pbsys_main_program_start_request_type_t start_request_type) {

// Can't start new program if already running or new requested.
if (pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING) || program.start_requested) {
if (pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING) || pbsys_main_program_start_requested()) {
return PBIO_ERROR_BUSY;
}

Expand All @@ -49,7 +70,7 @@ pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id
return err;
}

program.start_requested = true;
program.start_request_type = start_request_type;

return PBIO_SUCCESS;
}
Expand All @@ -68,15 +89,15 @@ int main(int argc, char **argv) {
while (!pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) {

#if PBSYS_CONFIG_USER_PROGRAM_AUTO_START
pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_REPL);
pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_REPL, PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_BOOT);
#endif

// REVISIT: this can be long waiting, so we could do a more efficient
// wait (i.e. __WFI() on embedded system)
while (pbio_do_one_event()) {
}

if (!program.start_requested) {
if (!pbsys_main_program_start_requested()) {
continue;
}

Expand All @@ -98,7 +119,7 @@ int main(int argc, char **argv) {
pbsys_bluetooth_rx_set_callback(NULL);
pbsys_program_stop_set_buttons(PBIO_BUTTON_CENTER);
pbio_stop_all(true);
program.start_requested = false;
program.start_request_type = PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE;
}

// Stop system processes and save user data before we shutdown.
Expand Down
2 changes: 2 additions & 0 deletions pybricks/common/pb_type_system.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <pbdrv/bluetooth.h>
#include <pbdrv/reset.h>
#include <pbsys/main.h>
#include <pbsys/program_stop.h>
#include <pbsys/status.h>
#include <pbsys/storage.h>
Expand Down Expand Up @@ -48,6 +49,7 @@ static mp_obj_t pb_type_System_info(void) {
{MP_OBJ_NEW_QSTR(MP_QSTR_reset_reason), mp_obj_new_int(pbdrv_reset_get_reason())},
#endif // PBDRV_CONFIG_RESET
{MP_OBJ_NEW_QSTR(MP_QSTR_host_connected_ble), mp_obj_new_bool(pbsys_status_test(PBIO_PYBRICKS_STATUS_BLE_HOST_CONNECTED))},
{MP_OBJ_NEW_QSTR(MP_QSTR_program_start_type), mp_obj_new_int(pbsys_main_program_get_start_request_type())},
};
mp_obj_t info_dict = mp_obj_new_dict(MP_ARRAY_SIZE(info));

Expand Down

0 comments on commit 2a30d93

Please sign in to comment.