diff --git a/docs/environment.rst b/docs/environment.rst index b1619f37bf..5766b54380 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -35,6 +35,12 @@ CIRCUITPY_BLE_NAME ~~~~~~~~~~~~~~~~~~ Default BLE name the board advertises as, including for the BLE workflow. +CIRCUITPY_RESERVED_PSRAM +~~~~~~~~~~~~~~~~~~~~~~~~ +On boards with Espressif microcontrollers with PSRAM (also called SPIRAM), permanently reserve a portion of PSRAM for use by esp-idf. +This storage is removed from the CircuitPython "heap" and is available for allocation by esp-idf routines in the core instead. +Generally, only set this to a non-zero value when it is required by a specific core module. + CIRCUITPY_WEB_API_PASSWORD ~~~~~~~~~~~~~~~~~~~~~~~~~~ Password required to make modifications to the board from the Web Workflow. diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index decd4816a3..549a7b54ed 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -209,7 +209,7 @@ msgstr "" msgid "%q=%q" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c #, c-format msgid "%s error 0x%x" msgstr "" @@ -645,7 +645,7 @@ msgstr "" msgid "CIRCUITPY drive could not be found or created." msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "CRC or checksum was invalid" msgstr "" @@ -863,6 +863,10 @@ msgstr "" msgid "Display rotation must be in 90 degree increments" msgstr "" +#: main.c +msgid "Done" +msgstr "" + #: shared-bindings/digitalio/DigitalInOut.c msgid "Drive mode not used when direction is input." msgstr "" @@ -1019,7 +1023,7 @@ msgstr "" msgid "GNSS init" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Generic Failure" msgstr "" @@ -1188,7 +1192,7 @@ msgstr "" msgid "Invalid MAC address" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c #: py/moduerrno.c msgid "Invalid argument" msgstr "" @@ -1218,7 +1222,7 @@ msgstr "" msgid "Invalid pins" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Invalid size" msgstr "" @@ -1226,7 +1230,7 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Invalid state" msgstr "" @@ -1250,7 +1254,7 @@ msgstr "" msgid "Layer must be a Group or TileGrid subclass" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "MAC address was invalid" msgstr "" @@ -1535,6 +1539,14 @@ msgstr "" msgid "Odd parity is not supported" msgstr "" +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Off" +msgstr "" + +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Ok" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/PDMIn.c #: ports/raspberrypi/common-hal/audiobusio/PDMIn.c msgid "Only 8 or 16 bit mono with " @@ -1600,15 +1612,15 @@ msgstr "" msgid "Operation not permitted" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Operation or feature not supported" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Operation timed out" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Out of memory" msgstr "" @@ -1783,10 +1795,14 @@ msgstr "" msgid "Read-only object" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Received response was invalid" msgstr "" +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Reconnecting" +msgstr "" + #: shared-bindings/displayio/EPaperDisplay.c msgid "Refresh too soon" msgstr "" @@ -1799,7 +1815,7 @@ msgstr "" msgid "Requested AES mode is unsupported" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Requested resource not found" msgstr "" @@ -2221,7 +2237,7 @@ msgstr "" msgid "Value length > max_length" msgstr "" -#: ports/espressif/bindings/espidf/__init__.c ports/espressif/esp_error.c +#: ports/espressif/common-hal/espidf/__init__.c ports/espressif/esp_error.c msgid "Version was invalid" msgstr "" diff --git a/locale/pt_BR.po b/locale/pt_BR.po index 3ddb5d65a8..f56153ccfb 100644 --- a/locale/pt_BR.po +++ b/locale/pt_BR.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2022-07-24 22:22+0000\n" +"PO-Revision-Date: 2022-08-05 14:18+0000\n" "Last-Translator: Wellington Terumi Uemura \n" "Language-Team: \n" "Language: pt_BR\n" @@ -3860,7 +3860,7 @@ msgstr "o pow() com 3 argumentos requer números inteiros" #: ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.h msgid "pressing BOOT button at start up.\n" -msgstr "" +msgstr "pressionando o botão BOOT na inicialização.\n" #: ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h msgid "pressing SW38 button at start up.\n" @@ -3868,7 +3868,7 @@ msgstr "pressionando o botão SW38 na inicialização.\n" #: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h msgid "pressing VOLUME button at start up.\n" -msgstr "" +msgstr "pressionando o botão VOLUME na inicialização.\n" #: ports/espressif/boards/adafruit_qtpy_esp32c3/mpconfigboard.h #: ports/espressif/boards/beetle-esp32-c3/mpconfigboard.h diff --git a/locale/sv.po b/locale/sv.po index e5ae4df26e..82a66f44da 100644 --- a/locale/sv.po +++ b/locale/sv.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2022-07-21 20:15+0000\n" +"PO-Revision-Date: 2022-08-05 14:18+0000\n" "Last-Translator: Jonny Bergdahl \n" "Language-Team: LANGUAGE \n" "Language: sv\n" @@ -3819,7 +3819,7 @@ msgstr "pow() med 3 argument kräver heltal" #: ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.h msgid "pressing BOOT button at start up.\n" -msgstr "" +msgstr "genom att trycka på BOOT-knappen vid start.\n" #: ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h msgid "pressing SW38 button at start up.\n" @@ -3827,7 +3827,7 @@ msgstr "genom att trycka på SW38-knappen vid start.\n" #: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h msgid "pressing VOLUME button at start up.\n" -msgstr "" +msgstr "genom att trycka på VOLUME-knappen vid start.\n" #: ports/espressif/boards/adafruit_qtpy_esp32c3/mpconfigboard.h #: ports/espressif/boards/beetle-esp32-c3/mpconfigboard.h diff --git a/locale/zh_Latn_pinyin.po b/locale/zh_Latn_pinyin.po index b98cab412f..dca177c139 100644 --- a/locale/zh_Latn_pinyin.po +++ b/locale/zh_Latn_pinyin.po @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: circuitpython-cn\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2022-04-23 21:13+0000\n" -"Last-Translator: hexthat \n" +"PO-Revision-Date: 2022-08-05 14:18+0000\n" +"Last-Translator: River Wang \n" "Language-Team: Chinese Hanyu Pinyin\n" "Language: zh_Latn_pinyin\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.12.1-dev\n" +"X-Generator: Weblate 4.14-dev\n" #: main.c msgid "" @@ -634,9 +634,9 @@ msgid "Buffer must be a multiple of 512 bytes" msgstr "Huǎnchōngqū bìxū shì 512 zìjié de bèishù" #: shared-bindings/_bleio/PacketBuffer.c -#, fuzzy, c-format +#, c-format msgid "Buffer too short by %d bytes" -msgstr "Huǎn chōng qū tài duǎn , àn %d zì jié" +msgstr "Huǎnchōngqū tàiduǎn , mùqián zhǐyǒu %d zìjié" #: ports/espressif/common-hal/imagecapture/ParallelImageCapture.c msgid "Buffers must be same size" @@ -742,15 +742,15 @@ msgstr "tōng guò USB kě jiàn shí wú fǎ chóng xīn ān zhuāng '/'." #: ports/cxd56/common-hal/microcontroller/__init__.c #: ports/mimxrt10xx/common-hal/microcontroller/__init__.c msgid "Cannot reset into bootloader because no bootloader is present" -msgstr "" +msgstr "wúfǎ chóngxīn qǐdòng dào yǐdǎo chéngxù, yīnwéi yǐdǎo chéngxù bù cúnzài." #: ports/espressif/common-hal/socketpool/Socket.c msgid "Cannot set socket options" -msgstr "wú fǎ shè zhì tào jiē zì xuǎn xiàng" +msgstr "wúfǎ shèzhì tàojiēzì xuǎnxiàng" #: shared-bindings/digitalio/DigitalInOut.c msgid "Cannot set value when direction is input." -msgstr "Dāng fāngxiàng xiàng nèi shí, bùnéng shèzhì gāi zhí." +msgstr "Dāng fāngxiàng wéi shūrù shí, bùnéng shèzhì zhí." #: ports/espressif/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c diff --git a/main.c b/main.c index d310f571a6..f17731950f 100644 --- a/main.c +++ b/main.c @@ -204,6 +204,22 @@ STATIC void stop_mp(void) { gc_deinit(); } +STATIC const char *_current_executing_filename = NULL; + +STATIC pyexec_result_t _exec_result = {0, MP_OBJ_NULL, 0}; + +void supervisor_execution_status(void) { + mp_obj_exception_t *exception = MP_OBJ_TO_PTR(_exec_result.exception); + if (_current_executing_filename != NULL) { + serial_write(_current_executing_filename); + } else if ((_exec_result.return_code & PYEXEC_EXCEPTION) != 0 && + exception != NULL) { + mp_printf(&mp_plat_print, "@%d %q", _exec_result.exception_line, exception->base.type->name); + } else { + serial_write_compressed(translate("Done")); + } +} + #define STRING_LIST(...) {__VA_ARGS__, ""} // Look for the first file that exists in the list of filenames, using mp_import_stat(). @@ -218,17 +234,23 @@ STATIC const char *first_existing_file_in_list(const char *const *filenames) { return NULL; } -STATIC bool maybe_run_list(const char *const *filenames, pyexec_result_t *exec_result) { - const char *filename = first_existing_file_in_list(filenames); - if (filename == NULL) { +STATIC bool maybe_run_list(const char *const *filenames) { + _exec_result.return_code = 0; + _exec_result.exception = MP_OBJ_NULL; + _exec_result.exception_line = 0; + _current_executing_filename = first_existing_file_in_list(filenames); + if (_current_executing_filename == NULL) { return false; } - mp_hal_stdout_tx_str(filename); + mp_hal_stdout_tx_str(_current_executing_filename); serial_write_compressed(translate(" output:\n")); - pyexec_file(filename, exec_result); + supervisor_title_bar_update(); + pyexec_file(_current_executing_filename, &_exec_result); #if CIRCUITPY_ATEXIT - shared_module_atexit_execute(exec_result); + shared_module_atexit_execute(&_exec_result); #endif + _current_executing_filename = NULL; + supervisor_title_bar_update(); return true; } @@ -347,12 +369,6 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re } #endif - pyexec_result_t result; - - result.return_code = 0; - result.exception = MP_OBJ_NULL; - result.exception_line = 0; - bool skip_repl = false; bool skip_wait = false; bool found_main = false; @@ -391,7 +407,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re if (((next_code_info_t *)next_code_allocation->ptr)->filename[0] != '\0') { const char *next_list[] = {((next_code_info_t *)next_code_allocation->ptr)->filename, ""}; // This is where the user's python code is actually executed: - found_main = maybe_run_list(next_list, &result); + found_main = maybe_run_list(next_list); if (!found_main) { serial_write(((next_code_info_t *)next_code_allocation->ptr)->filename); serial_write_compressed(translate(" not found.\n")); @@ -401,11 +417,11 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re // Otherwise, default to the standard list of filenames if (!found_main) { // This is where the user's python code is actually executed: - found_main = maybe_run_list(supported_filenames, &result); + found_main = maybe_run_list(supported_filenames); // If that didn't work, double check the extensions #if CIRCUITPY_FULL_BUILD if (!found_main) { - found_main = maybe_run_list(double_extension_filenames, &result); + found_main = maybe_run_list(double_extension_filenames); if (found_main) { serial_write_compressed(translate("WARNING: Your code filename has two extensions\n")); } @@ -417,7 +433,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re // Print done before resetting everything so that we get the message over // BLE before it is reset and we have a delay before reconnect. - if ((result.return_code & PYEXEC_RELOAD) && supervisor_get_run_reason() == RUN_REASON_AUTO_RELOAD) { + if ((_exec_result.return_code & PYEXEC_RELOAD) && supervisor_get_run_reason() == RUN_REASON_AUTO_RELOAD) { serial_write_compressed(translate("\nCode stopped by auto-reload. Reloading soon.\n")); } else { serial_write_compressed(translate("\nCode done running.\n")); @@ -425,7 +441,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re // Finished executing python code. Cleanup includes filesystem flush and a board reset. - cleanup_after_vm(heap, result.exception); + cleanup_after_vm(heap, _exec_result.exception); // If a new next code file was set, that is a reason to keep it (obviously). Stuff this into // the options because it can be treated like any other reason-for-stickiness bit. The @@ -436,7 +452,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re next_code_options |= SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET; } - if (result.return_code & PYEXEC_RELOAD) { + if (_exec_result.return_code & PYEXEC_RELOAD) { next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD; // Reload immediately unless the reload is due to autoreload. In that // case, we wait below to see if any other writes occur. @@ -444,7 +460,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re skip_repl = true; skip_wait = true; } - } else if (result.return_code == 0) { + } else if (_exec_result.return_code == 0) { next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS; if (next_code_options & SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS) { skip_repl = true; @@ -455,12 +471,12 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re // Deep sleep cannot be skipped // TODO: settings in deep sleep should persist, using a new sleep memory API if (next_code_options & SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_ERROR - && !(result.return_code & PYEXEC_DEEP_SLEEP)) { + && !(_exec_result.return_code & PYEXEC_DEEP_SLEEP)) { skip_repl = true; skip_wait = true; } } - if (result.return_code & PYEXEC_FORCED_EXIT) { + if (_exec_result.return_code & PYEXEC_FORCED_EXIT) { skip_repl = false; skip_wait = true; } @@ -478,12 +494,12 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re uint8_t blink_count; bool led_active = false; #if CIRCUITPY_ALARM - if (result.return_code & PYEXEC_DEEP_SLEEP) { + if (_exec_result.return_code & PYEXEC_DEEP_SLEEP) { color = BLACK; blink_count = 0; } else #endif - if (result.return_code != PYEXEC_EXCEPTION) { + if (_exec_result.return_code != PYEXEC_EXCEPTION) { if (safe_mode == NO_SAFE_MODE) { color = ALL_DONE; blink_count = ALL_DONE_BLINKS; @@ -568,7 +584,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool first_run, bool *simulate_re // Sleep until our next interrupt. #if CIRCUITPY_ALARM - if (result.return_code & PYEXEC_DEEP_SLEEP) { + if (_exec_result.return_code & PYEXEC_DEEP_SLEEP) { const bool awoke_from_true_deep_sleep = common_hal_mcu_processor_get_reset_reason() == RESET_REASON_DEEP_SLEEP_ALARM; @@ -730,10 +746,10 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { usb_set_defaults(); #endif - pyexec_result_t result = {0, MP_OBJ_NULL, 0}; - if (ok_to_run) { #ifdef CIRCUITPY_BOOT_OUTPUT_FILE + // Turn off title bar updates when writing out to boot_out.txt. + supervisor_title_bar_suspend(); vstr_t boot_text; vstr_init(&boot_text, 512); boot_output = &boot_text; @@ -741,8 +757,17 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { // Write version info mp_printf(&mp_plat_print, "%s\nBoard ID:%s\n", MICROPY_FULL_VERSION_INFO, CIRCUITPY_BOARD_ID); + #if CIRCUITPY_MICROCONTROLLER && COMMON_HAL_MCU_PROCESSOR_UID_LENGTH > 0 + uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; + common_hal_mcu_processor_get_uid(raw_id); + mp_printf(&mp_plat_print, "UID:"); + for (uint8_t i = 0; i < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH; i++) { + mp_printf(&mp_plat_print, "%02X", raw_id[i]); + } + mp_printf(&mp_plat_print, "\n"); + #endif - bool found_boot = maybe_run_list(boot_py_filenames, &result); + bool found_boot = maybe_run_list(boot_py_filenames); (void)found_boot; @@ -752,6 +777,7 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { FATFS *fs = &vfs->fatfs; boot_output = NULL; + supervisor_title_bar_resume(); bool write_boot_output = true; FIL boot_output_file; if (f_open(fs, &boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { @@ -792,7 +818,11 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { usb_get_boot_py_data(usb_boot_py_data, size); #endif - cleanup_after_vm(heap, result.exception); + port_post_boot_py(true); + + cleanup_after_vm(heap, _exec_result.exception); + + port_post_boot_py(false); #if CIRCUITPY_USB // Now give back the data we saved from the heap going away. @@ -827,7 +857,11 @@ STATIC int run_repl(bool first_run) { exit_code = pyexec_raw_repl(); supervisor_title_bar_resume(); } else { + _current_executing_filename = "REPL"; + supervisor_title_bar_update(); exit_code = pyexec_friendly_repl(); + _current_executing_filename = NULL; + supervisor_title_bar_update(); } #if CIRCUITPY_ATEXIT pyexec_result_t result; @@ -837,6 +871,13 @@ STATIC int run_repl(bool first_run) { } #endif cleanup_after_vm(heap, MP_OBJ_SENTINEL); + + // Also reset bleio. The above call omits it in case workflows should continue. In this case, + // we're switching straight to another VM so we want to reset. + #if CIRCUITPY_BLEIO + bleio_reset(); + #endif + #if CIRCUITPY_STATUS_LED status_led_init(); new_status_color(BLACK); diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index a0a211edb0..4405766d00 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -164,6 +164,9 @@ CFLAGS += \ -msoft-float \ -mfloat-abi=soft \ -DSAMD21 +LIBS := libs/libgcc-12.1.0-Os-v6-m-nofp.a -lc +else +LIBS := -lgcc -lc endif ifeq ($(CHIP_FAMILY), samd51) CFLAGS += \ @@ -200,7 +203,6 @@ endif CFLAGS += -Wno-stringop-overread -Wno-stringop-overflow LDFLAGS = $(CFLAGS) -nostartfiles -Wl,-nostdlib -Wl,-T,$(GENERATED_LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections -specs=nano.specs -LIBS := -lgcc -lc # Use toolchain libm if we're not using our own. ifndef INTERNAL_LIBM diff --git a/ports/atmel-samd/libs/libgcc-12.1.0-Os-v6-m-nofp.a b/ports/atmel-samd/libs/libgcc-12.1.0-Os-v6-m-nofp.a new file mode 100644 index 0000000000..56692d5d05 Binary files /dev/null and b/ports/atmel-samd/libs/libgcc-12.1.0-Os-v6-m-nofp.a differ diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index febc6764bb..e111fa3441 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -281,6 +281,8 @@ ifneq ($(CIRCUITPY_BLEIO),0) SRC_C += common-hal/_bleio/ble_events.c endif +SRC_C += $(wildcard common-hal/espidf/*.c) + SRC_COMMON_HAL_EXPANDED = \ $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ $(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \ diff --git a/ports/espressif/bindings/espidf/__init__.c b/ports/espressif/bindings/espidf/__init__.c index 4f7da39540..c73e494f6c 100644 --- a/ports/espressif/bindings/espidf/__init__.c +++ b/ports/espressif/bindings/espidf/__init__.c @@ -120,6 +120,20 @@ const mp_obj_type_t mp_type_espidf_MemoryError = { .parent = &mp_type_MemoryError, }; +//| def get_total_psram() -> int: +//| """Returns the number of bytes of psram detected, or 0 if psram is not present or not configured""" +STATIC mp_obj_t espidf_get_total_psram(void) { + return MP_OBJ_NEW_SMALL_INT(common_hal_espidf_get_total_psram()); +} +MP_DEFINE_CONST_FUN_OBJ_0(espidf_get_total_psram_obj, espidf_get_total_psram); + +//| def get_reserved_psram() -> int: +//| """Returns number of bytes of psram reserved for use by esp-idf, either a board-specific default value or the value defined in ``/.env``.""" +STATIC mp_obj_t espidf_get_reserved_psram(void) { + return MP_OBJ_NEW_SMALL_INT(common_hal_espidf_get_reserved_psram()); +} +MP_DEFINE_CONST_FUN_OBJ_0(espidf_get_reserved_psram_obj, espidf_get_reserved_psram); + STATIC const mp_rom_map_elem_t espidf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_espidf) }, @@ -129,6 +143,8 @@ STATIC const mp_rom_map_elem_t espidf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_erase_nvs), MP_ROM_PTR(&espidf_erase_nvs_obj)}, + { MP_ROM_QSTR(MP_QSTR_get_total_psram), MP_ROM_PTR(&espidf_get_total_psram_obj)}, + { MP_ROM_QSTR(MP_QSTR_get_reserved_psram), MP_ROM_PTR(&espidf_get_reserved_psram_obj)}, { MP_ROM_QSTR(MP_QSTR_IDFError), MP_ROM_PTR(&mp_type_espidf_IDFError) }, { MP_ROM_QSTR(MP_QSTR_MemoryError), MP_ROM_PTR(&mp_type_espidf_MemoryError) }, }; @@ -139,66 +155,3 @@ const mp_obj_module_t espidf_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&espidf_module_globals, }; - -void raise_esp_error(esp_err_t err) { - const compressed_string_t *msg = NULL; - const mp_obj_type_t *exception_type = &mp_type_espidf_IDFError; - switch (err) { - case ESP_FAIL: - msg = translate("Generic Failure"); - break; - case ESP_ERR_NO_MEM: - exception_type = &mp_type_espidf_MemoryError; - msg = translate("Out of memory"); - break; - case ESP_ERR_INVALID_ARG: - msg = translate("Invalid argument"); - break; - case ESP_ERR_INVALID_STATE: - msg = translate("Invalid state"); - break; - case ESP_ERR_INVALID_SIZE: - msg = translate("Invalid size"); - break; - case ESP_ERR_NOT_FOUND: - msg = translate("Requested resource not found"); - break; - case ESP_ERR_NOT_SUPPORTED: - msg = translate("Operation or feature not supported"); - break; - case ESP_ERR_TIMEOUT: - msg = translate("Operation timed out"); - break; - case ESP_ERR_INVALID_RESPONSE: - msg = translate("Received response was invalid"); - break; - case ESP_ERR_INVALID_CRC: - msg = translate("CRC or checksum was invalid"); - break; - case ESP_ERR_INVALID_VERSION: - msg = translate("Version was invalid"); - break; - case ESP_ERR_INVALID_MAC: - msg = translate("MAC address was invalid"); - break; - } - if (msg) { - mp_raise_msg(exception_type, msg); - } - - const char *group = "ESP-IDF"; - - // tests must be in descending order - MP_STATIC_ASSERT(ESP_ERR_FLASH_BASE > ESP_ERR_MESH_BASE); - MP_STATIC_ASSERT(ESP_ERR_MESH_BASE > ESP_ERR_WIFI_BASE); - if (err >= ESP_ERR_FLASH_BASE) { - group = "Flash"; - } else if (err >= ESP_ERR_MESH_BASE) { - group = "Mesh"; - } else if (err >= ESP_ERR_WIFI_BASE) { - group = "WiFi"; - } - mp_raise_msg_varg(exception_type, translate("%s error 0x%x"), group, err); -} - -MP_REGISTER_MODULE(MP_QSTR_espidf, espidf_module, CIRCUITPY_ESPIDF); diff --git a/ports/espressif/bindings/espidf/__init__.h b/ports/espressif/bindings/espidf/__init__.h index 5891d2c5c0..e091a5c477 100644 --- a/ports/espressif/bindings/espidf/__init__.h +++ b/ports/espressif/bindings/espidf/__init__.h @@ -39,4 +39,11 @@ NORETURN void mp_raise_espidf_MemoryError(void); void raise_esp_error(esp_err_t err) NORETURN; #define CHECK_ESP_RESULT(x) do { int res = (x); if (res != ESP_OK) raise_esp_error(res); } while (0) +void common_hal_espidf_reserve_psram(void); +bool common_hal_espidf_set_reserved_psram(size_t amount); +size_t common_hal_espidf_get_reserved_psram(void); +size_t common_hal_espidf_get_total_psram(void); +intptr_t common_hal_espidf_get_psram_start(void); +intptr_t common_hal_espidf_get_psram_end(void); + #endif // MICROPY_INCLUDED_ESPRESSIF_BINDINGS_ESPIDF___INIT___H diff --git a/ports/espressif/boards/mixgo_ce_serial/board.c b/ports/espressif/boards/mixgo_ce_serial/board.c index e07fe5cfd7..32cb1749ca 100644 --- a/ports/espressif/boards/mixgo_ce_serial/board.c +++ b/ports/espressif/boards/mixgo_ce_serial/board.c @@ -31,6 +31,7 @@ #include "lib/oofatfs/ff.h" #include "extmod/vfs_fat.h" #include "py/mpstate.h" +#include "supervisor/filesystem.h" void board_init(void) { // Debug UART @@ -41,7 +42,7 @@ void board_init(void) { mp_import_stat_t stat_b = mp_import_stat("boot.py"); if (stat_b != MP_IMPORT_STAT_FILE) { - FATFS *fatfs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fatfs = filesystem_circuitpy(); FIL fs; UINT char_written = 0; const byte buffer[] = "#Serial port upload mode\nimport storage\nstorage.remount(\"/\", False)\nstorage.disable_usb_drive()\n"; diff --git a/ports/espressif/common-hal/espidf/__init__.c b/ports/espressif/common-hal/espidf/__init__.c new file mode 100644 index 0000000000..183ebb3817 --- /dev/null +++ b/ports/espressif/common-hal/espidf/__init__.c @@ -0,0 +1,194 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * 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 "bindings/espidf/__init__.h" +#include "supervisor/shared/translate/translate.h" +#include "supervisor/memory.h" +#include "py/runtime.h" + +#include "esp_log.h" +#define TAG "espidf" + +#ifdef CONFIG_SPIRAM +#include "esp32/spiram.h" +#include "esp_heap_caps.h" +#include "esp_heap_caps_init.h" +#include "soc/soc.h" +#ifdef CONFIG_IDF_TARGET_ESP32 +#include "esp32/himem.h" +#else +#define esp_himem_reserved_area_size() (0) +#endif +bool ok_to_reserve_psram = true; +size_t reserved_psram = DEFAULT_RESERVED_PSRAM; +#endif + +static size_t psram_size_usable(void) { + #ifdef CONFIG_SPIRAM + /* PSRAM chip may be larger than the size we can map into address space */ + size_t s = MIN(esp_spiram_get_size(), SOC_EXTRAM_DATA_SIZE); + return s - esp_himem_reserved_area_size(); + #else + return 0; + #endif +} + +bool common_hal_espidf_set_reserved_psram(size_t amount) { + #ifdef CONFIG_SPIRAM + if (!esp_spiram_is_initialized()) { + return false; + } + if (!ok_to_reserve_psram) { + return false; + } + if (amount > psram_size_usable()) { + return false; + } + reserved_psram = amount; + return true; + #else + return false; + #endif +} + +supervisor_allocation *psram_for_idf; + +void common_hal_espidf_reserve_psram(void) { + #ifdef CONFIG_SPIRAM + if (!psram_for_idf) { + ESP_LOGI(TAG, "Reserving %d bytes of psram", reserved_psram); + if (reserved_psram == 0) { + return; + } + psram_for_idf = allocate_memory(reserved_psram, true, false); + if (psram_for_idf) { + intptr_t psram_for_idf_start = (intptr_t)psram_for_idf->ptr; + intptr_t psram_for_idf_end = psram_for_idf_start + reserved_psram; + ESP_LOGI(TAG, "Reserved %x..%x", psram_for_idf_start, psram_for_idf_end); + heap_caps_add_region(psram_for_idf_start, psram_for_idf_end); + } else { + ESP_LOGE(TAG, "supervisor allocation failed"); + } + } + #endif +} + +size_t common_hal_espidf_get_reserved_psram(void) { + #ifdef CONFIG_SPIRAM + return reserved_psram; + #else + return 0; + #endif +} + +size_t common_hal_espidf_get_total_psram(void) { + return psram_size_usable(); +} + +intptr_t common_hal_espidf_get_psram_start(void) { + #ifdef CONFIG_SPIRAM + if (esp_spiram_is_initialized()) { + #ifdef CONFIG_IDF_TARGET_ESP32 + return SOC_EXTRAM_DATA_LOW; + #else + return SOC_EXTRAM_DATA_HIGH - psram_size_usable(); + #endif + } + #endif + return 0; +} + +intptr_t common_hal_espidf_get_psram_end(void) { + #ifdef CONFIG_SPIRAM + if (esp_spiram_is_initialized()) { + return common_hal_espidf_get_psram_start() + psram_size_usable(); + } + #endif + return 0; +} + +void raise_esp_error(esp_err_t err) { + const compressed_string_t *msg = NULL; + const mp_obj_type_t *exception_type = &mp_type_espidf_IDFError; + switch (err) { + case ESP_FAIL: + msg = translate("Generic Failure"); + break; + case ESP_ERR_NO_MEM: + exception_type = &mp_type_espidf_MemoryError; + msg = translate("Out of memory"); + break; + case ESP_ERR_INVALID_ARG: + msg = translate("Invalid argument"); + break; + case ESP_ERR_INVALID_STATE: + msg = translate("Invalid state"); + break; + case ESP_ERR_INVALID_SIZE: + msg = translate("Invalid size"); + break; + case ESP_ERR_NOT_FOUND: + msg = translate("Requested resource not found"); + break; + case ESP_ERR_NOT_SUPPORTED: + msg = translate("Operation or feature not supported"); + break; + case ESP_ERR_TIMEOUT: + msg = translate("Operation timed out"); + break; + case ESP_ERR_INVALID_RESPONSE: + msg = translate("Received response was invalid"); + break; + case ESP_ERR_INVALID_CRC: + msg = translate("CRC or checksum was invalid"); + break; + case ESP_ERR_INVALID_VERSION: + msg = translate("Version was invalid"); + break; + case ESP_ERR_INVALID_MAC: + msg = translate("MAC address was invalid"); + break; + } + if (msg) { + mp_raise_msg(exception_type, msg); + } + + const char *group = "ESP-IDF"; + + // tests must be in descending order + MP_STATIC_ASSERT(ESP_ERR_FLASH_BASE > ESP_ERR_MESH_BASE); + MP_STATIC_ASSERT(ESP_ERR_MESH_BASE > ESP_ERR_WIFI_BASE); + if (err >= ESP_ERR_FLASH_BASE) { + group = "Flash"; + } else if (err >= ESP_ERR_MESH_BASE) { + group = "Mesh"; + } else if (err >= ESP_ERR_WIFI_BASE) { + group = "WiFi"; + } + mp_raise_msg_varg(exception_type, translate("%s error 0x%x"), group, err); +} + +MP_REGISTER_MODULE(MP_QSTR_espidf, espidf_module, CIRCUITPY_ESPIDF); diff --git a/ports/espressif/common-hal/espidf/__init__.h b/ports/espressif/common-hal/espidf/__init__.h new file mode 100644 index 0000000000..0d168b105c --- /dev/null +++ b/ports/espressif/common-hal/espidf/__init__.h @@ -0,0 +1,29 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * 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. + */ + +#pragma once + +#include "bindings/espidf/__init__.h" diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 72a9b32478..632a7a13cd 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -90,4 +90,13 @@ #define CIRCUITPY_ESP_USB_SERIAL_JTAG (0) #endif +#ifndef DEFAULT_RESERVED_PSRAM +#define DEFAULT_RESERVED_PSRAM (0) +#endif + +#if defined(CONFIG_SPIRAM) +#undef CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS +#define CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS (1) +#endif + #endif // MICROPY_INCLUDED_ESPRESSIF_MPCONFIGPORT_H diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 84ff736c47..65bdf00901 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -29,11 +29,13 @@ #include #include "supervisor/board.h" #include "supervisor/port.h" +#include "supervisor/filesystem.h" #include "py/runtime.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "bindings/espidf/__init__.h" #include "common-hal/microcontroller/Pin.h" #include "common-hal/analogio/AnalogOut.h" #include "common-hal/busio/I2C.h" @@ -53,6 +55,7 @@ #include "shared-bindings/microcontroller/RunMode.h" #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/socketpool/__init__.h" +#include "shared-module/dotenv/__init__.h" #include "peripherals/rmt.h" #include "peripherals/timer.h" @@ -95,20 +98,8 @@ #include "esp32/rom/efuse.h" #endif -#ifdef CONFIG_SPIRAM -#include "esp32/spiram.h" -#ifdef CONFIG_IDF_TARGET_ESP32 -#include "esp32/himem.h" -#else -#define esp_himem_reserved_area_size() (0) -#endif - -static size_t spiram_size_usable(void) { - /* SPIRAM chip may be larger than the size we can map into address space */ - size_t s = MIN(esp_spiram_get_size(), SOC_EXTRAM_DATA_SIZE); - return s - esp_himem_reserved_area_size(); -} -#endif +#include "esp_log.h" +#define TAG "port" uint32_t *heap; uint32_t heap_size; @@ -302,14 +293,16 @@ safe_mode_t port_init(void) { #endif #ifdef CONFIG_SPIRAM - if (esp_spiram_is_initialized()) { - size_t spiram_size = spiram_size_usable(); - #ifdef CONFIG_IDF_TARGET_ESP32 - heap = (uint32_t *)SOC_EXTRAM_DATA_LOW; - #else - heap = (uint32_t *)(SOC_EXTRAM_DATA_HIGH - spiram_size); - #endif - heap_size = spiram_size / sizeof(uint32_t); + { + intptr_t heap_start = common_hal_espidf_get_psram_start(); + intptr_t heap_end = common_hal_espidf_get_psram_end(); + size_t spiram_size = heap_end - heap_start; + if (spiram_size > 0) { + heap = (uint32_t *)heap_start; + heap_size = (heap_end - heap_start) / sizeof(uint32_t); + } else { + ESP_LOGE(TAG, "CONFIG_SPIRAM enabled but no spiram heap available"); + } } #endif @@ -518,6 +511,16 @@ void port_idle_until_interrupt(void) { } } +void port_post_boot_py(bool heap_valid) { + if (!heap_valid && filesystem_present()) { + mp_int_t reserved; + if (dotenv_get_key_int("/.env", "CIRCUITPY_RESERVED_PSRAM", &reserved)) { + common_hal_espidf_set_reserved_psram(reserved); + } + common_hal_espidf_reserve_psram(); + } +} + // Wrap main in app_main that the IDF expects. extern void main(void); extern void app_main(void); diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index 5cebf2fa0f..49b5d638f3 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.c +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -449,15 +449,23 @@ bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_addre return sd_ble_gap_addr_set(&local_address) == NRF_SUCCESS; } +uint16_t bleio_adapter_get_name(char *buf, uint16_t len) { + uint16_t full_len = 0; + sd_ble_gap_device_name_get(NULL, &full_len); + + uint32_t err_code = sd_ble_gap_device_name_get((uint8_t *)buf, &len); + if (err_code != NRF_SUCCESS) { + return 0; + } + return full_len; +} + mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) { uint16_t len = 0; sd_ble_gap_device_name_get(NULL, &len); - uint8_t buf[len]; - uint32_t err_code = sd_ble_gap_device_name_get(buf, &len); - if (err_code != NRF_SUCCESS) { - return NULL; - } - return mp_obj_new_str((char *)buf, len); + char buf[len]; + bleio_adapter_get_name(buf, len); + return mp_obj_new_str(buf, len); } void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name) { diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 950cf91e2e..acdf08e6a4 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -564,6 +564,10 @@ void supervisor_run_background_tasks_if_tick(void); #error "CIRCUITPY_USB_HID_MAX_REPORT_IDS_PER_DESCRIPTOR must be at least 1" #endif +#ifndef CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS +#define CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS (0) +#endif + #ifndef USB_MIDI_EP_NUM_OUT #define USB_MIDI_EP_NUM_OUT (0) #endif diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 4d7d026aa5..6b6f629e18 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -369,7 +369,7 @@ CFLAGS += -DCIRCUITPY_SSL=$(CIRCUITPY_SSL) CIRCUITPY_STAGE ?= 0 CFLAGS += -DCIRCUITPY_STAGE=$(CIRCUITPY_STAGE) -CIRCUITPY_STATUS_BAR ?= $(CIRCUITPY_WEB_WORKFLOW) +CIRCUITPY_STATUS_BAR ?= 1 CFLAGS += -DCIRCUITPY_STATUS_BAR=$(CIRCUITPY_STATUS_BAR) CIRCUITPY_STORAGE ?= 1 diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c index ce1080934e..53bd6be3c2 100644 --- a/shared-bindings/_bleio/Adapter.c +++ b/shared-bindings/_bleio/Adapter.c @@ -158,10 +158,10 @@ MP_PROPERTY_GETSET(bleio_adapter_address_obj, //| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``, //| to make it easy to distinguish multiple CircuitPython boards.""" //| -STATIC mp_obj_t bleio_adapter_get_name(mp_obj_t self) { +STATIC mp_obj_t _bleio_adapter_get_name(mp_obj_t self) { return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_name(self)); } -MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_name_obj, bleio_adapter_get_name); +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_name_obj, _bleio_adapter_get_name); STATIC mp_obj_t bleio_adapter_set_name(mp_obj_t self, mp_obj_t new_name) { diff --git a/shared-bindings/_bleio/Adapter.h b/shared-bindings/_bleio/Adapter.h index 1dce615a40..49bb4421e7 100644 --- a/shared-bindings/_bleio/Adapter.h +++ b/shared-bindings/_bleio/Adapter.h @@ -50,6 +50,9 @@ extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self); extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self); extern bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address); +// Copies the adapter name into the given buffer up to len and returns the full length (may be more +// than len if the buffer was too short.) +uint16_t bleio_adapter_get_name(char *buf, uint16_t len); extern mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self); extern void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name); diff --git a/shared-bindings/supervisor/Runtime.c b/shared-bindings/supervisor/Runtime.c index 55217ec97a..bbfcb05b51 100644 --- a/shared-bindings/supervisor/Runtime.c +++ b/shared-bindings/supervisor/Runtime.c @@ -33,6 +33,8 @@ #include "shared-bindings/supervisor/RunReason.h" #include "shared-bindings/supervisor/Runtime.h" +#include "supervisor/shared/reload.h" + #if (CIRCUITPY_USB) #include "tusb.h" #endif @@ -105,7 +107,7 @@ void supervisor_set_run_reason(supervisor_run_reason_t run_reason) { } //| run_reason: RunReason -//| """Returns why CircuitPython started running this particular time.""" +//| """Why CircuitPython started running this particular time.""" //| STATIC mp_obj_t supervisor_runtime_get_run_reason(mp_obj_t self) { return cp_enum_find(&supervisor_run_reason_type, _run_reason); @@ -115,11 +117,34 @@ MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_run_reason_obj, supervisor_runt MP_PROPERTY_GETTER(supervisor_runtime_run_reason_obj, (mp_obj_t)&supervisor_runtime_get_run_reason_obj); +//| autoreload: bool +//| """Whether CircuitPython may autoreload based on workflow writes to the filesystem.""" +//| +STATIC mp_obj_t supervisor_runtime_get_autoreload(mp_obj_t self) { + return mp_obj_new_bool(autoreload_is_enabled()); +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_autoreload_obj, supervisor_runtime_get_autoreload); + +STATIC mp_obj_t supervisor_runtime_set_autoreload(mp_obj_t self, mp_obj_t state_in) { + if (mp_obj_is_true(state_in)) { + autoreload_enable(); + } else { + autoreload_disable(); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(supervisor_runtime_set_autoreload_obj, supervisor_runtime_set_autoreload); + +MP_PROPERTY_GETSET(supervisor_runtime_autoreload_obj, + (mp_obj_t)&supervisor_runtime_get_autoreload_obj, + (mp_obj_t)&supervisor_runtime_set_autoreload_obj); + STATIC const mp_rom_map_elem_t supervisor_runtime_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_usb_connected), MP_ROM_PTR(&supervisor_runtime_usb_connected_obj) }, { MP_ROM_QSTR(MP_QSTR_serial_connected), MP_ROM_PTR(&supervisor_runtime_serial_connected_obj) }, { MP_ROM_QSTR(MP_QSTR_serial_bytes_available), MP_ROM_PTR(&supervisor_runtime_serial_bytes_available_obj) }, { MP_ROM_QSTR(MP_QSTR_run_reason), MP_ROM_PTR(&supervisor_runtime_run_reason_obj) }, + { MP_ROM_QSTR(MP_QSTR_autoreload), MP_ROM_PTR(&supervisor_runtime_autoreload_obj) }, }; STATIC MP_DEFINE_CONST_DICT(supervisor_runtime_locals_dict, supervisor_runtime_locals_dict_table); diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index cad8ccdeec..c74b3751c0 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -53,27 +53,6 @@ //| This object is the sole instance of `supervisor.Runtime`.""" //| -//| def enable_autoreload() -> None: -//| """Enable autoreload based on USB file write activity.""" -//| ... -//| -STATIC mp_obj_t supervisor_enable_autoreload(void) { - autoreload_enable(); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(supervisor_enable_autoreload_obj, supervisor_enable_autoreload); - -//| def disable_autoreload() -> None: -//| """Disable autoreload based on USB file write activity until -//| `enable_autoreload` is called.""" -//| ... -//| -STATIC mp_obj_t supervisor_disable_autoreload(void) { - autoreload_disable(); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(supervisor_disable_autoreload_obj, supervisor_disable_autoreload); - //| def set_rgb_status_brightness(brightness: int) -> None: //| """Set brightness of status RGB LED from 0-255. This will take effect //| after the current code finishes and the status LED is used to show @@ -312,8 +291,6 @@ MP_DEFINE_CONST_FUN_OBJ_2(supervisor_reset_terminal_obj, supervisor_reset_termin STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) }, - { MP_ROM_QSTR(MP_QSTR_enable_autoreload), MP_ROM_PTR(&supervisor_enable_autoreload_obj) }, - { MP_ROM_QSTR(MP_QSTR_disable_autoreload), MP_ROM_PTR(&supervisor_disable_autoreload_obj) }, { MP_ROM_QSTR(MP_QSTR_set_rgb_status_brightness), MP_ROM_PTR(&supervisor_set_rgb_status_brightness_obj) }, { MP_ROM_QSTR(MP_QSTR_runtime), MP_ROM_PTR(&common_hal_supervisor_runtime_obj) }, { MP_ROM_QSTR(MP_QSTR_reload), MP_ROM_PTR(&supervisor_reload_obj) }, diff --git a/shared-module/dotenv/__init__.c b/shared-module/dotenv/__init__.c index bd1973366d..1837cef2f5 100644 --- a/shared-module/dotenv/__init__.c +++ b/shared-module/dotenv/__init__.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include #include #include "shared-bindings/dotenv/__init__.h" @@ -32,6 +33,7 @@ #include "extmod/vfs_fat.h" #include "py/mpstate.h" #include "py/objstr.h" +#include "supervisor/filesystem.h" STATIC uint8_t consume_spaces(FIL *active_file) { uint8_t character = ' '; @@ -188,7 +190,7 @@ STATIC mp_int_t read_value(FIL *active_file, char *value, size_t value_len) { mp_int_t dotenv_get_key(const char *path, const char *key, char *value, mp_int_t value_len) { FIL active_file; - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fs = filesystem_circuitpy(); FRESULT result = f_open(fs, &active_file, path, FA_READ); if (result != FR_OK) { return -1; @@ -223,3 +225,26 @@ mp_obj_t common_hal_dotenv_get_key(const char *path, const char *key) { } return mp_obj_new_str(value, actual_len); } + +bool dotenv_get_key_terminated(const char *path, const char *key, char *value, mp_int_t value_len) { + mp_int_t actual_len = dotenv_get_key(path, key, value, value_len - 1); + if (actual_len >= value_len) { + return false; + } + value[actual_len] = '\0'; // terminate string + return true; +} + +bool dotenv_get_key_int(const char *path, const char *key, mp_int_t *value) { + char buf[16]; + if (!dotenv_get_key_terminated(path, key, buf, (mp_int_t)sizeof(buf))) { + return false; + } + char *end; + long result = strtol(buf, &end, 0); + if (end == buf || *end) { // If the whole buffer was not consumed it's an error + return false; + } + *value = (mp_int_t)result; + return true; +} diff --git a/shared-module/dotenv/__init__.h b/shared-module/dotenv/__init__.h index 6cfb209324..fb27233692 100644 --- a/shared-module/dotenv/__init__.h +++ b/shared-module/dotenv/__init__.h @@ -26,3 +26,11 @@ // Allocation free version that returns the full length of the value. mp_int_t dotenv_get_key(const char *path, const char *key, char *value, mp_int_t value_len); + +// Returns true and sets value to a '\0'-terminated string if key is present +// and the value (including the terminating '\0') fits strictly within +// value_len bytes. +bool dotenv_get_key_terminated(const char *path, const char *key, char *value, mp_int_t value_len); + +// Returns true and sets value to the read value. Returns false if the value was not numeric. +bool dotenv_get_key_int(const char *path, const char *key, mp_int_t *value); diff --git a/supervisor/filesystem.h b/supervisor/filesystem.h index 6f4faf0b82..3954291513 100644 --- a/supervisor/filesystem.h +++ b/supervisor/filesystem.h @@ -45,4 +45,6 @@ void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concu bool filesystem_is_writable_by_python(fs_user_mount_t *vfs); bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs); +FATFS *filesystem_circuitpy(void); + #endif // MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H diff --git a/supervisor/port.h b/supervisor/port.h index d693d49440..0d6862fae8 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -109,4 +109,10 @@ void port_wake_main_task(void); // default weak implementation is provided that does nothing. void port_wake_main_task_from_isr(void); +// Some ports need special handling just after completing boot.py execution. +// This function is called once while boot.py's VM is still valid, and +// then a second time after the VM is finalized. +// A default weak implementation is provided that does nothing. +void port_post_boot_py(bool heap_valid); + #endif // MICROPY_INCLUDED_SUPERVISOR_PORT_H diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index 426aad9891..a007219e4d 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -39,8 +39,11 @@ #include "common-hal/_bleio/__init__.h" +#include "supervisor/serial.h" #include "supervisor/shared/status_leds.h" #include "supervisor/shared/tick.h" +#include "supervisor/shared/title_bar.h" +#include "supervisor/shared/translate/translate.h" #include "py/mpstate.h" @@ -75,18 +78,13 @@ const uint8_t public_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags const uint8_t private_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags 0x02, 0x0a, 0x00 // 3-5 TX power level 0 }; -// This scan response advertises the full CIRCPYXXXX device name. -uint8_t circuitpython_scan_response_data[] = { - 0x0a, 0x09, 0x43, 0x49, 0x52, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00, - #if CIRCUITPY_SERIAL_BLE - 0x11, 0x06, 0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x01, 0x00, 0xaf, 0xad - #endif -}; - +// This scan response advertises the full device name (if it fits.) +uint8_t circuitpython_scan_response_data[31]; #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE STATIC bool boot_in_discovery_mode = false; STATIC bool advertising = false; +STATIC bool _private_advertising = false; STATIC bool ble_started = false; #define WORKFLOW_UNSET 0 @@ -96,6 +94,36 @@ STATIC bool ble_started = false; STATIC uint8_t workflow_state = WORKFLOW_UNSET; STATIC bool was_connected = false; +// To detect when the title bar changes. +STATIC bool _last_connected = false; +STATIC bool _last_advertising = false; + +// Title bar status +bool supervisor_bluetooth_status_dirty(void) { + return _last_advertising != advertising || + _last_connected != was_connected; +} + +void supervisor_bluetooth_status(void) { + serial_write("BLE:"); + if (advertising) { + if (_private_advertising) { + serial_write_compressed(translate("Reconnecting")); + } else { + const char *name = (char *)circuitpython_scan_response_data + 2; + int len = MIN(strlen(name), sizeof(circuitpython_scan_response_data) - 2); + serial_write_substring(name, len); + } + } else if (was_connected) { + serial_write_compressed(translate("Ok")); + } else { + serial_write_compressed(translate("Off")); + } + + _last_connected = was_connected; + _last_advertising = advertising; +} + STATIC void supervisor_bluetooth_start_advertising(void) { if (workflow_state != WORKFLOW_ENABLED) { return; @@ -118,6 +146,7 @@ STATIC void supervisor_bluetooth_start_advertising(void) { size_t adv_len = sizeof(private_advertising_data); const uint8_t *scan_response = NULL; size_t scan_response_len = 0; + _private_advertising = true; // Advertise with less power when doing so publicly to reduce who can hear us. This will make it // harder for someone with bad intentions to pair from a distance. if (!bonded) { @@ -126,6 +155,18 @@ STATIC void supervisor_bluetooth_start_advertising(void) { adv_len = sizeof(public_advertising_data); scan_response = circuitpython_scan_response_data; scan_response_len = sizeof(circuitpython_scan_response_data); + uint16_t max_name_len = sizeof(circuitpython_scan_response_data) - 2; + uint16_t name_len = bleio_adapter_get_name((char *)circuitpython_scan_response_data + 2, + max_name_len); + if (name_len > max_name_len) { + circuitpython_scan_response_data[0] = max_name_len + 1; + circuitpython_scan_response_data[1] = 0x8; + } else { + circuitpython_scan_response_data[0] = name_len + 1; + circuitpython_scan_response_data[1] = 0x9; + } + scan_response_len = circuitpython_scan_response_data[0] + 1; + _private_advertising = false; } uint32_t status = _common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj, true, @@ -232,10 +273,15 @@ void supervisor_bluetooth_background(void) { supervisor_bluetooth_file_transfer_disconnected(); #endif } + if (was_connected != is_connected) { + supervisor_title_bar_request_update(false); + } was_connected = is_connected; if (!is_connected) { supervisor_bluetooth_start_advertising(); return; + } else { + advertising = false; } #if CIRCUITPY_BLE_FILE_SERVICE @@ -247,7 +293,7 @@ void supervisor_bluetooth_background(void) { void supervisor_start_bluetooth(void) { #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE - if (workflow_state != WORKFLOW_ENABLED) { + if (workflow_state != WORKFLOW_ENABLED || ble_started) { return; } @@ -266,6 +312,7 @@ void supervisor_start_bluetooth(void) { // Kick off advertisements supervisor_bluetooth_background(); + supervisor_title_bar_request_update(false); #endif } @@ -277,6 +324,8 @@ void supervisor_stop_bluetooth(void) { return; } + ble_started = false; + #if CIRCUITPY_SERIAL_BLE supervisor_stop_bluetooth_serial(); #endif diff --git a/supervisor/shared/bluetooth/bluetooth.h b/supervisor/shared/bluetooth/bluetooth.h index 9de82719a5..231fde3c54 100644 --- a/supervisor/shared/bluetooth/bluetooth.h +++ b/supervisor/shared/bluetooth/bluetooth.h @@ -38,4 +38,8 @@ void supervisor_stop_bluetooth(void); void supervisor_bluetooth_enable_workflow(void); void supervisor_bluetooth_disable_workflow(void); +// Title bar status +bool supervisor_bluetooth_status_dirty(void); +void supervisor_bluetooth_status(void); + #endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H diff --git a/supervisor/shared/bluetooth/file_transfer.c b/supervisor/shared/bluetooth/file_transfer.c index a6a2f8062a..216cf3d567 100644 --- a/supervisor/shared/bluetooth/file_transfer.c +++ b/supervisor/shared/bluetooth/file_transfer.c @@ -43,6 +43,7 @@ #include "common-hal/_bleio/__init__.h" #include "supervisor/fatfs_port.h" +#include "supervisor/filesystem.h" #include "supervisor/shared/reload.h" #include "supervisor/shared/bluetooth/file_transfer.h" #include "supervisor/shared/bluetooth/file_transfer_protocol.h" @@ -172,7 +173,7 @@ STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) { char *path = (char *)((uint8_t *)command) + header_size; path[command->path_length] = '\0'; - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fs = filesystem_circuitpy(); FRESULT result = f_open(fs, &active_file, path, FA_READ); if (result != FR_OK) { response.status = STATUS_ERROR; @@ -289,7 +290,7 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) { return ANY_COMMAND; } - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fs = filesystem_circuitpy(); DWORD fattime; _truncated_time = truncate_time(command->modification_time, &fattime); override_fattime(fattime); @@ -438,7 +439,7 @@ STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) { if (command_len < header_size + command->path_length) { return THIS_COMMAND; } - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fs = filesystem_circuitpy(); char *path = (char *)((uint8_t *)command) + header_size; path[command->path_length] = '\0'; FILINFO file; @@ -495,7 +496,7 @@ STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) { if (command_len < header_size + command->path_length) { return THIS_COMMAND; } - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fs = filesystem_circuitpy(); char *path = (char *)command->path; _terminate_path(path, command->path_length); @@ -552,7 +553,7 @@ STATIC uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) { return THIS_COMMAND; } - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fs = filesystem_circuitpy(); char *path = (char *)&command->path; _terminate_path(path, command->path_length); // mp_printf(&mp_plat_print, "list %s\n", path); @@ -640,7 +641,7 @@ STATIC uint8_t _process_move(const uint8_t *raw_buf, size_t command_len) { if (command_len < header_size + total_path_length) { return THIS_COMMAND; } - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fs = filesystem_circuitpy(); char *old_path = (char *)command->paths; old_path[command->old_path_length] = '\0'; diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index 849b9c3fe2..fdd0a0341a 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -34,6 +34,7 @@ #include "shared-bindings/displayio/Palette.h" #include "shared-bindings/displayio/TileGrid.h" #include "supervisor/memory.h" +#include "supervisor/shared/title_bar.h" #if CIRCUITPY_RGBMATRIX #include "shared-module/displayio/__init__.h" @@ -65,7 +66,7 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { displayio_tilegrid_t *title_bar = &supervisor_terminal_title_bar_text_grid; bool reset_tiles = false; uint16_t width_in_tiles = width_px / scroll_area->tile_width; - // determine scale based on h + // determine scale based on width if (width_in_tiles < 80) { scale = 1; } @@ -103,9 +104,9 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { uint8_t *tiles = (uint8_t *)tilegrid_tiles->ptr; #if CIRCUITPY_REPL_LOGO - title_bar->x = blinka_bitmap.width; + title_bar->x = supervisor_blinka_sprite.pixel_width + 1; // Align the title bar to the bottom of the logo. - title_bar->y = blinka_bitmap.height - title_bar->tile_height; + title_bar->y = supervisor_blinka_sprite.pixel_height - title_bar->tile_height; #else title_bar->x = 0; title_bar->y = 0; @@ -120,11 +121,6 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { title_bar->full_change = true; scroll_area->x = 0; - #if CIRCUITPY_REPL_LOGO - scroll_area->y = blinka_bitmap.height; - #else - scroll_area->y = scroll_area->tile_height; - #endif scroll_area->top_left_y = 0; scroll_area->width_in_tiles = width_in_tiles; scroll_area->height_in_tiles = height_in_tiles - 1; @@ -132,10 +128,21 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { assert(height_in_tiles > 1); scroll_area->pixel_width = width_in_tiles * scroll_area->tile_width; scroll_area->pixel_height = (height_in_tiles - 1) * scroll_area->tile_height; + #if CIRCUITPY_REPL_LOGO + scroll_area->y = blinka_bitmap.height; + #else + scroll_area->y = title_bar->tile_height; + #endif + int16_t extra_height = (scroll_area->pixel_height + scroll_area->y) - height_px; + // Subtract extra height so that the bottom line fully shows. The top line will be under the + // title bar and Blinka logo. + scroll_area->y -= extra_height; scroll_area->tiles = tiles + width_in_tiles; scroll_area->full_change = true; common_hal_terminalio_terminal_construct(&supervisor_terminal, scroll_area, &supervisor_terminal_font, title_bar); + // Update the title bar since we just cleared the terminal. + supervisor_title_bar_update(); } #endif @@ -186,126 +193,9 @@ void supervisor_display_move_memory(void) { #endif } -#if CIRCUITPY_REPL_LOGO -uint32_t blinka_bitmap_data[32] = { - 0x00000011, 0x11000000, - 0x00000111, 0x53100000, - 0x00000111, 0x56110000, - 0x00000111, 0x11140000, - 0x00000111, 0x20002000, - 0x00000011, 0x13000000, - 0x00000001, 0x11200000, - 0x00000000, 0x11330000, - 0x00000000, 0x01122000, - 0x00001111, 0x44133000, - 0x00032323, 0x24112200, - 0x00111114, 0x44113300, - 0x00323232, 0x34112200, - 0x11111144, 0x44443300, - 0x11111111, 0x11144401, - 0x23232323, 0x21111110 -}; - -displayio_bitmap_t blinka_bitmap = { - .base = {.type = &displayio_bitmap_type }, - .width = 16, - .height = 16, - .data = blinka_bitmap_data, - .stride = 2, - .bits_per_value = 4, - .x_shift = 3, - .x_mask = 0x7, - .bitmask = 0xf, - .read_only = true -}; - -_displayio_color_t blinka_colors[7] = { - { - .rgb888 = 0x000000, - .rgb565 = 0x0000, - .luma = 0x00, - .chroma = 0, - .transparent = true - }, - { - .rgb888 = 0x8428bc, - .rgb565 = 0x8978, - .luma = 0xff, // We cheat the luma here. It is actually 0x60 - .hue = 184, - .chroma = 148 - }, - { - .rgb888 = 0xff89bc, - .rgb565 = 0xFCB8, - .luma = 0xb5, - .hue = 222, - .chroma = 118 - }, - { - .rgb888 = 0x7beffe, - .rgb565 = 0x869F, - .luma = 0xe0, - .hue = 124, - .chroma = 131 - }, - { - .rgb888 = 0x51395f, - .rgb565 = 0x5A0D, - .luma = 0x47, - .hue = 185, - .chroma = 38 - }, - { - .rgb888 = 0xffffff, - .rgb565 = 0xffff, - .luma = 0xff, - .chroma = 0 - }, - { - .rgb888 = 0x0736a0, - .rgb565 = 0x01f5, - .luma = 0x44, - .hue = 147, - .chroma = 153 - }, -}; - -displayio_palette_t blinka_palette = { - .base = {.type = &displayio_palette_type }, - .colors = blinka_colors, - .color_count = 7, - .needs_refresh = false -}; - -displayio_tilegrid_t blinka_sprite = { - .base = {.type = &displayio_tilegrid_type }, - .bitmap = &blinka_bitmap, - .pixel_shader = &blinka_palette, - .x = 0, - .y = 0, - .pixel_width = 16, - .pixel_height = 16, - .bitmap_width_in_tiles = 1, - .width_in_tiles = 1, - .height_in_tiles = 1, - .tile_width = 16, - .tile_height = 16, - .top_left_x = 16, - .top_left_y = 16, - .tiles = 0, - .partial_change = false, - .full_change = false, - .hidden = false, - .hidden_by_parent = false, - .moved = false, - .inline_tiles = true, - .in_group = true -}; -#endif - #if CIRCUITPY_TERMINALIO #if CIRCUITPY_REPL_LOGO -mp_obj_t members[] = { &blinka_sprite, &supervisor_terminal_title_bar_text_grid, &supervisor_terminal_scroll_area_text_grid, }; +mp_obj_t members[] = { &supervisor_terminal_scroll_area_text_grid, &supervisor_blinka_sprite, &supervisor_terminal_title_bar_text_grid, }; mp_obj_list_t splash_children = { .base = {.type = &mp_type_list }, .alloc = 3, @@ -313,7 +203,7 @@ mp_obj_list_t splash_children = { .items = members, }; #else -mp_obj_t members[] = { &supervisor_terminal_title_bar_text_grid, &supervisor_terminal_scroll_area_text_grid, }; +mp_obj_t members[] = { &supervisor_terminal_scroll_area_text_grid, &supervisor_terminal_title_bar_text_grid,}; mp_obj_list_t splash_children = { .base = {.type = &mp_type_list }, .alloc = 2, @@ -323,7 +213,7 @@ mp_obj_list_t splash_children = { #endif #else #if CIRCUITPY_REPL_LOGO -mp_obj_t members[] = { &blinka_sprite }; +mp_obj_t members[] = { &supervisor_blinka_sprite }; mp_obj_list_t splash_children = { .base = {.type = &mp_type_list }, .alloc = 1, diff --git a/supervisor/shared/display.h b/supervisor/shared/display.h index d965fdc764..a7e28a8f75 100644 --- a/supervisor/shared/display.h +++ b/supervisor/shared/display.h @@ -29,10 +29,11 @@ #include +#include "shared-bindings/displayio/TileGrid.h" + #if CIRCUITPY_TERMINALIO #include "shared-bindings/displayio/Bitmap.h" -#include "shared-bindings/displayio/TileGrid.h" #include "shared-bindings/fontio/BuiltinFont.h" #include "shared-bindings/terminalio/Terminal.h" @@ -46,9 +47,11 @@ extern displayio_bitmap_t supervisor_terminal_font_bitmap; extern displayio_tilegrid_t supervisor_terminal_scroll_area_text_grid; extern displayio_tilegrid_t supervisor_terminal_title_bar_text_grid; extern terminalio_terminal_obj_t supervisor_terminal; - #endif +// Always shown. +extern displayio_tilegrid_t supervisor_blinka_sprite; + void supervisor_start_terminal(uint16_t width_px, uint16_t height_px); void supervisor_stop_terminal(void); diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index 820dbe9783..283849adc3 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -92,6 +92,9 @@ bool filesystem_init(bool create_allowed, bool force_create) { vfs_fat->blockdev.flags = 0; supervisor_flash_init_vfs(vfs_fat); + mp_vfs_mount_t *vfs = &_mp_vfs; + vfs->len = 0; + // try to mount the flash FRESULT res = f_mount(&vfs_fat->fatfs); if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) { @@ -140,7 +143,6 @@ bool filesystem_init(bool create_allowed, bool force_create) { } else if (res != FR_OK) { return false; } - mp_vfs_mount_t *vfs = &_mp_vfs; vfs->str = "/"; vfs->len = 1; vfs->obj = MP_OBJ_FROM_PTR(vfs_fat); @@ -199,5 +201,12 @@ void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concu } bool filesystem_present(void) { - return true; + return _mp_vfs.len > 0; +} + +FATFS *filesystem_circuitpy(void) { + if (!filesystem_present()) { + return NULL; + } + return &_internal_vfs.fatfs; } diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c index ff0382fc17..06c568b42e 100644 --- a/supervisor/shared/memory.c +++ b/supervisor/shared/memory.c @@ -56,6 +56,8 @@ enum { #if CIRCUITPY_USB_VENDOR + 1 // usb_vendor_add_descriptor #endif + + + CIRCUITPY_PORT_NUM_SUPERVISOR_ALLOCATIONS , CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT = diff --git a/supervisor/shared/title_bar.c b/supervisor/shared/title_bar.c index 7fa3116b47..9326a1f9ed 100644 --- a/supervisor/shared/title_bar.c +++ b/supervisor/shared/title_bar.c @@ -34,11 +34,47 @@ #if CIRCUITPY_WEB_WORKFLOW #include "supervisor/shared/web_workflow/web_workflow.h" #endif + +#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE +#include "supervisor/shared/bluetooth/bluetooth.h" +#endif + static background_callback_t title_bar_background_cb; static bool _forced_dirty = false; static bool _suspended = false; + +void supervisor_title_bar_update(void) { + #if !CIRCUITPY_STATUS_BAR + return; + #endif + if (_suspended) { + supervisor_title_bar_request_update(true); + return; + } + _forced_dirty = false; + // Neighboring "" "" are concatenated by the compiler. Without this separation, the hex code + // doesn't get terminated after two following characters and the value is invalid. + // This is the OSC command to set the title and the icon text. It can be up to 255 characters + // but some may be cut off. + serial_write("\x1b" "]0;"); + serial_write("🐍"); + #if CIRCUITPY_WEB_WORKFLOW + supervisor_web_workflow_status(); + serial_write(" | "); + #endif + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE + supervisor_bluetooth_status(); + serial_write(" | "); + #endif + supervisor_execution_status(); + serial_write(" | "); + serial_write(MICROPY_GIT_TAG); + // Send string terminator + serial_write("\x1b" "\\"); +} + static void title_bar_background(void *data) { #if !CIRCUITPY_STATUS_BAR return; @@ -52,23 +88,14 @@ static void title_bar_background(void *data) { dirty = dirty || supervisor_web_workflow_status_dirty(); #endif + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE + dirty = dirty || supervisor_bluetooth_status_dirty(); + #endif + if (!dirty) { return; } - _forced_dirty = false; - // Neighboring "" "" are concatenated by the compiler. Without this separation, the hex code - // doesn't get terminated after two following characters and the value is invalid. - // This is the OSC command to set the title and the icon text. It can be up to 255 characters - // but some may be cut off. - serial_write("\x1b" "]0;"); - serial_write("🐍 "); - #if CIRCUITPY_WEB_WORKFLOW - supervisor_web_workflow_status(); - #endif - serial_write("|"); - serial_write(MICROPY_GIT_TAG); - // Send string terminator - serial_write("\x1b" "\\"); + supervisor_title_bar_update(); } void supervisor_title_bar_start(void) { diff --git a/supervisor/shared/title_bar.h b/supervisor/shared/title_bar.h index 778d768086..092b5add32 100644 --- a/supervisor/shared/title_bar.h +++ b/supervisor/shared/title_bar.h @@ -31,4 +31,14 @@ void supervisor_title_bar_start(void); void supervisor_title_bar_suspend(void); void supervisor_title_bar_resume(void); + +// Update the title bar immediately. Useful from main.c where we know state has changed and the code +// will only be run once. +void supervisor_title_bar_update(void); + +// Use this if requesting from the background, as code is executing or if the status may not have +// changed. void supervisor_title_bar_request_update(bool force_dirty); + +// Provided by main.c +void supervisor_execution_status(void); diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 9e8c32d963..bc76112ed3 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -36,6 +36,7 @@ #include "shared-module/storage/__init__.h" #include "shared/timeutils/timeutils.h" #include "supervisor/fatfs_port.h" +#include "supervisor/filesystem.h" #include "supervisor/shared/reload.h" #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/web_workflow/web_workflow.h" @@ -195,10 +196,20 @@ bool supervisor_web_workflow_status_dirty(void) { } void supervisor_web_workflow_status(void) { - serial_write_compressed(translate("Wi-Fi: ")); _last_enabled = common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj); if (_last_enabled) { uint32_t ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj); + if (ipv4_address != 0) { + _update_encoded_ip(); + _last_ip = _encoded_ip; + mp_printf(&mp_plat_print, "%s", _our_ip_encoded); + if (web_api_port != 80) { + mp_printf(&mp_plat_print, ":%d", web_api_port); + } + // TODO: Use these unicode to show signal strength: ▂▄▆█ + return; + } + serial_write_compressed(translate("Wi-Fi: ")); _last_wifi_status = _wifi_status; if (_wifi_status == WIFI_RADIO_ERROR_AUTH_EXPIRE || _wifi_status == WIFI_RADIO_ERROR_AUTH_FAIL) { @@ -209,15 +220,10 @@ void supervisor_web_workflow_status(void) { _last_ip = 0; serial_write_compressed(translate("No IP")); } else { - _update_encoded_ip(); - _last_ip = _encoded_ip; - mp_printf(&mp_plat_print, "%s", _our_ip_encoded); - if (web_api_port != 80) { - mp_printf(&mp_plat_print, ":%d", web_api_port); - } - // TODO: Use these unicode to show signal strength: ▂▄▆█ } } else { + // Keep Wi-Fi print separate so its data can be matched with the one above. + serial_write_compressed(translate("Wi-Fi: ")); serial_write_compressed(translate("off")); } } @@ -575,7 +581,7 @@ static void _reply_redirect(socketpool_socket_obj_t *socket, _request *request, lwip_setsockopt(socket->num, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)); const char *hostname = common_hal_mdns_server_get_hostname(&mdns); _send_strs(socket, - "HTTP/1.1 301 Moved Permanently\r\n", + "HTTP/1.1 307 Temporary Redirect\r\n", "Connection: close\r\n", "Content-Length: 0\r\n", "Location: ", NULL); @@ -979,7 +985,7 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) { } else { char *path = request->path + 3; size_t pathlen = strlen(path); - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FATFS *fs = filesystem_circuitpy(); // Trailing / is a directory. bool directory = false; if (path[pathlen - 1] == '/') { diff --git a/supervisor/stub/misc.c b/supervisor/stub/misc.c new file mode 100644 index 0000000000..a17188beff --- /dev/null +++ b/supervisor/stub/misc.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * 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 "stdbool.h" + +#include "supervisor/port.h" +#include "py/mpconfig.h" + + +MP_WEAK void port_post_boot_py(bool heap_valid) { +} diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 912b5c5f37..cbfaae507e 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -11,14 +11,15 @@ SRC_SUPERVISOR = \ supervisor/shared/port.c \ supervisor/shared/reload.c \ supervisor/shared/safe_mode.c \ - supervisor/shared/serial.c \ + supervisor/shared/serial.c \ supervisor/shared/stack.c \ supervisor/shared/status_leds.c \ supervisor/shared/tick.c \ supervisor/shared/title_bar.c \ supervisor/shared/traceback.c \ supervisor/shared/translate/translate.c \ - supervisor/shared/workflow.c + supervisor/shared/workflow.c \ + supervisor/stub/misc.c \ NO_USB ?= $(wildcard supervisor/usb.c) @@ -184,9 +185,8 @@ ifeq ($(CIRCUITPY_DISPLAYIO), 1) SRC_SUPERVISOR += \ supervisor/shared/display.c - ifeq ($(CIRCUITPY_TERMINALIO), 1) - SUPERVISOR_O += $(BUILD)/autogen_display_resources-$(TRANSLATION).o - endif + # Include the display resources because it includes the Blinka logo as well. + SUPERVISOR_O += $(BUILD)/autogen_display_resources-$(TRANSLATION).o endif # Preserve double quotes in these values by single-quoting them. diff --git a/tools/gen_display_resources.py b/tools/gen_display_resources.py index dc9dbd69a4..5da6062823 100644 --- a/tools/gen_display_resources.py +++ b/tools/gen_display_resources.py @@ -104,14 +104,170 @@ c_file = args.output_c_file c_file.write( """\ +#include "shared-bindings/displayio/Bitmap.h" #include "shared-bindings/displayio/Palette.h" #include "supervisor/shared/display.h" """ ) + c_file.write( """\ +#if CIRCUITPY_REPL_LOGO +""" +) +if tile_y == 16: + blinka_size = 16 + c_file.write( + """\ +uint32_t blinka_bitmap_data[32] = { + 0x00000011, 0x11000000, + 0x00000111, 0x53100000, + 0x00000111, 0x56110000, + 0x00000111, 0x11140000, + 0x00000111, 0x20002000, + 0x00000011, 0x13000000, + 0x00000001, 0x11200000, + 0x00000000, 0x11330000, + 0x00000000, 0x01122000, + 0x00001111, 0x44133000, + 0x00032323, 0x24112200, + 0x00111114, 0x44113300, + 0x00323232, 0x34112200, + 0x11111144, 0x44443300, + 0x11111111, 0x11144401, + 0x23232323, 0x21111110 +}; +""" + ) +else: + blinka_size = 12 + c_file.write( + """\ +uint32_t blinka_bitmap_data[28] = { + 0x00000111, 0x00000000, + 0x00001153, 0x10000000, + 0x00001156, 0x11000000, + 0x00001111, 0x14000000, + 0x00000112, 0x00200000, + 0x00000011, 0x30000000, + 0x00000011, 0x20000000, + 0x00011144, 0x13000000, + 0x00232324, 0x12000000, + 0x01111444, 0x13000000, + 0x32323234, 0x12010000, + 0x11111144, 0x44100000 +}; +""" + ) + +c_file.write( + """\ +displayio_bitmap_t blinka_bitmap = {{ + .base = {{.type = &displayio_bitmap_type }}, + .width = {0}, + .height = {0}, + .data = blinka_bitmap_data, + .stride = 2, + .bits_per_value = 4, + .x_shift = 3, + .x_mask = 0x7, + .bitmask = 0xf, + .read_only = true +}}; + +_displayio_color_t blinka_colors[7] = {{ + {{ + .rgb888 = 0x000000, + .rgb565 = 0x0000, + .luma = 0x00, + .chroma = 0, + .transparent = true + }}, + {{ + .rgb888 = 0x8428bc, + .rgb565 = 0x8978, + .luma = 0xff, // We cheat the luma here. It is actually 0x60 + .hue = 184, + .chroma = 148 + }}, + {{ + .rgb888 = 0xff89bc, + .rgb565 = 0xFCB8, + .luma = 0xb5, + .hue = 222, + .chroma = 118 + }}, + {{ + .rgb888 = 0x7beffe, + .rgb565 = 0x869F, + .luma = 0xe0, + .hue = 124, + .chroma = 131 + }}, + {{ + .rgb888 = 0x51395f, + .rgb565 = 0x5A0D, + .luma = 0x47, + .hue = 185, + .chroma = 38 + }}, + {{ + .rgb888 = 0xffffff, + .rgb565 = 0xffff, + .luma = 0xff, + .chroma = 0 + }}, + {{ + .rgb888 = 0x0736a0, + .rgb565 = 0x01f5, + .luma = 0x44, + .hue = 147, + .chroma = 153 + }}, +}}; + +displayio_palette_t blinka_palette = {{ + .base = {{.type = &displayio_palette_type }}, + .colors = blinka_colors, + .color_count = 7, + .needs_refresh = false +}}; + +displayio_tilegrid_t supervisor_blinka_sprite = {{ + .base = {{.type = &displayio_tilegrid_type }}, + .bitmap = &blinka_bitmap, + .pixel_shader = &blinka_palette, + .x = 0, + .y = 0, + .pixel_width = {0}, + .pixel_height = {0}, + .bitmap_width_in_tiles = 1, + .width_in_tiles = 1, + .height_in_tiles = 1, + .tile_width = {0}, + .tile_height = {0}, + .top_left_x = {0}, + .top_left_y = {0}, + .tiles = 0, + .partial_change = false, + .full_change = false, + .hidden = false, + .hidden_by_parent = false, + .moved = false, + .inline_tiles = true, + .in_group = true +}}; +#endif +""".format( + blinka_size + ) +) + +c_file.write( + """\ +#if CIRCUITPY_TERMINALIO _displayio_color_t terminal_colors[2] = { { .rgb888 = 0x000000, @@ -260,5 +416,7 @@ terminalio_terminal_obj_t supervisor_terminal = { .scroll_area = NULL, .title_bar = NULL }; + +#endif """ )