From ed3272de761c0e717d2fa81d8510694ab5d0ffb5 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 28 Jun 2025 20:30:35 -0500 Subject: [PATCH] pbio/protocol: Add slots info to hub capabilities and status. In order to support multiple program slots, connected apps will need to know how many slots are available on the hub and what the currently selected slot is. These are added to the hub capabilities and status report respectively and the Pybricks Profile version is bumped to 1.5.0. --- CHANGELOG.md | 5 +++++ .../drv/bluetooth/bluetooth_stm32_bluenrg.c | 2 +- .../drv/bluetooth/bluetooth_stm32_cc2640.c | 2 +- .../drv/bluetooth/pybricks_service_server.c | 2 +- lib/pbio/drv/usb/stm32_usbd/usbd_desc.c | 3 ++- lib/pbio/include/pbio/protocol.h | 11 +++++----- lib/pbio/include/pbsys/status.h | 2 +- lib/pbio/platform/prime_hub/pbsysconfig.h | 2 +- lib/pbio/platform/test/pbsysconfig.h | 1 + lib/pbio/src/protocol/pybricks.c | 20 ++++++++++++++++--- lib/pbio/sys/bluetooth.c | 10 +++++++++- lib/pbio/sys/status.c | 9 ++++++++- 12 files changed, 53 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1935d0913..f94993e16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ ### Added - Experimental support for USB connectivity on SPIKE Prime ([pybricks-micropython#208]). - Initial support for `pybricks.iodevices.UARTDevice` ([support#220]). +- Enabled previously hidden support for multiple code + slots ([pybricks-micropython#264], [pybricks-micropython#312]). A new + (unreleased) version of Pybricks Code is needed to use this. ### Changed - Extensive overhaul of UART and port drivers on all hubs. This affects all @@ -21,6 +24,8 @@ [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 +[pybricks-micropython#264]: https://github.com/pybricks/pybricks-micropython/pull/264 +[pybricks-micropython#312]: https://github.com/pybricks/pybricks-micropython/pull/312 ## [3.6.1] - 2025-03-11 diff --git a/lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c b/lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c index c421b0013..068875f9b 100644 --- a/lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c +++ b/lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c @@ -1078,7 +1078,7 @@ static PT_THREAD(init_pybricks_service(struct pt *pt)) { PT_WAIT_WHILE(pt, write_xfer_size); { uint8_t buf[PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE]; - pbio_pybricks_hub_capabilities(buf, ATT_MTU - 3, PBSYS_CONFIG_APP_FEATURE_FLAGS, pbsys_storage_get_maximum_program_size()); + pbio_pybricks_hub_capabilities(buf, ATT_MTU - 3, PBSYS_CONFIG_APP_FEATURE_FLAGS, pbsys_storage_get_maximum_program_size(), 0); aci_gatt_update_char_value_begin(pybricks_service_handle, pybricks_hub_capabilities_char_handle, 0, PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE, buf); } diff --git a/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c b/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c index da702df4e..2c976c69e 100644 --- a/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c +++ b/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c @@ -1483,7 +1483,7 @@ static void handle_event(uint8_t *packet) { uint8_t buf[PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE]; // REVISIT: this assumes connection_handle == conn_handle - pbio_pybricks_hub_capabilities(buf, conn_mtu - 3, PBSYS_CONFIG_APP_FEATURE_FLAGS, pbsys_storage_get_maximum_program_size()); + pbio_pybricks_hub_capabilities(buf, conn_mtu - 3, PBSYS_CONFIG_APP_FEATURE_FLAGS, pbsys_storage_get_maximum_program_size(), 0); rsp.len = sizeof(buf); rsp.pValue = buf; ATT_ReadRsp(connection_handle, &rsp); diff --git a/lib/pbio/drv/bluetooth/pybricks_service_server.c b/lib/pbio/drv/bluetooth/pybricks_service_server.c index 57cd80ec3..c41b1fc18 100644 --- a/lib/pbio/drv/bluetooth/pybricks_service_server.c +++ b/lib/pbio/drv/bluetooth/pybricks_service_server.c @@ -89,7 +89,7 @@ static uint16_t pybricks_service_read_callback(hci_con_handle_t con_handle, uint if (attribute_handle == pybricks_hub_capabilities_value_handle) { if (buffer && buffer_size >= PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE) { pbio_pybricks_hub_capabilities(buffer, pbio_int_math_min(att_server_get_mtu(con_handle) - 3, 512), - PBSYS_CONFIG_APP_FEATURE_FLAGS, pbsys_storage_get_maximum_program_size()); + PBSYS_CONFIG_APP_FEATURE_FLAGS, pbsys_storage_get_maximum_program_size(), PBSYS_CONFIG_HMI_NUM_SLOTS); } return PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE; } diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c index 8a5a3fb14..9d2d83794 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c @@ -536,7 +536,8 @@ void USBD_Pybricks_Desc_Init(void) { pbio_pybricks_hub_capabilities(ptr, USBD_PYBRICKS_MAX_PACKET_SIZE - 1, PBSYS_CONFIG_APP_FEATURE_FLAGS, - pbsys_storage_get_maximum_program_size()); + pbsys_storage_get_maximum_program_size(), + PBSYS_CONFIG_HMI_NUM_SLOTS); ptr += PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE; /* Update wTotalLength field in BOS Descriptor */ diff --git a/lib/pbio/include/pbio/protocol.h b/lib/pbio/include/pbio/protocol.h index 8610bdb54..ea2322977 100644 --- a/lib/pbio/include/pbio/protocol.h +++ b/lib/pbio/include/pbio/protocol.h @@ -24,7 +24,7 @@ #define PBIO_PROTOCOL_VERSION_MAJOR 1 /** The minor version number for the protocol. */ -#define PBIO_PROTOCOL_VERSION_MINOR 4 +#define PBIO_PROTOCOL_VERSION_MINOR 5 /** The patch version number for the protocol. */ #define PBIO_PROTOCOL_VERSION_PATCH 0 @@ -342,9 +342,9 @@ typedef enum { #define PBIO_PYBRICKS_STATUS_FLAG(status) (1 << status) /** Size of status report event message in bytes. */ -#define PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE 6 +#define PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE 7 -uint32_t pbio_pybricks_event_status_report(uint8_t *buf, uint32_t flags, pbio_pybricks_user_program_id_t program_id); +uint32_t pbio_pybricks_event_status_report(uint8_t *buf, uint32_t flags, pbio_pybricks_user_program_id_t program_id, uint8_t slot); /** * Application-specific feature flag supported by a hub. @@ -391,12 +391,13 @@ typedef enum { void pbio_pybricks_hub_capabilities(uint8_t *buf, uint16_t max_char_size, pbio_pybricks_feature_flags_t feature_flags, - uint32_t max_user_prog_size); + uint32_t max_user_prog_size, + uint8_t num_slots); /** * Number of bytes in the Pybricks hub capabilities characteristic value. */ -#define PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE 10 +#define PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE 11 extern const uint8_t pbio_pybricks_service_uuid[]; extern const uint8_t pbio_pybricks_command_event_char_uuid[]; diff --git a/lib/pbio/include/pbsys/status.h b/lib/pbio/include/pbsys/status.h index a3e8ddb19..e78c9443f 100644 --- a/lib/pbio/include/pbsys/status.h +++ b/lib/pbio/include/pbsys/status.h @@ -14,7 +14,7 @@ #include -#define PBSYS_STATUS_REPORT_SIZE 6 +#define PBSYS_STATUS_REPORT_SIZE 7 /** * Status flag change. diff --git a/lib/pbio/platform/prime_hub/pbsysconfig.h b/lib/pbio/platform/prime_hub/pbsysconfig.h index b5bfc7082..d05b40e25 100644 --- a/lib/pbio/platform/prime_hub/pbsysconfig.h +++ b/lib/pbio/platform/prime_hub/pbsysconfig.h @@ -10,7 +10,7 @@ #define PBSYS_CONFIG_BLUETOOTH (1) #define PBSYS_CONFIG_BLUETOOTH_TOGGLE (1) #define PBSYS_CONFIG_BLUETOOTH_TOGGLE_BUTTON (512) // PBIO_BUTTON_RIGHT_UP, but enum value cannot be used here. -#define PBSYS_CONFIG_HMI_NUM_SLOTS (0) +#define PBSYS_CONFIG_HMI_NUM_SLOTS (5) #define PBSYS_CONFIG_HUB_LIGHT_MATRIX (1) #define PBSYS_CONFIG_HOST (1) #define PBSYS_CONFIG_MAIN (1) diff --git a/lib/pbio/platform/test/pbsysconfig.h b/lib/pbio/platform/test/pbsysconfig.h index c30269868..b45d9dc86 100644 --- a/lib/pbio/platform/test/pbsysconfig.h +++ b/lib/pbio/platform/test/pbsysconfig.h @@ -8,6 +8,7 @@ #define PBSYS_CONFIG_FEATURE_PROGRAM_FORMAT_MULTI_MPY_V6_1_NATIVE (0) #define PBSYS_CONFIG_BLUETOOTH (1) #define PBSYS_CONFIG_HOST (1) +#define PBSYS_CONFIG_HMI_NUM_SLOTS (0) #define PBSYS_CONFIG_HUB_LIGHT_MATRIX (0) #define PBSYS_CONFIG_MAIN (0) #define PBSYS_CONFIG_STORAGE (0) diff --git a/lib/pbio/src/protocol/pybricks.c b/lib/pbio/src/protocol/pybricks.c index ddd3aa392..ccf201cd0 100644 --- a/lib/pbio/src/protocol/pybricks.c +++ b/lib/pbio/src/protocol/pybricks.c @@ -17,15 +17,22 @@ _Static_assert(NUM_PBIO_PYBRICKS_STATUS <= sizeof(uint32_t) * 8, * * The buffer must be at least ::PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE bytes. * + * @since Pybricks Profile v1.0.0 + * + * Program ID parameter was added in Pybricks Profile v1.4.0. + * Slot parameter was added in Pybricks Profile v1.5.0. + * * @param [in] buf The buffer to hold the binary data. * @param [in] flags The status flags. - * @param [in] program_id Program identifier. + * @param [in] program_id Program identifier of currently running program. + * @param [in] slot The currently selected program slot. * @return The number of bytes written to @p buf. */ -uint32_t pbio_pybricks_event_status_report(uint8_t *buf, uint32_t flags, pbio_pybricks_user_program_id_t program_id) { +uint32_t pbio_pybricks_event_status_report(uint8_t *buf, uint32_t flags, pbio_pybricks_user_program_id_t program_id, uint8_t slot) { buf[0] = PBIO_PYBRICKS_EVENT_STATUS_REPORT; pbio_set_uint32_le(&buf[1], flags); buf[5] = program_id; + buf[6] = slot; return PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE; } @@ -37,15 +44,22 @@ uint32_t pbio_pybricks_event_status_report(uint8_t *buf, uint32_t flags, pbio_py * @param [in] max_char_size The maximum characteristic value size (negotiated MTU - 3). * @param [in] feature_flags The feature flags. * @param [in] max_user_prog_size The maximum allowable size for the user program. + * @param [in] num_slots The number of program slots available on the hub. + * + * @since Pybricks Profile v1.2.0 + * + * num_slots was added in Pybricks Profile v1.5.0. */ void pbio_pybricks_hub_capabilities(uint8_t *buf, uint16_t max_char_size, pbio_pybricks_feature_flags_t feature_flags, - uint32_t max_user_prog_size) { + uint32_t max_user_prog_size, + uint8_t num_slots) { pbio_set_uint16_le(&buf[0], max_char_size); pbio_set_uint32_le(&buf[2], feature_flags); pbio_set_uint32_le(&buf[6], max_user_prog_size); + buf[10] = num_slots; } /** diff --git a/lib/pbio/sys/bluetooth.c b/lib/pbio/sys/bluetooth.c index 1848f41d9..d1599841c 100644 --- a/lib/pbio/sys/bluetooth.c +++ b/lib/pbio/sys/bluetooth.c @@ -22,6 +22,7 @@ #include #include +#include "hmi.h" #include "storage.h" // REVISIT: this can be the negotiated MTU - 3 to allow for better throughput @@ -254,6 +255,9 @@ static PT_THREAD(pbsys_bluetooth_monitor_status(struct pt *pt)) { static struct etimer timer; static uint32_t old_status_flags; static send_msg_t msg; + #if PBSYS_CONFIG_HMI_NUM_SLOTS + static uint8_t old_program_slot; + #endif PT_BEGIN(pt); @@ -267,7 +271,11 @@ static PT_THREAD(pbsys_bluetooth_monitor_status(struct pt *pt)) { for (;;) { // wait for status to change or timeout - PT_WAIT_UNTIL(pt, pbsys_status_get_flags() != old_status_flags || etimer_expired(&timer)); + PT_WAIT_UNTIL(pt, pbsys_status_get_flags() != old_status_flags || + #if PBSYS_CONFIG_HMI_NUM_SLOTS + pbsys_hmi_get_selected_program_slot() != old_program_slot || + #endif + etimer_expired(&timer)); etimer_restart(&timer); diff --git a/lib/pbio/sys/status.c b/lib/pbio/sys/status.c index 7c9ad5726..bffdb87df 100644 --- a/lib/pbio/sys/status.c +++ b/lib/pbio/sys/status.c @@ -59,9 +59,16 @@ static void pbsys_status_update_flag(pbio_pybricks_status_t status, bool set) { * @return The number of bytes written to @p buf. */ uint32_t pbsys_status_get_status_report(uint8_t *buf) { + #if PBSYS_CONFIG_HMI_NUM_SLOTS + uint8_t slot = pbsys_hmi_get_selected_program_slot(); + #else + uint8_t slot = 0; + #endif + _Static_assert(PBSYS_STATUS_REPORT_SIZE == PBIO_PYBRICKS_EVENT_STATUS_REPORT_SIZE, "size of status report does not match size of event"); - return pbio_pybricks_event_status_report(buf, pbsys_status.flags, pbsys_status.program_id); + + return pbio_pybricks_event_status_report(buf, pbsys_status.flags, pbsys_status.program_id, slot); } /**