Skip to content

Web workflow skip init when no password, freeing socket. Also reconnect wifi on reload. #8367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,14 @@ conflicts with user created NUS services.
Read-only characteristic that returns the UTF-8 encoded version string.

## Web
If the keys `CIRCUITPY_WIFI_SSID` and `CIRCUITPY_WIFI_PASSWORD` are set in `settings.toml`,
CircuitPython will automatically connect to the given Wi-Fi network on boot and upon reload.

The web workflow is depends on adding Wi-Fi credentials into the `settings.toml` file. The keys are
`CIRCUITPY_WIFI_SSID` and `CIRCUITPY_WIFI_PASSWORD`. Once these are defined, CircuitPython will
automatically connect to the network and start the webserver used for the workflow. The webserver
is on port 80 unless overridden by `CIRCUITPY_WEB_API_PORT`. It also enables MDNS. The name
of the board as advertised to the network can be overridden by `CIRCUITPY_WEB_INSTANCE_NAME`.
If `CIRCUITPY_WEB_API_PASSWORD` is also set, the web workflow will also start.
The web workflow will only be enabled if the Wi-Fi connection succeeds upon boot.

The webserver is on port 80 unless overridden by `CIRCUITPY_WEB_API_PORT`. It also enables MDNS.
The name of the board as advertised to the network can be overridden by `CIRCUITPY_WEB_INSTANCE_NAME`.

Here is an example `/settings.toml`:

Expand All @@ -82,7 +84,7 @@ Here is an example `/settings.toml`:
CIRCUITPY_WIFI_SSID="scottswifi"
CIRCUITPY_WIFI_PASSWORD="secretpassword"

# To enable modifying files from the web. Change this too!
# To enable the the webserver. Change this too!
# Leave the User field blank in the browser.
CIRCUITPY_WEB_API_PASSWORD="passw0rd"

Expand Down
113 changes: 61 additions & 52 deletions supervisor/shared/web_workflow/web_workflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,19 +258,9 @@ void supervisor_web_workflow_status(void) {
}
#endif

bool supervisor_start_web_workflow(void) {
bool supervisor_start_web_workflow(bool reload) {
#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV

// Skip starting the workflow if we're not starting from power on or reset.
const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason();
if (reset_reason != RESET_REASON_POWER_ON &&
reset_reason != RESET_REASON_RESET_PIN &&
reset_reason != RESET_REASON_DEEP_SLEEP_ALARM &&
reset_reason != RESET_REASON_UNKNOWN &&
reset_reason != RESET_REASON_SOFTWARE) {
return false;
}

char ssid[33];
char password[64];

Expand All @@ -287,11 +277,6 @@ bool supervisor_start_web_workflow(void) {
return false;
}

result = common_hal_os_getenv_str("CIRCUITPY_WEB_INSTANCE_NAME", web_instance_name, sizeof(web_instance_name));
if (result != GETENV_OK || web_instance_name[0] == '\0') {
strcpy(web_instance_name, MICROPY_HW_BOARD_NAME);
}

if (!common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) {
common_hal_wifi_init(false);
common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, true);
Expand All @@ -303,6 +288,7 @@ bool supervisor_start_web_workflow(void) {
// We can all connect again because it will return early if we're already connected to the
// network. If we are connected to a different network, then it will disconnect before
// attempting to connect to the given network.

_wifi_status = common_hal_wifi_radio_connect(
&common_hal_wifi_radio_obj, (uint8_t *)ssid, strlen(ssid), (uint8_t *)password, strlen(password),
0, 8, NULL, 0);
Expand All @@ -312,12 +298,36 @@ bool supervisor_start_web_workflow(void) {
return false;
}

// (leaves new_port unchanged on any failure)
(void)common_hal_os_getenv_int("CIRCUITPY_WEB_API_PORT", &web_api_port);
// Skip starting the workflow if we're not starting from power on or reset.
const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason();
if (reset_reason != RESET_REASON_POWER_ON &&
reset_reason != RESET_REASON_RESET_PIN &&
reset_reason != RESET_REASON_DEEP_SLEEP_ALARM &&
reset_reason != RESET_REASON_UNKNOWN &&
reset_reason != RESET_REASON_SOFTWARE) {
return false;
}

bool initialized = pool.base.type == &socketpool_socketpool_type;

if (!initialized && !reload) {
result = common_hal_os_getenv_str("CIRCUITPY_WEB_INSTANCE_NAME", web_instance_name, sizeof(web_instance_name));
if (result != GETENV_OK || web_instance_name[0] == '\0') {
strcpy(web_instance_name, MICROPY_HW_BOARD_NAME);
}

// (leaves new_port unchanged on any failure)
(void)common_hal_os_getenv_int("CIRCUITPY_WEB_API_PORT", &web_api_port);

bool first_start = pool.base.type != &socketpool_socketpool_type;
const size_t api_password_len = sizeof(_api_password) - 1;
result = common_hal_os_getenv_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len);
if (result == GETENV_OK) {
_api_password[0] = ':';
_base64_in_place(_api_password, strlen(_api_password), sizeof(_api_password) - 1);
} else { // Skip starting web-workflow when no password is passed.
return false;
}

if (first_start) {
pool.base.type = &socketpool_socketpool_type;
common_hal_socketpool_socketpool_construct(&pool, &common_hal_wifi_radio_obj);

Expand All @@ -327,43 +337,42 @@ bool supervisor_start_web_workflow(void) {
websocket_init();
}

if (!common_hal_socketpool_socket_get_closed(&active)) {
common_hal_socketpool_socket_close(&active);
}
initialized = pool.base.type == &socketpool_socketpool_type;

#if CIRCUITPY_MDNS
// Try to start MDNS if the user deinited it.
if (mdns.base.type != &mdns_server_type ||
common_hal_mdns_server_deinited(&mdns)) {
mdns_server_construct(&mdns, true);
mdns.base.type = &mdns_server_type;
if (!common_hal_mdns_server_deinited(&mdns)) {
common_hal_mdns_server_set_instance_name(&mdns, web_instance_name);
if (initialized){
if (!common_hal_socketpool_socket_get_closed(&active)) {
common_hal_socketpool_socket_close(&active);
}
}
if (!common_hal_mdns_server_deinited(&mdns)) {
common_hal_mdns_server_advertise_service(&mdns, "_circuitpython", "_tcp", web_api_port);
}
#endif

const size_t api_password_len = sizeof(_api_password) - 1;
result = common_hal_os_getenv_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len);
if (result == GETENV_OK) {
_api_password[0] = ':';
_base64_in_place(_api_password, strlen(_api_password), sizeof(_api_password) - 1);
}
#if CIRCUITPY_MDNS
// Try to start MDNS if the user deinited it.
if (mdns.base.type != &mdns_server_type ||
common_hal_mdns_server_deinited(&mdns) ||
reload) { // Always reconstruct on reload, since we don't know if the net changed.
mdns_server_construct(&mdns, true);
mdns.base.type = &mdns_server_type;
if (!common_hal_mdns_server_deinited(&mdns)) {
common_hal_mdns_server_set_instance_name(&mdns, web_instance_name);
}
}
if (!common_hal_mdns_server_deinited(&mdns)) {
common_hal_mdns_server_advertise_service(&mdns, "_circuitpython", "_tcp", web_api_port);
}
#endif

if (common_hal_socketpool_socket_get_closed(&listening)) {
socketpool_socket(&pool, SOCKETPOOL_AF_INET, SOCKETPOOL_SOCK_STREAM, &listening);
common_hal_socketpool_socket_settimeout(&listening, 0);
// Bind to any ip. (Not checking for failures)
common_hal_socketpool_socket_bind(&listening, "", 0, web_api_port);
common_hal_socketpool_socket_listen(&listening, 1);
if (common_hal_socketpool_socket_get_closed(&listening)) {
socketpool_socket(&pool, SOCKETPOOL_AF_INET, SOCKETPOOL_SOCK_STREAM, &listening);
common_hal_socketpool_socket_settimeout(&listening, 0);
// Bind to any ip. (Not checking for failures)
common_hal_socketpool_socket_bind(&listening, "", 0, web_api_port);
common_hal_socketpool_socket_listen(&listening, 1);
}
// Wake polling thread (maybe)
socketpool_socket_poll_resume();
#endif
return true;
}
// Wake polling thread (maybe)
socketpool_socket_poll_resume();
#endif
return true;
return false;
}

void web_workflow_send_raw(socketpool_socket_obj_t *socket, const uint8_t *buf, int len) {
Expand Down
2 changes: 1 addition & 1 deletion supervisor/shared/web_workflow/web_workflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
void supervisor_web_workflow_background(void *data);
bool supervisor_web_workflow_status_dirty(void);
void supervisor_web_workflow_status(void);
bool supervisor_start_web_workflow(void);
bool supervisor_start_web_workflow(bool);
void supervisor_stop_web_workflow(void);

// Share the MDNS object with user code.
Expand Down
5 changes: 3 additions & 2 deletions supervisor/shared/workflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ void supervisor_workflow_reset(void) {
#endif

#if CIRCUITPY_WEB_WORKFLOW
bool result = supervisor_start_web_workflow(true);
if (workflow_background_cb.fun) {
if (supervisor_start_web_workflow()) {
if (result) {
supervisor_workflow_request_background();
}
}
Expand Down Expand Up @@ -105,7 +106,7 @@ void supervisor_workflow_start(void) {
#endif

#if CIRCUITPY_WEB_WORKFLOW
if (supervisor_start_web_workflow()) {
if (supervisor_start_web_workflow(false)) {
// Enable background callbacks if web_workflow startup successful
memset(&workflow_background_cb, 0, sizeof(workflow_background_cb));
workflow_background_cb.fun = supervisor_web_workflow_background;
Expand Down