diff --git a/README.rst b/README.rst index 801466cbad..1f45007082 100644 --- a/README.rst +++ b/README.rst @@ -120,7 +120,7 @@ Behavior make each file independent from each other. - ``boot.py`` runs only once on start up before - USB is initialized. This lays the ground work for configuring USB at + workflows are initialized. This lays the ground work for configuring USB at startup rather than it being fixed. Since serial is not available, output is written to ``boot_out.txt``. - ``code.py`` (or ``main.py``) is run after every reload until it @@ -135,7 +135,10 @@ Behavior possible to fix code that causes nasty crashes by making it available through mass storage after the crash. A reset (the button) is needed after it's fixed to get back into normal mode. - RGB status LED indicating CircuitPython state. -- Re-runs ``code.py`` or other main file after file system writes over USB mass storage. (Disable with + - One green flash - code completed without error. + - Two red flashes - code ended due to an exception. + - Three yellow flashes - safe mode. May be due to CircuitPython internal error. +- Re-runs ``code.py`` or other main file after file system writes by a workflow. (Disable with ``supervisor.disable_autoreload()``) - Autoreload is disabled while the REPL is active. - Main is one of these: ``code.txt``, ``code.py``, ``main.py``, diff --git a/docs/environment.rst b/docs/environment.rst new file mode 100644 index 0000000000..2e5d81fca8 --- /dev/null +++ b/docs/environment.rst @@ -0,0 +1,40 @@ +Environment Variables +===================== + +CircuitPython 8.0.0 introduces support for environment variables. Environment +variables are commonly used to store "secrets" such as Wi-Fi passwords and API +keys. This method *does not* make them secure. It only separates them from the +code. + +CircuitPython supports these by mimicking the `dotenv `_ +CPython library. Other languages such as Javascript, PHP and Ruby also have +dotenv libraries. + +These libraries store environment variables in a ``.env`` file. Here is a simple +example: + +.. code-block:: bash + + KEY1='value1' + # Comment + KEY2='value2 + is multiple lines' + +CircuitPython uses the ``.env`` at the drive root (no folder) as the environment. +User code can access the values from the file using `os.getenv()`. It is +recommended to save any values used repeatedly in a variable because `os.getenv()` +will parse the ``/.env`` on every access. + +CircuitPython behavior +---------------------- + +CircuitPython will also read the environment to configure its behavior. Other +keys are ignored by CircuitPython. Here are the keys it uses: + +CIRCUITPY_WIFI_PASSWORD +~~~~~~~~~~~~~~~~~~~~~~~ +Wi-Fi password used to auto connect to CIRCUITPY_WIFI_SSID + +CIRCUITPY_WIFI_SSID +~~~~~~~~~~~~~~~~~~~ +Wi-Fi SSID to auto-connect to even if user code is not running. diff --git a/docs/index.rst b/docs/index.rst index 6e9789852e..6a67fe0b7c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,6 +22,7 @@ Full Table of Contents supported_ports.rst troubleshooting.rst drivers.rst + environment.rst .. toctree:: :maxdepth: 1 diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index aa94f2485f..f965506d1b 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1375,6 +1375,10 @@ msgstr "" msgid "No I2C device at address: 0x%x" msgstr "" +#: supervisor/shared/web_workflow/web_workflow.c +msgid "No IP" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/mimxrt10xx/common-hal/busio/SPI.c msgid "No MISO Pin" @@ -2254,6 +2258,10 @@ msgid "" "To list built-in modules type `help(\"modules\")`.\n" msgstr "" +#: supervisor/shared/web_workflow/web_workflow.c +msgid "Wi-Fi: " +msgstr "" + #: main.c msgid "Woken up by alarm.\n" msgstr "" @@ -3579,6 +3587,10 @@ msgstr "" msgid "odd-length string" msgstr "" +#: supervisor/shared/web_workflow/web_workflow.c +msgid "off" +msgstr "" + #: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c msgid "offset is too large" msgstr "" diff --git a/main.c b/main.c index d2b3edb16a..3488899946 100644 --- a/main.c +++ b/main.c @@ -101,6 +101,10 @@ #include "shared-module/memorymonitor/__init__.h" #endif +#if CIRCUITPY_SOCKETPOOL +#include "shared-bindings/socketpool/__init__.h" +#endif + #if CIRCUITPY_USB_HID #include "shared-module/usb_hid/__init__.h" #endif @@ -290,6 +294,16 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, mp_obj_t exception) { keypad_reset(); #endif + // Close user-initiated sockets. + #if CIRCUITPY_SOCKETPOOL + socketpool_user_reset(); + #endif + + // Turn off user initiated WiFi connections. + #if CIRCUITPY_WIFI + wifi_user_reset(); + #endif + // reset_board_buses() first because it may release pins from the never_reset state, so that // reset_port() can reset them. #if CIRCUITPY_BOARD @@ -303,6 +317,9 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, mp_obj_t exception) { stop_mp(); free_memory(heap); supervisor_move_memory(); + + // Let the workflows know we've reset in case they want to restart. + supervisor_workflow_reset(); } STATIC void print_code_py_status_message(safe_mode_t safe_mode) { @@ -889,21 +906,7 @@ int __attribute__((used)) main(void) { run_boot_py(safe_mode); - // Start USB after giving boot.py a chance to tweak behavior. - #if CIRCUITPY_USB - // Setup USB connection after heap is available. - // It needs the heap to build descriptors. - usb_init(); - #endif - - // Set up any other serial connection. - serial_init(); - - #if CIRCUITPY_BLEIO - bleio_reset(); - supervisor_bluetooth_enable_workflow(); - supervisor_start_bluetooth(); - #endif + supervisor_workflow_start(); // Boot script is finished, so now go into REPL or run code.py. int exit_code = PYEXEC_FORCED_EXIT; diff --git a/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c b/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c index 0cfedf9933..f278c7ddbc 100644 --- a/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c +++ b/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c @@ -117,6 +117,10 @@ void board_init(void) { true, // backlight_on_high false, // SH1107_addressing 50000); // backlight pwm frequency + + #if CIRCUITPY_DEBUG + common_hal_never_reset_pin(DEFAULT_UART_BUS_TX); + #endif } bool espressif_board_reset_pin_number(gpio_num_t pin_number) { diff --git a/ports/espressif/common-hal/socketpool/Socket.c b/ports/espressif/common-hal/socketpool/Socket.c index c16be01998..856400a4e7 100644 --- a/ports/espressif/common-hal/socketpool/Socket.c +++ b/ports/espressif/common-hal/socketpool/Socket.c @@ -39,7 +39,7 @@ STATIC socketpool_socket_obj_t *open_socket_handles[CONFIG_LWIP_MAX_SOCKETS]; -void socket_reset(void) { +void socket_user_reset(void) { for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_handles); i++) { if (open_socket_handles[i]) { if (open_socket_handles[i]->num > 0) { diff --git a/ports/espressif/common-hal/socketpool/Socket.h b/ports/espressif/common-hal/socketpool/Socket.h index 943b5d9fdd..2b8ea9fcbf 100644 --- a/ports/espressif/common-hal/socketpool/Socket.h +++ b/ports/espressif/common-hal/socketpool/Socket.h @@ -45,7 +45,7 @@ typedef struct { mp_uint_t timeout_ms; } socketpool_socket_obj_t; -void socket_reset(void); +void socket_user_reset(void); bool register_open_socket(socketpool_socket_obj_t *self); #endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_SOCKETPOOL_SOCKET_H diff --git a/ports/espressif/common-hal/socketpool/__init__.c b/ports/espressif/common-hal/socketpool/__init__.c index fa0e7d5f3f..595977d24f 100644 --- a/ports/espressif/common-hal/socketpool/__init__.c +++ b/ports/espressif/common-hal/socketpool/__init__.c @@ -23,3 +23,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + +#include "shared-bindings/socketpool/__init__.h" + +#include "common-hal/socketpool/Socket.h" + +void socketpool_user_reset(void) { + socket_user_reset(); +} diff --git a/ports/espressif/common-hal/wifi/Radio.c b/ports/espressif/common-hal/wifi/Radio.c index 6451cb1414..e4a9aa4646 100644 --- a/ports/espressif/common-hal/wifi/Radio.c +++ b/ports/espressif/common-hal/wifi/Radio.c @@ -236,6 +236,7 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t if (!common_hal_wifi_radio_get_enabled(self)) { mp_raise_RuntimeError(translate("wifi is not enabled")); } + wifi_config_t *config = &self->sta_config; EventBits_t bits; // can't block since both bits are false after wifi_init @@ -245,16 +246,31 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t pdTRUE, pdTRUE, 0); - if (((bits & WIFI_CONNECTED_BIT) != 0) && - !((bits & WIFI_DISCONNECTED_BIT) != 0)) { - return WIFI_RADIO_ERROR_NONE; + bool connected = ((bits & WIFI_CONNECTED_BIT) != 0) && + !((bits & WIFI_DISCONNECTED_BIT) != 0); + if (connected) { + if (memcmp(ssid, config->sta.ssid, ssid_len) == 0) { + // Already connected to the desired network. + return WIFI_RADIO_ERROR_NONE; + } else { + xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT); + // Trying to switch networks so disconnect first. + esp_wifi_disconnect(); + do { + RUN_BACKGROUND_TASKS; + bits = xEventGroupWaitBits(self->event_group_handle, + WIFI_DISCONNECTED_BIT, + pdTRUE, + pdTRUE, + 0); + } while ((bits & WIFI_DISCONNECTED_BIT) == 0 && !mp_hal_is_interrupted()); + } } // explicitly clear bits since xEventGroupWaitBits may have timed out xEventGroupClearBits(self->event_group_handle, WIFI_CONNECTED_BIT); xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT); set_mode_station(self, true); - wifi_config_t *config = &self->sta_config; memcpy(&config->sta.ssid, ssid, ssid_len); config->sta.ssid[ssid_len] = 0; memcpy(&config->sta.password, password, password_len); @@ -368,6 +384,14 @@ mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) { return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.netmask.addr); } +uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { + if (!esp_netif_is_netif_up(self->netif)) { + return 0; + } + esp_netif_get_ip_info(self->netif, &self->ip_info); + return self->ip_info.ip.addr; +} + mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { if (!esp_netif_is_netif_up(self->netif)) { return mp_const_none; diff --git a/ports/espressif/common-hal/wifi/__init__.c b/ports/espressif/common-hal/wifi/__init__.c index 36cf27a69f..3da0fc7297 100644 --- a/ports/espressif/common-hal/wifi/__init__.c +++ b/ports/espressif/common-hal/wifi/__init__.c @@ -42,6 +42,8 @@ wifi_radio_obj_t common_hal_wifi_radio_obj; #include "components/log/include/esp_log.h" +#include "supervisor/workflow.h" + static const char *TAG = "wifi"; static void event_handler(void *arg, esp_event_base_t event_base, @@ -106,12 +108,19 @@ static void event_handler(void *arg, esp_event_base_t event_base, radio->retries_left = radio->starting_retries; xEventGroupSetBits(radio->event_group_handle, WIFI_CONNECTED_BIT); } + supervisor_workflow_request_background(); } -static bool wifi_inited, wifi_ever_inited; +static bool wifi_inited; +static bool wifi_ever_inited; +static bool wifi_user_initiated; -void common_hal_wifi_init(void) { +void common_hal_wifi_init(bool user_initiated) { + if (wifi_inited) { + return; + } wifi_inited = true; + wifi_user_initiated = user_initiated; common_hal_wifi_radio_obj.base.type = &wifi_radio_type; if (!wifi_ever_inited) { @@ -157,6 +166,12 @@ void common_hal_wifi_init(void) { common_hal_wifi_radio_set_enabled(self, true); } +void wifi_user_reset(void) { + if (wifi_user_initiated) { + wifi_reset(); + } +} + void wifi_reset(void) { if (!wifi_inited) { return; @@ -176,6 +191,7 @@ void wifi_reset(void) { esp_netif_destroy(radio->ap_netif); radio->ap_netif = NULL; wifi_inited = false; + supervisor_workflow_request_background(); } void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address) { diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index bf606ad159..7ecdd140a2 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -282,14 +282,6 @@ void reset_port(void) { #if CIRCUITPY_WATCHDOG watchdog_reset(); #endif - - #if CIRCUITPY_WIFI - wifi_reset(); - #endif - - #if CIRCUITPY_SOCKETPOOL - socket_reset(); - #endif } void reset_to_bootloader(void) { diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 1918938255..c5c89ab19f 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -70,7 +70,9 @@ endif CIRCUITPY_LTO ?= 0 CIRCUITPY_LTO_PARTITION ?= balanced ifeq ($(CIRCUITPY_LTO),1) -CFLAGS += -flto -flto-partition=$(CIRCUITPY_LTO_PARTITION) +CFLAGS += -flto -flto-partition=$(CIRCUITPY_LTO_PARTITION) -DCIRCUITPY_LTO=1 +else +CFLAGS += -DCIRCUITPY_LTO=0 endif # Produce an object file for translate.c instead of including it in a header. diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 69f4c248bc..758e0226a8 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -480,6 +480,9 @@ CFLAGS += -DCIRCUITPY_WATCHDOG=$(CIRCUITPY_WATCHDOG) CIRCUITPY_WIFI ?= 0 CFLAGS += -DCIRCUITPY_WIFI=$(CIRCUITPY_WIFI) +CIRCUITPY_WEB_WORKFLOW ?= $(CIRCUITPY_WIFI) +CFLAGS += -DCIRCUITPY_WEB_WORKFLOW=$(CIRCUITPY_WEB_WORKFLOW) + # tinyusb port tailored configuration CIRCUITPY_TUSB_MEM_ALIGN ?= 4 CFLAGS += -DCIRCUITPY_TUSB_MEM_ALIGN=$(CIRCUITPY_TUSB_MEM_ALIGN) diff --git a/shared-bindings/displayio/TileGrid.h b/shared-bindings/displayio/TileGrid.h index 2b4e42cb46..fb56ba6101 100644 --- a/shared-bindings/displayio/TileGrid.h +++ b/shared-bindings/displayio/TileGrid.h @@ -69,5 +69,6 @@ void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t // Private API for scrolling the TileGrid. void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint16_t x, uint16_t y); +void common_hal_displayio_tilegrid_set_all_tiles(displayio_tilegrid_t *self, uint8_t tile_index); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_TILEGRID_H diff --git a/shared-bindings/socketpool/__init__.h b/shared-bindings/socketpool/__init__.h index a017e96c6d..46034f257d 100644 --- a/shared-bindings/socketpool/__init__.h +++ b/shared-bindings/socketpool/__init__.h @@ -27,4 +27,6 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H #define MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H +void socketpool_user_reset(void); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H diff --git a/shared-bindings/terminalio/Terminal.c b/shared-bindings/terminalio/Terminal.c index c6fd389b12..605ee2727f 100644 --- a/shared-bindings/terminalio/Terminal.c +++ b/shared-bindings/terminalio/Terminal.c @@ -38,31 +38,52 @@ #include "supervisor/shared/translate/translate.h" //| class Terminal: -//| """Display a character stream with a TileGrid""" +//| """Display a character stream with a TileGrid //| -//| def __init__(self, tilegrid: displayio.TileGrid, font: fontio.BuiltinFont) -> None: +//| ASCII control: +//| * ``\\r`` - Move cursor to column 1 +//| * ``\\n`` - Move cursor down a row +//| * ``\\b`` - Move cursor left one if possible +//| +//| OSC control sequences: +//| * ``ESC ] 0; ESC \\`` - Set title bar to +//| * ``ESC ] ####; ESC \\`` - Ignored +//| +//| VT100 control sequences: +//| * ``ESC [ K`` - Clear the remainder of the line +//| * ``ESC [ #### D`` - Move the cursor to the left by #### +//| * ``ESC [ 2 J`` - Erase the entire display +//| * ``ESC [ nnnn ; mmmm H`` - Move the cursor to mmmm, nnnn. +//| """ +//| +//| def __init__(self, scroll_area: displayio.TileGrid, font: fontio.BuiltinFont, *, title_bar: displayio.TileGrid = None) -> None: //| """Terminal manages tile indices and cursor position based on VT100 commands. The font should be //| a `fontio.BuiltinFont` and the TileGrid's bitmap should match the font's bitmap.""" //| ... //| STATIC mp_obj_t terminalio_terminal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_tilegrid, ARG_font }; + enum { ARG_scroll_area, ARG_font, ARG_title_bar }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_tilegrid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_scroll_area, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_font, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_title_bar, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_tilegrid_t *tilegrid = mp_arg_validate_type(args[ARG_tilegrid].u_obj, &displayio_tilegrid_type, MP_QSTR_tilegrid); + displayio_tilegrid_t *scroll_area = mp_arg_validate_type(args[ARG_scroll_area].u_obj, &displayio_tilegrid_type, MP_QSTR_scroll_area); + displayio_tilegrid_t *title_bar = NULL; + if (args[ARG_title_bar].u_obj != mp_const_none) { + title_bar = mp_arg_validate_type(args[ARG_title_bar].u_obj, &displayio_tilegrid_type, MP_QSTR_title_bar); + } fontio_builtinfont_t *font = mp_arg_validate_type(args[ARG_font].u_obj, &fontio_builtinfont_type, MP_QSTR_font); terminalio_terminal_obj_t *self = m_new_obj(terminalio_terminal_obj_t); self->base.type = &terminalio_terminal_type; - common_hal_terminalio_terminal_construct(self, tilegrid, font); + common_hal_terminalio_terminal_construct(self, scroll_area, font, title_bar); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/terminalio/Terminal.h b/shared-bindings/terminalio/Terminal.h index f884edd6d5..64609b49b1 100644 --- a/shared-bindings/terminalio/Terminal.h +++ b/shared-bindings/terminalio/Terminal.h @@ -34,7 +34,7 @@ extern const mp_obj_type_t terminalio_terminal_type; extern void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self, - displayio_tilegrid_t *tilegrid, const fontio_builtinfont_t *font); + displayio_tilegrid_t *scroll_area, const fontio_builtinfont_t *font, displayio_tilegrid_t *title_bar); // Write characters. len is in characters NOT bytes! extern size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, diff --git a/shared-bindings/wifi/Radio.c b/shared-bindings/wifi/Radio.c index cb5090d0f4..0b55df70fa 100644 --- a/shared-bindings/wifi/Radio.c +++ b/shared-bindings/wifi/Radio.c @@ -439,11 +439,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_set_ipv4_address_obj, 1, wifi_radio //| ipv4_address: Optional[ipaddress.IPv4Address] //| """IP v4 Address of the station when connected to an access point. None otherwise.""" //| -STATIC mp_obj_t wifi_radio_get_ipv4_address(mp_obj_t self) { +STATIC mp_obj_t _wifi_radio_get_ipv4_address(mp_obj_t self) { return common_hal_wifi_radio_get_ipv4_address(self); } -MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_address_obj, wifi_radio_get_ipv4_address); +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_address_obj, _wifi_radio_get_ipv4_address); MP_PROPERTY_GETTER(wifi_radio_ipv4_address_obj, (mp_obj_t)&wifi_radio_get_ipv4_address_obj); diff --git a/shared-bindings/wifi/Radio.h b/shared-bindings/wifi/Radio.h index 09a66b2e63..b2593ba8ff 100644 --- a/shared-bindings/wifi/Radio.h +++ b/shared-bindings/wifi/Radio.h @@ -103,6 +103,7 @@ extern mp_obj_t common_hal_wifi_radio_get_ipv4_gateway(wifi_radio_obj_t *self); extern mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self); extern mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self); extern mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self); +uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self); extern mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self); extern mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self); diff --git a/shared-bindings/wifi/__init__.c b/shared-bindings/wifi/__init__.c index 0f9dee8462..f5bfd3a362 100644 --- a/shared-bindings/wifi/__init__.c +++ b/shared-bindings/wifi/__init__.c @@ -42,7 +42,7 @@ // Called when wifi is imported. STATIC mp_obj_t wifi___init__(void) { - common_hal_wifi_init(); + common_hal_wifi_init(true); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(wifi___init___obj, wifi___init__); diff --git a/shared-bindings/wifi/__init__.h b/shared-bindings/wifi/__init__.h index e626727e77..9553e92ce1 100644 --- a/shared-bindings/wifi/__init__.h +++ b/shared-bindings/wifi/__init__.h @@ -31,7 +31,9 @@ extern wifi_radio_obj_t common_hal_wifi_radio_obj; -void common_hal_wifi_init(void); +void common_hal_wifi_init(bool user_initiated); void common_hal_wifi_gc_collect(void); +void wifi_user_reset(void); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI___INIT___H diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index 36cbf76619..769c334ac6 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -296,6 +296,27 @@ void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t self->partial_change = true; } +void common_hal_displayio_tilegrid_set_all_tiles(displayio_tilegrid_t *self, uint8_t tile_index) { + if (tile_index >= self->tiles_in_bitmap) { + mp_raise_ValueError(translate("Tile index out of bounds")); + } + uint8_t *tiles = self->tiles; + if (self->inline_tiles) { + tiles = (uint8_t *)&self->tiles; + } + if (tiles == NULL) { + return; + } + + for (uint16_t x = 0; x < self->width_in_tiles; x++) { + for (uint16_t y = 0; y < self->height_in_tiles; y++) { + tiles[y * self->width_in_tiles + x] = tile_index; + } + } + + self->full_change = true; +} + bool common_hal_displayio_tilegrid_get_flip_x(displayio_tilegrid_t *self) { return self->flip_x; } diff --git a/shared-module/terminalio/Terminal.c b/shared-module/terminalio/Terminal.c index fc33533b17..d45495d14a 100644 --- a/shared-module/terminalio/Terminal.c +++ b/shared-module/terminalio/Terminal.c @@ -30,24 +30,28 @@ #include "shared-bindings/displayio/TileGrid.h" #include "shared-bindings/terminalio/Terminal.h" -void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self, displayio_tilegrid_t *tilegrid, const fontio_builtinfont_t *font) { +void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self, + displayio_tilegrid_t *scroll_area, const fontio_builtinfont_t *font, + displayio_tilegrid_t *title_bar) { self->cursor_x = 0; self->cursor_y = 0; self->font = font; - self->tilegrid = tilegrid; + self->scroll_area = scroll_area; + self->title_bar = title_bar; + self->title_x = 0; + self->title_y = 0; self->first_row = 0; - for (uint16_t x = 0; x < self->tilegrid->width_in_tiles; x++) { - for (uint16_t y = 0; y < self->tilegrid->height_in_tiles; y++) { - common_hal_displayio_tilegrid_set_tile(self->tilegrid, x, y, 0); - } + common_hal_displayio_tilegrid_set_all_tiles(self->scroll_area, 0); + if (self->title_bar) { + common_hal_displayio_tilegrid_set_all_tiles(self->title_bar, 0); } - common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, 1); + common_hal_displayio_tilegrid_set_top_left(self->scroll_area, 0, 1); } size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, const byte *data, size_t len, int *errcode) { // Make sure the terminal is initialized before we do anything with it. - if (self->tilegrid == NULL) { + if (self->scroll_area == NULL) { return len; } const byte *i = data; @@ -55,11 +59,34 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con while (i < data + len) { unichar c = utf8_get_char(i); i = utf8_next_char(i); + if (self->in_osc_command) { + if (c == 0x1b && i[0] == '\\') { + self->in_osc_command = false; + self->title_x = 0; + self->title_y = 0; + i += 1; + } else if (self->osc_command == 0 && self->title_bar != NULL && self->title_y < self->title_bar->height_in_tiles) { + uint8_t tile_index = fontio_builtinfont_get_glyph_index(self->font, c); + if (tile_index != 0xff) { + // Clear the tile grid before we start putting new info. + if (self->title_x == 0 && self->title_y == 0) { + common_hal_displayio_tilegrid_set_all_tiles(self->title_bar, 0); + } + common_hal_displayio_tilegrid_set_tile(self->title_bar, self->title_x, self->title_y, tile_index); + self->title_x++; + if (self->title_x >= self->title_bar->width_in_tiles) { + self->title_y++; + self->title_x %= self->title_bar->width_in_tiles; + } + } + } + continue; + } // Always handle ASCII. if (c < 128) { if (c >= 0x20 && c <= 0x7e) { uint8_t tile_index = fontio_builtinfont_get_glyph_index(self->font, c); - common_hal_displayio_tilegrid_set_tile(self->tilegrid, self->cursor_x, self->cursor_y, tile_index); + common_hal_displayio_tilegrid_set_tile(self->scroll_area, self->cursor_x, self->cursor_y, tile_index); self->cursor_x++; } else if (c == '\r') { self->cursor_x = 0; @@ -71,25 +98,25 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con self->cursor_x--; } } else if (c == 0x1b) { + // Handle commands of the form \x1b.####D where . is ignored. + uint16_t n = 0; + uint8_t j = 1; + for (; j < 6; j++) { + if ('0' <= i[j] && i[j] <= '9') { + n = n * 10 + (i[j] - '0'); + } else { + c = i[j]; + break; + } + } if (i[0] == '[') { if (i[1] == 'K') { // Clear the rest of the line. - for (uint16_t j = self->cursor_x; j < self->tilegrid->width_in_tiles; j++) { - common_hal_displayio_tilegrid_set_tile(self->tilegrid, j, self->cursor_y, 0); + for (uint16_t k = self->cursor_x; k < self->scroll_area->width_in_tiles; k++) { + common_hal_displayio_tilegrid_set_tile(self->scroll_area, k, self->cursor_y, 0); } i += 2; } else { - // Handle commands of the form \x1b[####D - uint16_t n = 0; - uint8_t j = 1; - for (; j < 6; j++) { - if ('0' <= i[j] && i[j] <= '9') { - n = n * 10 + (i[j] - '0'); - } else { - c = i[j]; - break; - } - } if (c == 'D') { if (n > self->cursor_x) { self->cursor_x = 0; @@ -99,14 +126,10 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con } if (c == 'J') { if (n == 2) { - common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, 0); + common_hal_displayio_tilegrid_set_top_left(self->scroll_area, 0, 0); self->cursor_x = self->cursor_y = start_y = 0; n = 0; - for (uint16_t x = 0; x < self->tilegrid->width_in_tiles; x++) { - for (uint16_t y = 0; y < self->tilegrid->height_in_tiles; y++) { - common_hal_displayio_tilegrid_set_tile(self->tilegrid, x, y, 0); - } - } + common_hal_displayio_tilegrid_set_all_tiles(self->scroll_area, 0); } } if (c == ';') { @@ -126,13 +149,13 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con if (m > 0) { m--; } - if (n >= self->tilegrid->height_in_tiles) { - n = self->tilegrid->height_in_tiles - 1; + if (n >= self->scroll_area->height_in_tiles) { + n = self->scroll_area->height_in_tiles - 1; } - if (m >= self->tilegrid->width_in_tiles) { - m = self->tilegrid->width_in_tiles - 1; + if (m >= self->scroll_area->width_in_tiles) { + m = self->scroll_area->width_in_tiles - 1; } - n = (n + self->tilegrid->top_left_y) % self->tilegrid->height_in_tiles; + n = (n + self->scroll_area->top_left_y) % self->scroll_area->height_in_tiles; self->cursor_x = m; self->cursor_y = n; start_y = self->cursor_y; @@ -141,30 +164,34 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con i += j + 1; continue; } + } else if (i[0] == ']' && c == ';') { + self->in_osc_command = true; + self->osc_command = n; + i += j + 1; } } } else { uint8_t tile_index = fontio_builtinfont_get_glyph_index(self->font, c); if (tile_index != 0xff) { - common_hal_displayio_tilegrid_set_tile(self->tilegrid, self->cursor_x, self->cursor_y, tile_index); + common_hal_displayio_tilegrid_set_tile(self->scroll_area, self->cursor_x, self->cursor_y, tile_index); self->cursor_x++; } } - if (self->cursor_x >= self->tilegrid->width_in_tiles) { + if (self->cursor_x >= self->scroll_area->width_in_tiles) { self->cursor_y++; - self->cursor_x %= self->tilegrid->width_in_tiles; + self->cursor_x %= self->scroll_area->width_in_tiles; } - if (self->cursor_y >= self->tilegrid->height_in_tiles) { - self->cursor_y %= self->tilegrid->height_in_tiles; + if (self->cursor_y >= self->scroll_area->height_in_tiles) { + self->cursor_y %= self->scroll_area->height_in_tiles; } if (self->cursor_y != start_y) { // clear the new row in case of scroll up - if (self->cursor_y == self->tilegrid->top_left_y) { - for (uint16_t j = 0; j < self->tilegrid->width_in_tiles; j++) { - common_hal_displayio_tilegrid_set_tile(self->tilegrid, j, self->cursor_y, 0); + if (self->cursor_y == self->scroll_area->top_left_y) { + for (uint16_t j = 0; j < self->scroll_area->width_in_tiles; j++) { + common_hal_displayio_tilegrid_set_tile(self->scroll_area, j, self->cursor_y, 0); } - common_hal_displayio_tilegrid_set_top_left(self->tilegrid, 0, (self->cursor_y + self->tilegrid->height_in_tiles + 1) % self->tilegrid->height_in_tiles); + common_hal_displayio_tilegrid_set_top_left(self->scroll_area, 0, (self->cursor_y + self->scroll_area->height_in_tiles + 1) % self->scroll_area->height_in_tiles); } start_y = self->cursor_y; } @@ -173,5 +200,5 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con } bool common_hal_terminalio_terminal_ready_to_tx(terminalio_terminal_obj_t *self) { - return self->tilegrid != NULL; + return self->scroll_area != NULL; } diff --git a/shared-module/terminalio/Terminal.h b/shared-module/terminalio/Terminal.h index 2ba7e21f72..2e628f5df6 100644 --- a/shared-module/terminalio/Terminal.h +++ b/shared-module/terminalio/Terminal.h @@ -39,8 +39,13 @@ typedef struct { const fontio_builtinfont_t *font; uint16_t cursor_x; uint16_t cursor_y; - displayio_tilegrid_t *tilegrid; + displayio_tilegrid_t *scroll_area; + displayio_tilegrid_t *title_bar; + uint16_t title_x; + uint16_t title_y; uint16_t first_row; + uint16_t osc_command; + bool in_osc_command; } terminalio_terminal_obj_t; #endif /* SHARED_MODULE_TERMINALIO_TERMINAL_H */ diff --git a/supervisor/shared/background_callback.c b/supervisor/shared/background_callback.c index e53edb506f..88ffc15911 100644 --- a/supervisor/shared/background_callback.c +++ b/supervisor/shared/background_callback.c @@ -111,16 +111,28 @@ void background_callback_end_critical_section() { CALLBACK_CRITICAL_END; } + +// Filter out queued callbacks if they are allocated on the heap. void background_callback_reset() { + background_callback_t *new_head = NULL; + background_callback_t **previous_next = &new_head; + background_callback_t *new_tail = NULL; CALLBACK_CRITICAL_BEGIN; background_callback_t *cb = (background_callback_t *)callback_head; while (cb) { background_callback_t *next = cb->next; - memset(cb, 0, sizeof(*cb)); + if (!HEAP_PTR((void *)cb)) { + *previous_next = cb; + previous_next = &cb->next; + cb->next = NULL; + new_tail = cb; + } else { + memset(cb, 0, sizeof(*cb)); + } cb = next; } - callback_head = NULL; - callback_tail = NULL; + callback_head = new_head; + callback_tail = new_tail; in_background_callback = false; CALLBACK_CRITICAL_END; } diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index a8a9b056f2..849b9c3fe2 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -61,37 +61,26 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { uint8_t scale = 2; #if CIRCUITPY_TERMINALIO - displayio_tilegrid_t *grid = &supervisor_terminal_text_grid; - bool tall = height_px > width_px; + displayio_tilegrid_t *scroll_area = &supervisor_terminal_scroll_area_text_grid; + displayio_tilegrid_t *title_bar = &supervisor_terminal_title_bar_text_grid; bool reset_tiles = false; - #if CIRCUITPY_REPL_LOGO - uint16_t terminal_width_px = tall ? width_px : width_px - blinka_bitmap.width; - uint16_t terminal_height_px = tall ? height_px - blinka_bitmap.height : height_px; - #else - uint16_t terminal_width_px = width_px; - uint16_t terminal_height_px = height_px; - #endif - uint16_t width_in_tiles = terminal_width_px / grid->tile_width; + uint16_t width_in_tiles = width_px / scroll_area->tile_width; // determine scale based on h if (width_in_tiles < 80) { scale = 1; } - width_in_tiles = terminal_width_px / (grid->tile_width * scale); + width_in_tiles = width_px / (scroll_area->tile_width * scale); if (width_in_tiles < 1) { width_in_tiles = 1; } - uint16_t height_in_tiles = terminal_height_px / (grid->tile_height * scale); - uint16_t remaining_pixels = tall ? 0 : terminal_height_px % (grid->tile_height * scale); - if (height_in_tiles < 1 || remaining_pixels > 0) { - height_in_tiles += 1; - } + uint16_t height_in_tiles = height_px / (scroll_area->tile_height * scale); uint16_t total_tiles = width_in_tiles * height_in_tiles; // check if the terminal tile dimensions are the same - if ((grid->width_in_tiles != width_in_tiles) || - (grid->height_in_tiles != height_in_tiles)) { + if ((scroll_area->width_in_tiles != width_in_tiles) || + (scroll_area->height_in_tiles != height_in_tiles - 1)) { reset_tiles = true; } // Reuse the previous allocation if possible @@ -114,27 +103,39 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { uint8_t *tiles = (uint8_t *)tilegrid_tiles->ptr; #if CIRCUITPY_REPL_LOGO - grid->y = tall ? blinka_bitmap.height : 0; - grid->x = tall ? 0 : blinka_bitmap.width; + title_bar->x = blinka_bitmap.width; + // Align the title bar to the bottom of the logo. + title_bar->y = blinka_bitmap.height - title_bar->tile_height; #else - grid->y = 0; - grid->x = 0; + title_bar->x = 0; + title_bar->y = 0; #endif - grid->top_left_y = 0; - if (remaining_pixels > 0) { - grid->y -= (grid->tile_height - remaining_pixels); - } - grid->width_in_tiles = width_in_tiles; - grid->height_in_tiles = height_in_tiles; + title_bar->top_left_y = 0; + title_bar->width_in_tiles = width_in_tiles; + title_bar->height_in_tiles = 1; assert(width_in_tiles > 0); - assert(height_in_tiles > 0); - grid->pixel_width = width_in_tiles * grid->tile_width; - grid->pixel_height = height_in_tiles * grid->tile_height; - grid->tiles = tiles; + title_bar->pixel_width = width_in_tiles * title_bar->tile_width; + title_bar->pixel_height = title_bar->tile_height; + title_bar->tiles = tiles; + title_bar->full_change = true; - grid->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; + assert(width_in_tiles > 0); + 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; + scroll_area->tiles = tiles + width_in_tiles; + scroll_area->full_change = true; - common_hal_terminalio_terminal_construct(&supervisor_terminal, grid, &supervisor_terminal_font); + common_hal_terminalio_terminal_construct(&supervisor_terminal, scroll_area, &supervisor_terminal_font, title_bar); } #endif @@ -146,18 +147,24 @@ void supervisor_stop_terminal(void) { if (tilegrid_tiles != NULL) { free_memory(tilegrid_tiles); tilegrid_tiles = NULL; - supervisor_terminal_text_grid.tiles = NULL; - supervisor_terminal.tilegrid = NULL; + supervisor_terminal_scroll_area_text_grid.tiles = NULL; + supervisor_terminal_title_bar_text_grid.tiles = NULL; + supervisor_terminal.scroll_area = NULL; + supervisor_terminal.title_bar = NULL; } #endif } void supervisor_display_move_memory(void) { #if CIRCUITPY_TERMINALIO + displayio_tilegrid_t *scroll_area = &supervisor_terminal_scroll_area_text_grid; + displayio_tilegrid_t *title_bar = &supervisor_terminal_title_bar_text_grid; if (tilegrid_tiles != NULL) { - supervisor_terminal_text_grid.tiles = (uint8_t *)tilegrid_tiles->ptr; + title_bar->tiles = (uint8_t *)tilegrid_tiles->ptr; + scroll_area->tiles = (uint8_t *)tilegrid_tiles->ptr + scroll_area->width_in_tiles; } else { - supervisor_terminal_text_grid.tiles = NULL; + scroll_area->tiles = NULL; + title_bar->tiles = NULL; } #endif @@ -298,21 +305,21 @@ displayio_tilegrid_t blinka_sprite = { #if CIRCUITPY_TERMINALIO #if CIRCUITPY_REPL_LOGO -mp_obj_t members[] = { &blinka_sprite, &supervisor_terminal_text_grid, }; +mp_obj_t members[] = { &blinka_sprite, &supervisor_terminal_title_bar_text_grid, &supervisor_terminal_scroll_area_text_grid, }; +mp_obj_list_t splash_children = { + .base = {.type = &mp_type_list }, + .alloc = 3, + .len = 3, + .items = members, +}; +#else +mp_obj_t members[] = { &supervisor_terminal_title_bar_text_grid, &supervisor_terminal_scroll_area_text_grid, }; mp_obj_list_t splash_children = { .base = {.type = &mp_type_list }, .alloc = 2, .len = 2, .items = members, }; -#else -mp_obj_t members[] = { &supervisor_terminal_text_grid, }; -mp_obj_list_t splash_children = { - .base = {.type = &mp_type_list }, - .alloc = 1, - .len = 1, - .items = members, -}; #endif #else #if CIRCUITPY_REPL_LOGO diff --git a/supervisor/shared/display.h b/supervisor/shared/display.h index 4110cfe8e4..d965fdc764 100644 --- a/supervisor/shared/display.h +++ b/supervisor/shared/display.h @@ -43,7 +43,8 @@ extern const fontio_builtinfont_t supervisor_terminal_font; // These will change so they must live in RAM. extern displayio_bitmap_t supervisor_terminal_font_bitmap; -extern displayio_tilegrid_t supervisor_terminal_text_grid; +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 diff --git a/supervisor/shared/translate/translate_impl.h b/supervisor/shared/translate/translate_impl.h index ff90aae525..1c144197cb 100644 --- a/supervisor/shared/translate/translate_impl.h +++ b/supervisor/shared/translate/translate_impl.h @@ -45,7 +45,7 @@ static #endif inline // gcc10 -flto has issues with this being always_inline for debug builds. -#if CIRCUITPY_DEBUG < 1 +#if !CIRCUITPY_LTO || CIRCUITPY_DEBUG < 1 __attribute__((always_inline)) #endif const compressed_string_t *translate(const char *original) { diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c new file mode 100644 index 0000000000..dcc79ecf27 --- /dev/null +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft 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 + +#include "shared-bindings/wifi/Radio.h" +#include "supervisor/shared/translate/translate.h" +#include "supervisor/shared/web_workflow/web_workflow.h" + +#if CIRCUITPY_WIFI +#include "shared-bindings/wifi/__init__.h" +#endif + +#if CIRCUITPY_DOTENV +#include "shared-module/dotenv/__init__.h" +#endif + +static wifi_radio_error_t wifi_status = WIFI_RADIO_ERROR_NONE; + +void supervisor_web_workflow_status(void) { + serial_write_compressed(translate("Wi-Fi: ")); + if (common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) { + uint32_t ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj); + if (wifi_status != WIFI_RADIO_ERROR_NONE) { + mp_printf(&mp_plat_print, "%d", wifi_status); + } else if (ipv4_address == 0) { + serial_write_compressed(translate("No IP")); + } else { + uint8_t *octets = (uint8_t *)&ipv4_address; + mp_printf(&mp_plat_print, "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]); + } + } else { + serial_write_compressed(translate("off")); + } +} + +void supervisor_start_web_workflow(void) { + #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI + char ssid[33]; + char password[64]; + mp_int_t ssid_len = 0; + mp_int_t password_len = 0; + + #if CIRCUITPY_DOTENV + ssid_len = dotenv_get_key("/.env", "CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid) - 1); + password_len = dotenv_get_key("/.env", "CIRCUITPY_WIFI_PASSWORD", password, sizeof(password) - 1); + #endif + if (ssid_len <= 0 || (size_t)ssid_len >= sizeof(ssid) || + password_len <= 0 || (size_t)password_len >= sizeof(password)) { + return; + } + common_hal_wifi_init(false); + common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, true); + + // NUL terminate the strings because dotenv doesn't. + ssid[ssid_len] = '\0'; + password[password_len] = '\0'; + wifi_status = common_hal_wifi_radio_connect( + &common_hal_wifi_radio_obj, (uint8_t *)ssid, ssid_len, (uint8_t *)password, password_len, + 0, 1, NULL, 0); + + if (wifi_status != WIFI_RADIO_ERROR_NONE) { + common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, false); + } + #endif +} + +void supervisor_stop_web_workflow(void) { +} diff --git a/supervisor/shared/web_workflow/web_workflow.h b/supervisor/shared/web_workflow/web_workflow.h new file mode 100644 index 0000000000..6e550aaa9d --- /dev/null +++ b/supervisor/shared/web_workflow/web_workflow.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft 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 + +void supervisor_wifi_background(void); +void supervisor_wifi_init(void); +void supervisor_web_workflow_status(void); +void supervisor_start_web_workflow(void); +void supervisor_stop_web_workflow(void); diff --git a/supervisor/shared/workflow.c b/supervisor/shared/workflow.c index 8d2c0f74fc..335f0f3b97 100644 --- a/supervisor/shared/workflow.c +++ b/supervisor/shared/workflow.c @@ -26,14 +26,61 @@ #include #include "py/mpconfig.h" +#include "supervisor/background_callback.h" #include "supervisor/workflow.h" +#include "supervisor/serial.h" #include "supervisor/shared/workflow.h" +#if CIRCUITPY_BLEIO +#include "shared-bindings/_bleio/__init__.h" +#include "supervisor/shared/bluetooth/bluetooth.h" +#endif + #if CIRCUITPY_USB +#include "supervisor/usb.h" #include "tusb.h" #endif +#if CIRCUITPY_WEB_WORKFLOW +#include "supervisor/shared/web_workflow/web_workflow.h" +#endif +static background_callback_t workflow_background_cb; + +static void supervisor_workflow_update_status_bar(void) { + // 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 + // Send string terminator + serial_write("\x1b" "\\"); +} + +static void workflow_background(void *data) { + supervisor_workflow_update_status_bar(); +} + +// Called during a VM reset. Doesn't actually reset things. void supervisor_workflow_reset(void) { + #if CIRCUITPY_BLEIO + supervisor_start_bluetooth(); + #endif + + #if CIRCUITPY_WEB_WORKFLOW + supervisor_start_web_workflow(); + #endif + + workflow_background_cb.fun = workflow_background; + workflow_background_cb.data = NULL; + supervisor_workflow_request_background(); +} + +void supervisor_workflow_request_background(void) { + background_callback_add_core(&workflow_background_cb); } // Return true as soon as USB communication with host has started, @@ -58,3 +105,25 @@ bool supervisor_workflow_active(void) { return false; #endif } + +void supervisor_workflow_start(void) { + // Start USB after giving boot.py a chance to tweak behavior. + #if CIRCUITPY_USB + // Setup USB connection after heap is available. + // It needs the heap to build descriptors. + usb_init(); + #endif + + // Set up any other serial connection. + serial_init(); + + #if CIRCUITPY_BLEIO + bleio_reset(); + supervisor_bluetooth_enable_workflow(); + supervisor_start_bluetooth(); + #endif + + #if CIRCUITPY_WEB_WORKFLOW + supervisor_start_web_workflow(); + #endif +} diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index d5c49aa7fc..2fa9ff1f0e 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -146,6 +146,10 @@ ifeq ($(CIRCUITPY_USB),1) endif endif +ifeq ($(CIRCUITPY_WEB_WORKFLOW),1) + SRC_SUPERVISOR += supervisor/shared/web_workflow/web_workflow.c +endif + SRC_TINYUSB = $(filter lib/tinyusb/%.c, $(SRC_SUPERVISOR)) $(patsubst %.c,$(BUILD)/%.o,$(SRC_TINYUSB)): CFLAGS += -Wno-missing-prototypes diff --git a/supervisor/workflow.h b/supervisor/workflow.h index 31900392cd..f550f8e88c 100755 --- a/supervisor/workflow.h +++ b/supervisor/workflow.h @@ -30,3 +30,7 @@ void supervisor_workflow_reset(void); // True when the user could be actively iterating on their code. bool supervisor_workflow_active(void); + +void supervisor_workflow_request_background(void); + +void supervisor_workflow_start(void); diff --git a/tools/gen_display_resources.py b/tools/gen_display_resources.py index 552e5f1245..3c481cc812 100644 --- a/tools/gen_display_resources.py +++ b/tools/gen_display_resources.py @@ -139,11 +139,41 @@ displayio_palette_t supervisor_terminal_color = { c_file.write( """\ -displayio_tilegrid_t supervisor_terminal_text_grid = {{ +displayio_tilegrid_t supervisor_terminal_scroll_area_text_grid = {{ .base = {{ .type = &displayio_tilegrid_type }}, .bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap, .pixel_shader = &supervisor_terminal_color, - .x = 16, + .x = 0, + .y = 0, + .pixel_width = {1}, + .pixel_height = {2}, + .bitmap_width_in_tiles = {0}, + .tiles_in_bitmap = {0}, + .width_in_tiles = 1, + .height_in_tiles = 1, + .tile_width = {1}, + .tile_height = {2}, + .tiles = NULL, + .partial_change = false, + .full_change = false, + .hidden = false, + .hidden_by_parent = false, + .moved = false, + .inline_tiles = false, + .in_group = true +}}; +""".format( + len(all_characters), tile_x, tile_y + ) +) + +c_file.write( + """\ +displayio_tilegrid_t supervisor_terminal_title_bar_text_grid = {{ + .base = {{ .type = &displayio_tilegrid_type }}, + .bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap, + .pixel_shader = &supervisor_terminal_color, + .x = 0, .y = 0, .pixel_width = {1}, .pixel_height = {2}, @@ -228,7 +258,8 @@ terminalio_terminal_obj_t supervisor_terminal = { .font = &supervisor_terminal_font, .cursor_x = 0, .cursor_y = 0, - .tilegrid = NULL + .scroll_area = NULL, + .title_bar = NULL }; """ )