diff --git a/bricks/cityhub/pbsys_app_config.h b/bricks/cityhub/pbsys_app_config.h new file mode 100644 index 000000000..b9f478040 --- /dev/null +++ b/bricks/cityhub/pbsys_app_config.h @@ -0,0 +1,3 @@ + +#define PBSYS_APP_HUB_FEATURE_FLAGS (PBIO_PYBRICKS_FEATURE_REPL | PBIO_PYBRICKS_FEATURE_USER_PROG_FORMAT_MULTI_MPY_V6) +#define PBSYS_APP_USER_PROGRAM_SIZE (16 * 1024 - 512) diff --git a/bricks/essentialhub/pbsys_app_config.h b/bricks/essentialhub/pbsys_app_config.h new file mode 100644 index 000000000..995698e41 --- /dev/null +++ b/bricks/essentialhub/pbsys_app_config.h @@ -0,0 +1,3 @@ + +#define PBSYS_APP_HUB_FEATURE_FLAGS (PBIO_PYBRICKS_FEATURE_REPL | PBIO_PYBRICKS_FEATURE_USER_PROG_FORMAT_MULTI_MPY_V6) +#define PBSYS_APP_USER_PROGRAM_SIZE (255 * 1024) diff --git a/bricks/movehub/pbsys_app_config.h b/bricks/movehub/pbsys_app_config.h new file mode 100644 index 000000000..751e87abb --- /dev/null +++ b/bricks/movehub/pbsys_app_config.h @@ -0,0 +1,3 @@ + +#define PBSYS_APP_HUB_FEATURE_FLAGS (PBIO_PYBRICKS_FEATURE_USER_PROG_FORMAT_MULTI_MPY_V6) +#define PBSYS_APP_USER_PROGRAM_SIZE (4 * 1024 - 128) diff --git a/bricks/primehub/pbsys_app_config.h b/bricks/primehub/pbsys_app_config.h new file mode 100644 index 000000000..995698e41 --- /dev/null +++ b/bricks/primehub/pbsys_app_config.h @@ -0,0 +1,3 @@ + +#define PBSYS_APP_HUB_FEATURE_FLAGS (PBIO_PYBRICKS_FEATURE_REPL | PBIO_PYBRICKS_FEATURE_USER_PROG_FORMAT_MULTI_MPY_V6) +#define PBSYS_APP_USER_PROGRAM_SIZE (255 * 1024) diff --git a/bricks/technichub/pbsys_app_config.h b/bricks/technichub/pbsys_app_config.h new file mode 100644 index 000000000..b9f478040 --- /dev/null +++ b/bricks/technichub/pbsys_app_config.h @@ -0,0 +1,3 @@ + +#define PBSYS_APP_HUB_FEATURE_FLAGS (PBIO_PYBRICKS_FEATURE_REPL | PBIO_PYBRICKS_FEATURE_USER_PROG_FORMAT_MULTI_MPY_V6) +#define PBSYS_APP_USER_PROGRAM_SIZE (16 * 1024 - 512) diff --git a/lib/ble5stack/central/att.h b/lib/ble5stack/central/att.h index 68d7565ea..1b8a4500a 100755 --- a/lib/ble5stack/central/att.h +++ b/lib/ble5stack/central/att.h @@ -151,7 +151,8 @@ extern "C" * Refer to ble_user_config.h for the device-specific maximum MTU value. */ #define ATT_MTU_SIZE 23 //L2CAP_MTU_SIZE //!< Minimum ATT MTU size -//#define ATT_MAX_MTU_SIZE (255-L2CAP_HDR_SIZE) //!< Maximum ATT MTU size +#define ATT_MAX_MTU_SIZE 158 //(255-L2CAP_HDR_SIZE) //!< Maximum ATT MTU size +// Pybricks: official LEGO firmware uses 158 /** @} End ATT_MTU_Sizes */ /** diff --git a/lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c b/lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c index 844231d45..ae5dd5387 100644 --- a/lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c +++ b/lib/pbio/drv/bluetooth/bluetooth_stm32_bluenrg.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -94,7 +95,9 @@ static uint16_t remote_handle; static uint16_t remote_lwp3_char_handle; // Pybricks GATT service handles -static uint16_t pybricks_service_handle, pybricks_char_handle; +static uint16_t pybricks_service_handle; +static uint16_t pybricks_command_event_char_handle; +static uint16_t pybricks_hub_capabilities_char_handle; // Nordic UART GATT service handles static uint16_t uart_service_handle, uart_rx_char_handle, uart_tx_char_handle; @@ -339,7 +342,7 @@ static PT_THREAD(send_value_notification(struct pt *pt, pbio_task_t *task)) goto done; } service_handle = pybricks_service_handle; - attr_handle = pybricks_char_handle; + attr_handle = pybricks_command_event_char_handle; } else if (send->connection == PBDRV_BLUETOOTH_CONNECTION_UART) { if (!uart_tx_notify_en) { goto done; @@ -807,24 +810,47 @@ static PT_THREAD(init_pybricks_service(struct pt *pt)) { }; // c5f50002-8280-46da-89f4-6d8051e4aeef - static const uint8_t pybricks_char_uuid[] = { + static const uint8_t pybricks_command_event_char_uuid[] = { 0xef, 0xae, 0xe4, 0x51, 0x80, 0x6d, 0xf4, 0x89, 0xda, 0x46, 0x80, 0x82, 0x02, 0x00, 0xf5, 0xc5 }; + // c5f50003-8280-46da-89f4-6d8051e4aeef + static const uint8_t pybricks_hub_capabilities_char_uuid[] = { + 0xef, 0xae, 0xe4, 0x51, 0x80, 0x6d, 0xf4, 0x89, + 0xda, 0x46, 0x80, 0x82, 0x03, 0x00, 0xf5, 0xc5 + }; + PT_BEGIN(pt); PT_WAIT_WHILE(pt, write_xfer_size); - aci_gatt_add_serv_begin(UUID_TYPE_128, pybricks_service_uuid, PRIMARY_SERVICE, 4); + aci_gatt_add_serv_begin(UUID_TYPE_128, pybricks_service_uuid, PRIMARY_SERVICE, 6); PT_WAIT_UNTIL(pt, hci_command_complete); aci_gatt_add_serv_end(&pybricks_service_handle); PT_WAIT_WHILE(pt, write_xfer_size); - aci_gatt_add_char_begin(pybricks_service_handle, UUID_TYPE_128, pybricks_char_uuid, + aci_gatt_add_char_begin(pybricks_service_handle, UUID_TYPE_128, pybricks_command_event_char_uuid, NUS_CHAR_SIZE, CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, ATTR_PERMISSION_NONE, GATT_NOTIFY_ATTRIBUTE_WRITE, MIN_ENCRY_KEY_SIZE, CHAR_VALUE_LEN_VARIABLE); PT_WAIT_UNTIL(pt, hci_command_complete); - aci_gatt_add_char_end(&pybricks_char_handle); + aci_gatt_add_char_end(&pybricks_command_event_char_handle); + + PT_WAIT_WHILE(pt, write_xfer_size); + aci_gatt_add_char_begin(pybricks_service_handle, UUID_TYPE_128, pybricks_hub_capabilities_char_uuid, + PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE, CHAR_PROP_READ, ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, MIN_ENCRY_KEY_SIZE, CHAR_VALUE_LEN_CONSTANT); + PT_WAIT_UNTIL(pt, hci_command_complete); + aci_gatt_add_char_end(&pybricks_hub_capabilities_char_handle); + + 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_APP_HUB_FEATURE_FLAGS, PBSYS_APP_USER_PROGRAM_SIZE); + aci_gatt_update_char_value_begin(pybricks_service_handle, pybricks_hub_capabilities_char_handle, + 0, PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE, buf); + } + PT_WAIT_UNTIL(pt, hci_command_complete); + aci_gatt_update_char_value_end(); PT_END(pt); } @@ -886,11 +912,11 @@ static void handle_event(hci_event_pckt *event) { case EVT_BLUE_GATT_ATTRIBUTE_MODIFIED: { evt_gatt_attr_modified *subevt = (evt_gatt_attr_modified *)evt->data; - if (subevt->attr_handle == pybricks_char_handle + 1) { + if (subevt->attr_handle == pybricks_command_event_char_handle + 1) { if (receive_handler) { receive_handler(PBDRV_BLUETOOTH_CONNECTION_PYBRICKS, subevt->att_data, subevt->data_length); } - } else if (subevt->attr_handle == pybricks_char_handle + 2) { + } else if (subevt->attr_handle == pybricks_command_event_char_handle + 2) { pybricks_notify_en = subevt->att_data[0]; } else if (subevt->attr_handle == uart_rx_char_handle + 1) { if (receive_handler) { diff --git a/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c b/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c index 22b97b34b..0d9a3c671 100644 --- a/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c +++ b/lib/pbio/drv/bluetooth/bluetooth_stm32_cc2640.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -131,7 +132,8 @@ static uint16_t gap_service_handle, gap_service_end_handle; // Device information service handles static uint16_t dev_info_service_handle, dev_info_service_end_handle; // Pybricks service handles -static uint16_t pybricks_service_handle, pybricks_service_end_handle, pybricks_char_handle; +static uint16_t pybricks_service_handle, pybricks_service_end_handle; +static uint16_t pybricks_command_event_char_handle, pybricks_capabilities_char_handle; // Pybricks tx notifications enabled static bool pybricks_notify_en; // Nordic UART service handles @@ -379,7 +381,7 @@ static PT_THREAD(send_value_notification(struct pt *pt, pbio_task_t *task)) task->status = PBIO_ERROR_INVALID_OP; goto done; } - attr_handle = pybricks_char_handle; + attr_handle = pybricks_command_event_char_handle; } else if (send->connection == PBDRV_BLUETOOTH_CONNECTION_UART) { if (!uart_tx_notify_en) { task->status = PBIO_ERROR_INVALID_OP; @@ -808,9 +810,12 @@ static void handle_event(uint8_t *packet) { switch (event_code) { case ATT_EVENT_EXCHANGE_MTU_REQ: { + // uint16_t client_mtu = (data[7] << 8) | data[6]; attExchangeMTURsp_t rsp; - rsp.serverRxMTU = 158; + rsp.serverRxMTU = ATT_MAX_MTU_SIZE; + // REVISIT: may need to keep a table of min(client_mtu, MAX_ATT_MTU_SIZE) + // for each connection if any known clients have smaller MTU ATT_ExchangeMTURsp(connection_handle, &rsp); } break; @@ -867,7 +872,11 @@ static void handle_event(uint8_t *packet) { } else if (start_handle <= pybricks_service_handle + 1) { read_by_type_response_uuid128(connection_handle, pybricks_service_handle + 1, GATT_PROP_WRITE_NO_RSP | GATT_PROP_WRITE | GATT_PROP_NOTIFY, - pbio_pybricks_control_char_uuid); + pbio_pybricks_command_event_char_uuid); + } else if (start_handle <= pybricks_service_handle + 4) { + read_by_type_response_uuid128(connection_handle, pybricks_service_handle + 4, + GATT_PROP_READ, + pbio_pybricks_hub_capabilities_char_uuid); } else if (start_handle <= uart_service_handle + 1) { read_by_type_response_uuid128(connection_handle, uart_service_handle + 1, GATT_PROP_WRITE_NO_RSP, pbio_nus_rx_char_uuid); @@ -971,7 +980,7 @@ static void handle_event(uint8_t *packet) { rsp.len = 7; rsp.pValue = buf; ATT_ReadRsp(connection_handle, &rsp); - } else if (handle == pybricks_char_handle + 1) { + } else if (handle == pybricks_command_event_char_handle + 1) { attReadRsp_t rsp; uint8_t buf[ATT_MTU_SIZE - 1]; @@ -980,6 +989,15 @@ static void handle_event(uint8_t *packet) { rsp.len = 2; rsp.pValue = buf; ATT_ReadRsp(connection_handle, &rsp); + } else if (handle == pybricks_capabilities_char_handle + 1) { + attReadRsp_t rsp; + uint8_t buf[PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE]; + + // REVISIT: client MTU may be smaller, in which case we can't used fixed value for MTU + pbio_pybricks_hub_capabilities(buf, ATT_MAX_MTU_SIZE - 3, PBSYS_APP_HUB_FEATURE_FLAGS, PBSYS_APP_USER_PROGRAM_SIZE); + rsp.len = sizeof(buf); + rsp.pValue = buf; + ATT_ReadRsp(connection_handle, &rsp); } else if (handle == uart_tx_char_handle + 1) { attReadRsp_t rsp; uint8_t buf[ATT_MTU_SIZE - 1]; @@ -1089,11 +1107,11 @@ static void handle_event(uint8_t *packet) { uint16_t char_handle = (data[9] << 8) | data[8]; DBG("w: %04X %04X %d", char_handle, uart_tx_char_handle, pdu_len - 4); - if (char_handle == pybricks_char_handle) { + if (char_handle == pybricks_command_event_char_handle) { if (receive_handler) { receive_handler(PBDRV_BLUETOOTH_CONNECTION_PYBRICKS, &data[10], pdu_len - 4); } - } else if (char_handle == pybricks_char_handle + 1) { + } else if (char_handle == pybricks_command_event_char_handle + 1) { pybricks_notify_en = data[10]; DBG("noti: %d", pybricks_notify_en); } else if (char_handle == uart_rx_char_handle) { @@ -1529,7 +1547,7 @@ static PT_THREAD(init_pybricks_service(struct pt *pt)) { PT_BEGIN(pt); PT_WAIT_WHILE(pt, write_xfer_size); - GATT_AddService(GATT_PRIMARY_SERVICE_UUID, 4, GATT_MIN_ENCRYPT_KEY_SIZE); + GATT_AddService(GATT_PRIMARY_SERVICE_UUID, 6, GATT_MIN_ENCRYPT_KEY_SIZE); PT_WAIT_UNTIL(pt, hci_command_status); // ignoring response data @@ -1539,7 +1557,7 @@ static PT_THREAD(init_pybricks_service(struct pt *pt)) { // ignoring response data PT_WAIT_WHILE(pt, write_xfer_size); - GATT_AddAttribute2(pbio_pybricks_control_char_uuid, GATT_PERMIT_READ | GATT_PERMIT_WRITE); + GATT_AddAttribute2(pbio_pybricks_command_event_char_uuid, GATT_PERMIT_READ | GATT_PERMIT_WRITE); PT_WAIT_UNTIL(pt, hci_command_status); // ignoring response data @@ -1547,11 +1565,22 @@ static PT_THREAD(init_pybricks_service(struct pt *pt)) { GATT_AddAttribute(GATT_CLIENT_CHAR_CFG_UUID, GATT_PERMIT_READ | GATT_PERMIT_WRITE); PT_WAIT_UNTIL(pt, hci_command_status); + PT_WAIT_WHILE(pt, write_xfer_size); + GATT_AddAttribute(GATT_CHARACTER_UUID, GATT_PERMIT_READ); + PT_WAIT_UNTIL(pt, hci_command_status); + // ignoring response data + + PT_WAIT_WHILE(pt, write_xfer_size); + GATT_AddAttribute2(pbio_pybricks_hub_capabilities_char_uuid, GATT_PERMIT_READ | GATT_PERMIT_WRITE); + PT_WAIT_UNTIL(pt, hci_command_status); + // ignoring response data + // the response to the last GATT_AddAttribute contains the first and last handles // that were allocated. pybricks_service_handle = (read_buf[13] << 8) | read_buf[12]; pybricks_service_end_handle = (read_buf[15] << 8) | read_buf[14]; - pybricks_char_handle = pybricks_service_handle + 2; + pybricks_command_event_char_handle = pybricks_service_handle + 2; + pybricks_capabilities_char_handle = pybricks_service_handle + 4; DBG("pybricks: %04X", pybricks_service_handle); PT_END(pt); diff --git a/lib/pbio/drv/bluetooth/pybricks_service.gatt b/lib/pbio/drv/bluetooth/pybricks_service.gatt index 78a4109e8..ef37b0ba3 100644 --- a/lib/pbio/drv/bluetooth/pybricks_service.gatt +++ b/lib/pbio/drv/bluetooth/pybricks_service.gatt @@ -10,5 +10,6 @@ CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID, DYNAMIC | READ, // Pybricks service PRIMARY_SERVICE, C5F50001-8280-46DA-89F4-6D8051E4AEEF CHARACTERISTIC, C5F50002-8280-46DA-89F4-6D8051E4AEEF, NOTIFY | WRITE | WRITE_WITHOUT_RESPONSE | DYNAMIC, +CHARACTERISTIC, C5F50003-8280-46DA-89F4-6D8051E4AEEF, READ | DYNAMIC, #import diff --git a/lib/pbio/drv/bluetooth/pybricks_service_server.c b/lib/pbio/drv/bluetooth/pybricks_service_server.c index 72c39693c..f65f2a97e 100644 --- a/lib/pbio/drv/bluetooth/pybricks_service_server.c +++ b/lib/pbio/drv/bluetooth/pybricks_service_server.c @@ -49,6 +49,8 @@ #include #include +#include +#include #include #include "btstack_defines.h" @@ -64,37 +66,45 @@ static att_service_handler_t pybricks_service; static pybricks_characteristic_write_callback_t client_callback; static pybricks_characteristic_configuration_callback_t client_configuration_callback; -static uint16_t pybricks_value_handle; -static uint16_t pybricks_client_configuration_handle; -static uint16_t pybricks_client_configuration_value; +static uint16_t pybricks_command_event_value_handle; +static uint16_t pybricks_command_event_client_configuration_handle; +static uint16_t pybricks_command_event_client_configuration_value; +static uint16_t pybricks_hub_capabilities_value_handle; -// TODO: need a way to reset pybricks_client_configuration_value on disconnect +// TODO: need a way to reset pybricks_command_event_client_configuration_value on disconnect static uint16_t pybricks_service_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { UNUSED(con_handle); UNUSED(offset); - UNUSED(buffer_size); - if (attribute_handle == pybricks_client_configuration_handle) { + if (attribute_handle == pybricks_command_event_client_configuration_handle) { if (buffer) { - little_endian_store_16(buffer, 0, pybricks_client_configuration_value); + little_endian_store_16(buffer, 0, pybricks_command_event_client_configuration_value); } return 2; } + + if (attribute_handle == pybricks_hub_capabilities_value_handle) { + if (buffer && buffer_size >= PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE) { + pbio_pybricks_hub_capabilities(buffer, pbio_math_min(att_server_get_mtu(con_handle) - 3, 512), + PBSYS_APP_HUB_FEATURE_FLAGS, PBSYS_APP_USER_PROGRAM_SIZE); + } + return PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE; + } + return 0; } static int pybricks_service_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { UNUSED(transaction_mode); UNUSED(offset); - UNUSED(buffer_size); - if (attribute_handle == pybricks_value_handle) { + if (attribute_handle == pybricks_command_event_value_handle) { client_callback(con_handle, &buffer[0], buffer_size); } - if (attribute_handle == pybricks_client_configuration_handle) { - pybricks_client_configuration_value = little_endian_read_16(buffer, 0); - client_configuration_callback(con_handle, pybricks_client_configuration_value); + else if (attribute_handle == pybricks_command_event_client_configuration_handle) { + pybricks_command_event_client_configuration_value = little_endian_read_16(buffer, 0); + client_configuration_callback(con_handle, pybricks_command_event_client_configuration_value); } return 0; @@ -117,12 +127,13 @@ void pybricks_service_server_init( btstack_assert(service_found != 0); UNUSED(service_found); - // get characteristic value handle and client configuration handle - pybricks_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(start_handle, end_handle, pbio_pybricks_control_char_uuid); - pybricks_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(start_handle, end_handle, pbio_pybricks_control_char_uuid); + // get characteristic value and descriptor handles + pybricks_command_event_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(start_handle, end_handle, pbio_pybricks_command_event_char_uuid); + pybricks_command_event_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(start_handle, end_handle, pbio_pybricks_command_event_char_uuid); + pybricks_hub_capabilities_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(start_handle, end_handle, pbio_pybricks_hub_capabilities_char_uuid); - log_info("pybricks_value_handle 0x%02x", pybricks_value_handle); - log_info("pybricks_client_configuration_handle 0x%02x", pybricks_client_configuration_handle); + log_info("pybricks_command_event_value_handle 0x%02x", pybricks_command_event_value_handle); + log_info("pybricks_command_event_client_configuration_handle 0x%02x", pybricks_command_event_client_configuration_handle); // register service with ATT Server pybricks_service.start_handle = start_handle; @@ -148,7 +159,7 @@ void pybricks_service_server_request_can_send_now(btstack_context_callback_regis * @param size */ int pybricks_service_server_send(hci_con_handle_t con_handle, const uint8_t *data, uint16_t size) { - return att_server_notify(con_handle, pybricks_value_handle, data, size); + return att_server_notify(con_handle, pybricks_command_event_value_handle, data, size); } #endif // PBDRV_CONFIG_BLUETOOTH_BTSTACK diff --git a/lib/pbio/include/pbio/protocol.h b/lib/pbio/include/pbio/protocol.h index 38cfb9ec9..f55c04e0a 100644 --- a/lib/pbio/include/pbio/protocol.h +++ b/lib/pbio/include/pbio/protocol.h @@ -140,8 +140,35 @@ typedef enum { uint32_t pbio_pybricks_event_status_report(uint8_t *buf, uint32_t flags); +/** + * Application-specific feature flag supported by a hub. + */ +typedef enum { + // NB: the values are part of the protocol, so don't change the values! + + /** + * Hub support interactive REPL. + */ + PBIO_PYBRICKS_FEATURE_REPL = 1 << 0, + /** + * Hub supports user program with multiple MicroPython .mpy files ABI v6. + */ + PBIO_PYBRICKS_FEATURE_USER_PROG_FORMAT_MULTI_MPY_V6 = 1 << 1, +} pbio_pybricks_feature_flags_t; + +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); + +/** + * Number of bytes in the Pybricks hub capabilities characteristic value. + */ +#define PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE 10 + extern const uint8_t pbio_pybricks_service_uuid[]; -extern const uint8_t pbio_pybricks_control_char_uuid[]; +extern const uint8_t pbio_pybricks_command_event_char_uuid[]; +extern const uint8_t pbio_pybricks_hub_capabilities_char_uuid[]; extern const uint16_t pbio_gatt_device_info_service_uuid; extern const uint16_t pbio_gatt_firmware_version_char_uuid; diff --git a/lib/pbio/include/pbsys/app.h b/lib/pbio/include/pbsys/app.h new file mode 100644 index 000000000..3c0952cdd --- /dev/null +++ b/lib/pbio/include/pbsys/app.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2022 The Pybricks Authors + +/** + * @addtogroup SystemApp System: Application-specific config + * + * The following macros must be defined in a "pbsys_app_config.h" file by the + * application that is using pbsys. + * + * @{ + */ + +#ifndef _PBSYS_APP_H_ +#define _PBSYS_APP_H_ + + +#include "pbsys_app_config.h" + +#if DOXYGEN +/** + * Specifies the hub features enabled by the application. See ::pbio_pybricks_feature_flags_t. + */ +#define PBSYS_APP_HUB_FEATURE_FLAGS +#endif +#ifndef PBSYS_APP_HUB_FEATURE_FLAGS +#error "Application must define PBSYS_APP_HUB_FEATURE_FLAGS" +#endif + +#if DOXYGEN +/** + * Specifies the maximum allowable user program size for the application. + */ +#define PBSYS_APP_USER_PROGRAM_SIZE +#endif +#ifndef PBSYS_APP_USER_PROGRAM_SIZE +#error "Application must define PBSYS_APP_USER_PROGRAM_SIZE" +#endif + +#endif // _PBSYS_APP_H_ + +/** @} */ diff --git a/lib/pbio/src/protocol/pybricks.c b/lib/pbio/src/protocol/pybricks.c index 54a42431b..819c0c2eb 100644 --- a/lib/pbio/src/protocol/pybricks.c +++ b/lib/pbio/src/protocol/pybricks.c @@ -24,10 +24,31 @@ uint32_t pbio_pybricks_event_status_report(uint8_t *buf, uint32_t flags) { return 5; } +/** + * Encodes the value of the Pybricks hub capabilities characteristic. + * + * @param [in] buf A buffer where the result will be written. + * Must be at least ::PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE bytes. + * @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. + */ +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) { + + 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); +} + /** * Pybricks Service UUID. * * C5F50001-8280-46DA-89F4-6D8051E4AEEF + * + * @since Protocol v1.0.0 */ const uint8_t pbio_pybricks_service_uuid[] = { 0xC5, 0xF5, 0x00, 0x01, 0x82, 0x80, 0x46, 0xDA, @@ -35,15 +56,29 @@ const uint8_t pbio_pybricks_service_uuid[] = { }; /** - * Pybricks Characteristic UUID. + * Pybricks Command/Event Characteristic UUID. * * C5F50002-8280-46DA-89F4-6D8051E4AEEF + * + * @since Protocol v1.0.0 */ -const uint8_t pbio_pybricks_control_char_uuid[] = { +const uint8_t pbio_pybricks_command_event_char_uuid[] = { 0xC5, 0xF5, 0x00, 0x02, 0x82, 0x80, 0x46, 0xDA, 0x89, 0xF4, 0x6D, 0x80, 0x51, 0xE4, 0xAE, 0xEF, }; +/** + * Pybricks Hub Capabilities Characteristic UUID. + * + * C5F50003-8280-46DA-89F4-6D8051E4AEEF + * + * @since Protocol v1.2.0 + */ +const uint8_t pbio_pybricks_hub_capabilities_char_uuid[] = { + 0xC5, 0xF5, 0x00, 0x03, 0x82, 0x80, 0x46, 0xDA, + 0x89, 0xF4, 0x6D, 0x80, 0x51, 0xE4, 0xAE, 0xEF, +}; + // Standard BLE UUIDs used as part of Pybricks "protocol". /** Bluetooth Device Information Service UUID. */ diff --git a/lib/pbio/test/drv/bluetooth.c b/lib/pbio/test/drv/bluetooth.c index 6dfdb99d0..0003f1f5d 100644 --- a/lib/pbio/test/drv/bluetooth.c +++ b/lib/pbio/test/drv/bluetooth.c @@ -142,7 +142,7 @@ static void pbio_test_bluetooth_enable_notifications(uint16_t attribute_handle) */ void pbio_test_bluetooth_enable_uart_service_notifications(void) { // client characteristic configuration descriptor (comes from header file generated by .gatt) - pbio_test_bluetooth_enable_notifications(0x0014); + pbio_test_bluetooth_enable_notifications(0x0016); } static uint32_t uart_service_notification_count; @@ -157,7 +157,7 @@ uint32_t pbio_test_bluetooth_get_uart_service_notification_count(void) { void pbio_test_bluetooth_send_uart_data(const uint8_t *data, uint32_t size) { // Nordic UART Rx characteristic value (comes from header file generated by .gatt) - const uint16_t attribute_handle = 0x0011; + const uint16_t attribute_handle = 0x0013; uint16_t length = 3 + size; uint8_t buffer[9 + HCI_ACL_PAYLOAD_SIZE]; diff --git a/lib/pbio/test/pbsys_app_config.h b/lib/pbio/test/pbsys_app_config.h new file mode 100644 index 000000000..ace5e3f79 --- /dev/null +++ b/lib/pbio/test/pbsys_app_config.h @@ -0,0 +1,2 @@ +#define PBSYS_APP_HUB_FEATURE_FLAGS 0 +#define PBSYS_APP_USER_PROGRAM_SIZE 0