Skip to content

Improve connection management for esp32 network driver#1181

Merged
bettio merged 7 commits intoatomvm:mainfrom
UncleGrumpy:wifi_roaming
Feb 27, 2026
Merged

Improve connection management for esp32 network driver#1181
bettio merged 7 commits intoatomvm:mainfrom
UncleGrumpy:wifi_roaming

Conversation

@UncleGrumpy
Copy link
Collaborator

This PR adds the ability to stop a connection to an access point, and to start a connection to a new, or the last configured access point. The driver can also enable STA mode without performing an initial connection by supplying the managed atom in the STA configuration and omitting the ssid and psk settings. Combined with the network scan feature in #1165 this gives the esp32 platform the ability to act as a roaming device, and connect to any known wifi networks found after performing a wifi scan. The disconnected event callback can be used to override the default automatic re-connect action of the driver, and a network scan or other means may now be used to determine if, when, and which access point to connect to.

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

@UncleGrumpy UncleGrumpy force-pushed the wifi_roaming branch 2 times, most recently from 246d3b5 to ae6e359 Compare August 19, 2024 19:21
@UncleGrumpy UncleGrumpy changed the base branch from release-0.6 to main November 20, 2024 06:51
@UncleGrumpy UncleGrumpy changed the title Support roaming for esp32 network driver Enhanced connection management for esp32 network driver Nov 20, 2024
@UncleGrumpy UncleGrumpy force-pushed the wifi_roaming branch 4 times, most recently from 7911e22 to 4506849 Compare December 28, 2024 23:11
@UncleGrumpy UncleGrumpy force-pushed the wifi_roaming branch 2 times, most recently from 5d6cd60 to a782e8b Compare January 4, 2025 23:38
@UncleGrumpy UncleGrumpy changed the title Enhanced connection management for esp32 network driver Improve connection management for esp32 network driver Feb 21, 2026
{reply, ok, State#state{config = NewConfig}};
{Ref, {error, _Reason} = ER} ->
{reply, ER, State#state{sta_state = disconnected}}
end.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be aware there is no timeout here.

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fine, the call to esp_wifi_disconnect() does not wait for a connection to be established before returning. It will return ESP_OK or an error right away if it cannot start to initiate a connection. The user will need to rely on callback to get updates for connected, an got_ip events. This is implied in the documentation, stating that a new connection is started in the background, maybe I need to be more explicit that callbacks are the only way to know when a connection is established. Maybe a 5000ms timeout as a safety would not be a bad idea though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a timeout as a safety so we don't end up blocking forever.

@UncleGrumpy UncleGrumpy force-pushed the wifi_roaming branch 2 times, most recently from 961f87a to 1308f97 Compare February 22, 2026 00:27
@UncleGrumpy UncleGrumpy force-pushed the wifi_roaming branch 2 times, most recently from 000f030 to da1c770 Compare February 22, 2026 09:59
@petermm
Copy link
Contributor

petermm commented Feb 25, 2026

The big full_sim_test workflow is green, so we are down to satisfying the code scanning and then ship it

@UncleGrumpy UncleGrumpy force-pushed the wifi_roaming branch 2 times, most recently from 3025372 to 1dd3e5d Compare February 26, 2026 00:29
@UncleGrumpy UncleGrumpy requested a review from bettio February 26, 2026 00:30
@petermm
Copy link
Contributor

petermm commented Feb 26, 2026

comments + can we unify the managed logic?

Then we are good to go! Thank you for this one!

Critical Bug: Inconsistent managed/roaming logic between functions
The check in start_network() at line 663 has inverted logic compared to get_sta_wifi_config() at line 399:

start_network (line 662-665):

term managed = interop_kv_get_value_default(sta_config, managed_atom, FALSE_ATOM, ctx->global);
if ((term_is_invalid_term(managed)) || (managed != FALSE_ATOM)) {
roaming = true; // invalid term → roaming = true (WRONG)
}

get_sta_wifi_config (line 399):

if ((!term_is_invalid_term(managed_term)) && (managed_term != FALSE_ATOM)) {
roaming = true; // invalid term → roaming = false (CORRECT)
}

The start_network version sets roaming = true when managed is an invalid term, which can happen in AP-only mode (no STA config). When sta_config is term_invalid_term() (line 652), passing it to interop_kv_get_value_default on line 662 may return an invalid term, incorrectly enabling roaming. The start_network check should match the get_sta_wifi_config logic:

if ((!term_is_invalid_term(managed)) && (managed != FALSE_ATOM)) {

@UncleGrumpy UncleGrumpy force-pushed the wifi_roaming branch 5 times, most recently from 46acae1 to 5c521a3 Compare February 26, 2026 23:29
Signed-off-by: Winford <winford@object.stream>
Signed-off-by: Winford <winford@object.stream>
The sta and ap configurations should not be freed until after the null
pointer checks of these configurations for set_dhcp_hostname, otherwise
the default hostname will always be used and any custom hostname in the
configuration will not be set.

Signed-off-by: Winford <winford@object.stream>
@UncleGrumpy UncleGrumpy force-pushed the wifi_roaming branch 2 times, most recently from 7b1e917 to eb25717 Compare February 27, 2026 00:57
@UncleGrumpy UncleGrumpy removed the request for review from bettio February 27, 2026 00:58
UncleGrumpy and others added 4 commits February 27, 2026 01:29
For more fine grained connection management in applications the driver
can now be started, without perfoming an inital connection by the use
of the key `managed` in the STA configuration.

Adds network:sta_connect/0,1 to allow connecting to an access point
after the driver has been started in STA or STA+AP mode. When used
without parameters a connection to the last configured access point
will be started.

Adds network:sta_disconnect/0 to disconnect a station from an access
point. The station mode disconnected default callback behavior is
unchanged unless a custom callback is provided, in which case the
automatic re-connection will not happen, allowing for users to take
advantage of scan results or some other means to determine when and
which access point to associate with.

The combination of the use of a disconnected callback and `managed`
mode allow for the use of `network:wifi_scan/0,1` (PR atomvm#1165), since the
wifi must not be connected to a station when perfoming a scan and old
implementation always started a connection immediatly and always
persisted in attempts to reconnect when disconnected.

Signed-off-by: Winford <winford@object.stream>
Adds a new function `sta_status/0` to get the current connection state
of the sta interface, which is now tracked by the gen_server
internally.

Signed-off-by: Winford <winford@object.stream>
Signed-off-by: Peter M <petermm@gmail.com>
Adds descriptions of new configuration options and `connect/0,1`,
`disconnect/0`, and `sta_status/0` functions.

Signed-off-by: Winford <winford@object.stream>
Copy link
Contributor

@petermm petermm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AMAZING - thank you for the perseverance!

Simtest is green across all devices!

the llm overlords are down to commas, and c style - surely for another day:

memory (medium) - network_driver.c: SNTP host string leaks on reconfiguration. interop_term_to_string mallocs a new string each call to maybe_set_sntp, but the previous string (held by esp_sntp_setservername) is never freed. This accumulates when sta_connect/1 is called repeatedly with SNTP config.

logic (medium) - network_driver.c: sta_reconnect() is dead code — the Erlang API always sends {connect, Config} (a 2-tuple), never the bare atom connect. sta_connect/0 in handle_call sends the full saved config, so the C-side always takes the sta_connect path (with full config re-parse and esp_wifi_set_config). Consider either wiring sta_connect/0 to send the bare atom for a lightweight reconnect, or removing sta_reconnect.

memory (low) - network_driver.c: ClientData is malloc'd in start_network but never freed in stop_network (after event handlers are unregistered). Pre-existing leak, but now more impactful since start/stop cycles are a supported use case with managed mode.

style (low) - network_driver.c: if (roaming != true) — idiomatic C is if (!roaming).

correctness (low) - network.erl: In wait_connect_reply, after a successful {Ref, ok} the NewConfig is stored — but this same NewConfig was already set in State#state{config = NewConfig} before calling the wait function (line 460). The redundant update is harmless but confusing.

doc (low) - network-programming-guide.md: {managed, true} typo: "the managed boolean may be used, to start the driver" — remove comma after "used".

Compliments

✅ Excellent refactor of start_network error paths to use a unified cleanup: label, fixing multiple pre-existing memory leaks of sta_wifi_config/ap_wifi_config.
✅ Moving SNTP stop before WiFi deinit in stop_network is correct — SNTP depends on the network stack.
✅ update_config in Erlang cleanly merges old/new configs while preserving callbacks, managed flag, mdns, and sntp settings.
✅ Comprehensive test in test_wifi_managed.erl covering the full managed-mode lifecycle.
✅ Well-documented sta_status state machine (disconnected → connecting → associated → connected, with degraded on beacon timeout).
✅ The default sta_disconnected_default_callback preserving backward-compatible auto-reconnect behavior is a good design choice, with the doc clearly warning about the sta_disconnect race condition.

@bettio bettio merged commit ac69559 into atomvm:main Feb 27, 2026
159 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants