From 8958e7ef087c86030590fe936738b85360f9d25d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 10 Feb 2022 11:31:57 -0800 Subject: [PATCH 01/15] Add S3 GATT client support This allows you to connect to GATT services on the other device. It also adds connection initiation (GAP central). More progress on #5926 --- locale/circuitpython.pot | 13 + ports/espressif/Makefile | 4 + ports/espressif/common-hal/_bleio/Adapter.c | 97 +++++- .../common-hal/_bleio/Characteristic.c | 170 ++++++++-- .../common-hal/_bleio/Characteristic.h | 8 +- .../common-hal/_bleio/CharacteristicBuffer.c | 66 +++- .../espressif/common-hal/_bleio/Connection.c | 290 +++++++++++++++++- .../espressif/common-hal/_bleio/Descriptor.c | 1 - .../espressif/common-hal/_bleio/Descriptor.h | 3 + .../common-hal/_bleio/PacketBuffer.c | 161 +++++++++- ports/espressif/common-hal/_bleio/Service.c | 8 +- ports/espressif/common-hal/_bleio/__init__.c | 35 +++ ports/espressif/common-hal/_bleio/__init__.h | 7 + .../espressif/common-hal/_bleio/ble_events.c | 116 +++++++ .../espressif/common-hal/_bleio/ble_events.h | 51 +++ ports/espressif/mpconfigport.h | 12 + .../common-hal/_bleio/CharacteristicBuffer.c | 1 + shared-module/vectorio/VectorShape.c | 16 +- 18 files changed, 985 insertions(+), 74 deletions(-) create mode 100644 ports/espressif/common-hal/_bleio/ble_events.c create mode 100644 ports/espressif/common-hal/_bleio/ble_events.h diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 893e1b339e..f5f82a3001 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -2427,6 +2427,16 @@ msgstr "" msgid "Unhandled ESP TLS error %d %d %x %d" msgstr "" +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown BLE error at %s:%d: %d" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown BLE error: %d" +msgstr "" + #: shared-bindings/wifi/Radio.c #, c-format msgid "Unknown failure %d" @@ -2498,12 +2508,14 @@ msgstr "" msgid "Update Failed" msgstr "" +#: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/espressif/common-hal/_bleio/Descriptor.c #: ports/nrf/common-hal/_bleio/Characteristic.c #: ports/nrf/common-hal/_bleio/Descriptor.c msgid "Value length != required fixed length" msgstr "" +#: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/espressif/common-hal/_bleio/Descriptor.c #: ports/nrf/common-hal/_bleio/Characteristic.c #: ports/nrf/common-hal/_bleio/Descriptor.c @@ -3784,6 +3796,7 @@ msgstr "" msgid "non-Device in %q" msgstr "" +#: ports/espressif/common-hal/_bleio/Connection.c #: ports/nrf/common-hal/_bleio/Connection.c msgid "non-UUID found in service_uuids_whitelist" msgstr "" diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index d95812ad63..f086e8eb06 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -268,6 +268,10 @@ ifneq ($(CIRCUITPY_USB),0) SRC_C += lib/tinyusb/src/portable/espressif/esp32sx/dcd_esp32sx.c endif +ifneq ($(CIRCUITPY_BLEIO),0) +SRC_C += common-hal/_bleio/ble_events.c +endif + SRC_COMMON_HAL_EXPANDED = \ $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ $(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \ diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c index 288a908546..d72f9dbf02 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.c +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -256,6 +256,17 @@ STATIC void _convert_address(const bleio_address_obj_t *address, ble_addr_t *nim memcpy(nimble_address->val, (uint8_t *)address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); } +STATIC int _mtu_reply(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t mtu, void *arg) { + bleio_connection_internal_t *connection = (bleio_connection_internal_t *)arg; + if (conn_handle != connection->conn_handle || error->status != 0) { + return 0; + } + connection->mtu = mtu; + return 0; +} + STATIC void _new_connection(uint16_t conn_handle) { // Set the tx_power for the connection higher than the advertisement. esp_ble_tx_power_set(conn_handle, ESP_PWR_LVL_N0); @@ -275,12 +286,96 @@ STATIC void _new_connection(uint16_t conn_handle) { connection->pair_status = PAIR_NOT_PAIRED; connection->mtu = 0; + ble_gattc_exchange_mtu(conn_handle, _mtu_reply, connection); + // Change the callback for the connection. ble_gap_set_event_cb(conn_handle, bleio_connection_event_cb, connection); } +static int _connect_event(struct ble_gap_event *event, void *self_in) { + bleio_adapter_obj_t *self = (bleio_adapter_obj_t *)self_in; + + #if CIRCUITPY_VERBOSE_BLE + mp_printf(&mp_plat_print, "Connect event: %d\n", event->type); + #endif + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + if (event->connect.status == 0) { + _new_connection(event->connect.conn_handle); + // Set connections objs back to NULL since we have a new + // connection and need a new tuple. + self->connection_objs = NULL; + xTaskNotify(cp_task, event->connect.conn_handle, eSetValueWithOverwrite); + } else { + xTaskNotify(cp_task, -event->connect.status, eSetValueWithOverwrite); + } + break; + + default: + #if CIRCUITPY_VERBOSE_BLE + // For debugging. + mp_printf(&mp_plat_print, "Unhandled connect event: %d\n", event->type); + #endif + break; + } + return 0; +} + mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { - mp_raise_NotImplementedError(NULL); + // Stop any active scan. + if (self->scan_results != NULL) { + common_hal_bleio_adapter_stop_scan(self); + } + + struct ble_gap_conn_params conn_params = { + .scan_itvl = MSEC_TO_UNITS(100, UNIT_0_625_MS), + .scan_window = MSEC_TO_UNITS(100, UNIT_0_625_MS), + .itvl_min = MSEC_TO_UNITS(15, UNIT_1_25_MS), + .itvl_max = MSEC_TO_UNITS(300, UNIT_1_25_MS), + .latency = 0, + .supervision_timeout = MSEC_TO_UNITS(4000, UNIT_10_MS), + .min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN, + .max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN + }; + + uint8_t own_addr_type; + // TODO: Use a resolvable address if the peer has our key. + CHECK_NIMBLE_ERROR(ble_hs_id_infer_auto(false, &own_addr_type)); + + ble_addr_t addr; + _convert_address(address, &addr); + + cp_task = xTaskGetCurrentTaskHandle(); + // Make sure we don't have a pending notification from a previous time. This + // can happen if a previous wait timed out before the notification was given. + xTaskNotifyStateClear(cp_task); + CHECK_NIMBLE_ERROR( + ble_gap_connect(own_addr_type, &addr, + SEC_TO_UNITS(timeout, UNIT_1_MS) + 0.5f, + &conn_params, + _connect_event, self)); + + int error_code; + CHECK_NOTIFY(xTaskNotifyWait(0, 0, (uint32_t *)&error_code, 200)); + // Negative values are error codes, connection handle otherwise. + if (error_code < 0) { + CHECK_BLE_ERROR(-error_code); + } + uint16_t conn_handle = error_code; + + // TODO: If we have keys, then try and encrypt the connection. + + // TODO: Negotiate for better PHY and data lengths since we are the central. These are + // nice-to-haves so ignore any errors. + + // Make the connection object and return it. + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn_handle == conn_handle) { + connection->is_central = true; + return bleio_connection_new_from_internal(connection); + } + } mp_raise_bleio_BluetoothError(translate("Failed to connect: internal error")); diff --git a/ports/espressif/common-hal/_bleio/Characteristic.c b/ports/espressif/common-hal/_bleio/Characteristic.c index 161e17ea1a..0344e38df1 100644 --- a/ports/espressif/common-hal/_bleio/Characteristic.c +++ b/ports/espressif/common-hal/_bleio/Characteristic.c @@ -42,40 +42,22 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo, const char *user_description) { - mp_raise_NotImplementedError(NULL); self->service = service; self->uuid = uuid; self->handle = BLEIO_HANDLE_INVALID; + self->cccd_handle = BLEIO_HANDLE_INVALID; + self->sccd_handle = BLEIO_HANDLE_INVALID; self->props = props; self->read_perm = read_perm; self->write_perm = write_perm; - self->initial_value_len = 0; - self->initial_value = NULL; - if (initial_value_bufinfo != NULL) { - // Copy the initial value if it's on the heap. Otherwise it's internal and we may not be able - // to allocate. - self->initial_value_len = initial_value_bufinfo->len; - if (gc_alloc_possible()) { - if (gc_nbytes(initial_value_bufinfo->buf) > 0) { - uint8_t *initial_value = m_malloc(self->initial_value_len, false); - memcpy(initial_value, initial_value_bufinfo->buf, self->initial_value_len); - self->initial_value = initial_value; - } else { - self->initial_value = initial_value_bufinfo->buf; - } - self->descriptor_list = mp_obj_new_list(0, NULL); - } else { - self->initial_value = initial_value_bufinfo->buf; - self->descriptor_list = NULL; - } + common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo); + + if (gc_alloc_possible()) { + self->descriptor_list = mp_obj_new_list(0, NULL); + } else { + self->descriptor_list = NULL; } - // const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; - // if (max_length < 0 || max_length > max_length_max) { - // mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"), - // max_length_max, fixed_length ? "True" : "False"); - // } - // TODO: Implement this. self->max_length = max_length; self->fixed_length = fixed_length; @@ -97,8 +79,60 @@ bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_character return self->service; } +typedef struct { + TaskHandle_t task; + uint8_t *buf; + uint16_t len; +} _read_info_t; + +STATIC int _read_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) { + _read_info_t *read_info = (_read_info_t *)arg; + switch (error->status) { + case 0: { + int len = MIN(read_info->len, OS_MBUF_PKTLEN(attr->om)); + os_mbuf_copydata(attr->om, attr->offset, len, read_info->buf); + read_info->len = len; + } + MP_FALLTHROUGH; + + default: + #if CIRCUITPY_VERBOSE_BLE + // For debugging. + mp_printf(&mp_plat_print, "Read status: %d\n", error->status); + #endif + xTaskNotify(read_info->task, error->status, eSetValueWithOverwrite); + break; + } + + return 0; +} + size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len) { - // TODO: Implement this. + // Do GATT operations only if this characteristic has been added to a registered service. + if (self->handle == BLEIO_HANDLE_INVALID) { + return 0; + } + uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); + if (common_hal_bleio_service_get_is_remote(self->service)) { + _read_info_t read_info = { + .task = xTaskGetCurrentTaskHandle(), + .buf = buf, + .len = len + }; + CHECK_NIMBLE_ERROR(ble_gattc_read(conn_handle, self->handle, _read_cb, &read_info)); + int error_code; + xTaskNotifyWait(0, 0, (uint32_t *)&error_code, 200); + CHECK_BLE_ERROR(error_code); + return read_info.len; + } else { + len = MIN(self->current_value_len, len); + memcpy(buf, self->current_value, len); + return len; + } + return 0; } @@ -106,8 +140,62 @@ size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t return self->max_length; } +STATIC int _write_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) { + TaskHandle_t task = (TaskHandle_t)arg; + xTaskNotify(task, error->status, eSetValueWithOverwrite); + + return 0; +} + void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { - // TODO: Implement this. + if (common_hal_bleio_service_get_is_remote(self->service)) { + uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); + if ((self->props & CHAR_PROP_WRITE_NO_RESPONSE) != 0) { + CHECK_NIMBLE_ERROR(ble_gattc_write_no_rsp_flat(conn_handle, self->handle, bufinfo->buf, bufinfo->len)); + } else { + CHECK_NIMBLE_ERROR(ble_gattc_write_flat(conn_handle, self->handle, bufinfo->buf, bufinfo->len, _write_cb, xTaskGetCurrentTaskHandle())); + int error_code; + xTaskNotifyWait(0, 0, (uint32_t *)&error_code, 200); + CHECK_BLE_ERROR(error_code); + } + } else { + // Validate data length for local characteristics only. + // TODO: Test this once we can get servers going. + if (self->fixed_length && bufinfo->len != self->max_length) { + mp_raise_ValueError(translate("Value length != required fixed length")); + } + if (bufinfo->len > self->max_length) { + mp_raise_ValueError(translate("Value length > max_length")); + } + + if (bufinfo == NULL) { + self->current_value_len = 0; + ble_gatts_chr_updated(self->handle); + return; + } + + self->current_value_len = bufinfo->len; + // If we've already allocated an internal buffer or the provided buffer + // is on the heap, then copy into the internal buffer. + if (self->current_value_alloc > 0 || gc_nbytes(bufinfo->buf) > 0) { + if (self->current_value_alloc < bufinfo->len) { + self->current_value = m_realloc(self->current_value, bufinfo->len); + // Get the number of bytes from the heap because it may be more + // than the len due to gc block size. + self->current_value_alloc = gc_nbytes(self->current_value); + } + memcpy(self->current_value, bufinfo->buf, bufinfo->len); + } else { + // Otherwise, use the provided buffer to delay any heap allocation. + self->current_value = bufinfo->buf; + self->current_value_alloc = 0; + } + + ble_gatts_chr_updated(self->handle); + } } bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) { @@ -118,10 +206,32 @@ bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties return self->props; } -void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { +void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, + bleio_descriptor_obj_t *descriptor) { // TODO: Implement this. + + mp_obj_list_append(MP_OBJ_FROM_PTR(self->descriptor_list), + MP_OBJ_FROM_PTR(descriptor)); } void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) { - // TODO: Implement this. + if (self->cccd_handle == BLEIO_HANDLE_INVALID) { + mp_raise_bleio_BluetoothError(translate("No CCCD for this Characteristic")); + } + + if (!common_hal_bleio_service_get_is_remote(self->service)) { + mp_raise_bleio_RoleError(translate("Can't set CCCD on local Characteristic")); + } + + const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); + common_hal_bleio_check_connected(conn_handle); + + uint16_t cccd_value = + (notify ? 1 << 0 : 0) | + (indicate ? 1 << 1: 0); + + CHECK_NIMBLE_ERROR(ble_gattc_write_flat(conn_handle, self->cccd_handle, &cccd_value, 2, _write_cb, xTaskGetCurrentTaskHandle())); + int error_code; + xTaskNotifyWait(0, 0, (uint32_t *)&error_code, 200); + CHECK_BLE_ERROR(error_code); } diff --git a/ports/espressif/common-hal/_bleio/Characteristic.h b/ports/espressif/common-hal/_bleio/Characteristic.h index 57195ea265..ed812a9805 100644 --- a/ports/espressif/common-hal/_bleio/Characteristic.h +++ b/ports/espressif/common-hal/_bleio/Characteristic.h @@ -39,9 +39,13 @@ typedef struct _bleio_characteristic_obj { // Will be MP_OBJ_NULL before being assigned to a Service. bleio_service_obj_t *service; bleio_uuid_obj_t *uuid; - const uint8_t *initial_value; - uint16_t initial_value_len; + uint8_t *current_value; + uint16_t current_value_len; + // Our internal allocation length. If > 0, then current_value is managed by + // this characteristic. + uint16_t current_value_alloc; uint16_t max_length; + uint16_t def_handle; uint16_t handle; bleio_characteristic_properties_t props; bleio_attribute_security_mode_t read_perm; diff --git a/ports/espressif/common-hal/_bleio/CharacteristicBuffer.c b/ports/espressif/common-hal/_bleio/CharacteristicBuffer.c index bc6619cef4..fc3d0bbf06 100644 --- a/ports/espressif/common-hal/_bleio/CharacteristicBuffer.c +++ b/ports/espressif/common-hal/_bleio/CharacteristicBuffer.c @@ -28,6 +28,7 @@ #include #include "shared/runtime/interrupt_char.h" +#include "py/ringbuf.h" #include "py/runtime.h" #include "py/stream.h" @@ -37,14 +38,38 @@ #include "common-hal/_bleio/CharacteristicBuffer.h" #include "shared-bindings/_bleio/CharacteristicBuffer.h" +STATIC int characteristic_buffer_on_ble_evt(struct ble_gap_event *event, void *param) { + bleio_characteristic_buffer_obj_t *self = (bleio_characteristic_buffer_obj_t *)param; + switch (event->type) { + case BLE_GAP_EVENT_NOTIFY_RX: { + // A remote service wrote to this characteristic. + + // Must be a notification, and event handle must match the handle for my characteristic. + if (event->notify_rx.indication == 0 && + event->notify_rx.attr_handle == self->characteristic->handle) { + const struct os_mbuf *m = event->notify_rx.om; + while (m != NULL) { + ringbuf_put_n(&self->ringbuf, m->om_data, m->om_len); + m = SLIST_NEXT(m, om_next); + } + } + break; + } + default: + #if CIRCUITPY_VERBOSE_BLE + mp_printf(&mp_plat_print, "Unhandled gap event %d\n", event->type); + #endif + return 0; + break; + } + return 0; +} + void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_float_t timeout, uint8_t *buffer, size_t buffer_size, void *static_handler_entry) { - - mp_raise_NotImplementedError(NULL); - self->characteristic = characteristic; self->timeout_ms = timeout * 1000; @@ -53,6 +78,11 @@ void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buff self->ringbuf.iget = 0; self->ringbuf.iput = 0; + if (static_handler_entry != NULL) { + ble_event_add_handler_entry((ble_event_handler_entry_t *)static_handler_entry, characteristic_buffer_on_ble_evt, self); + } else { + ble_event_add_handler(characteristic_buffer_on_ble_evt, self); + } } // Assumes that timeout and buffer_size have been validated before call. @@ -65,17 +95,32 @@ void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffe } uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) { - // TODO: Implement this. - return 0; + uint64_t start_ticks = supervisor_ticks_ms64(); + + // Wait for all bytes received or timeout + while ((ringbuf_num_filled(&self->ringbuf) < len) && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) { + RUN_BACKGROUND_TASKS; + // Allow user to break out of a timeout with a KeyboardInterrupt. + if (mp_hal_is_interrupted()) { + return 0; + } + } + + uint32_t num_bytes_read = ringbuf_get_n(&self->ringbuf, data, len); + + return num_bytes_read; } +// NOTE: The nRF port has protection around these operations because the ringbuf +// is filled from an interrupt. On ESP the ringbuf is filled from the BLE host +// task that won't interrupt us. + uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) { - // TODO: Implement this. - return 0; + return ringbuf_num_filled(&self->ringbuf); } void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) { - // TODO: Implement this. + ringbuf_clear(&self->ringbuf); } bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self) { @@ -83,7 +128,10 @@ bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer } void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) { - // TODO: Implement this. + if (!common_hal_bleio_characteristic_buffer_deinited(self)) { + ble_event_remove_handler(characteristic_buffer_on_ble_evt, self); + self->characteristic = NULL; + } } bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) { diff --git a/ports/espressif/common-hal/_bleio/Connection.c b/ports/espressif/common-hal/_bleio/Connection.c index a3668073a8..7dbcbd6e5f 100644 --- a/ports/espressif/common-hal/_bleio/Connection.c +++ b/ports/espressif/common-hal/_bleio/Connection.c @@ -48,6 +48,9 @@ #include "host/ble_att.h" +// Give 20 seconds for discovery +#define DISCOVERY_TIMEOUT_MS 20000 + int bleio_connection_event_cb(struct ble_gap_event *event, void *connection_in) { bleio_connection_internal_t *connection = (bleio_connection_internal_t *)connection_in; @@ -69,9 +72,35 @@ int bleio_connection_event_cb(struct ble_gap_event *event, void *connection_in) } case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: { + #if CIRCUITPY_VERBOSE_BLE + mp_printf(&mp_plat_print, "TODO connection event: PHY update complete\n"); + #endif break; } + case BLE_GAP_EVENT_CONN_UPDATE: { + #if CIRCUITPY_VERBOSE_BLE + mp_printf(&mp_plat_print, "TODO connection event: connection update\n"); + #endif + break; + } + case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { + #if CIRCUITPY_VERBOSE_BLE + mp_printf(&mp_plat_print, "TODO connection event: l2cap update request\n"); + #endif + break; + } + + // These events are actually att specific so forward to all registered + // handlers for them. The handlers themselves decide whether an event + // is interesting to them. + case BLE_GAP_EVENT_NOTIFY_RX: + MP_FALLTHROUGH; + case BLE_GAP_EVENT_NOTIFY_TX: + MP_FALLTHROUGH; + case BLE_GAP_EVENT_SUBSCRIBE: + return ble_event_run_handlers(event); + default: #if CIRCUITPY_VERBOSE_BLE mp_printf(&mp_plat_print, "Unhandled connection event: %d\n", event->type); @@ -96,7 +125,7 @@ bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self) { } void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) { - // TODO: Implement this. + ble_gap_terminate(self->conn_handle, BLE_ERR_REM_USER_CONN_TERM); } void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) { @@ -121,7 +150,265 @@ void common_hal_bleio_connection_set_connection_interval(bleio_connection_intern // TODO: Implement this. } +STATIC volatile int _last_discovery_status; +static TaskHandle_t discovery_task = NULL; + +STATIC int _discovered_service_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_svc *svc, + void *arg) { + bleio_connection_internal_t *self = (bleio_connection_internal_t *)arg; + + if (error->status != BLE_ERR_SUCCESS) { + // Keep the first error in case it's due to memory. + if (_last_discovery_status == BLE_ERR_SUCCESS) { + _last_discovery_status = error->status; + xTaskNotifyGive(discovery_task); + } + return 0; + } + + // If any of these memory allocations fail, we set _last_discovery_status + // and let the process continue. + if (_last_discovery_status != BLE_ERR_SUCCESS) { + return 0; + } + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + if (service == NULL) { + _last_discovery_status = BLE_ERR_MEM_CAPACITY; + return 0; + } + service->base.type = &bleio_service_type; + + // Initialize several fields at once. + bleio_service_from_connection(service, bleio_connection_new_from_internal(self)); + + service->is_remote = true; + service->start_handle = svc->start_handle; + service->end_handle = svc->end_handle; + service->handle = svc->start_handle; + + bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); + if (uuid == NULL) { + _last_discovery_status = BLE_ERR_MEM_CAPACITY; + return 0; + } + uuid->base.type = &bleio_uuid_type; + uuid->nimble_ble_uuid = svc->uuid; + service->uuid = uuid; + + mp_obj_list_append(MP_OBJ_FROM_PTR(self->remote_service_list), + MP_OBJ_FROM_PTR(service)); + return 0; +} + +STATIC int _discovered_characteristic_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + const struct ble_gatt_chr *chr, + void *arg) { + bleio_service_obj_t *service = (bleio_service_obj_t *)arg; + + if (error->status != BLE_ERR_SUCCESS) { + // Keep the first error in case it's due to memory. + if (_last_discovery_status == BLE_ERR_SUCCESS) { + _last_discovery_status = error->status; + xTaskNotifyGive(discovery_task); + } + } + // If any of these memory allocations fail, we set _last_discovery_status + // and let the process continue. + if (_last_discovery_status != BLE_ERR_SUCCESS) { + return 0; + } + + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + if (characteristic == NULL) { + _last_discovery_status = BLE_ERR_MEM_CAPACITY; + return 0; + } + characteristic->base.type = &bleio_characteristic_type; + + // Known characteristic UUID. + bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); + if (uuid == NULL) { + _last_discovery_status = BLE_ERR_MEM_CAPACITY; + return 0; + } + uuid->base.type = &bleio_uuid_type; + uuid->nimble_ble_uuid = chr->uuid; + + bleio_characteristic_properties_t props = + ((chr->properties & BLE_GATT_CHR_PROP_BROADCAST) != 0 ? CHAR_PROP_BROADCAST : 0) | + ((chr->properties & BLE_GATT_CHR_PROP_INDICATE) != 0 ? CHAR_PROP_INDICATE : 0) | + ((chr->properties & BLE_GATT_CHR_PROP_NOTIFY) != 0 ? CHAR_PROP_NOTIFY : 0) | + ((chr->properties & BLE_GATT_CHR_PROP_READ) != 0 ? CHAR_PROP_READ : 0) | + ((chr->properties & BLE_GATT_CHR_PROP_WRITE) != 0 ? CHAR_PROP_WRITE : 0) | + ((chr->properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0 ? CHAR_PROP_WRITE_NO_RESPONSE : 0); + + // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler. + common_hal_bleio_characteristic_construct( + characteristic, service, chr->val_handle, uuid, + props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + 0, false, // max_length, fixed_length: values don't matter for gattc + mp_const_empty_bytes, + NULL); + // Set def_handle directly since it is only used in discovery. + characteristic->def_handle = chr->def_handle; + + mp_obj_list_append(MP_OBJ_FROM_PTR(service->characteristic_list), + MP_OBJ_FROM_PTR(characteristic)); + return 0; +} + +STATIC int _discovered_descriptor_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + uint16_t chr_val_handle, + const struct ble_gatt_dsc *dsc, + void *arg) { + bleio_characteristic_obj_t *characteristic = (bleio_characteristic_obj_t *)arg; + + if (error->status != BLE_ERR_SUCCESS) { + // Keep the first error in case it's due to memory. + if (_last_discovery_status == BLE_ERR_SUCCESS) { + _last_discovery_status = error->status; + } + xTaskNotifyGive(discovery_task); + } + // If any of these memory allocations fail, we set _last_discovery_status + // and let the process continue. + if (_last_discovery_status != BLE_ERR_SUCCESS) { + return 0; + } + + // Remember handles for certain well-known descriptors. + switch (dsc->uuid.u16.value) { + case 0x2902: + characteristic->cccd_handle = dsc->handle; + break; + + case 0x2903: + characteristic->sccd_handle = dsc->handle; + break; + + case 0x2901: + characteristic->user_desc_handle = dsc->handle; + break; + + default: + break; + } + + bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); + if (descriptor == NULL) { + _last_discovery_status = BLE_ERR_MEM_CAPACITY; + return 0; + } + descriptor->base.type = &bleio_descriptor_type; + + bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t); + if (uuid == NULL) { + _last_discovery_status = BLE_ERR_MEM_CAPACITY; + return 0; + } + uuid->base.type = &bleio_uuid_type; + uuid->nimble_ble_uuid = dsc->uuid; + + common_hal_bleio_descriptor_construct( + descriptor, characteristic, uuid, + SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + 0, false, mp_const_empty_bytes); + descriptor->handle = dsc->handle; + + mp_obj_list_append(MP_OBJ_FROM_PTR(characteristic->descriptor_list), + MP_OBJ_FROM_PTR(descriptor)); + return 0; +} + +STATIC void discover_remote_services(bleio_connection_internal_t *self, mp_obj_t service_uuids_whitelist) { + // Start over with an empty list. + self->remote_service_list = mp_obj_new_list(0, NULL); + + discovery_task = xTaskGetCurrentTaskHandle(); + if (service_uuids_whitelist == mp_const_none) { + _last_discovery_status = BLE_ERR_SUCCESS; + CHECK_NIMBLE_ERROR(ble_gattc_disc_all_svcs(self->conn_handle, _discovered_service_cb, self)); + + // Wait for sync. + ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(DISCOVERY_TIMEOUT_MS)); + if (_last_discovery_status != BLE_HS_EDONE) { + CHECK_BLE_ERROR(_last_discovery_status); + } + } else { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf); + mp_obj_t uuid_obj; + while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (!mp_obj_is_type(uuid_obj, &bleio_uuid_type)) { + mp_raise_TypeError(translate("non-UUID found in service_uuids_whitelist")); + } + bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); + + _last_discovery_status = BLE_ERR_SUCCESS; + // Make sure we start with a clean notification state + ulTaskNotifyValueClear(discovery_task, 0xffffffff); + CHECK_NIMBLE_ERROR(ble_gattc_disc_svc_by_uuid(self->conn_handle, &uuid->nimble_ble_uuid.u, + _discovered_service_cb, self)); + // Wait for sync. + CHECK_NOTIFY(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(DISCOVERY_TIMEOUT_MS))); + if (_last_discovery_status != BLE_HS_EDONE) { + CHECK_BLE_ERROR(_last_discovery_status); + } + } + } + + for (size_t i = 0; i < self->remote_service_list->len; i++) { + bleio_service_obj_t *service = MP_OBJ_TO_PTR(self->remote_service_list->items[i]); + + _last_discovery_status = BLE_ERR_SUCCESS; + CHECK_NIMBLE_ERROR(ble_gattc_disc_all_chrs(self->conn_handle, + service->start_handle, + service->end_handle, + _discovered_characteristic_cb, + service)); + + // Wait for sync. + CHECK_NOTIFY(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(DISCOVERY_TIMEOUT_MS))); + if (_last_discovery_status != BLE_HS_EDONE) { + CHECK_BLE_ERROR(_last_discovery_status); + } + + // Got characteristics for this service. Now discover descriptors for each characteristic. + size_t char_list_len = service->characteristic_list->len; + for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) { + bleio_characteristic_obj_t *characteristic = + MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]); + // Determine the handle range for the given characteristic's descriptors. + // The end of the range is dictated by the next characteristic or the end + // handle of the service. + const bool last_characteristic = char_idx == char_list_len - 1; + bleio_characteristic_obj_t *next_characteristic = last_characteristic + ? NULL + : MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]); + + uint16_t end_handle = next_characteristic == NULL + ? service->end_handle + : next_characteristic->def_handle - 1; + + _last_discovery_status = BLE_ERR_SUCCESS; + CHECK_NIMBLE_ERROR(ble_gattc_disc_all_dscs(self->conn_handle, characteristic->handle, + end_handle, + _discovered_descriptor_cb, characteristic)); + // Wait for sync. + ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(DISCOVERY_TIMEOUT_MS)); + if (_last_discovery_status != BLE_HS_EDONE) { + CHECK_BLE_ERROR(_last_discovery_status); + } + } + } +} + mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist) { + discover_remote_services(self->connection, service_uuids_whitelist); bleio_connection_ensure_connected(self); // Convert to a tuple and then clear the list so the callee will take ownership. mp_obj_tuple_t *services_tuple = @@ -129,7 +416,6 @@ mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_conne self->connection->remote_service_list->items); mp_obj_list_clear(MP_OBJ_FROM_PTR(self->connection->remote_service_list)); - // TODO: Implement this. return services_tuple; } diff --git a/ports/espressif/common-hal/_bleio/Descriptor.c b/ports/espressif/common-hal/_bleio/Descriptor.c index 016626dbf9..31bdca51b0 100644 --- a/ports/espressif/common-hal/_bleio/Descriptor.c +++ b/ports/espressif/common-hal/_bleio/Descriptor.c @@ -36,7 +36,6 @@ #include "host/ble_att.h" void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { - mp_raise_NotImplementedError(NULL); self->characteristic = characteristic; self->uuid = uuid; self->handle = BLEIO_HANDLE_INVALID; diff --git a/ports/espressif/common-hal/_bleio/Descriptor.h b/ports/espressif/common-hal/_bleio/Descriptor.h index 4a2b298ca2..e52251ce82 100644 --- a/ports/espressif/common-hal/_bleio/Descriptor.h +++ b/ports/espressif/common-hal/_bleio/Descriptor.h @@ -33,6 +33,8 @@ #include "common-hal/_bleio/UUID.h" +#include "host/ble_gatt.h" + // Forward declare characteristic because it includes a Descriptor. struct _bleio_characteristic_obj; @@ -45,6 +47,7 @@ typedef struct _bleio_descriptor_obj { uint16_t max_length; bool fixed_length; uint16_t handle; + struct ble_gatt_dsc_def def; bleio_attribute_security_mode_t read_perm; bleio_attribute_security_mode_t write_perm; } bleio_descriptor_obj_t; diff --git a/ports/espressif/common-hal/_bleio/PacketBuffer.c b/ports/espressif/common-hal/_bleio/PacketBuffer.c index b318ad27d7..4e65bf309c 100644 --- a/ports/espressif/common-hal/_bleio/PacketBuffer.c +++ b/ports/espressif/common-hal/_bleio/PacketBuffer.c @@ -40,13 +40,113 @@ #include "host/ble_att.h" +STATIC void write_to_ringbuf(bleio_packet_buffer_obj_t *self, const struct os_mbuf *mbuf) { + size_t len = OS_MBUF_PKTLEN(mbuf); + if (len + sizeof(uint16_t) > ringbuf_capacity(&self->ringbuf)) { + // This shouldn't happen but can if our buffer size was much smaller than + // the writes the client actually makes. + return; + } + // Make room for the new value by dropping the oldest packets first. + while (ringbuf_capacity(&self->ringbuf) - ringbuf_num_filled(&self->ringbuf) < len + sizeof(uint16_t)) { + uint16_t packet_length; + ringbuf_get_n(&self->ringbuf, (uint8_t *)&packet_length, sizeof(uint16_t)); + for (uint16_t i = 0; i < packet_length; i++) { + ringbuf_get(&self->ringbuf); + } + // set an overflow flag? + } + ringbuf_put_n(&self->ringbuf, (uint8_t *)&len, sizeof(uint16_t)); + while (mbuf != NULL) { + ringbuf_put_n(&self->ringbuf, mbuf->om_data, mbuf->om_len); + mbuf = SLIST_NEXT(mbuf, om_next); + } +} + +STATIC int packet_buffer_on_ble_client_evt(struct ble_gap_event *event, void *param); +STATIC int queue_next_write(bleio_packet_buffer_obj_t *self); + +STATIC int _write_cb(uint16_t conn_handle, + const struct ble_gatt_error *error, + struct ble_gatt_attr *attr, + void *arg) { + if (error->status != 0) { + mp_printf(&mp_plat_print, "write failed %d\n", error->status); + } + bleio_packet_buffer_obj_t *self = (bleio_packet_buffer_obj_t *)arg; + queue_next_write(self); + + return 0; +} + +STATIC int queue_next_write(bleio_packet_buffer_obj_t *self) { + // Queue up the next outgoing buffer. We use two, one that has been passed to the SD for + // transmission (when packet_queued is true) and the other is `pending` and can still be + // modified. By primarily appending to the `pending` buffer we can reduce the protocol overhead + // of the lower level link and ATT layers. + self->packet_queued = false; + if (self->pending_size > 0) { + uint16_t conn_handle = self->conn_handle; + int err_code = NIMBLE_OK; + if (self->client) { + if (self->write_type == CHAR_PROP_WRITE_NO_RESPONSE) { + err_code = ble_gattc_write_no_rsp_flat(conn_handle, + self->characteristic->handle, + self->outgoing[self->pending_index], + self->pending_size); + // We don't set packet_queued because we NimBLE will buffer our + // outgoing packets. + } else { + err_code = ble_gattc_write_flat(conn_handle, + self->characteristic->handle, + self->outgoing[self->pending_index], + self->pending_size, + _write_cb, self); + self->pending_index = (self->pending_index + 1) % 2; + self->packet_queued = true; + } + self->pending_size = 0; + } else { + // TODO: Notify because we're the server. + } + if (err_code != NIMBLE_OK) { + // On error, simply skip updating the pending buffers so that the next HVC or WRITE + // complete event triggers another attempt. + return err_code; + } + } + return NIMBLE_OK; +} + +STATIC int packet_buffer_on_ble_client_evt(struct ble_gap_event *event, void *param) { + bleio_packet_buffer_obj_t *self = (bleio_packet_buffer_obj_t *)param; + if (event->type == BLE_GAP_EVENT_DISCONNECT && self->conn_handle == event->disconnect.conn.conn_handle) { + self->conn_handle = BLEIO_HANDLE_INVALID; + } + + switch (event->type) { + case BLE_GAP_EVENT_NOTIFY_RX: { + if (event->notify_rx.conn_handle != self->conn_handle) { + return false; + } + // Must be a notification, and event handle must match the handle for my characteristic. + if (event->notify_rx.attr_handle == self->characteristic->handle) { + write_to_ringbuf(self, event->notify_rx.om); + } + break; + } + default: + return false; + break; + } + return true; +} + void _common_hal_bleio_packet_buffer_construct( bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, uint32_t *incoming_buffer, size_t incoming_buffer_size, uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, void *static_handler_entry) { - - mp_raise_NotImplementedError(NULL); self->characteristic = characteristic; self->client = self->characteristic->service->is_remote; self->max_packet_size = max_packet_size; @@ -76,6 +176,29 @@ void _common_hal_bleio_packet_buffer_construct( self->outgoing[0] = outgoing_buffer1; self->outgoing[1] = outgoing_buffer2; + if (self->client) { + if (static_handler_entry != NULL) { + ble_event_add_handler_entry((ble_event_handler_entry_t *)static_handler_entry, packet_buffer_on_ble_client_evt, self); + } else { + ble_event_add_handler(packet_buffer_on_ble_client_evt, self); + } + if (incoming) { + // Prefer notify if both are available. + if (incoming & CHAR_PROP_NOTIFY) { + common_hal_bleio_characteristic_set_cccd(self->characteristic, true, false); + } else { + common_hal_bleio_characteristic_set_cccd(self->characteristic, false, true); + } + } + if (outgoing) { + self->write_type = CHAR_PROP_WRITE; + if (outgoing & CHAR_PROP_WRITE_NO_RESPONSE) { + self->write_type = CHAR_PROP_WRITE_NO_RESPONSE; + } + } + } else { + // TODO: Setup for server. + } } void common_hal_bleio_packet_buffer_construct( @@ -104,7 +227,12 @@ void common_hal_bleio_packet_buffer_construct( uint32_t *outgoing2 = NULL; if (outgoing) { outgoing1 = m_malloc(max_packet_size, false); - outgoing2 = m_malloc(max_packet_size, false); + // Only allocate the second buffer if we are doing writes with responses. + // Without responses, we just write as quickly as we can. + if (outgoing == CHAR_PROP_WRITE) { + outgoing2 = m_malloc(max_packet_size, false); + } + } _common_hal_bleio_packet_buffer_construct(self, characteristic, incoming_buffer, incoming_buffer_size, @@ -117,9 +245,25 @@ mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self return 0; } - // Copy received data. Lock out write interrupt handler while copying. - // TODO: Implement this. - return 0; + // Get packet length, which is in first two bytes of packet. + uint16_t packet_length; + ringbuf_get_n(&self->ringbuf, (uint8_t *)&packet_length, sizeof(uint16_t)); + + mp_int_t ret; + if (packet_length > len) { + // Packet is longer than requested. Return negative of overrun value. + ret = len - packet_length; + // Discard the packet if it's too large. Don't fill data. + while (packet_length--) { + (void)ringbuf_get(&self->ringbuf); + } + } else { + // Read as much as possible, but might be shorter than len. + ringbuf_get_n(&self->ringbuf, data, packet_length); + ret = packet_length; + } + + return ret; } mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len) { @@ -172,10 +316,9 @@ mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, c self->pending_size += len; num_bytes_written += len; - // TODO: Implement this. - // If no writes are queued then sneak in this data. if (!self->packet_queued) { + CHECK_NIMBLE_ERROR(queue_next_write(self)); } return num_bytes_written; } @@ -270,6 +413,6 @@ bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self) { void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) { if (!common_hal_bleio_packet_buffer_deinited(self)) { + ble_event_remove_handler(packet_buffer_on_ble_client_evt, self); } - // TODO: Implement this. } diff --git a/ports/espressif/common-hal/_bleio/Service.c b/ports/espressif/common-hal/_bleio/Service.c index beaf930136..7aff2905e3 100644 --- a/ports/espressif/common-hal/_bleio/Service.c +++ b/ports/espressif/common-hal/_bleio/Service.c @@ -41,12 +41,6 @@ uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uu self->is_remote = false; self->connection = NULL; self->is_secondary = is_secondary; - - // uint8_t service_type = BLE_GATT_SVC_TYPE_PRIMARY; - // if (is_secondary) { - // service_type = BLE_GATT_SVC_TYPE_SECONDARY; - // } - return 0; } @@ -57,7 +51,7 @@ void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_ob } void bleio_service_from_connection(bleio_service_obj_t *self, mp_obj_t connection) { - self->handle = 0xFFFF; + self->handle = BLEIO_HANDLE_INVALID; self->uuid = NULL; self->characteristic_list = mp_obj_new_list(0, NULL); self->is_remote = true; diff --git a/ports/espressif/common-hal/_bleio/__init__.c b/ports/espressif/common-hal/_bleio/__init__.c index bd10b0a519..7eba47d6a1 100644 --- a/ports/espressif/common-hal/_bleio/__init__.c +++ b/ports/espressif/common-hal/_bleio/__init__.c @@ -40,6 +40,7 @@ #include "common-hal/_bleio/__init__.h" // #include "common-hal/_bleio/bonding.h" +#include "common-hal/_bleio/ble_events.h" // Turn off BLE on a reset or reload. void bleio_reset() { @@ -50,6 +51,7 @@ void bleio_reset() { } supervisor_stop_bluetooth(); + ble_event_reset(); bleio_adapter_reset(&common_hal_bleio_adapter_obj); common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); supervisor_start_bluetooth(); @@ -97,3 +99,36 @@ void check_nimble_error(int rc, const char *file, size_t line) { break; } } + +void check_ble_error(int error_code, const char *file, size_t line) { + if (error_code == BLE_ERR_SUCCESS) { + return; + } + switch (error_code) { + default: + #if CIRCUITPY_VERBOSE_BLE + if (file) { + mp_raise_bleio_BluetoothError(translate("Unknown BLE error at %s:%d: %d"), file, line, error_code); + } + #else + (void)file; + (void)line; + mp_raise_bleio_BluetoothError(translate("Unknown BLE error: %d"), error_code); + #endif + + break; + } +} + +void check_notify(BaseType_t result) { + if (result == pdTRUE) { + return; + } + mp_raise_msg(&mp_type_TimeoutError, NULL); +} + +void common_hal_bleio_check_connected(uint16_t conn_handle) { + if (conn_handle == BLEIO_HANDLE_INVALID) { + mp_raise_ConnectionError(translate("Not connected")); + } +} diff --git a/ports/espressif/common-hal/_bleio/__init__.h b/ports/espressif/common-hal/_bleio/__init__.h index 6c32bd5d4e..41e9792603 100644 --- a/ports/espressif/common-hal/_bleio/__init__.h +++ b/ports/espressif/common-hal/_bleio/__init__.h @@ -27,6 +27,8 @@ #ifndef MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_INIT_H #define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_INIT_H +#include "FreeRTOS.h" + void bleio_background(void); // typedef struct { @@ -43,6 +45,10 @@ void bleio_background(void); void check_nimble_error(int rc, const char *file, size_t line); #define CHECK_NIMBLE_ERROR(rc) check_nimble_error(rc, __FILE__, __LINE__) +void check_ble_error(int error_code, const char *file, size_t line); +#define CHECK_BLE_ERROR(error_code) check_ble_error(error_code, __FILE__, __LINE__) +void check_notify(BaseType_t result); +#define CHECK_NOTIFY(result) check_notify(result) #define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) #define SEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000000) / (RESOLUTION)) @@ -51,6 +57,7 @@ void check_nimble_error(int rc, const char *file, size_t line); #define ADV_INTERVAL_UNIT_FLOAT_SECS (0.000625) // Microseconds is the base unit. The macros above know that. #define UNIT_0_625_MS (625) +#define UNIT_1_MS (1000) #define UNIT_1_25_MS (1250) #define UNIT_10_MS (10000) diff --git a/ports/espressif/common-hal/_bleio/ble_events.c b/ports/espressif/common-hal/_bleio/ble_events.c new file mode 100644 index 0000000000..1ccd72cb61 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/ble_events.c @@ -0,0 +1,116 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "common-hal/_bleio/ble_events.h" + +#include +#include + +#include "py/misc.h" +#include "py/mpstate.h" +#include "py/runtime.h" + +#if CIRCUITPY_SERIAL_BLE && CIRCUITPY_VERBOSE_BLE +#include "supervisor/shared/bluetooth/serial.h" +#endif + +void ble_event_reset(void) { + // Linked list items will be gc'd. + MP_STATE_VM(ble_event_handler_entries) = NULL; +} + +void ble_event_add_handler_entry(ble_event_handler_entry_t *entry, + ble_gap_event_fn *func, void *param) { + ble_event_handler_entry_t *it = MP_STATE_VM(ble_event_handler_entries); + while (it != NULL) { + // If event handler and its corresponding param are already on the list, don't add again. + if ((it->func == func) && (it->param == param)) { + return; + } + it = it->next; + } + entry->next = MP_STATE_VM(ble_event_handler_entries); + entry->param = param; + entry->func = func; + + MP_STATE_VM(ble_event_handler_entries) = entry; +} + +void ble_event_add_handler(ble_gap_event_fn *func, void *param) { + ble_event_handler_entry_t *it = MP_STATE_VM(ble_event_handler_entries); + while (it != NULL) { + // If event handler and its corresponding param are already on the list, don't add again. + if ((it->func == func) && (it->param == param)) { + return; + } + it = it->next; + } + + // Add a new handler to the front of the list + ble_event_handler_entry_t *handler = m_new_ll(ble_event_handler_entry_t, 1); + ble_event_add_handler_entry(handler, func, param); +} + +void ble_event_remove_handler(ble_gap_event_fn *func, void *param) { + ble_event_handler_entry_t *it = MP_STATE_VM(ble_event_handler_entries); + ble_event_handler_entry_t **prev = &MP_STATE_VM(ble_event_handler_entries); + while (it != NULL) { + if ((it->func == func) && (it->param == param)) { + // Splice out the matching handler. + *prev = it->next; + // Clear next of the removed node so it's clearly not in a list. + it->next = NULL; + return; + } + prev = &(it->next); + it = it->next; + } +} + +int ble_event_run_handlers(struct ble_gap_event *event) { + #if CIRCUITPY_SERIAL_BLE && CIRCUITPY_VERBOSE_BLE + ble_serial_disable(); + #endif + + #if CIRCUITPY_VERBOSE_BLE + mp_printf(&mp_plat_print, "BLE GAP event: 0x%04x\n", event->type); + #endif + + ble_event_handler_entry_t *it = MP_STATE_VM(ble_event_handler_entries); + bool done = false; + while (it != NULL) { + // Capture next before calling the function in case it removes itself from the list. + ble_event_handler_entry_t *next = it->next; + done = it->func(event, it->param) || done; + it = next; + } + #if CIRCUITPY_SERIAL_BLE && CIRCUITPY_VERBOSE_BLE + ble_serial_enable(); + #endif + return 0; +} diff --git a/ports/espressif/common-hal/_bleio/ble_events.h b/ports/espressif/common-hal/_bleio/ble_events.h new file mode 100644 index 0000000000..03ff351118 --- /dev/null +++ b/ports/espressif/common-hal/_bleio/ble_events.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL__BLEIO_BLE_EVENTS_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL__BLEIO_BLE_EVENTS_H + +#include + +#include "host/ble_gap.h" + +typedef struct ble_event_handler_entry { + struct ble_event_handler_entry *next; + void *param; + ble_gap_event_fn *func; +} ble_event_handler_entry_t; + +void ble_event_reset(void); +void ble_event_add_handler(ble_gap_event_fn *func, void *param); +void ble_event_remove_handler(ble_gap_event_fn *func, void *param); + +// Allow for user provided entries to prevent allocations outside the VM. +void ble_event_add_handler_entry(ble_event_handler_entry_t *entry, ble_gap_event_fn *func, void *param); + +int ble_event_run_handlers(struct ble_gap_event *event); + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL__BLEIO_BLE_EVENTS_H diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 62e37d9374..1c5f1b1463 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -35,8 +35,19 @@ #include "py/circuitpy_mpconfig.h" +#if CIRCUITPY_BLEIO +#include "common-hal/_bleio/ble_events.h" +#endif + +#if CIRCUITPY_BLEIO +#define MICROPY_PORT_ROOT_POINTERS \ + CIRCUITPY_COMMON_ROOT_POINTERS \ + ble_event_handler_entry_t *ble_event_handler_entries; +#else #define MICROPY_PORT_ROOT_POINTERS \ CIRCUITPY_COMMON_ROOT_POINTERS +#endif + #define MICROPY_NLR_SETJMP (1) #define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000 @@ -61,4 +72,5 @@ #ifndef CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP #define CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP (0) #endif + #endif // MICROPY_INCLUDED_ESPRESSIF_MPCONFIGPORT_H diff --git a/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c index df389df0f8..5772616ee5 100644 --- a/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c +++ b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c @@ -158,6 +158,7 @@ bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) { if (!common_hal_bleio_characteristic_buffer_deinited(self)) { ble_drv_remove_event_handler(characteristic_buffer_on_ble_evt, self); + self->characteristic = NULL; } } diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c index 52d3354946..04213d36e7 100644 --- a/shared-module/vectorio/VectorShape.c +++ b/shared-module/vectorio/VectorShape.c @@ -4,6 +4,7 @@ #include "shared-module/vectorio/__init__.h" #include "shared-bindings/vectorio/VectorShape.h" +#include "py/misc.h" #include "py/runtime.h" #include "shared-bindings/time/__init__.h" #include "shared-bindings/displayio/ColorConverter.h" @@ -61,17 +62,6 @@ (u32 & 0x2 ? '1' : '0'), \ (u32 & 0x1 ? '1' : '0') - -inline __attribute__((always_inline)) -static int32_t max(int32_t a, int32_t b) { - return a > b ? a : b; -} - -inline __attribute__((always_inline)) -static uint32_t min(uint32_t a, uint32_t b) { - return a < b ? a : b; -} - static void short_bound_check(mp_int_t i, qstr name) { if (i < SHRT_MIN || i > SHRT_MAX) { mp_raise_ValueError_varg(translate("%q must be between %d and %d"), name, SHRT_MIN, SHRT_MAX); @@ -456,7 +446,7 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ mp_obj_get_type_str(self->ishape.shape), (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1), (double)((end - start) / 1000000.0), - (double)(max(1, pixels * (1000000000.0 / (end - start)))), + (double)(MAX(1, pixels * (1000000000.0 / (end - start)))), (double)(pixel_time / 1000.0), (double)(pixel_time / 1000.0 / pixels) ); @@ -514,7 +504,7 @@ displayio_area_t *vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_ union_size, dirty_size, current_size, overlap_size, (int32_t)union_size - dirty_size - current_size + overlap_size ); - if ((int32_t)union_size - dirty_size - current_size + overlap_size <= min(dirty_size, current_size)) { + if ((int32_t)union_size - dirty_size - current_size + overlap_size <= MIN(dirty_size, current_size)) { // The excluded / non-overlapping area from the disjoint dirty and current areas is smaller // than the smallest area we need to draw. Redrawing the overlapping area would cost more // than just drawing the union disjoint area once. From 726bf02ea830147838af5e7cbc36adb1b5d565cb Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 16 Feb 2022 10:12:54 -0800 Subject: [PATCH 02/15] Add PortalBase and Fake Requests and update Frozen Libs on Portal boards --- .gitmodules | 6 ++++++ frozen/Adafruit_CircuitPython_FakeRequests | 1 + frozen/Adafruit_CircuitPython_PortalBase | 1 + ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk | 9 +++++++++ ports/atmel-samd/boards/pyportal/mpconfigboard.mk | 8 ++++++++ ports/atmel-samd/boards/pyportal_titano/mpconfigboard.mk | 8 ++++++++ .../espressif/boards/adafruit_funhouse/mpconfigboard.mk | 7 +++++++ .../adafruit_magtag_2.9_grayscale/mpconfigboard.mk | 8 ++++++++ 8 files changed, 48 insertions(+) create mode 160000 frozen/Adafruit_CircuitPython_FakeRequests create mode 160000 frozen/Adafruit_CircuitPython_PortalBase diff --git a/.gitmodules b/.gitmodules index 9d14ad6da1..43fc7672ad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -277,3 +277,9 @@ [submodule "ports/stm/st_driver/stm32f4xx_hal_driver"] path = ports/stm/st_driver/stm32f4xx_hal_driver url = https://github.com/adafruit/stm32f4xx_hal_driver.git +[submodule "frozen/Adafruit_CircuitPython_PortalBase"] + path = frozen/Adafruit_CircuitPython_PortalBase + url = https://github.com/adafruit/Adafruit_CircuitPython_PortalBase.git +[submodule "frozen/Adafruit_CircuitPython_FakeRequests"] + path = frozen/Adafruit_CircuitPython_FakeRequests + url = https://github.com/adafruit/Adafruit_CircuitPython_FakeRequests.git diff --git a/frozen/Adafruit_CircuitPython_FakeRequests b/frozen/Adafruit_CircuitPython_FakeRequests new file mode 160000 index 0000000000..a3ac00fc30 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_FakeRequests @@ -0,0 +1 @@ +Subproject commit a3ac00fc3001942cf38703a87eda3a3ab55c8dcf diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase new file mode 160000 index 0000000000..01f050e401 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -0,0 +1 @@ +Subproject commit 01f050e401687066de7bc6017c1ef8df2b4e76ed diff --git a/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk b/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk index 41e24f1995..4f489fa8e3 100644 --- a/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk +++ b/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk @@ -9,3 +9,12 @@ CHIP_FAMILY = samd51 QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C" LONGINT_IMPL = MPZ + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PortalBase +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ESP32SPI +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LIS3DH +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests diff --git a/ports/atmel-samd/boards/pyportal/mpconfigboard.mk b/ports/atmel-samd/boards/pyportal/mpconfigboard.mk index 0a70db50c9..5ac39ee735 100644 --- a/ports/atmel-samd/boards/pyportal/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pyportal/mpconfigboard.mk @@ -9,3 +9,11 @@ CHIP_FAMILY = samd51 QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ, GD25Q64C" LONGINT_IMPL = MPZ + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PortalBase +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ESP32SPI +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests diff --git a/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.mk b/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.mk index 2202fca2f7..ad46c79a98 100644 --- a/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pyportal_titano/mpconfigboard.mk @@ -9,3 +9,11 @@ CHIP_FAMILY = samd51 QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ, GD25Q64C" LONGINT_IMPL = MPZ + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PortalBase +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ESP32SPI +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests diff --git a/ports/espressif/boards/adafruit_funhouse/mpconfigboard.mk b/ports/espressif/boards/adafruit_funhouse/mpconfigboard.mk index bcad455108..fb7b969b80 100644 --- a/ports/espressif/boards/adafruit_funhouse/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_funhouse/mpconfigboard.mk @@ -17,3 +17,10 @@ CIRCUITPY_ESP_FLASH_FREQ=40m CIRCUITPY_ESP_FLASH_SIZE=4MB CIRCUITPY_MODULE=wrover + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PortalBase +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/mpconfigboard.mk b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/mpconfigboard.mk index 4acef7fd94..3091d298e9 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/mpconfigboard.mk @@ -17,3 +17,11 @@ CIRCUITPY_ESP_FLASH_FREQ=40m CIRCUITPY_ESP_FLASH_SIZE=4MB CIRCUITPY_MODULE=wrover + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PortalBase +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LIS3DH From bcc5cea9407777571fe573f0ed3cc93d39f4ed3e Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 16 Feb 2022 11:42:53 -0800 Subject: [PATCH 03/15] Switch new frozen libraries to release tag (hopefully) --- frozen/Adafruit_CircuitPython_FakeRequests | 2 +- frozen/Adafruit_CircuitPython_PortalBase | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_FakeRequests b/frozen/Adafruit_CircuitPython_FakeRequests index a3ac00fc30..f6cdec74b6 160000 --- a/frozen/Adafruit_CircuitPython_FakeRequests +++ b/frozen/Adafruit_CircuitPython_FakeRequests @@ -1 +1 @@ -Subproject commit a3ac00fc3001942cf38703a87eda3a3ab55c8dcf +Subproject commit f6cdec74b64112016c459abe4a5d31a3b34caeb3 diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase index 01f050e401..77ba8eedf8 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit 01f050e401687066de7bc6017c1ef8df2b4e76ed +Subproject commit 77ba8eedf89b96c85a6194e5da2061c9d5c20242 From bfa732309a3d5e00bb7f8e80dbb33c4330833014 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 16 Feb 2022 11:48:47 -0800 Subject: [PATCH 04/15] Ran make update-frozen-libraries --- frozen/Adafruit_CircuitPython_APDS9960 | 2 +- frozen/Adafruit_CircuitPython_BLE | 2 +- frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center | 2 +- frozen/Adafruit_CircuitPython_BusDevice | 2 +- frozen/Adafruit_CircuitPython_CircuitPlayground | 2 +- frozen/Adafruit_CircuitPython_Crickit | 2 +- frozen/Adafruit_CircuitPython_DRV2605 | 2 +- frozen/Adafruit_CircuitPython_DS3231 | 2 +- frozen/Adafruit_CircuitPython_Display_Shapes | 2 +- frozen/Adafruit_CircuitPython_Display_Text | 2 +- frozen/Adafruit_CircuitPython_DotStar | 2 +- frozen/Adafruit_CircuitPython_ESP32SPI | 2 +- frozen/Adafruit_CircuitPython_FocalTouch | 2 +- frozen/Adafruit_CircuitPython_HID | 2 +- frozen/Adafruit_CircuitPython_IRRemote | 2 +- frozen/Adafruit_CircuitPython_LC709203F | 2 +- frozen/Adafruit_CircuitPython_LIS3DH | 2 +- frozen/Adafruit_CircuitPython_LSM6DS | 2 +- frozen/Adafruit_CircuitPython_MIDI | 2 +- frozen/Adafruit_CircuitPython_Motor | 2 +- frozen/Adafruit_CircuitPython_NeoPixel | 2 +- frozen/Adafruit_CircuitPython_ProgressBar | 2 +- frozen/Adafruit_CircuitPython_RFM69 | 2 +- frozen/Adafruit_CircuitPython_RFM9x | 2 +- frozen/Adafruit_CircuitPython_Register | 2 +- frozen/Adafruit_CircuitPython_Requests | 2 +- frozen/Adafruit_CircuitPython_SD | 2 +- frozen/Adafruit_CircuitPython_ST7789 | 2 +- frozen/Adafruit_CircuitPython_SimpleIO | 2 +- frozen/Adafruit_CircuitPython_SimpleMath | 2 +- frozen/Adafruit_CircuitPython_Thermistor | 2 +- frozen/Adafruit_CircuitPython_seesaw | 2 +- 32 files changed, 32 insertions(+), 32 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_APDS9960 b/frozen/Adafruit_CircuitPython_APDS9960 index c55da0dee6..baab505fd4 160000 --- a/frozen/Adafruit_CircuitPython_APDS9960 +++ b/frozen/Adafruit_CircuitPython_APDS9960 @@ -1 +1 @@ -Subproject commit c55da0dee66302d2fa8ed31623d047c307f409b2 +Subproject commit baab505fd4dcc54d8e9d45e6463c68bdc6d100eb diff --git a/frozen/Adafruit_CircuitPython_BLE b/frozen/Adafruit_CircuitPython_BLE index a695cde1b1..beec030657 160000 --- a/frozen/Adafruit_CircuitPython_BLE +++ b/frozen/Adafruit_CircuitPython_BLE @@ -1 +1 @@ -Subproject commit a695cde1b1cc957bcd10875b12ae82d1deeb0157 +Subproject commit beec03065712cd62f79e839d5cf8f7c9847fc3b1 diff --git a/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center b/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center index c24a5310bc..859a7d403e 160000 --- a/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center +++ b/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center @@ -1 +1 @@ -Subproject commit c24a5310bc259cd9d93b1f42468e07f41d4b0e56 +Subproject commit 859a7d403e4e79ec1c8915c81ba581dbaab8a4ac diff --git a/frozen/Adafruit_CircuitPython_BusDevice b/frozen/Adafruit_CircuitPython_BusDevice index 169715b344..43b2b52618 160000 --- a/frozen/Adafruit_CircuitPython_BusDevice +++ b/frozen/Adafruit_CircuitPython_BusDevice @@ -1 +1 @@ -Subproject commit 169715b3444c614e55827ccf79b35b2b5e11f1d2 +Subproject commit 43b2b5261845839bb31cf2507755b1f1efa73a48 diff --git a/frozen/Adafruit_CircuitPython_CircuitPlayground b/frozen/Adafruit_CircuitPython_CircuitPlayground index 2017afdfb4..b04042addd 160000 --- a/frozen/Adafruit_CircuitPython_CircuitPlayground +++ b/frozen/Adafruit_CircuitPython_CircuitPlayground @@ -1 +1 @@ -Subproject commit 2017afdfb43d3d9c5a73f8e85e951a583b18206a +Subproject commit b04042addd47c2645e139032b02a3b9ddeeb3425 diff --git a/frozen/Adafruit_CircuitPython_Crickit b/frozen/Adafruit_CircuitPython_Crickit index 8d09b29a1a..938f6bb335 160000 --- a/frozen/Adafruit_CircuitPython_Crickit +++ b/frozen/Adafruit_CircuitPython_Crickit @@ -1 +1 @@ -Subproject commit 8d09b29a1a92499dbbd10dd832f27db71057af5f +Subproject commit 938f6bb335ba5e4c56a8062c591ff9f3c18c4297 diff --git a/frozen/Adafruit_CircuitPython_DRV2605 b/frozen/Adafruit_CircuitPython_DRV2605 index 8a6ab89b7d..8e7e111a9f 160000 --- a/frozen/Adafruit_CircuitPython_DRV2605 +++ b/frozen/Adafruit_CircuitPython_DRV2605 @@ -1 +1 @@ -Subproject commit 8a6ab89b7d19f45a20b3f794cf900e23c9a8453b +Subproject commit 8e7e111a9ff39d3f4311caa7babeb451422c759f diff --git a/frozen/Adafruit_CircuitPython_DS3231 b/frozen/Adafruit_CircuitPython_DS3231 index 4fc5a32763..df24498154 160000 --- a/frozen/Adafruit_CircuitPython_DS3231 +++ b/frozen/Adafruit_CircuitPython_DS3231 @@ -1 +1 @@ -Subproject commit 4fc5a32763c4a6eac3a9446e296a9e925cc29a5c +Subproject commit df2449815433e05ea0f89c19518ccde7a10a2faa diff --git a/frozen/Adafruit_CircuitPython_Display_Shapes b/frozen/Adafruit_CircuitPython_Display_Shapes index 993bd12e17..708bb0c82c 160000 --- a/frozen/Adafruit_CircuitPython_Display_Shapes +++ b/frozen/Adafruit_CircuitPython_Display_Shapes @@ -1 +1 @@ -Subproject commit 993bd12e1747ec117e8d104a5e9f4659c8a347a3 +Subproject commit 708bb0c82c7b075bd6912c97231aea880b1a1cb8 diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index 0ec87891f9..0bd04a2355 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit 0ec87891f9a28ee3c5ae3b020b60d361684f466d +Subproject commit 0bd04a235556979bd13a373821a6602445fe132b diff --git a/frozen/Adafruit_CircuitPython_DotStar b/frozen/Adafruit_CircuitPython_DotStar index e070901177..eb6124fdff 160000 --- a/frozen/Adafruit_CircuitPython_DotStar +++ b/frozen/Adafruit_CircuitPython_DotStar @@ -1 +1 @@ -Subproject commit e07090117766d4a9ea2de07cd6f5418990cc598b +Subproject commit eb6124fdff59b98d7d49dd86072df99c0e97167b diff --git a/frozen/Adafruit_CircuitPython_ESP32SPI b/frozen/Adafruit_CircuitPython_ESP32SPI index de4829a027..13775b0584 160000 --- a/frozen/Adafruit_CircuitPython_ESP32SPI +++ b/frozen/Adafruit_CircuitPython_ESP32SPI @@ -1 +1 @@ -Subproject commit de4829a027a45882ae5477e50a75985e0e59f759 +Subproject commit 13775b058422085762874fde8e587f2e9f066855 diff --git a/frozen/Adafruit_CircuitPython_FocalTouch b/frozen/Adafruit_CircuitPython_FocalTouch index 68744ede79..bccbe3da75 160000 --- a/frozen/Adafruit_CircuitPython_FocalTouch +++ b/frozen/Adafruit_CircuitPython_FocalTouch @@ -1 +1 @@ -Subproject commit 68744ede79c992a3df8322c21a1468c5ccaef2ee +Subproject commit bccbe3da75f42b540b3faebb9d5a2d1ccf5e7147 diff --git a/frozen/Adafruit_CircuitPython_HID b/frozen/Adafruit_CircuitPython_HID index d79dd180cf..2fddabcaf0 160000 --- a/frozen/Adafruit_CircuitPython_HID +++ b/frozen/Adafruit_CircuitPython_HID @@ -1 +1 @@ -Subproject commit d79dd180cf6062e97d6a12cbc8dc7fdbedcc752b +Subproject commit 2fddabcaf0df1763111ed9dbf9e2d4cdb5b0434e diff --git a/frozen/Adafruit_CircuitPython_IRRemote b/frozen/Adafruit_CircuitPython_IRRemote index 2ca37f927b..9d78269db9 160000 --- a/frozen/Adafruit_CircuitPython_IRRemote +++ b/frozen/Adafruit_CircuitPython_IRRemote @@ -1 +1 @@ -Subproject commit 2ca37f927b3ee3aad379c2991f36b3ef1be0203d +Subproject commit 9d78269db987df8657baea554c725a6b6be3f62c diff --git a/frozen/Adafruit_CircuitPython_LC709203F b/frozen/Adafruit_CircuitPython_LC709203F index 8fc5eaecb3..29816fbe98 160000 --- a/frozen/Adafruit_CircuitPython_LC709203F +++ b/frozen/Adafruit_CircuitPython_LC709203F @@ -1 +1 @@ -Subproject commit 8fc5eaecb3e24e4109bcc788f41461f1c45c3719 +Subproject commit 29816fbe98c012ea0a1b5cae7f07aeae7ebf8b52 diff --git a/frozen/Adafruit_CircuitPython_LIS3DH b/frozen/Adafruit_CircuitPython_LIS3DH index ebbe69667d..acc4bdd73f 160000 --- a/frozen/Adafruit_CircuitPython_LIS3DH +++ b/frozen/Adafruit_CircuitPython_LIS3DH @@ -1 +1 @@ -Subproject commit ebbe69667d53ae76bc6d82e5296f87520ffbb5ae +Subproject commit acc4bdd73fdceb74d75cd5a1f261ae157ee32613 diff --git a/frozen/Adafruit_CircuitPython_LSM6DS b/frozen/Adafruit_CircuitPython_LSM6DS index f94ef67425..75e9ec62e4 160000 --- a/frozen/Adafruit_CircuitPython_LSM6DS +++ b/frozen/Adafruit_CircuitPython_LSM6DS @@ -1 +1 @@ -Subproject commit f94ef67425516f23c889d217ffe5a3a710c1d278 +Subproject commit 75e9ec62e4fe47a7212a69fb84aa1cfa7848e2b3 diff --git a/frozen/Adafruit_CircuitPython_MIDI b/frozen/Adafruit_CircuitPython_MIDI index 1127e3f7bc..6641509ef4 160000 --- a/frozen/Adafruit_CircuitPython_MIDI +++ b/frozen/Adafruit_CircuitPython_MIDI @@ -1 +1 @@ -Subproject commit 1127e3f7bcefa9fddb5b7f30533ecc6c58b420ea +Subproject commit 6641509ef43b672a82addf41f02b6466d6c67f01 diff --git a/frozen/Adafruit_CircuitPython_Motor b/frozen/Adafruit_CircuitPython_Motor index 9995c45a5e..fd478fda7a 160000 --- a/frozen/Adafruit_CircuitPython_Motor +++ b/frozen/Adafruit_CircuitPython_Motor @@ -1 +1 @@ -Subproject commit 9995c45a5ed1d455a4a8b7bfb9eb134de7f2b9db +Subproject commit fd478fda7adbd254282b8cad5000f06a96760c91 diff --git a/frozen/Adafruit_CircuitPython_NeoPixel b/frozen/Adafruit_CircuitPython_NeoPixel index 9ca3bf00c6..a115fc30df 160000 --- a/frozen/Adafruit_CircuitPython_NeoPixel +++ b/frozen/Adafruit_CircuitPython_NeoPixel @@ -1 +1 @@ -Subproject commit 9ca3bf00c6a2dd1de2d315a3b952a381d720aa7d +Subproject commit a115fc30df1c230c09c8a533ca77f3a4afd9f6c3 diff --git a/frozen/Adafruit_CircuitPython_ProgressBar b/frozen/Adafruit_CircuitPython_ProgressBar index 4ac4328893..011acd627f 160000 --- a/frozen/Adafruit_CircuitPython_ProgressBar +++ b/frozen/Adafruit_CircuitPython_ProgressBar @@ -1 +1 @@ -Subproject commit 4ac43288938abb4c3db127eeb79ef0d4ea4c16ea +Subproject commit 011acd627fc24342c397fc640b204a798f7b69dd diff --git a/frozen/Adafruit_CircuitPython_RFM69 b/frozen/Adafruit_CircuitPython_RFM69 index 9ac4909058..c58defd709 160000 --- a/frozen/Adafruit_CircuitPython_RFM69 +++ b/frozen/Adafruit_CircuitPython_RFM69 @@ -1 +1 @@ -Subproject commit 9ac490905834466319279a3390c914f1fd83733f +Subproject commit c58defd70947531c5a9c37ddcb569f240567a78b diff --git a/frozen/Adafruit_CircuitPython_RFM9x b/frozen/Adafruit_CircuitPython_RFM9x index 0df4521b4a..742ac7c8fb 160000 --- a/frozen/Adafruit_CircuitPython_RFM9x +++ b/frozen/Adafruit_CircuitPython_RFM9x @@ -1 +1 @@ -Subproject commit 0df4521b4a04ca1236960ff889ede118ec4305b5 +Subproject commit 742ac7c8fb52bb85d9fd367b60a7f80475d7ed14 diff --git a/frozen/Adafruit_CircuitPython_Register b/frozen/Adafruit_CircuitPython_Register index 79678c6adb..49ab415d6b 160000 --- a/frozen/Adafruit_CircuitPython_Register +++ b/frozen/Adafruit_CircuitPython_Register @@ -1 +1 @@ -Subproject commit 79678c6adb2252de8fed6273604bc6ac676132a5 +Subproject commit 49ab415d6b601c99979262f9e91c21dcb3a927a7 diff --git a/frozen/Adafruit_CircuitPython_Requests b/frozen/Adafruit_CircuitPython_Requests index 900b28cbae..270565665a 160000 --- a/frozen/Adafruit_CircuitPython_Requests +++ b/frozen/Adafruit_CircuitPython_Requests @@ -1 +1 @@ -Subproject commit 900b28cbae008e3253c4c40496e49faea9fb7034 +Subproject commit 270565665ada26fe8d7a99a3cb5941b452444471 diff --git a/frozen/Adafruit_CircuitPython_SD b/frozen/Adafruit_CircuitPython_SD index b7a76420d1..9dd51fecfc 160000 --- a/frozen/Adafruit_CircuitPython_SD +++ b/frozen/Adafruit_CircuitPython_SD @@ -1 +1 @@ -Subproject commit b7a76420d1dec119f8744aa7c0ea500e235561d1 +Subproject commit 9dd51fecfcbb15cb2a00eeadbd66b36ce0c09ee2 diff --git a/frozen/Adafruit_CircuitPython_ST7789 b/frozen/Adafruit_CircuitPython_ST7789 index c5b480434d..79c70a4928 160000 --- a/frozen/Adafruit_CircuitPython_ST7789 +++ b/frozen/Adafruit_CircuitPython_ST7789 @@ -1 +1 @@ -Subproject commit c5b480434de8fa56d8ba978a57cd3919fdc9da2a +Subproject commit 79c70a49285be8b6548de3f5ca20aa5ac1fafa22 diff --git a/frozen/Adafruit_CircuitPython_SimpleIO b/frozen/Adafruit_CircuitPython_SimpleIO index ca56187fe7..272d225365 160000 --- a/frozen/Adafruit_CircuitPython_SimpleIO +++ b/frozen/Adafruit_CircuitPython_SimpleIO @@ -1 +1 @@ -Subproject commit ca56187fe7af315130808191b004432fdfdc1b09 +Subproject commit 272d225365eed46916390cf1f393dd08bc00b7d4 diff --git a/frozen/Adafruit_CircuitPython_SimpleMath b/frozen/Adafruit_CircuitPython_SimpleMath index 755784b6ac..fad0f89e76 160000 --- a/frozen/Adafruit_CircuitPython_SimpleMath +++ b/frozen/Adafruit_CircuitPython_SimpleMath @@ -1 +1 @@ -Subproject commit 755784b6acc8ba419a085bee2d2dc4374f0d0030 +Subproject commit fad0f89e760829a76f553ef8459f61001597a846 diff --git a/frozen/Adafruit_CircuitPython_Thermistor b/frozen/Adafruit_CircuitPython_Thermistor index d4ac6ce3ee..e86f258e43 160000 --- a/frozen/Adafruit_CircuitPython_Thermistor +++ b/frozen/Adafruit_CircuitPython_Thermistor @@ -1 +1 @@ -Subproject commit d4ac6ce3eea2c87781fa2df4e431d9440c610fad +Subproject commit e86f258e43591ce4a04661277e77e9fdf6fec27e diff --git a/frozen/Adafruit_CircuitPython_seesaw b/frozen/Adafruit_CircuitPython_seesaw index f06ac21e96..c89c868916 160000 --- a/frozen/Adafruit_CircuitPython_seesaw +++ b/frozen/Adafruit_CircuitPython_seesaw @@ -1 +1 @@ -Subproject commit f06ac21e96321724258e00f7596d874eff53f0b8 +Subproject commit c89c8689161e5b35bfe4fa8355615696e03f0648 From c9213481b3f91b8fe9a6d496aaa82ad7388ce33b Mon Sep 17 00:00:00 2001 From: Kattni Rembor Date: Wed, 16 Feb 2022 16:32:08 -0500 Subject: [PATCH 05/15] Update pin name to match TFT pins. --- ports/espressif/boards/adafruit_feather_esp32s2_tft/pins.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_tft/pins.c b/ports/espressif/boards/adafruit_feather_esp32s2_tft/pins.c index f3b1c30a0d..fb2f898a18 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_tft/pins.c +++ b/ports/espressif/boards/adafruit_feather_esp32s2_tft/pins.c @@ -43,7 +43,7 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO33) }, { MP_ROM_QSTR(MP_QSTR_NEOPIXEL_POWER), MP_ROM_PTR(&pin_GPIO34) }, - { MP_ROM_QSTR(MP_QSTR_I2C_TFT_POWER), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_TFT_I2C_POWER), MP_ROM_PTR(&pin_GPIO21) }, { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO35) }, { MP_ROM_QSTR(MP_QSTR_D35), MP_ROM_PTR(&pin_GPIO35) }, From 0aadb4e2565dd7c7035bc79b882ae6962b47e8f8 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 16 Feb 2022 14:04:00 -0800 Subject: [PATCH 06/15] Remove DisplayText Frozen Lib from MatrixPortal saving about 60K --- ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk b/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk index 4f489fa8e3..dce3da108b 100644 --- a/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk +++ b/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk @@ -15,6 +15,5 @@ FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PortalBase FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ESP32SPI FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LIS3DH FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests From 8f741e77675e0b773579fe1cf2384e446ea4fb5e Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 16 Feb 2022 14:47:12 -0800 Subject: [PATCH 07/15] Removed more frozen libs and disabled unlikely used modules --- ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk b/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk index dce3da108b..81e1cd00cd 100644 --- a/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk +++ b/ports/atmel-samd/boards/matrixportal_m4/mpconfigboard.mk @@ -15,5 +15,8 @@ FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PortalBase FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ESP32SPI FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LIS3DH -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests + +CIRCUITPY_SHARPDISPLAY=0 +CIRCUITPY_SDCARDIO=0 +CIRCUITPY_BLEIO_HCI=0 +CIRCUITPY_BLEIO=0 From bfb897a0b8ef374981f8604391e3a86fd22049a7 Mon Sep 17 00:00:00 2001 From: Michael Himing Date: Thu, 17 Feb 2022 12:57:27 +1100 Subject: [PATCH 08/15] Fix press any key responding slowly on espressif --- ports/espressif/supervisor/usb.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ports/espressif/supervisor/usb.c b/ports/espressif/supervisor/usb.c index 30f9be82a3..a4f26b366c 100644 --- a/ports/espressif/supervisor/usb.c +++ b/ports/espressif/supervisor/usb.c @@ -138,3 +138,10 @@ void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { mp_sched_keyboard_interrupt(); } } + +void tud_cdc_rx_cb(uint8_t itf) { + (void)itf; + // Workaround for "press any key to enter REPL" response being delayed on espressif. + // Wake main task when any key is pressed. + port_wake_main_task(); +} From 2b32dce25660c960915edbfd4178e6f5444e1a6c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 17 Feb 2022 08:36:29 -0600 Subject: [PATCH 09/15] genlast: Actually catch errors when preprocessing files Due to a number of problems, an error calling the preprocessor wasn't making the whole genlast process fail. Now, after an execption during preprocess is printed, it is re-raised. Then, by actually collating the results of executor.map, the exception will be raised in the main thread context. Any CalledProcessError is simply converted to a nonzero exit status. --- py/genlast.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/py/genlast.py b/py/genlast.py index 0071c7b849..ad44745d97 100644 --- a/py/genlast.py +++ b/py/genlast.py @@ -48,6 +48,7 @@ def preprocess(command, output_dir, fn): process_file(fn, output_dir, output) except Exception as e: print(e, file=sys.stderr) + raise def maybe_preprocess(command, output_dir, fn): @@ -72,6 +73,18 @@ if __name__ == "__main__": # Mac and Windows use 'spawn'. Uncomment this during testing to catch spawn-specific problems on Linux. # multiprocessing.set_start_method("spawn") executor = ProcessPoolExecutor(max_workers=multiprocessing.cpu_count() + 1) - executor.map(maybe_preprocess, itertools.repeat(command), itertools.repeat(output_dir), check) - executor.map(preprocess, itertools.repeat(command), itertools.repeat(output_dir), always) + results = [] + try: + results.extend( + executor.map( + maybe_preprocess, itertools.repeat(command), itertools.repeat(output_dir), check + ) + ) + results.extend( + executor.map( + preprocess, itertools.repeat(command), itertools.repeat(output_dir), always + ) + ) + except subprocess.CalledProcessError: + raise SystemExit(1) executor.shutdown() From 1309ef083bd69ade9308aea3b25540d6f62ccaa1 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 17 Feb 2022 08:38:02 -0600 Subject: [PATCH 10/15] translate: Can't use compress_max_length_bits during qstr generation --- supervisor/shared/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/supervisor/shared/translate.c b/supervisor/shared/translate.c index 674df7579a..fefda46006 100644 --- a/supervisor/shared/translate.c +++ b/supervisor/shared/translate.c @@ -87,11 +87,13 @@ STATIC int put_utf8(char *buf, int u) { } uint16_t decompress_length(const compressed_string_t *compressed) { + #ifndef NO_QSTR #if (compress_max_length_bits <= 8) return 1 + (compressed->data >> (8 - compress_max_length_bits)); #else return 1 + ((compressed->data * 256 + compressed->tail[0]) >> (16 - compress_max_length_bits)); #endif + #endif } char *decompress(const compressed_string_t *compressed, char *decompressed) { From 28714963d5b22519dba8b9c6366f536ec078dc1c Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 17 Feb 2022 15:24:31 -0500 Subject: [PATCH 11/15] don't wait for USB or BLE workflow after true deep sleep --- main.c | 30 +++++++++++++++++++++--------- py/circuitpy_mpconfig.h | 6 +++--- shared-bindings/alarm/__init__.c | 10 +++++++--- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/main.c b/main.c index b4537b1d69..3e6a34843f 100644 --- a/main.c +++ b/main.c @@ -301,7 +301,8 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, mp_obj_t exception) { STATIC void print_code_py_status_message(safe_mode_t safe_mode) { if (autoreload_is_enabled()) { - serial_write_compressed(translate("Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.\n")); + serial_write_compressed( + translate("Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.\n")); } else { serial_write_compressed(translate("Auto-reload is off.\n")); } @@ -401,7 +402,8 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re // the options because it can be treated like any other reason-for-stickiness bit. The // source is different though: it comes from the options that will apply to the next run, // while the rest of next_code_options is what applied to this run. - if (next_code_allocation != NULL && (((next_code_info_t *)next_code_allocation->ptr)->options & SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET)) { + if (next_code_allocation != NULL && + (((next_code_info_t *)next_code_allocation->ptr)->options & SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET)) { next_code_options |= SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET; } @@ -527,9 +529,9 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re // Sleep until our next interrupt. #if CIRCUITPY_ALARM if (result.return_code & PYEXEC_DEEP_SLEEP) { - // Make sure we have been awake long enough for USB to connect (enumeration delay). - int64_t connecting_delay_ticks = CIRCUITPY_USB_CONNECTED_SLEEP_DELAY * 1024 - port_get_raw_ticks(NULL); - // Until it's safe to decide whether we're real/fake sleeping + const bool awoke_from_true_deep_sleep = + common_hal_mcu_processor_get_reset_reason() == RESET_REASON_DEEP_SLEEP_ALARM; + if (fake_sleeping) { // This waits until a pretend deep sleep alarm occurs. They are set // during common_hal_alarm_set_deep_sleep_alarms. On some platforms @@ -537,18 +539,28 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re // for deep sleep alarms above. If it wasn't a deep sleep alarm, // then we'll idle here again. common_hal_alarm_pretending_deep_sleep(); - } else if (connecting_delay_ticks < 0) { - // Entering deep sleep (may be fake or real.) + } + // The first time we go into a deep sleep, make sure we have been awake long enough + // for USB to connect (enumeration delay), or for the BLE workflow to start. + // We wait CIRCUITPY_WORKFLOW_CONNECTION_SLEEP_DELAY seconds after a restart. + // But if we woke up from a real deep sleep, don't wait for connection. The user will need to + // do a hard reset to get out of the real deep sleep. + else if (awoke_from_true_deep_sleep || + port_get_raw_ticks(NULL) > CIRCUITPY_WORKFLOW_CONNECTION_SLEEP_DELAY * 1024) { + // OK to start sleeping, real or fake. status_led_deinit(); deinit_rxtx_leds(); board_deinit(); - if (!supervisor_workflow_active()) { + + // Continue with true deep sleep even if workflow is available. + if (awoke_from_true_deep_sleep || !supervisor_workflow_active()) { // Enter true deep sleep. When we wake up we'll be back at the // top of main(), not in this loop. common_hal_alarm_enter_deep_sleep(); // Does not return. } else { - serial_write_compressed(translate("Pretending to deep sleep until alarm, CTRL-C or file write.\n")); + serial_write_compressed( + translate("Pretending to deep sleep until alarm, CTRL-C or file write.\n")); fake_sleeping = true; } } else { diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 2893df46ec..070403877f 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -447,9 +447,9 @@ void supervisor_run_background_tasks_if_tick(void); #define CIRCUITPY_PYSTACK_SIZE 1536 #endif -// Wait this long imediately after startup to see if we are connected to USB. -#ifndef CIRCUITPY_USB_CONNECTED_SLEEP_DELAY -#define CIRCUITPY_USB_CONNECTED_SLEEP_DELAY 5 +// Wait this long before sleeping immediately after startup, to see if we are connected via USB or BLE. +#ifndef CIRCUITPY_WORKFLOW_CONNECTION_SLEEP_DELAY +#define CIRCUITPY_WORKFLOW_CONNECTION_SLEEP_DELAY 5 #endif #ifndef CIRCUITPY_PROCESSOR_COUNT diff --git a/shared-bindings/alarm/__init__.c b/shared-bindings/alarm/__init__.c index 9f9e01885a..340a4ea679 100644 --- a/shared-bindings/alarm/__init__.c +++ b/shared-bindings/alarm/__init__.c @@ -125,11 +125,15 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_light_sleep_until_alarms_obj, 1, MP_OB //| //| If no alarms are specified, the microcontroller will deep sleep until reset. //| -//| **If CircuitPython is connected to a host computer, the connection will be maintained, -//| and the system will not go into deep sleep.** +//| **If CircuitPython is connected to a host computer via USB or BLE +//| the first time a deep sleep is requested, +//| the connection will be maintained and the system will not go into deep sleep.** //| This allows the user to interrupt an existing program with ctrl-C, //| and to edit the files in CIRCUITPY, which would not be possible in true deep sleep. -//| Thus, to use deep sleep and save significant power, you will need to disconnect from the host. +//| +//| If CircuitPython goes into a true deep sleep, and USB or BLE is reconnected, +//| the next deep sleep will still be a true deep sleep. You must do a hard reset +//| or power-cycle to exit a true deep sleep loop. //| //| Here is skeletal example that deep-sleeps and restarts every 60 seconds: //| From bdee6cf3b627db6e1b33265092cdc0330c9cf5f4 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 17 Feb 2022 14:21:03 -0800 Subject: [PATCH 12/15] Don't update status LED color on brightness change Brightness changes now happen when the LED isn't active or initialized. When not init, CP may crash. Fixes #5872 --- supervisor/shared/status_leds.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/supervisor/shared/status_leds.c b/supervisor/shared/status_leds.c index 08b2935cb6..07ff9634e9 100644 --- a/supervisor/shared/status_leds.c +++ b/supervisor/shared/status_leds.c @@ -324,12 +324,9 @@ uint32_t color_brightness(uint32_t color, uint8_t brightness) { void set_status_brightness(uint8_t level) { #if CIRCUITPY_STATUS_LED rgb_status_brightness = level; - uint32_t current_color = current_status_color; - // Temporarily change the current color global to force the new_status_color call to update the - // LED. Usually duplicate calls of the same color are ignored without regard to brightness - // changes. - current_status_color = 0; - new_status_color(current_color); + // This is only called by user code and we're never controlling the status + // LED when user code is running. So, we don't need to update the current + // state (there is none.) #endif } From 7f3f4e409d2da94602d173ffd4803f855b2e4dc0 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 17 Feb 2022 14:37:25 -0800 Subject: [PATCH 13/15] Update set_rgb_status_brightness doc and arg check --- shared-bindings/supervisor/__init__.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index e7c4479d2a..c69f0e4174 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -76,16 +76,15 @@ STATIC mp_obj_t supervisor_disable_autoreload(void) { MP_DEFINE_CONST_FUN_OBJ_0(supervisor_disable_autoreload_obj, supervisor_disable_autoreload); //| def set_rgb_status_brightness(brightness: int) -> None: -//| """Set brightness of status neopixel from 0-255 -//| `set_rgb_status_brightness` is called.""" +//| """Set brightness of status RGB LED from 0-255. This will take effect +//| after the current code finishes and the status LED is used to show +//| the finish state.""" //| ... //| STATIC mp_obj_t supervisor_set_rgb_status_brightness(mp_obj_t lvl) { // This must be int. If cast to uint8_t first, will never raise a ValueError. int brightness_int = mp_obj_get_int(lvl); - if (brightness_int < 0 || brightness_int > 255) { - mp_raise_ValueError(translate("Brightness must be between 0 and 255")); - } + mp_arg_validate_int_range(brightness_int, 0, 255, MP_QSTR_brightness); set_status_brightness((uint8_t)brightness_int); return mp_const_none; } From 3acb0d48bcaf624328d64dcdc85b23c3ac313d44 Mon Sep 17 00:00:00 2001 From: Neradoc Date: Fri, 18 Feb 2022 00:56:19 +0100 Subject: [PATCH 14/15] free RX and TX on QTPY-ESP32S2 in non debug builds --- ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h b/ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h index 7ae713c4bb..4afce4c042 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_qtpy_esp32s2/mpconfigboard.h @@ -50,5 +50,7 @@ #define DOUBLE_TAP_PIN (&pin_GPIO10) +#ifdef DEBUG #define DEBUG_UART_RX (&pin_GPIO16) #define DEBUG_UART_TX (&pin_GPIO5) +#endif From c4fb5f7df131902fa1a50b07ac758a5cc5fc2778 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 17 Feb 2022 16:32:29 -0800 Subject: [PATCH 15/15] Allow ESP boards to customize how a pin is reset This allows board code to override the default pull up reset state. It is useful for pins that are already externally connected, pulled or otherwise used by the board. Fixes #5931 --- .../adafruit_feather_esp32s2_tft/board.c | 25 ++++++++----------- .../adafruit_magtag_2.9_grayscale/board.c | 19 ++++++++++++++ .../boards/microdev_micro_s2/board.c | 13 ++++++++++ .../common-hal/microcontroller/Pin.c | 13 +++++++--- .../common-hal/microcontroller/Pin.h | 8 ++++-- 5 files changed, 58 insertions(+), 20 deletions(-) diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c index f25a2a99a8..74187899ef 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c @@ -71,14 +71,6 @@ uint8_t display_init_sequence[] = { void board_init(void) { - // Never reset the I2C/TFT power pin because doing so will reset the display. - // Instead, on reset set the default value and free the pin for user use. - // Relying on the normal pin reset would briefly float/pull the pin that - // could lead to a power brownout. - common_hal_never_reset_pin(&pin_GPIO21); - - reset_board(); - busio_spi_obj_t *spi = common_hal_board_create_spi(0); displayio_fourwire_obj_t *bus = &displays[0].fourwire_bus; bus->base.type = &displayio_fourwire_type; @@ -99,7 +91,6 @@ void board_init(void) { // workaround as board_init() is called before reset_port() in main.c pwmout_reset(); - common_hal_displayio_display_construct( display, bus, @@ -138,12 +129,18 @@ bool board_requests_safe_mode(void) { return false; } -void reset_board(void) { - // Turn on TFT and I2C - gpio_set_direction(21, GPIO_MODE_DEF_OUTPUT); - gpio_set_level(21, true); +bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + // Override the I2C/TFT power pin reset to prevent resetting the display. + if (pin_number == 21) { + // Turn on TFT and I2C + gpio_set_direction(21, GPIO_MODE_DEF_OUTPUT); + gpio_set_level(21, true); + return true; + } + return false; +} - free_pin_number(21); +void reset_board(void) { } void board_deinit(void) { diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index c55f2bf703..18dd280cea 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -172,6 +172,25 @@ void reset_board(void) { } +bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + // Pin 16 is speaker enable and it's pulled down on the board. We don't want + // to pull it high because then we'll compete with the external pull down. + // So, reset without any pulls internally. + if (pin_number == 16) { + gpio_config_t cfg = { + .pin_bit_mask = BIT64(16), + .mode = GPIO_MODE_DISABLE, + // The pin is externally pulled down, so we don't need to pull it. + .pull_up_en = false, + .pull_down_en = false, + .intr_type = GPIO_INTR_DISABLE, + }; + gpio_config(&cfg); + return true; + } + return false; +} + void board_deinit(void) { displayio_epaperdisplay_obj_t *display = &displays[0].epaper_display; if (display->base.type == &displayio_epaperdisplay_type) { diff --git a/ports/espressif/boards/microdev_micro_s2/board.c b/ports/espressif/boards/microdev_micro_s2/board.c index 6ba5d975d7..0a2b17e001 100644 --- a/ports/espressif/boards/microdev_micro_s2/board.c +++ b/ports/espressif/boards/microdev_micro_s2/board.c @@ -28,6 +28,8 @@ #include "mpconfigboard.h" #include "shared-bindings/microcontroller/Pin.h" +#include "components/driver/include/driver/gpio.h" + void board_init(void) { // Debug UART #ifdef DEBUG @@ -40,6 +42,17 @@ bool board_requests_safe_mode(void) { return false; } +bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + // Pin 21 is a high side LED so pull it down to prevent lighting the LED. + if (pin_number == 21) { + gpio_reset_pin(21); + gpio_pullup_dis(21); + gpio_pulldown_en(21); + return true; + } + return false; +} + void reset_board(void) { } diff --git a/ports/espressif/common-hal/microcontroller/Pin.c b/ports/espressif/common-hal/microcontroller/Pin.c index 1ea187c795..828d85d5ee 100644 --- a/ports/espressif/common-hal/microcontroller/Pin.c +++ b/ports/espressif/common-hal/microcontroller/Pin.c @@ -50,6 +50,10 @@ void common_hal_never_reset_pin(const mcu_pin_obj_t *pin) { never_reset_pin_number(pin->number); } +MP_WEAK bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + return false; +} + STATIC void _reset_pin(gpio_num_t pin_number) { #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) // Never ever reset pins used for flash and RAM. @@ -76,6 +80,11 @@ STATIC void _reset_pin(gpio_num_t pin_number) { } #endif + // Give the board a chance to reset the pin in a particular way. + if (espressif_board_reset_pin_number(pin_number)) { + return; + } + gpio_reset_pin(pin_number); #ifdef DOUBLE_TAP_PIN @@ -133,10 +142,6 @@ void claim_pin(const mcu_pin_obj_t *pin) { in_use[pin->number / 32] |= (1 << (pin->number % 32)); } -void free_pin_number(gpio_num_t pin_number) { - in_use[pin_number / 32] &= ~(1 << (pin_number % 32)); -} - void common_hal_mcu_pin_claim(const mcu_pin_obj_t *pin) { claim_pin(pin); } diff --git a/ports/espressif/common-hal/microcontroller/Pin.h b/ports/espressif/common-hal/microcontroller/Pin.h index c6db223bb2..e74346ef65 100644 --- a/ports/espressif/common-hal/microcontroller/Pin.h +++ b/ports/espressif/common-hal/microcontroller/Pin.h @@ -39,9 +39,13 @@ void common_hal_reset_pin(const mcu_pin_obj_t *pin); void common_hal_never_reset_pin(const mcu_pin_obj_t *pin); void claim_pin(const mcu_pin_obj_t *pin); void claim_pin_number(gpio_num_t pin_number); -// Free the pin without resetting it. -void free_pin_number(gpio_num_t pin_number); bool pin_number_is_free(gpio_num_t pin_number); void never_reset_pin_number(gpio_num_t pin_number); +// Allow the board to reset a pin in a board-specific way. This can be used +// for LEDs or enable pins to put them in a state beside the default pull-up. +// Return true to indicate that the pin was reset. Returning false will lead to +// the port-default reset behavior. +bool espressif_board_reset_pin_number(gpio_num_t pin_number); + #endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_MICROCONTROLLER_PIN_H