Merge remote-tracking branch 'origin/main' into merge-1.18

This commit is contained in:
Jeff Epler 2022-02-18 08:12:05 -06:00
commit f45a6d762f
No known key found for this signature in database
GPG Key ID: D5BF15AB975AB4DE
73 changed files with 1189 additions and 158 deletions

6
.gitmodules vendored
View File

@ -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

@ -1 +1 @@
Subproject commit c55da0dee66302d2fa8ed31623d047c307f409b2
Subproject commit baab505fd4dcc54d8e9d45e6463c68bdc6d100eb

@ -1 +1 @@
Subproject commit a695cde1b1cc957bcd10875b12ae82d1deeb0157
Subproject commit beec03065712cd62f79e839d5cf8f7c9847fc3b1

@ -1 +1 @@
Subproject commit c24a5310bc259cd9d93b1f42468e07f41d4b0e56
Subproject commit 859a7d403e4e79ec1c8915c81ba581dbaab8a4ac

@ -1 +1 @@
Subproject commit 169715b3444c614e55827ccf79b35b2b5e11f1d2
Subproject commit 43b2b5261845839bb31cf2507755b1f1efa73a48

@ -1 +1 @@
Subproject commit 2017afdfb43d3d9c5a73f8e85e951a583b18206a
Subproject commit b04042addd47c2645e139032b02a3b9ddeeb3425

@ -1 +1 @@
Subproject commit 8d09b29a1a92499dbbd10dd832f27db71057af5f
Subproject commit 938f6bb335ba5e4c56a8062c591ff9f3c18c4297

@ -1 +1 @@
Subproject commit 8a6ab89b7d19f45a20b3f794cf900e23c9a8453b
Subproject commit 8e7e111a9ff39d3f4311caa7babeb451422c759f

@ -1 +1 @@
Subproject commit 4fc5a32763c4a6eac3a9446e296a9e925cc29a5c
Subproject commit df2449815433e05ea0f89c19518ccde7a10a2faa

@ -1 +1 @@
Subproject commit 993bd12e1747ec117e8d104a5e9f4659c8a347a3
Subproject commit 708bb0c82c7b075bd6912c97231aea880b1a1cb8

@ -1 +1 @@
Subproject commit 0ec87891f9a28ee3c5ae3b020b60d361684f466d
Subproject commit 0bd04a235556979bd13a373821a6602445fe132b

@ -1 +1 @@
Subproject commit e07090117766d4a9ea2de07cd6f5418990cc598b
Subproject commit eb6124fdff59b98d7d49dd86072df99c0e97167b

@ -1 +1 @@
Subproject commit de4829a027a45882ae5477e50a75985e0e59f759
Subproject commit 13775b058422085762874fde8e587f2e9f066855

@ -0,0 +1 @@
Subproject commit f6cdec74b64112016c459abe4a5d31a3b34caeb3

@ -1 +1 @@
Subproject commit 68744ede79c992a3df8322c21a1468c5ccaef2ee
Subproject commit bccbe3da75f42b540b3faebb9d5a2d1ccf5e7147

@ -1 +1 @@
Subproject commit d79dd180cf6062e97d6a12cbc8dc7fdbedcc752b
Subproject commit 2fddabcaf0df1763111ed9dbf9e2d4cdb5b0434e

@ -1 +1 @@
Subproject commit 2ca37f927b3ee3aad379c2991f36b3ef1be0203d
Subproject commit 9d78269db987df8657baea554c725a6b6be3f62c

@ -1 +1 @@
Subproject commit 8fc5eaecb3e24e4109bcc788f41461f1c45c3719
Subproject commit 29816fbe98c012ea0a1b5cae7f07aeae7ebf8b52

@ -1 +1 @@
Subproject commit ebbe69667d53ae76bc6d82e5296f87520ffbb5ae
Subproject commit acc4bdd73fdceb74d75cd5a1f261ae157ee32613

@ -1 +1 @@
Subproject commit f94ef67425516f23c889d217ffe5a3a710c1d278
Subproject commit 75e9ec62e4fe47a7212a69fb84aa1cfa7848e2b3

@ -1 +1 @@
Subproject commit 1127e3f7bcefa9fddb5b7f30533ecc6c58b420ea
Subproject commit 6641509ef43b672a82addf41f02b6466d6c67f01

@ -1 +1 @@
Subproject commit 9995c45a5ed1d455a4a8b7bfb9eb134de7f2b9db
Subproject commit fd478fda7adbd254282b8cad5000f06a96760c91

@ -1 +1 @@
Subproject commit 9ca3bf00c6a2dd1de2d315a3b952a381d720aa7d
Subproject commit a115fc30df1c230c09c8a533ca77f3a4afd9f6c3

@ -0,0 +1 @@
Subproject commit 77ba8eedf89b96c85a6194e5da2061c9d5c20242

@ -1 +1 @@
Subproject commit 4ac43288938abb4c3db127eeb79ef0d4ea4c16ea
Subproject commit 011acd627fc24342c397fc640b204a798f7b69dd

@ -1 +1 @@
Subproject commit 9ac490905834466319279a3390c914f1fd83733f
Subproject commit c58defd70947531c5a9c37ddcb569f240567a78b

@ -1 +1 @@
Subproject commit 0df4521b4a04ca1236960ff889ede118ec4305b5
Subproject commit 742ac7c8fb52bb85d9fd367b60a7f80475d7ed14

@ -1 +1 @@
Subproject commit 79678c6adb2252de8fed6273604bc6ac676132a5
Subproject commit 49ab415d6b601c99979262f9e91c21dcb3a927a7

@ -1 +1 @@
Subproject commit 900b28cbae008e3253c4c40496e49faea9fb7034
Subproject commit 270565665ada26fe8d7a99a3cb5941b452444471

@ -1 +1 @@
Subproject commit b7a76420d1dec119f8744aa7c0ea500e235561d1
Subproject commit 9dd51fecfcbb15cb2a00eeadbd66b36ce0c09ee2

@ -1 +1 @@
Subproject commit c5b480434de8fa56d8ba978a57cd3919fdc9da2a
Subproject commit 79c70a49285be8b6548de3f5ca20aa5ac1fafa22

@ -1 +1 @@
Subproject commit ca56187fe7af315130808191b004432fdfdc1b09
Subproject commit 272d225365eed46916390cf1f393dd08bc00b7d4

@ -1 +1 @@
Subproject commit 755784b6acc8ba419a085bee2d2dc4374f0d0030
Subproject commit fad0f89e760829a76f553ef8459f61001597a846

@ -1 +1 @@
Subproject commit d4ac6ce3eea2c87781fa2df4e431d9440c610fad
Subproject commit e86f258e43591ce4a04661277e77e9fdf6fec27e

@ -1 +1 @@
Subproject commit f06ac21e96321724258e00f7596d874eff53f0b8
Subproject commit c89c8689161e5b35bfe4fa8355615696e03f0648

View File

@ -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
@ -3788,6 +3800,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 ""

30
main.c
View File

@ -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 {

View File

@ -9,3 +9,14 @@ 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
CIRCUITPY_SHARPDISPLAY=0
CIRCUITPY_SDCARDIO=0
CIRCUITPY_BLEIO_HCI=0
CIRCUITPY_BLEIO=0

View File

@ -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

View File

@ -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

View File

@ -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)) \

View File

@ -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) {

View File

@ -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) },

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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) {
}

View File

@ -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"));

View File

@ -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);
}

View File

@ -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;

View File

@ -28,6 +28,7 @@
#include <stdio.h>
#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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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.
}

View File

@ -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;

View File

@ -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"));
}
}

View File

@ -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)

View File

@ -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 <stdbool.h>
#include <stdio.h>
#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;
}

View File

@ -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 <stdbool.h>
#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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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()

View File

@ -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:
//|

View File

@ -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;
}

View File

@ -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.

View File

@ -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
}

View File

@ -87,14 +87,12 @@ STATIC int put_utf8(char *buf, int u) {
}
uint16_t decompress_length(const compressed_string_t *compressed) {
#if defined(compress_max_length_bits)
#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
#else
// generating qstrs
#endif
}