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
8 changes: 8 additions & 0 deletions bricks/_common/micropython.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ bool pbsys_main_stdin_event(uint8_t c) {

// Prints the exception that ended the program.
static void print_final_exception(mp_obj_t exc, int ret) {

// Ensure exception prints on new line.
mp_hal_stdout_tx_flush();

nlr_buf_t nlr;
nlr.ret_val = NULL;

Expand Down Expand Up @@ -406,6 +410,10 @@ void pbsys_main_run_program(pbsys_main_program_t *program) {
run_user_program();
break;
}

// Ensure everything is written before the user application is considered
// done, so that the host does not receive stdout after receiving stop.
mp_hal_stdout_tx_flush();
}

void pbsys_main_run_program_cleanup(void) {
Expand Down
25 changes: 21 additions & 4 deletions bricks/_common/mphalport.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
// Contains the MicroPython HAL for STM32-based Pybricks ports.

#include <stdint.h>

#include <contiki.h>
#include <string.h>

#include <pbdrv/clock.h>
#include <pbdrv/config.h>
Expand Down Expand Up @@ -55,6 +54,8 @@ int mp_hal_stdin_rx_chr(void) {
return c;
}

static bool ended_on_new_line = true;

// Send string of given length
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
size_t remaining = len;
Expand All @@ -63,6 +64,7 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
uint32_t size = remaining;
pbio_error_t err = pbsys_host_stdout_write((const uint8_t *)str, &size);
if (err == PBIO_SUCCESS) {
ended_on_new_line = str[size - 1] == '\n';
str += size;
remaining -= size;
} else if (err != PBIO_ERROR_AGAIN) {
Expand All @@ -71,14 +73,29 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
return len - remaining;
}

MICROPY_EVENT_POLL_HOOK
// Allow long prints to be interrupted.
if (remaining) {
MICROPY_EVENT_POLL_HOOK
}
}

return len;
}

void mp_hal_stdout_tx_flush(void) {
// Don't raise, just wait for data to clear.
while (!pbsys_host_tx_is_idle()) {
MICROPY_EVENT_POLL_HOOK
MICROPY_VM_HOOK_LOOP;
Copy link
Member

Choose a reason for hiding this comment

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

Is this really safe? If the host never becomes idle, there is no way to ever interrupt this other than turning off the hub.

Copy link
Member Author

Choose a reason for hiding this comment

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

It becomes idle when the host disconnects or when nothing more is scheduled. This is why we want this one. The poll hook can raise and schedule more data.

Copy link
Member

Choose a reason for hiding this comment

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

I guess that works as long as we have USB stall after a timeout in case USB is still connected but no app is servicing the endpoints.

}

// A program may be interrupted in the middle of a long print, or the user
// may have printed without a newline. Ensure we end on a new line.
if (!ended_on_new_line) {
const char *eol = "\r\n";
uint32_t size = strlen(eol);
pbsys_host_stdout_write((const uint8_t *)eol, &size);
while (!pbsys_host_tx_is_idle()) {
MICROPY_VM_HOOK_LOOP;
}
}
}
9 changes: 7 additions & 2 deletions lib/pbio/drv/bluetooth/bluetooth.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,18 @@ bool pbdrv_bluetooth_tx_is_idle(void) {
}

/**
* Buffer for generic notification, but not buffered or schedules.
* Buffer for generic awaitable notification.
*/
static uint8_t user_notification_send_buf[PBDRV_BLUETOOTH_MAX_CHAR_SIZE];
static size_t user_notification_size;

pbio_error_t pbdrv_bluetooth_send_event_notification(pbio_os_state_t *state, pbio_pybricks_event_t event_type, const uint8_t *data, size_t size) {
PBIO_OS_ASYNC_BEGIN(state);

if (!pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_PYBRICKS)) {
return PBIO_ERROR_INVALID_OP;
}

if (size + 1 > PBIO_ARRAY_SIZE(user_notification_send_buf)) {
return PBIO_ERROR_INVALID_ARG;
}
Expand All @@ -130,7 +134,8 @@ pbio_error_t pbdrv_bluetooth_send_event_notification(pbio_os_state_t *state, pbi
memcpy(&user_notification_send_buf[1], data, size);
pbio_os_request_poll();

// Await until main process has finished sending user data.
// Await until main process has finished sending user data. If it
// disconnected while sending, this completes as well.
PBIO_OS_AWAIT_WHILE(state, user_notification_size);

PBIO_OS_ASYNC_END(PBIO_SUCCESS);
Expand Down
10 changes: 2 additions & 8 deletions lib/pbio/drv/bluetooth/bluetooth_simulation.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,9 @@ pbio_error_t pbdrv_bluetooth_send_pybricks_value_notification(pbio_os_state_t *s
return PBIO_SUCCESS;
}

int ret = write(STDOUT_FILENO, data, size);
// uint32_t ret = *size;
// int r = write(STDOUT_FILENO, data, *size);
// if (r >= 0) {
// // in case of an error in the syscall, report no bytes written
// ret = 0;
// }
int ret = write(STDOUT_FILENO, data + 1, size - 1);
(void)ret;
// return PBIO_SUCCESS;

PBIO_OS_ASYNC_END(PBIO_SUCCESS);
}

Expand Down
1 change: 1 addition & 0 deletions lib/pbio/include/pbdrv/bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ bool pbdrv_bluetooth_tx_is_idle(void);
* @param [in] data Data to send.
* @param [in] size Data size, not counting event type byte.
* @return ::PBIO_SUCCESS on completion.
* ::PBIO_ERROR_INVALID_OP if there is no connection.
* ::PBIO_ERROR_AGAIN while awaiting.
* ::PBIO_ERROR_BUSY if this operation is already ongoing.
* ::PBIO_ERROR_INVALID_ARG if @p size is too large.
Expand Down
6 changes: 6 additions & 0 deletions lib/pbio/include/pbsys/host.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <stdint.h>

#include <pbio/error.h>
#include <pbio/os.h>
#include <pbsys/config.h>

/**
Expand All @@ -36,6 +37,7 @@ uint32_t pbsys_host_stdin_get_available(void);
pbio_error_t pbsys_host_stdin_read(uint8_t *data, uint32_t *size);
pbio_error_t pbsys_host_stdout_write(const uint8_t *data, uint32_t *size);
bool pbsys_host_tx_is_idle(void);
pbio_error_t pbsys_host_send_event(pbio_os_state_t *state, pbio_pybricks_event_t event_type, const uint8_t *data, size_t size);

#else // PBSYS_CONFIG_HOST

Expand All @@ -51,6 +53,10 @@ bool pbsys_host_tx_is_idle(void);
#define pbsys_host_stdout_write(data, size) { (void)(data); (void)(size); PBIO_ERROR_NOT_SUPPORTED; }
#define pbsys_host_tx_is_idle() false

static inline pbio_error_t pbsys_host_send_event(pbio_os_state_t *state, pbio_pybricks_event_t event_type, const uint8_t *data, size_t size) {
return PBIO_ERROR_NOT_SUPPORTED;
}

#endif // PBSYS_CONFIG_HOST

#endif // _PBSYS_HOST_H_
Expand Down
21 changes: 21 additions & 0 deletions lib/pbio/sys/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,25 @@ bool pbsys_host_tx_is_idle(void) {
#endif
}

/**
* Transmits data over any connected transport that is subscribed to Pybricks
* protocol events, and awaits until it is written.
*
* @param event_type [in] Event type.
* @param data [in] The data to transmit.
* @param size [in] The size of the data to transmit.
* contains the number of bytes actually processed.
* @return ::PBIO_ERROR_AGAIN while the operation is in progress.
* ::PBIO_ERROR_INVALID_OP if there is no connection to send it to.
* ::PBIO_ERROR_BUSY if another transfer is already queued or in progress.
* ::PBIO_SUCCESS on completion.
*/
pbio_error_t pbsys_host_send_event(pbio_os_state_t *state, pbio_pybricks_event_t event_type, const uint8_t *data, size_t size) {
#if BLE_ONLY
return pbdrv_bluetooth_send_event_notification(state, event_type, data, size);
#else
return PBIO_ERROR_NOT_IMPLEMENTED;
#endif
}

#endif // PBSYS_CONFIG_HOST
6 changes: 3 additions & 3 deletions pybricks/tools/pb_type_app_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <string.h>

#include <pbsys/command.h>
#include <pbdrv/bluetooth.h>
#include <pbsys/host.h>

#include "py/mphal.h"
#include "py/objstr.h"
Expand Down Expand Up @@ -65,7 +65,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(pb_type_app_data_get_values_obj, pb_type_app_da
static pbio_error_t app_data_write_bytes_iterate_once(pbio_os_state_t *state, mp_obj_t parent_obj) {
// No need to pass in buffered arguments since they were copied on the
// inital run. We can just keep calling this until completion.
return pbdrv_bluetooth_send_event_notification(state, 0, NULL, 0);
return pbsys_host_send_event(state, 0, NULL, 0);
}

static mp_obj_t pb_type_app_data_write_bytes(mp_obj_t self_in, mp_obj_t data_in) {
Expand All @@ -75,7 +75,7 @@ static mp_obj_t pb_type_app_data_write_bytes(mp_obj_t self_in, mp_obj_t data_in)
size_t size;
const uint8_t *data = (const uint8_t *)mp_obj_str_get_data(data_in, &size);
pbio_os_state_t state = 0;
pbio_error_t err = pbdrv_bluetooth_send_event_notification(&state, PBIO_PYBRICKS_EVENT_WRITE_STDOUT, data, size);
pbio_error_t err = pbsys_host_send_event(&state, PBIO_PYBRICKS_EVENT_WRITE_STDOUT, data, size);

// Expect yield after the initial call.
if (err == PBIO_SUCCESS) {
Expand Down