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"]
|
[submodule "ports/stm/st_driver/stm32f4xx_hal_driver"]
|
||||||
path = ports/stm/st_driver/stm32f4xx_hal_driver
|
path = ports/stm/st_driver/stm32f4xx_hal_driver
|
||||||
url = https://github.com/adafruit/stm32f4xx_hal_driver.git
|
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"
|
msgid "Unhandled ESP TLS error %d %d %x %d"
|
||||||
msgstr ""
|
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
|
#: shared-bindings/wifi/Radio.c
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unknown failure %d"
|
msgid "Unknown failure %d"
|
||||||
@ -2498,12 +2508,14 @@ msgstr ""
|
|||||||
msgid "Update Failed"
|
msgid "Update Failed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: ports/espressif/common-hal/_bleio/Characteristic.c
|
||||||
#: ports/espressif/common-hal/_bleio/Descriptor.c
|
#: ports/espressif/common-hal/_bleio/Descriptor.c
|
||||||
#: ports/nrf/common-hal/_bleio/Characteristic.c
|
#: ports/nrf/common-hal/_bleio/Characteristic.c
|
||||||
#: ports/nrf/common-hal/_bleio/Descriptor.c
|
#: ports/nrf/common-hal/_bleio/Descriptor.c
|
||||||
msgid "Value length != required fixed length"
|
msgid "Value length != required fixed length"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: ports/espressif/common-hal/_bleio/Characteristic.c
|
||||||
#: ports/espressif/common-hal/_bleio/Descriptor.c
|
#: ports/espressif/common-hal/_bleio/Descriptor.c
|
||||||
#: ports/nrf/common-hal/_bleio/Characteristic.c
|
#: ports/nrf/common-hal/_bleio/Characteristic.c
|
||||||
#: ports/nrf/common-hal/_bleio/Descriptor.c
|
#: ports/nrf/common-hal/_bleio/Descriptor.c
|
||||||
@ -3788,6 +3800,7 @@ msgstr ""
|
|||||||
msgid "non-Device in %q"
|
msgid "non-Device in %q"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: ports/espressif/common-hal/_bleio/Connection.c
|
||||||
#: ports/nrf/common-hal/_bleio/Connection.c
|
#: ports/nrf/common-hal/_bleio/Connection.c
|
||||||
msgid "non-UUID found in service_uuids_whitelist"
|
msgid "non-UUID found in service_uuids_whitelist"
|
||||||
msgstr ""
|
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) {
|
STATIC void print_code_py_status_message(safe_mode_t safe_mode) {
|
||||||
if (autoreload_is_enabled()) {
|
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 {
|
} else {
|
||||||
serial_write_compressed(translate("Auto-reload is off.\n"));
|
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
|
// 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,
|
// 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.
|
// 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;
|
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.
|
// Sleep until our next interrupt.
|
||||||
#if CIRCUITPY_ALARM
|
#if CIRCUITPY_ALARM
|
||||||
if (result.return_code & PYEXEC_DEEP_SLEEP) {
|
if (result.return_code & PYEXEC_DEEP_SLEEP) {
|
||||||
// Make sure we have been awake long enough for USB to connect (enumeration delay).
|
const bool awoke_from_true_deep_sleep =
|
||||||
int64_t connecting_delay_ticks = CIRCUITPY_USB_CONNECTED_SLEEP_DELAY * 1024 - port_get_raw_ticks(NULL);
|
common_hal_mcu_processor_get_reset_reason() == RESET_REASON_DEEP_SLEEP_ALARM;
|
||||||
// Until it's safe to decide whether we're real/fake sleeping
|
|
||||||
if (fake_sleeping) {
|
if (fake_sleeping) {
|
||||||
// This waits until a pretend deep sleep alarm occurs. They are set
|
// This waits until a pretend deep sleep alarm occurs. They are set
|
||||||
// during common_hal_alarm_set_deep_sleep_alarms. On some platforms
|
// 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,
|
// for deep sleep alarms above. If it wasn't a deep sleep alarm,
|
||||||
// then we'll idle here again.
|
// then we'll idle here again.
|
||||||
common_hal_alarm_pretending_deep_sleep();
|
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();
|
status_led_deinit();
|
||||||
deinit_rxtx_leds();
|
deinit_rxtx_leds();
|
||||||
board_deinit();
|
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
|
// Enter true deep sleep. When we wake up we'll be back at the
|
||||||
// top of main(), not in this loop.
|
// top of main(), not in this loop.
|
||||||
common_hal_alarm_enter_deep_sleep();
|
common_hal_alarm_enter_deep_sleep();
|
||||||
// Does not return.
|
// Does not return.
|
||||||
} else {
|
} 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;
|
fake_sleeping = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -9,3 +9,14 @@ CHIP_FAMILY = samd51
|
|||||||
QSPI_FLASH_FILESYSTEM = 1
|
QSPI_FLASH_FILESYSTEM = 1
|
||||||
EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C"
|
EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C"
|
||||||
LONGINT_IMPL = MPZ
|
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
|
QSPI_FLASH_FILESYSTEM = 1
|
||||||
EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ, GD25Q64C"
|
EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ, GD25Q64C"
|
||||||
LONGINT_IMPL = MPZ
|
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
|
QSPI_FLASH_FILESYSTEM = 1
|
||||||
EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ, GD25Q64C"
|
EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ, GD25Q64C"
|
||||||
LONGINT_IMPL = MPZ
|
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
|
SRC_C += lib/tinyusb/src/portable/espressif/esp32sx/dcd_esp32sx.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq ($(CIRCUITPY_BLEIO),0)
|
||||||
|
SRC_C += common-hal/_bleio/ble_events.c
|
||||||
|
endif
|
||||||
|
|
||||||
SRC_COMMON_HAL_EXPANDED = \
|
SRC_COMMON_HAL_EXPANDED = \
|
||||||
$(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \
|
$(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \
|
||||||
$(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \
|
$(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \
|
||||||
|
@ -71,14 +71,6 @@ uint8_t display_init_sequence[] = {
|
|||||||
|
|
||||||
|
|
||||||
void board_init(void) {
|
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);
|
busio_spi_obj_t *spi = common_hal_board_create_spi(0);
|
||||||
displayio_fourwire_obj_t *bus = &displays[0].fourwire_bus;
|
displayio_fourwire_obj_t *bus = &displays[0].fourwire_bus;
|
||||||
bus->base.type = &displayio_fourwire_type;
|
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
|
// workaround as board_init() is called before reset_port() in main.c
|
||||||
pwmout_reset();
|
pwmout_reset();
|
||||||
|
|
||||||
|
|
||||||
common_hal_displayio_display_construct(
|
common_hal_displayio_display_construct(
|
||||||
display,
|
display,
|
||||||
bus,
|
bus,
|
||||||
@ -138,12 +129,18 @@ bool board_requests_safe_mode(void) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_board(void) {
|
bool espressif_board_reset_pin_number(gpio_num_t pin_number) {
|
||||||
// Turn on TFT and I2C
|
// Override the I2C/TFT power pin reset to prevent resetting the display.
|
||||||
gpio_set_direction(21, GPIO_MODE_DEF_OUTPUT);
|
if (pin_number == 21) {
|
||||||
gpio_set_level(21, true);
|
// 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) {
|
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), MP_ROM_PTR(&pin_GPIO33) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL_POWER), MP_ROM_PTR(&pin_GPIO34) },
|
{ 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_MOSI), MP_ROM_PTR(&pin_GPIO35) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_D35), 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_ESP_FLASH_SIZE=4MB
|
||||||
|
|
||||||
CIRCUITPY_MODULE=wrover
|
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) {
|
void board_deinit(void) {
|
||||||
displayio_epaperdisplay_obj_t *display = &displays[0].epaper_display;
|
displayio_epaperdisplay_obj_t *display = &displays[0].epaper_display;
|
||||||
if (display->base.type == &displayio_epaperdisplay_type) {
|
if (display->base.type == &displayio_epaperdisplay_type) {
|
||||||
|
@ -17,3 +17,11 @@ CIRCUITPY_ESP_FLASH_FREQ=40m
|
|||||||
CIRCUITPY_ESP_FLASH_SIZE=4MB
|
CIRCUITPY_ESP_FLASH_SIZE=4MB
|
||||||
|
|
||||||
CIRCUITPY_MODULE=wrover
|
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)
|
#define DOUBLE_TAP_PIN (&pin_GPIO10)
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
#define DEBUG_UART_RX (&pin_GPIO16)
|
#define DEBUG_UART_RX (&pin_GPIO16)
|
||||||
#define DEBUG_UART_TX (&pin_GPIO5)
|
#define DEBUG_UART_TX (&pin_GPIO5)
|
||||||
|
#endif
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
#include "mpconfigboard.h"
|
#include "mpconfigboard.h"
|
||||||
#include "shared-bindings/microcontroller/Pin.h"
|
#include "shared-bindings/microcontroller/Pin.h"
|
||||||
|
|
||||||
|
#include "components/driver/include/driver/gpio.h"
|
||||||
|
|
||||||
void board_init(void) {
|
void board_init(void) {
|
||||||
// Debug UART
|
// Debug UART
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -40,6 +42,17 @@ bool board_requests_safe_mode(void) {
|
|||||||
return false;
|
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) {
|
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);
|
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) {
|
STATIC void _new_connection(uint16_t conn_handle) {
|
||||||
// Set the tx_power for the connection higher than the advertisement.
|
// Set the tx_power for the connection higher than the advertisement.
|
||||||
esp_ble_tx_power_set(conn_handle, ESP_PWR_LVL_N0);
|
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->pair_status = PAIR_NOT_PAIRED;
|
||||||
connection->mtu = 0;
|
connection->mtu = 0;
|
||||||
|
|
||||||
|
ble_gattc_exchange_mtu(conn_handle, _mtu_reply, connection);
|
||||||
|
|
||||||
// Change the callback for the connection.
|
// Change the callback for the connection.
|
||||||
ble_gap_set_event_cb(conn_handle, bleio_connection_event_cb, 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_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"));
|
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,
|
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_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo,
|
||||||
const char *user_description) {
|
const char *user_description) {
|
||||||
mp_raise_NotImplementedError(NULL);
|
|
||||||
self->service = service;
|
self->service = service;
|
||||||
self->uuid = uuid;
|
self->uuid = uuid;
|
||||||
self->handle = BLEIO_HANDLE_INVALID;
|
self->handle = BLEIO_HANDLE_INVALID;
|
||||||
|
self->cccd_handle = BLEIO_HANDLE_INVALID;
|
||||||
|
self->sccd_handle = BLEIO_HANDLE_INVALID;
|
||||||
self->props = props;
|
self->props = props;
|
||||||
self->read_perm = read_perm;
|
self->read_perm = read_perm;
|
||||||
self->write_perm = write_perm;
|
self->write_perm = write_perm;
|
||||||
self->initial_value_len = 0;
|
common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo);
|
||||||
self->initial_value = NULL;
|
|
||||||
if (initial_value_bufinfo != NULL) {
|
if (gc_alloc_possible()) {
|
||||||
// Copy the initial value if it's on the heap. Otherwise it's internal and we may not be able
|
self->descriptor_list = mp_obj_new_list(0, NULL);
|
||||||
// to allocate.
|
} else {
|
||||||
self->initial_value_len = initial_value_bufinfo->len;
|
self->descriptor_list = NULL;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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->max_length = max_length;
|
||||||
self->fixed_length = fixed_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;
|
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) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,8 +140,62 @@ size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t
|
|||||||
return self->max_length;
|
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) {
|
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) {
|
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;
|
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.
|
// 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) {
|
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.
|
// Will be MP_OBJ_NULL before being assigned to a Service.
|
||||||
bleio_service_obj_t *service;
|
bleio_service_obj_t *service;
|
||||||
bleio_uuid_obj_t *uuid;
|
bleio_uuid_obj_t *uuid;
|
||||||
const uint8_t *initial_value;
|
uint8_t *current_value;
|
||||||
uint16_t initial_value_len;
|
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 max_length;
|
||||||
|
uint16_t def_handle;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
bleio_characteristic_properties_t props;
|
bleio_characteristic_properties_t props;
|
||||||
bleio_attribute_security_mode_t read_perm;
|
bleio_attribute_security_mode_t read_perm;
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "shared/runtime/interrupt_char.h"
|
#include "shared/runtime/interrupt_char.h"
|
||||||
|
#include "py/ringbuf.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "py/stream.h"
|
#include "py/stream.h"
|
||||||
|
|
||||||
@ -37,14 +38,38 @@
|
|||||||
#include "common-hal/_bleio/CharacteristicBuffer.h"
|
#include "common-hal/_bleio/CharacteristicBuffer.h"
|
||||||
#include "shared-bindings/_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,
|
void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self,
|
||||||
bleio_characteristic_obj_t *characteristic,
|
bleio_characteristic_obj_t *characteristic,
|
||||||
mp_float_t timeout,
|
mp_float_t timeout,
|
||||||
uint8_t *buffer, size_t buffer_size,
|
uint8_t *buffer, size_t buffer_size,
|
||||||
void *static_handler_entry) {
|
void *static_handler_entry) {
|
||||||
|
|
||||||
mp_raise_NotImplementedError(NULL);
|
|
||||||
|
|
||||||
self->characteristic = characteristic;
|
self->characteristic = characteristic;
|
||||||
self->timeout_ms = timeout * 1000;
|
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.iget = 0;
|
||||||
self->ringbuf.iput = 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.
|
// 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) {
|
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.
|
uint64_t start_ticks = supervisor_ticks_ms64();
|
||||||
return 0;
|
|
||||||
|
// 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) {
|
uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) {
|
||||||
// TODO: Implement this.
|
return ringbuf_num_filled(&self->ringbuf);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) {
|
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) {
|
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) {
|
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) {
|
bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) {
|
||||||
|
@ -48,6 +48,9 @@
|
|||||||
|
|
||||||
#include "host/ble_att.h"
|
#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) {
|
int bleio_connection_event_cb(struct ble_gap_event *event, void *connection_in) {
|
||||||
bleio_connection_internal_t *connection = (bleio_connection_internal_t *)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: {
|
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;
|
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:
|
default:
|
||||||
#if CIRCUITPY_VERBOSE_BLE
|
#if CIRCUITPY_VERBOSE_BLE
|
||||||
mp_printf(&mp_plat_print, "Unhandled connection event: %d\n", event->type);
|
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) {
|
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) {
|
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.
|
// 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) {
|
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);
|
bleio_connection_ensure_connected(self);
|
||||||
// Convert to a tuple and then clear the list so the callee will take ownership.
|
// Convert to a tuple and then clear the list so the callee will take ownership.
|
||||||
mp_obj_tuple_t *services_tuple =
|
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);
|
self->connection->remote_service_list->items);
|
||||||
mp_obj_list_clear(MP_OBJ_FROM_PTR(self->connection->remote_service_list));
|
mp_obj_list_clear(MP_OBJ_FROM_PTR(self->connection->remote_service_list));
|
||||||
|
|
||||||
// TODO: Implement this.
|
|
||||||
return services_tuple;
|
return services_tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
#include "host/ble_att.h"
|
#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) {
|
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->characteristic = characteristic;
|
||||||
self->uuid = uuid;
|
self->uuid = uuid;
|
||||||
self->handle = BLEIO_HANDLE_INVALID;
|
self->handle = BLEIO_HANDLE_INVALID;
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
#include "common-hal/_bleio/UUID.h"
|
#include "common-hal/_bleio/UUID.h"
|
||||||
|
|
||||||
|
#include "host/ble_gatt.h"
|
||||||
|
|
||||||
// Forward declare characteristic because it includes a Descriptor.
|
// Forward declare characteristic because it includes a Descriptor.
|
||||||
struct _bleio_characteristic_obj;
|
struct _bleio_characteristic_obj;
|
||||||
|
|
||||||
@ -45,6 +47,7 @@ typedef struct _bleio_descriptor_obj {
|
|||||||
uint16_t max_length;
|
uint16_t max_length;
|
||||||
bool fixed_length;
|
bool fixed_length;
|
||||||
uint16_t handle;
|
uint16_t handle;
|
||||||
|
struct ble_gatt_dsc_def def;
|
||||||
bleio_attribute_security_mode_t read_perm;
|
bleio_attribute_security_mode_t read_perm;
|
||||||
bleio_attribute_security_mode_t write_perm;
|
bleio_attribute_security_mode_t write_perm;
|
||||||
} bleio_descriptor_obj_t;
|
} bleio_descriptor_obj_t;
|
||||||
|
@ -40,13 +40,113 @@
|
|||||||
|
|
||||||
#include "host/ble_att.h"
|
#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(
|
void _common_hal_bleio_packet_buffer_construct(
|
||||||
bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic,
|
bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic,
|
||||||
uint32_t *incoming_buffer, size_t incoming_buffer_size,
|
uint32_t *incoming_buffer, size_t incoming_buffer_size,
|
||||||
uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size,
|
uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size,
|
||||||
void *static_handler_entry) {
|
void *static_handler_entry) {
|
||||||
|
|
||||||
mp_raise_NotImplementedError(NULL);
|
|
||||||
self->characteristic = characteristic;
|
self->characteristic = characteristic;
|
||||||
self->client = self->characteristic->service->is_remote;
|
self->client = self->characteristic->service->is_remote;
|
||||||
self->max_packet_size = max_packet_size;
|
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[0] = outgoing_buffer1;
|
||||||
self->outgoing[1] = outgoing_buffer2;
|
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(
|
void common_hal_bleio_packet_buffer_construct(
|
||||||
@ -104,7 +227,12 @@ void common_hal_bleio_packet_buffer_construct(
|
|||||||
uint32_t *outgoing2 = NULL;
|
uint32_t *outgoing2 = NULL;
|
||||||
if (outgoing) {
|
if (outgoing) {
|
||||||
outgoing1 = m_malloc(max_packet_size, false);
|
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,
|
_common_hal_bleio_packet_buffer_construct(self, characteristic,
|
||||||
incoming_buffer, incoming_buffer_size,
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy received data. Lock out write interrupt handler while copying.
|
// Get packet length, which is in first two bytes of packet.
|
||||||
// TODO: Implement this.
|
uint16_t packet_length;
|
||||||
return 0;
|
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) {
|
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;
|
self->pending_size += len;
|
||||||
num_bytes_written += len;
|
num_bytes_written += len;
|
||||||
|
|
||||||
// TODO: Implement this.
|
|
||||||
|
|
||||||
// If no writes are queued then sneak in this data.
|
// If no writes are queued then sneak in this data.
|
||||||
if (!self->packet_queued) {
|
if (!self->packet_queued) {
|
||||||
|
CHECK_NIMBLE_ERROR(queue_next_write(self));
|
||||||
}
|
}
|
||||||
return num_bytes_written;
|
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) {
|
void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) {
|
||||||
if (!common_hal_bleio_packet_buffer_deinited(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->is_remote = false;
|
||||||
self->connection = NULL;
|
self->connection = NULL;
|
||||||
self->is_secondary = is_secondary;
|
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;
|
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) {
|
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->uuid = NULL;
|
||||||
self->characteristic_list = mp_obj_new_list(0, NULL);
|
self->characteristic_list = mp_obj_new_list(0, NULL);
|
||||||
self->is_remote = true;
|
self->is_remote = true;
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
#include "common-hal/_bleio/__init__.h"
|
#include "common-hal/_bleio/__init__.h"
|
||||||
// #include "common-hal/_bleio/bonding.h"
|
// #include "common-hal/_bleio/bonding.h"
|
||||||
|
#include "common-hal/_bleio/ble_events.h"
|
||||||
|
|
||||||
// Turn off BLE on a reset or reload.
|
// Turn off BLE on a reset or reload.
|
||||||
void bleio_reset() {
|
void bleio_reset() {
|
||||||
@ -50,6 +51,7 @@ void bleio_reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
supervisor_stop_bluetooth();
|
supervisor_stop_bluetooth();
|
||||||
|
ble_event_reset();
|
||||||
bleio_adapter_reset(&common_hal_bleio_adapter_obj);
|
bleio_adapter_reset(&common_hal_bleio_adapter_obj);
|
||||||
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false);
|
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false);
|
||||||
supervisor_start_bluetooth();
|
supervisor_start_bluetooth();
|
||||||
@ -97,3 +99,36 @@ void check_nimble_error(int rc, const char *file, size_t line) {
|
|||||||
break;
|
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
|
#ifndef MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_INIT_H
|
||||||
#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_INIT_H
|
#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_BLEIO_INIT_H
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
|
||||||
void bleio_background(void);
|
void bleio_background(void);
|
||||||
|
|
||||||
// typedef struct {
|
// typedef struct {
|
||||||
@ -43,6 +45,10 @@ void bleio_background(void);
|
|||||||
|
|
||||||
void check_nimble_error(int rc, const char *file, size_t line);
|
void check_nimble_error(int rc, const char *file, size_t line);
|
||||||
#define CHECK_NIMBLE_ERROR(rc) check_nimble_error(rc, __FILE__, __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 MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))
|
||||||
#define SEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000000) / (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)
|
#define ADV_INTERVAL_UNIT_FLOAT_SECS (0.000625)
|
||||||
// Microseconds is the base unit. The macros above know that.
|
// Microseconds is the base unit. The macros above know that.
|
||||||
#define UNIT_0_625_MS (625)
|
#define UNIT_0_625_MS (625)
|
||||||
|
#define UNIT_1_MS (1000)
|
||||||
#define UNIT_1_25_MS (1250)
|
#define UNIT_1_25_MS (1250)
|
||||||
#define UNIT_10_MS (10000)
|
#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);
|
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) {
|
STATIC void _reset_pin(gpio_num_t pin_number) {
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
// Never ever reset pins used for flash and RAM.
|
// Never ever reset pins used for flash and RAM.
|
||||||
@ -76,6 +80,11 @@ STATIC void _reset_pin(gpio_num_t pin_number) {
|
|||||||
}
|
}
|
||||||
#endif
|
#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);
|
gpio_reset_pin(pin_number);
|
||||||
|
|
||||||
#ifdef DOUBLE_TAP_PIN
|
#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));
|
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) {
|
void common_hal_mcu_pin_claim(const mcu_pin_obj_t *pin) {
|
||||||
claim_pin(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 common_hal_never_reset_pin(const mcu_pin_obj_t *pin);
|
||||||
void claim_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);
|
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);
|
bool pin_number_is_free(gpio_num_t pin_number);
|
||||||
void never_reset_pin_number(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
|
#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_MICROCONTROLLER_PIN_H
|
||||||
|
@ -35,8 +35,19 @@
|
|||||||
|
|
||||||
#include "py/circuitpy_mpconfig.h"
|
#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 \
|
#define MICROPY_PORT_ROOT_POINTERS \
|
||||||
CIRCUITPY_COMMON_ROOT_POINTERS
|
CIRCUITPY_COMMON_ROOT_POINTERS
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MICROPY_NLR_SETJMP (1)
|
#define MICROPY_NLR_SETJMP (1)
|
||||||
#define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000
|
#define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000
|
||||||
|
|
||||||
@ -61,4 +72,5 @@
|
|||||||
#ifndef CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP
|
#ifndef CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP
|
||||||
#define CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP (0)
|
#define CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_ESPRESSIF_MPCONFIGPORT_H
|
#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();
|
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) {
|
void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) {
|
||||||
if (!common_hal_bleio_characteristic_buffer_deinited(self)) {
|
if (!common_hal_bleio_characteristic_buffer_deinited(self)) {
|
||||||
ble_drv_remove_event_handler(characteristic_buffer_on_ble_evt, 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
|
#define CIRCUITPY_PYSTACK_SIZE 1536
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Wait this long imediately after startup to see if we are connected to USB.
|
// Wait this long before sleeping immediately after startup, to see if we are connected via USB or BLE.
|
||||||
#ifndef CIRCUITPY_USB_CONNECTED_SLEEP_DELAY
|
#ifndef CIRCUITPY_WORKFLOW_CONNECTION_SLEEP_DELAY
|
||||||
#define CIRCUITPY_USB_CONNECTED_SLEEP_DELAY 5
|
#define CIRCUITPY_WORKFLOW_CONNECTION_SLEEP_DELAY 5
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CIRCUITPY_PROCESSOR_COUNT
|
#ifndef CIRCUITPY_PROCESSOR_COUNT
|
||||||
|
@ -48,6 +48,7 @@ def preprocess(command, output_dir, fn):
|
|||||||
process_file(fn, output_dir, output)
|
process_file(fn, output_dir, output)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e, file=sys.stderr)
|
print(e, file=sys.stderr)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def maybe_preprocess(command, output_dir, fn):
|
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.
|
# Mac and Windows use 'spawn'. Uncomment this during testing to catch spawn-specific problems on Linux.
|
||||||
# multiprocessing.set_start_method("spawn")
|
# multiprocessing.set_start_method("spawn")
|
||||||
executor = ProcessPoolExecutor(max_workers=multiprocessing.cpu_count() + 1)
|
executor = ProcessPoolExecutor(max_workers=multiprocessing.cpu_count() + 1)
|
||||||
executor.map(maybe_preprocess, itertools.repeat(command), itertools.repeat(output_dir), check)
|
results = []
|
||||||
executor.map(preprocess, itertools.repeat(command), itertools.repeat(output_dir), always)
|
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()
|
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 no alarms are specified, the microcontroller will deep sleep until reset.
|
||||||
//|
|
//|
|
||||||
//| **If CircuitPython is connected to a host computer, the connection will be maintained,
|
//| **If CircuitPython is connected to a host computer via USB or BLE
|
||||||
//| and the system will not go into deep sleep.**
|
//| 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,
|
//| 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.
|
//| 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:
|
//| 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);
|
MP_DEFINE_CONST_FUN_OBJ_0(supervisor_disable_autoreload_obj, supervisor_disable_autoreload);
|
||||||
|
|
||||||
//| def set_rgb_status_brightness(brightness: int) -> None:
|
//| def set_rgb_status_brightness(brightness: int) -> None:
|
||||||
//| """Set brightness of status neopixel from 0-255
|
//| """Set brightness of status RGB LED from 0-255. This will take effect
|
||||||
//| `set_rgb_status_brightness` is called."""
|
//| 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) {
|
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.
|
// This must be int. If cast to uint8_t first, will never raise a ValueError.
|
||||||
int brightness_int = mp_obj_get_int(lvl);
|
int brightness_int = mp_obj_get_int(lvl);
|
||||||
if (brightness_int < 0 || brightness_int > 255) {
|
mp_arg_validate_int_range(brightness_int, 0, 255, MP_QSTR_brightness);
|
||||||
mp_raise_ValueError(translate("Brightness must be between 0 and 255"));
|
|
||||||
}
|
|
||||||
set_status_brightness((uint8_t)brightness_int);
|
set_status_brightness((uint8_t)brightness_int);
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "shared-module/vectorio/__init__.h"
|
#include "shared-module/vectorio/__init__.h"
|
||||||
#include "shared-bindings/vectorio/VectorShape.h"
|
#include "shared-bindings/vectorio/VectorShape.h"
|
||||||
|
|
||||||
|
#include "py/misc.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "shared-bindings/time/__init__.h"
|
#include "shared-bindings/time/__init__.h"
|
||||||
#include "shared-bindings/displayio/ColorConverter.h"
|
#include "shared-bindings/displayio/ColorConverter.h"
|
||||||
@ -61,17 +62,6 @@
|
|||||||
(u32 & 0x2 ? '1' : '0'), \
|
(u32 & 0x2 ? '1' : '0'), \
|
||||||
(u32 & 0x1 ? '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) {
|
static void short_bound_check(mp_int_t i, qstr name) {
|
||||||
if (i < SHRT_MIN || i > SHRT_MAX) {
|
if (i < SHRT_MIN || i > SHRT_MAX) {
|
||||||
mp_raise_ValueError_varg(translate("%q must be between %d and %d"), name, SHRT_MIN, 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),
|
mp_obj_get_type_str(self->ishape.shape),
|
||||||
(overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1),
|
(overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1),
|
||||||
(double)((end - start) / 1000000.0),
|
(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),
|
||||||
(double)(pixel_time / 1000.0 / pixels)
|
(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
|
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
|
// 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 the smallest area we need to draw. Redrawing the overlapping area would cost more
|
||||||
// than just drawing the union disjoint area once.
|
// 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) {
|
void set_status_brightness(uint8_t level) {
|
||||||
#if CIRCUITPY_STATUS_LED
|
#if CIRCUITPY_STATUS_LED
|
||||||
rgb_status_brightness = level;
|
rgb_status_brightness = level;
|
||||||
uint32_t current_color = current_status_color;
|
// This is only called by user code and we're never controlling the status
|
||||||
// Temporarily change the current color global to force the new_status_color call to update the
|
// LED when user code is running. So, we don't need to update the current
|
||||||
// LED. Usually duplicate calls of the same color are ignored without regard to brightness
|
// state (there is none.)
|
||||||
// changes.
|
|
||||||
current_status_color = 0;
|
|
||||||
new_status_color(current_color);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,14 +87,12 @@ STATIC int put_utf8(char *buf, int u) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t decompress_length(const compressed_string_t *compressed) {
|
uint16_t decompress_length(const compressed_string_t *compressed) {
|
||||||
#if defined(compress_max_length_bits)
|
#ifndef NO_QSTR
|
||||||
#if (compress_max_length_bits <= 8)
|
#if (compress_max_length_bits <= 8)
|
||||||
return 1 + (compressed->data >> (8 - compress_max_length_bits));
|
return 1 + (compressed->data >> (8 - compress_max_length_bits));
|
||||||
#else
|
#else
|
||||||
return 1 + ((compressed->data * 256 + compressed->tail[0]) >> (16 - compress_max_length_bits));
|
return 1 + ((compressed->data * 256 + compressed->tail[0]) >> (16 - compress_max_length_bits));
|
||||||
#endif
|
#endif
|
||||||
#else
|
|
||||||
// generating qstrs
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user