Skip to content

Commit 6446010

Browse files
committed
Wi-Fi autoconnect and title bar status
This adds support for CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD in `/.env`. When both are defined, CircuitPython will attempt to connect to the network even when user code isn't running. If the user code attempts to a network with the same SSID, it will return immediately. Connecting to another SSID will disconnect from the auto-connected network. If the user code initiates the connection, then it will be shutdown after user code exits. (Should match <8 behavior.) This PR also reworks the default displayio terminal. It now supports a title bar TileGrid in addition to the (newly renamed) scroll area. The default title bar is the top row of the display and is positioned to the right of the Blinka logo when it is enabled. The scroll area is now below the Blinka logo. The Wi-Fi auto-connect code now uses the title bar to show its state including the IP address when connected. It does this through the "standard" OSC control sequence `ESC ] 0 ; <s> ESC \` where <s> is the title bar string. This is commonly supported by terminals so it should work over USB and UART as well. Related to #6174
1 parent 6925a00 commit 6446010

File tree

35 files changed

+584
-141
lines changed

35 files changed

+584
-141
lines changed

README.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Behavior
120120
make each file independent from each other.
121121

122122
- ``boot.py`` runs only once on start up before
123-
USB is initialized. This lays the ground work for configuring USB at
123+
workflows are initialized. This lays the ground work for configuring USB at
124124
startup rather than it being fixed. Since serial is not available,
125125
output is written to ``boot_out.txt``.
126126
- ``code.py`` (or ``main.py``) is run after every reload until it
@@ -135,7 +135,10 @@ Behavior
135135
possible to fix code that causes nasty crashes by making it available through mass storage after
136136
the crash. A reset (the button) is needed after it's fixed to get back into normal mode.
137137
- RGB status LED indicating CircuitPython state.
138-
- Re-runs ``code.py`` or other main file after file system writes over USB mass storage. (Disable with
138+
- One green flash - code completed without error.
139+
- Two red flashes - code ended due to an exception.
140+
- Three yellow flashes - safe mode. May be due to CircuitPython internal error.
141+
- Re-runs ``code.py`` or other main file after file system writes by a workflow. (Disable with
139142
``supervisor.disable_autoreload()``)
140143
- Autoreload is disabled while the REPL is active.
141144
- Main is one of these: ``code.txt``, ``code.py``, ``main.py``,

docs/environment.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Environment Variables
2+
=====================
3+
4+
CircuitPython 8.0.0 introduces support for environment variables. Environment
5+
variables are commonly used to store "secrets" such as Wi-Fi passwords and API
6+
keys. This method *does not* make them secure. It only separates them from the
7+
code.
8+
9+
CircuitPython supports these by mimicking the `dotenv <https://github.com/theskumar/python-dotenv>`_
10+
CPython library. Other languages such as Javascript, PHP and Ruby also have
11+
dotenv libraries.
12+
13+
These libraries store environment variables in a ``.env`` file. Here is a simple
14+
example:
15+
16+
.. code-block:: bash
17+
18+
KEY1='value1'
19+
# Comment
20+
KEY2='value2
21+
is multiple lines'
22+
23+
CircuitPython uses the ``.env`` at the drive root (no folder) as the environment.
24+
User code can access the values from the file using `os.getenv()`. It is
25+
recommended to save any values used repeatedly in a variable because `os.getenv()`
26+
will parse the ``/.env`` on every access.
27+
28+
CircuitPython behavior
29+
----------------------
30+
31+
CircuitPython will also read the environment to configure its behavior. Other
32+
keys are ignored by CircuitPython. Here are the keys it uses:
33+
34+
CIRCUITPY_WIFI_PASSWORD
35+
~~~~~~~~~~~~~~~~~~~~~~~
36+
Wi-Fi password used to auto connect to CIRCUITPY_WIFI_SSID
37+
38+
CIRCUITPY_WIFI_SSID
39+
~~~~~~~~~~~~~~~~~~~
40+
Wi-Fi SSID to auto-connect to even if user code is not running.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Full Table of Contents
2222
supported_ports.rst
2323
troubleshooting.rst
2424
drivers.rst
25+
environment.rst
2526

2627
.. toctree::
2728
:maxdepth: 1

locale/circuitpython.pot

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,10 @@ msgstr ""
13751375
msgid "No I2C device at address: 0x%x"
13761376
msgstr ""
13771377

1378+
#: supervisor/shared/web_workflow/web_workflow.c
1379+
msgid "No IP"
1380+
msgstr ""
1381+
13781382
#: ports/espressif/common-hal/busio/SPI.c
13791383
#: ports/mimxrt10xx/common-hal/busio/SPI.c
13801384
msgid "No MISO Pin"
@@ -2254,6 +2258,10 @@ msgid ""
22542258
"To list built-in modules type `help(\"modules\")`.\n"
22552259
msgstr ""
22562260

2261+
#: supervisor/shared/web_workflow/web_workflow.c
2262+
msgid "Wi-Fi: "
2263+
msgstr ""
2264+
22572265
#: main.c
22582266
msgid "Woken up by alarm.\n"
22592267
msgstr ""
@@ -3579,6 +3587,10 @@ msgstr ""
35793587
msgid "odd-length string"
35803588
msgstr ""
35813589

3590+
#: supervisor/shared/web_workflow/web_workflow.c
3591+
msgid "off"
3592+
msgstr ""
3593+
35823594
#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c
35833595
msgid "offset is too large"
35843596
msgstr ""

main.c

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@
101101
#include "shared-module/memorymonitor/__init__.h"
102102
#endif
103103

104+
#if CIRCUITPY_SOCKETPOOL
105+
#include "shared-bindings/socketpool/__init__.h"
106+
#endif
107+
104108
#if CIRCUITPY_USB_HID
105109
#include "shared-module/usb_hid/__init__.h"
106110
#endif
@@ -290,6 +294,16 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, mp_obj_t exception) {
290294
keypad_reset();
291295
#endif
292296

297+
// Close user-initiated sockets.
298+
#if CIRCUITPY_SOCKETPOOL
299+
socketpool_user_reset();
300+
#endif
301+
302+
// Turn off user initiated WiFi connections.
303+
#if CIRCUITPY_WIFI
304+
wifi_user_reset();
305+
#endif
306+
293307
// reset_board_buses() first because it may release pins from the never_reset state, so that
294308
// reset_port() can reset them.
295309
#if CIRCUITPY_BOARD
@@ -303,6 +317,9 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, mp_obj_t exception) {
303317
stop_mp();
304318
free_memory(heap);
305319
supervisor_move_memory();
320+
321+
// Let the workflows know we've reset in case they want to restart.
322+
supervisor_workflow_reset();
306323
}
307324

308325
STATIC void print_code_py_status_message(safe_mode_t safe_mode) {
@@ -889,21 +906,7 @@ int __attribute__((used)) main(void) {
889906

890907
run_boot_py(safe_mode);
891908

892-
// Start USB after giving boot.py a chance to tweak behavior.
893-
#if CIRCUITPY_USB
894-
// Setup USB connection after heap is available.
895-
// It needs the heap to build descriptors.
896-
usb_init();
897-
#endif
898-
899-
// Set up any other serial connection.
900-
serial_init();
901-
902-
#if CIRCUITPY_BLEIO
903-
bleio_reset();
904-
supervisor_bluetooth_enable_workflow();
905-
supervisor_start_bluetooth();
906-
#endif
909+
supervisor_workflow_start();
907910

908911
// Boot script is finished, so now go into REPL or run code.py.
909912
int exit_code = PYEXEC_FORCED_EXIT;

ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ void board_init(void) {
117117
true, // backlight_on_high
118118
false, // SH1107_addressing
119119
50000); // backlight pwm frequency
120+
121+
#if CIRCUITPY_DEBUG
122+
common_hal_never_reset_pin(DEFAULT_UART_BUS_TX);
123+
#endif
120124
}
121125

122126
bool espressif_board_reset_pin_number(gpio_num_t pin_number) {

ports/espressif/common-hal/socketpool/Socket.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
STATIC socketpool_socket_obj_t *open_socket_handles[CONFIG_LWIP_MAX_SOCKETS];
4141

42-
void socket_reset(void) {
42+
void socket_user_reset(void) {
4343
for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_handles); i++) {
4444
if (open_socket_handles[i]) {
4545
if (open_socket_handles[i]->num > 0) {

ports/espressif/common-hal/socketpool/Socket.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ typedef struct {
4545
mp_uint_t timeout_ms;
4646
} socketpool_socket_obj_t;
4747

48-
void socket_reset(void);
48+
void socket_user_reset(void);
4949
bool register_open_socket(socketpool_socket_obj_t *self);
5050

5151
#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_SOCKETPOOL_SOCKET_H

ports/espressif/common-hal/socketpool/__init__.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,11 @@
2323
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2424
* THE SOFTWARE.
2525
*/
26+
27+
#include "shared-bindings/socketpool/__init__.h"
28+
29+
#include "common-hal/socketpool/Socket.h"
30+
31+
void socketpool_user_reset(void) {
32+
socket_user_reset();
33+
}

ports/espressif/common-hal/wifi/Radio.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
236236
if (!common_hal_wifi_radio_get_enabled(self)) {
237237
mp_raise_RuntimeError(translate("wifi is not enabled"));
238238
}
239+
wifi_config_t *config = &self->sta_config;
239240

240241
EventBits_t bits;
241242
// 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
245246
pdTRUE,
246247
pdTRUE,
247248
0);
248-
if (((bits & WIFI_CONNECTED_BIT) != 0) &&
249-
!((bits & WIFI_DISCONNECTED_BIT) != 0)) {
250-
return WIFI_RADIO_ERROR_NONE;
249+
bool connected = ((bits & WIFI_CONNECTED_BIT) != 0) &&
250+
!((bits & WIFI_DISCONNECTED_BIT) != 0);
251+
if (connected) {
252+
if (memcmp(ssid, config->sta.ssid, ssid_len) == 0) {
253+
// Already connected to the desired network.
254+
return WIFI_RADIO_ERROR_NONE;
255+
} else {
256+
xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT);
257+
// Trying to switch networks so disconnect first.
258+
esp_wifi_disconnect();
259+
do {
260+
RUN_BACKGROUND_TASKS;
261+
bits = xEventGroupWaitBits(self->event_group_handle,
262+
WIFI_DISCONNECTED_BIT,
263+
pdTRUE,
264+
pdTRUE,
265+
0);
266+
} while ((bits & WIFI_DISCONNECTED_BIT) == 0 && !mp_hal_is_interrupted());
267+
}
251268
}
252269
// explicitly clear bits since xEventGroupWaitBits may have timed out
253270
xEventGroupClearBits(self->event_group_handle, WIFI_CONNECTED_BIT);
254271
xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT);
255272
set_mode_station(self, true);
256273

257-
wifi_config_t *config = &self->sta_config;
258274
memcpy(&config->sta.ssid, ssid, ssid_len);
259275
config->sta.ssid[ssid_len] = 0;
260276
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) {
368384
return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.netmask.addr);
369385
}
370386

387+
uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) {
388+
if (!esp_netif_is_netif_up(self->netif)) {
389+
return 0;
390+
}
391+
esp_netif_get_ip_info(self->netif, &self->ip_info);
392+
return self->ip_info.ip.addr;
393+
}
394+
371395
mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) {
372396
if (!esp_netif_is_netif_up(self->netif)) {
373397
return mp_const_none;

ports/espressif/common-hal/wifi/__init__.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ wifi_radio_obj_t common_hal_wifi_radio_obj;
4242

4343
#include "components/log/include/esp_log.h"
4444

45+
#include "supervisor/workflow.h"
46+
4547
static const char *TAG = "wifi";
4648

4749
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,
106108
radio->retries_left = radio->starting_retries;
107109
xEventGroupSetBits(radio->event_group_handle, WIFI_CONNECTED_BIT);
108110
}
111+
supervisor_workflow_request_background();
109112
}
110113

111-
static bool wifi_inited, wifi_ever_inited;
114+
static bool wifi_inited;
115+
static bool wifi_ever_inited;
116+
static bool wifi_user_initiated;
112117

113-
void common_hal_wifi_init(void) {
118+
void common_hal_wifi_init(bool user_initiated) {
119+
if (wifi_inited) {
120+
return;
121+
}
114122
wifi_inited = true;
123+
wifi_user_initiated = user_initiated;
115124
common_hal_wifi_radio_obj.base.type = &wifi_radio_type;
116125

117126
if (!wifi_ever_inited) {
@@ -157,6 +166,12 @@ void common_hal_wifi_init(void) {
157166
common_hal_wifi_radio_set_enabled(self, true);
158167
}
159168

169+
void wifi_user_reset(void) {
170+
if (wifi_user_initiated) {
171+
wifi_reset();
172+
}
173+
}
174+
160175
void wifi_reset(void) {
161176
if (!wifi_inited) {
162177
return;
@@ -176,6 +191,7 @@ void wifi_reset(void) {
176191
esp_netif_destroy(radio->ap_netif);
177192
radio->ap_netif = NULL;
178193
wifi_inited = false;
194+
supervisor_workflow_request_background();
179195
}
180196

181197
void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address) {

ports/espressif/supervisor/port.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,6 @@ void reset_port(void) {
282282
#if CIRCUITPY_WATCHDOG
283283
watchdog_reset();
284284
#endif
285-
286-
#if CIRCUITPY_WIFI
287-
wifi_reset();
288-
#endif
289-
290-
#if CIRCUITPY_SOCKETPOOL
291-
socket_reset();
292-
#endif
293285
}
294286

295287
void reset_to_bootloader(void) {

py/circuitpy_defns.mk

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ endif
7070
CIRCUITPY_LTO ?= 0
7171
CIRCUITPY_LTO_PARTITION ?= balanced
7272
ifeq ($(CIRCUITPY_LTO),1)
73-
CFLAGS += -flto -flto-partition=$(CIRCUITPY_LTO_PARTITION)
73+
CFLAGS += -flto -flto-partition=$(CIRCUITPY_LTO_PARTITION) -DCIRCUITPY_LTO=1
74+
else
75+
CFLAGS += -DCIRCUITPY_LTO=0
7476
endif
7577

7678
# Produce an object file for translate.c instead of including it in a header.

py/circuitpy_mpconfig.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,9 @@ CFLAGS += -DCIRCUITPY_WATCHDOG=$(CIRCUITPY_WATCHDOG)
480480
CIRCUITPY_WIFI ?= 0
481481
CFLAGS += -DCIRCUITPY_WIFI=$(CIRCUITPY_WIFI)
482482

483+
CIRCUITPY_WEB_WORKFLOW ?= $(CIRCUITPY_WIFI)
484+
CFLAGS += -DCIRCUITPY_WEB_WORKFLOW=$(CIRCUITPY_WEB_WORKFLOW)
485+
483486
# tinyusb port tailored configuration
484487
CIRCUITPY_TUSB_MEM_ALIGN ?= 4
485488
CFLAGS += -DCIRCUITPY_TUSB_MEM_ALIGN=$(CIRCUITPY_TUSB_MEM_ALIGN)

shared-bindings/displayio/TileGrid.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,6 @@ void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t
6969

7070
// Private API for scrolling the TileGrid.
7171
void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint16_t x, uint16_t y);
72+
void common_hal_displayio_tilegrid_set_all_tiles(displayio_tilegrid_t *self, uint8_t tile_index);
7273

7374
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_TILEGRID_H

shared-bindings/socketpool/__init__.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@
2727
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H
2828
#define MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H
2929

30+
void socketpool_user_reset(void);
31+
3032
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H

0 commit comments

Comments
 (0)