Merge remote-tracking branch 'origin/main' into merge-1.18
This commit is contained in:
commit
f45a6d762f
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -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
|
1
frozen/Adafruit_CircuitPython_FakeRequests
Submodule
1
frozen/Adafruit_CircuitPython_FakeRequests
Submodule
@ -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
|
1
frozen/Adafruit_CircuitPython_PortalBase
Submodule
1
frozen/Adafruit_CircuitPython_PortalBase
Submodule
@ -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
|
@ -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
30
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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)) \
|
||||
|
@ -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) {
|
||||
|
@ -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) },
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
}
|
||||
|
||||
|
@ -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"));
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
116
ports/espressif/common-hal/_bleio/ble_events.c
Normal file
116
ports/espressif/common-hal/_bleio/ble_events.c
Normal 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;
|
||||
}
|
51
ports/espressif/common-hal/_bleio/ble_events.h
Normal file
51
ports/espressif/common-hal/_bleio/ble_events.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
//|
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user