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
32 changes: 23 additions & 9 deletions bricks/_common/micropython.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,30 @@ static void mp_vfs_map_minimal_new_reader(mp_reader_t *reader, mp_vfs_map_minima

// Prints the exception that ended the program.
static void print_final_exception(mp_obj_t exc, int ret) {
// Handle graceful stop with button.
if ((ret & PYEXEC_FORCED_EXIT) &&
mp_obj_exception_match(exc, MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
mp_printf(&mp_plat_print, "The program was stopped (%q).\n",
((mp_obj_exception_t *)MP_OBJ_TO_PTR(exc))->base.type->name);
return;
}
nlr_buf_t nlr;
nlr.ret_val = NULL;

if (nlr_push(&nlr) == 0) {
// Handle graceful stop with button.
if ((ret & PYEXEC_FORCED_EXIT) &&
mp_obj_exception_match(exc, MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
mp_printf(&mp_plat_print, "The program was stopped (%q).\n",
((mp_obj_exception_t *)MP_OBJ_TO_PTR(exc))->base.type->name);
return;
}

// Print unhandled exception with traceback.
mp_obj_print_exception(&mp_plat_print, exc);
// REVISIT: flash the light red a few times to indicate an unhandled exception?

// Print unhandled exception with traceback.
mp_obj_print_exception(&mp_plat_print, exc);

nlr_pop();
} else {
// If we couldn't print the exception, just return. There is nothing
// else we can do.

// REVISIT: flash the light with a different pattern here?
}
}

#if PBSYS_CONFIG_FEATURE_BUILTIN_USER_PROGRAM_REPL
Expand Down
20 changes: 16 additions & 4 deletions bricks/_common_stm32/mphalport.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void mp_hal_delay_ms(mp_uint_t Delay) {
uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
uintptr_t ret = 0;

if ((poll_flags & MP_STREAM_POLL_RD) && pbsys_host_rx_get_available()) {
if ((poll_flags & MP_STREAM_POLL_RD) && pbsys_host_stdin_get_available()) {
ret |= MP_STREAM_POLL_RD;
}

Expand All @@ -51,7 +51,7 @@ int mp_hal_stdin_rx_chr(void) {
uint8_t c;

// wait for rx interrupt
while (size = 1, pbsys_host_rx(&c, &size) != PBIO_SUCCESS) {
while (size = 1, pbsys_host_stdin_read(&c, &size) != PBIO_SUCCESS) {
MICROPY_EVENT_POLL_HOOK
}

Expand All @@ -60,10 +60,22 @@ int mp_hal_stdin_rx_chr(void) {

// Send string of given length
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
while (pbsys_host_tx((const uint8_t *)str, len) == PBIO_ERROR_AGAIN) {
size_t remaining = len;

while (remaining) {
uint32_t size = remaining;
pbio_error_t err = pbsys_host_stdout_write((const uint8_t *)str, &size);
if (err == PBIO_SUCCESS) {
str += size;
remaining -= size;
} else if (err != PBIO_ERROR_AGAIN) {
// Ignoring error for now. This means stdout is lost if Bluetooth/USB
// is disconnected.
return len - remaining;
}

MICROPY_EVENT_POLL_HOOK
}
// Not raising the error. This means stdout lost if host is not connected.

return len;
}
Expand Down
100 changes: 73 additions & 27 deletions lib/pbio/drv/usb/usb_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static volatile uint32_t usb_response_sz;
static volatile uint32_t usb_status_sz;
static volatile uint32_t usb_stdout_sz;
static volatile bool transmitting;
static volatile bool pbdrv_usb_stm32_is_events_subscribed;

static USBD_HandleTypeDef husbd;
static PCD_HandleTypeDef hpcd;
Expand Down Expand Up @@ -145,8 +146,8 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) {
* @param data [in] The data to be sent.
* @param size [in, out] The size of @p data in bytes. After return, @p size
* contains the number of bytes actually written.
* @return ::PBIO_SUCCESS if all @p data was queued, ::PBIO_ERROR_AGAIN
* if not all @p data could not be queued at this time (e.g. buffer
* @return ::PBIO_SUCCESS if some @p data was queued, ::PBIO_ERROR_AGAIN
* if no @p data could not be queued at this time (e.g. buffer
* is full), ::PBIO_ERROR_INVALID_OP if there is not an
* active USB connection or ::PBIO_ERROR_NOT_SUPPORTED
* if this platform does not support USB.
Expand All @@ -156,12 +157,13 @@ pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
return PBIO_ERROR_NOT_IMPLEMENTED;
#endif

if (!pbdrv_usb_stm32_is_events_subscribed) {
// If the app hasn't subscribed to events, we can't send stdout.
return PBIO_ERROR_INVALID_OP;
}

uint8_t *ptr = usb_stdout_buf;
uint32_t ptr_len = sizeof(usb_stdout_buf);
uint32_t full_size = *size;

// TODO: return PBIO_ERROR_INVALID_OP if app flag is not set. Also need a
// timeout in case the app crashes and doesn't clear the flag on exit.

if (usb_stdout_sz) {
*size = 0;
Expand All @@ -174,14 +176,26 @@ pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) {
*ptr++ = PBIO_PYBRICKS_EVENT_WRITE_STDOUT;
ptr_len--;

*size = MIN(full_size, ptr_len);
*size = MIN(*size, ptr_len);
memcpy(ptr, data, *size);

usb_stdout_sz = 1 + 1 + *size;

process_poll(&pbdrv_usb_process);

return *size == full_size ? PBIO_SUCCESS : PBIO_ERROR_AGAIN;
return PBIO_SUCCESS;
}

uint32_t pbdrv_usb_stdout_tx_available(void) {
if (!pbdrv_usb_stm32_is_events_subscribed) {
return UINT32_MAX;
}

if (usb_stdout_sz) {
return 0;
}

return sizeof(usb_stdout_buf) - 2; // 2 bytes for header
}

/**
Expand All @@ -192,6 +206,14 @@ bool pbdrv_usb_stdout_tx_is_idle(void) {
return usb_stdout_sz == 0;
}

static void pbdrv_usb_stm32_reset_tx_state(void) {
usb_response_sz = 0;
usb_status_sz = 0;
usb_stdout_sz = 0;
transmitting = false;
pbdrv_usb_stm32_is_events_subscribed = false;
}

/**
* @brief Pybricks_Itf_Init
* Initializes the Pybricks media low layer
Expand All @@ -201,10 +223,7 @@ bool pbdrv_usb_stdout_tx_is_idle(void) {
static USBD_StatusTypeDef Pybricks_Itf_Init(void) {
USBD_Pybricks_SetRxBuffer(&husbd, usb_in_buf);
usb_in_sz = 0;
usb_response_sz = 0;
usb_status_sz = 0;
usb_stdout_sz = 0;
transmitting = false;
pbdrv_usb_stm32_reset_tx_state();

return USBD_OK;
}
Expand All @@ -216,6 +235,7 @@ static USBD_StatusTypeDef Pybricks_Itf_Init(void) {
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static USBD_StatusTypeDef Pybricks_Itf_DeInit(void) {
pbdrv_usb_stm32_reset_tx_state();
return USBD_OK;
}

Expand Down Expand Up @@ -307,7 +327,8 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
static PBIO_ONESHOT(pwrdn_oneshot);
static bool bcd_busy;
static pbio_pybricks_error_t result;
static struct etimer timer;
static struct etimer status_timer;
static struct etimer transmit_timer;
static uint32_t prev_status_flags = ~0;
static uint32_t new_status_flags;

Expand All @@ -328,7 +349,7 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {

PROCESS_BEGIN();

etimer_set(&timer, 500);
etimer_set(&status_timer, 500);

for (;;) {
PROCESS_WAIT_EVENT();
Expand Down Expand Up @@ -360,6 +381,12 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {

if (usb_in_sz) {
switch (usb_in_buf[0]) {
case USBD_PYBRICKS_OUT_EP_MSG_SUBSCRIBE:
pbdrv_usb_stm32_is_events_subscribed = usb_in_buf[1];
usb_response_buf[0] = USBD_PYBRICKS_IN_EP_MSG_RESPONSE;
pbio_set_uint32_le(&usb_response_buf[1], PBIO_PYBRICKS_ERROR_OK);
usb_response_sz = sizeof(usb_response_buf);
break;
case USBD_PYBRICKS_OUT_EP_MSG_COMMAND:
if (usb_response_sz == 0) {
result = pbsys_command(usb_in_buf + 1, usb_in_sz - 1);
Expand All @@ -376,6 +403,13 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
}

if (transmitting) {
if (etimer_expired(&transmit_timer)) {
// Transmission has taken too long, so reset the state to allow
// new transmissions. This can happen if the host stops reading
// data for some reason.
pbdrv_usb_stm32_reset_tx_state();
}

continue;
}

Expand All @@ -385,20 +419,32 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) {
if (usb_response_sz) {
transmitting = true;
USBD_Pybricks_TransmitPacket(&husbd, usb_response_buf, usb_response_sz);
} else if ((new_status_flags != prev_status_flags) || etimer_expired(&timer)) {
usb_status_buf[0] = USBD_PYBRICKS_IN_EP_MSG_EVENT;
_Static_assert(sizeof(usb_status_buf) + 1 >= PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE,
"size of status report does not match size of event");
usb_status_sz = 1 + pbsys_status_get_status_report(&usb_status_buf[1]);

etimer_restart(&timer);
prev_status_flags = new_status_flags;
} else if (pbdrv_usb_stm32_is_events_subscribed) {
if ((new_status_flags != prev_status_flags) || etimer_expired(&status_timer)) {
usb_status_buf[0] = USBD_PYBRICKS_IN_EP_MSG_EVENT;
_Static_assert(sizeof(usb_status_buf) + 1 >= PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE,
"size of status report does not match size of event");
usb_status_sz = 1 + pbsys_status_get_status_report(&usb_status_buf[1]);

// REVISIT: we really shouldn't need a status timer on USB since
// it's not a lossy transport. We just need to make sure we send
// status updates when they change and send the current status
// immediately after subscribing to events.
etimer_restart(&status_timer);
prev_status_flags = new_status_flags;

transmitting = true;
USBD_Pybricks_TransmitPacket(&husbd, usb_status_buf, usb_status_sz);
} else if (usb_stdout_sz) {
transmitting = true;
USBD_Pybricks_TransmitPacket(&husbd, usb_stdout_buf, usb_stdout_sz);
}
}

transmitting = true;
USBD_Pybricks_TransmitPacket(&husbd, usb_status_buf, usb_status_sz);
} else if (usb_stdout_sz) {
transmitting = true;
USBD_Pybricks_TransmitPacket(&husbd, usb_stdout_buf, usb_stdout_sz);
if (transmitting) {
// If the FIFO isn't emptied quickly, then there probably isn't an
// app anymore. This timer is used to detect such a condition.
etimer_set(&transmit_timer, 5);
}
}

Expand Down
14 changes: 14 additions & 0 deletions lib/pbio/include/pbdrv/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void);
*/
pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size);

/**
* Gets the number of bytes that can be queued for sending stdout via USB.
*
* Returns UINT32_MAX if there is no USB connection or no app is subscribed to
* stdout.
*
* @return The number of bytes that can be queued.
*/
uint32_t pbdrv_usb_stdout_tx_available(void);

/**
* Indicates if the USB stdout stream is idle.
* @return true if the USB stdout stream is idle.
Expand All @@ -62,6 +72,10 @@ static inline pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *si
return PBIO_SUCCESS;
}

static inline uint32_t pbdrv_usb_stdout_tx_available(void) {
return UINT32_MAX;
}

static inline bool pbdrv_usb_stdout_tx_is_idle(void) {
return true;
}
Expand Down
37 changes: 15 additions & 22 deletions lib/pbio/include/pbsys/host.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,26 @@ typedef bool (*pbsys_host_stdin_event_callback_t)(uint8_t c);
#if PBSYS_CONFIG_HOST

void pbsys_host_init(void);
void pbsys_host_rx_set_callback(pbsys_host_stdin_event_callback_t callback);
void pbsys_host_rx_flush(void);
uint32_t pbsys_host_rx_get_available(void);
uint32_t pbsys_host_rx_get_free(void);
void pbsys_host_rx_write(const uint8_t *data, uint32_t size);
pbio_error_t pbsys_host_rx(uint8_t *data, uint32_t *size);
pbio_error_t pbsys_host_tx(const uint8_t *data, uint32_t size);
uint32_t pbsys_host_stdin_get_free(void);
void pbsys_host_stdin_write(const uint8_t *data, uint32_t size);
void pbsys_host_stdin_set_callback(pbsys_host_stdin_event_callback_t callback);
void pbsys_host_stdin_flush(void);
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);

#else // PBSYS_CONFIG_HOST

#define pbsys_host_init()
#define pbsys_host_rx_set_callback(callback)
#define pbsys_host_rx_flush()
#define pbsys_host_rx_get_available() 0
#define pbsys_host_rx_get_free() 0
#define pbsys_host_rx_write(data, size)

static inline pbio_error_t pbsys_host_rx(uint8_t *data, uint32_t *size) {
return PBIO_ERROR_NOT_SUPPORTED;
}
static inline pbio_error_t pbsys_host_tx(const uint8_t *data, uint32_t size) {
return PBIO_ERROR_NOT_SUPPORTED;
}
static inline bool pbsys_host_tx_is_idle(void) {
return false;
}
#define pbsys_host_stdin_get_free() 0
#define pbsys_host_stdin_write(data, size) { (void)(data); (void)(size); }
#define pbsys_host_stdin_set_callback(callback) { (void)(callback); }
#define pbsys_host_stdin_flush()
#define pbsys_host_stdin_get_available() 0
#define pbsys_host_stdin_read(data, size) PBIO_ERROR_NOT_SUPPORTED
#define pbsys_host_stdout_write(data, size) { (void)(data); (void)(size); PBIO_ERROR_NOT_SUPPORTED; }
#define pbsys_host_tx_is_idle() false

#endif // PBSYS_CONFIG_HOST

Expand Down
1 change: 1 addition & 0 deletions lib/pbio/platform/city_hub/pbsysconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define PBSYS_CONFIG_HMI_NUM_SLOTS (0)
#define PBSYS_CONFIG_HUB_LIGHT_MATRIX (0)
#define PBSYS_CONFIG_HOST (1)
#define PBSYS_CONFIG_HOST_STDIN_BUF_SIZE (21)
#define PBSYS_CONFIG_MAIN (1)
#define PBSYS_CONFIG_STORAGE (1)
#define PBSYS_CONFIG_STORAGE_NUM_SLOTS (1)
Expand Down
2 changes: 1 addition & 1 deletion lib/pbio/platform/essential_hub/pbdrvconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
#define PBDRV_CONFIG_USB_MFG_STR LEGO_USB_MFG_STR
#define PBDRV_CONFIG_USB_PROD_STR LEGO_USB_PROD_STR_TECHNIC_SMALL_HUB " + Pybricks"
#define PBDRV_CONFIG_USB_STM32F4 (1)
#define PBDRV_CONFIG_USB_CHARGE_ONLY (1)
#define PBDRV_CONFIG_USB_CHARGE_ONLY (0)

#define PBDRV_CONFIG_STACK (1)
#define PBDRV_CONFIG_STACK_EMBEDDED (1)
Expand Down
1 change: 1 addition & 0 deletions lib/pbio/platform/essential_hub/pbsysconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define PBSYS_CONFIG_HMI_NUM_SLOTS (0)
#define PBSYS_CONFIG_HUB_LIGHT_MATRIX (0)
#define PBSYS_CONFIG_HOST (1)
#define PBSYS_CONFIG_HOST_STDIN_BUF_SIZE (64)
#define PBSYS_CONFIG_MAIN (1)
#define PBSYS_CONFIG_STORAGE (1)
#define PBSYS_CONFIG_STORAGE_NUM_SLOTS (1)
Expand Down
1 change: 1 addition & 0 deletions lib/pbio/platform/ev3/pbsysconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define PBSYS_CONFIG_FEATURE_PROGRAM_FORMAT_MULTI_MPY_V6 (1)
#define PBSYS_CONFIG_FEATURE_PROGRAM_FORMAT_MULTI_MPY_V6_3_NATIVE (0)
#define PBSYS_CONFIG_HOST (1)
#define PBSYS_CONFIG_HOST_STDIN_BUF_SIZE (21)
#define PBSYS_CONFIG_MAIN (1)
#define PBSYS_CONFIG_STORAGE (1)
#define PBSYS_CONFIG_STORAGE_NUM_SLOTS (1)
Expand Down
1 change: 1 addition & 0 deletions lib/pbio/platform/move_hub/pbsysconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define PBSYS_CONFIG_HMI_NUM_SLOTS (0)
#define PBSYS_CONFIG_HUB_LIGHT_MATRIX (0)
#define PBSYS_CONFIG_HOST (1)
#define PBSYS_CONFIG_HOST_STDIN_BUF_SIZE (21)
#define PBSYS_CONFIG_MAIN (1)
#define PBSYS_CONFIG_STORAGE (1)
#define PBSYS_CONFIG_STORAGE_NUM_SLOTS (1)
Expand Down
Loading