Skip to content

Add support for esp32 network driver scanning for access points#1165

Open
UncleGrumpy wants to merge 4 commits intoatomvm:mainfrom
UncleGrumpy:esp32_wifi_scan
Open

Add support for esp32 network driver scanning for access points#1165
UncleGrumpy wants to merge 4 commits intoatomvm:mainfrom
UncleGrumpy:esp32_wifi_scan

Conversation

@UncleGrumpy
Copy link
Collaborator

@UncleGrumpy UncleGrumpy commented May 24, 2024

Add network:wifi_scan/0,1 to esp32 network driver, giving the ability for devices configured for "station mode" (or with "station + access point mode") to scan for available access points.

note: these changes depend on PR #1181

Closes #2024

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 changed the base branch from main to release-0.6 May 24, 2024 00:58
@UncleGrumpy UncleGrumpy force-pushed the esp32_wifi_scan branch 2 times, most recently from 58cf1f8 to cdadc33 Compare June 6, 2024 01:14
@petermm
Copy link
Contributor

petermm commented Jun 11, 2024

I struggled here:

I started out empty example and tried:

    scan = :network.wifi_scan()
    IO.inspect(scan)

This crashes with: {noproc,{gen_server,call,[network,get_config]}} - looks like there is a code path for handling the error but it's never reached

Then I read docs, about having to start the network, and tried:

 config = [
      {:sta,
       [
         {:ssid, "Wokwi-GUEST"},
         {:psk, ""},
         {:connected,
          fn ->
            IO.inspect("network CONNECTED")
            :ok
          end}
       ]}
    ]

    case :network.start(config) do
      {:ok, _pid} ->
        IO.inspect("network started")
      error ->
        :io.put_chars("\nAn error occurred starting network:")
        :erlang.display(error)
    end

    scan = :network.wifi_scan()
    IO.inspect(scan)

which crashed with: wifi:sta_scan: STA is connecting, scan are not allowed!

delaying things and scanning after connection is done made it further to a crash:

CRASH 
======
pid: <0.1.0>

Stacktrace:
undefined

cp: #CP<module: 11, label: 31, offset: 26>

x[0]: error
x[1]: badarg
x[2]: error

Stack 
------

#CP<module: 11, label: 32, offset: 26>
<<"0">>
#CP<module: 11, label: 24, offset: 20>
#CP<module: 10, label: 11, offset: 11>
{0,[{none,[{rssi,undefined},{authmode,undefined},{channel,undefined}]}]}

It also seems that connecting to a network not available, disallows scanning with: wifi:sta_scan: STA is connecting, scan are not allowed!

Would be great seeing an example that could be used in CI eg:

  1. scan with no network configured
  2. connect known good network - scan
  3. config known unavailable network - scan
  4. probably some ap stuff
    etc.

I'll work on getting wokwi CI going so this can be covered by CI.

@UncleGrumpy
Copy link
Collaborator Author

UncleGrumpy commented Jun 11, 2024

I did not add an example yet, my plan was to write a demonstration of “roaming” for atomvm_examples. It is a little difficult to use until #1181 is merged, that will allow starting WiFi without immediately starting a connection. Scans will always fail when a connection is in progress, so your application will need to start a scan after obtaining an IP address, or before any connection is started (which is not possible until after #1181).

@UncleGrumpy
Copy link
Collaborator Author

I wish your second example had a stacktrace! You can see the scan result did come back with no networks found… this can happen with default scans (the dwell time is very short by default - and can easily miss networks, in my experience). I think it’s just a bad match in your test that is crashing there.

@petermm
Copy link
Contributor

petermm commented Jun 11, 2024

I wish your second example had a stacktrace! You can see the scan result did come back with no networks found… this can happen with default scans (the dwell time is very short by default - and can easily miss networks, in my experience). I think it’s just a bad match in your test that is crashing there.

ohh the embarrassment lol - it's the IO.inspect call that crashes, will look into it - used to throwing anything at IO.inspect..

Makes sense with the other stuff, suppose some kind of WifiManager GenServer will materialize, while this PR is on driver primitives level..

I'll test #1181 and help land that first..

@UncleGrumpy
Copy link
Collaborator Author

My plan precisely! The PRs I have already submitted will provide all the necessary low level functionality, but I would like to create a higher level network_manager module that can simplify configuration and orchestration.

@UncleGrumpy
Copy link
Collaborator Author

As far as “dwell” time for the scan, I have found between 300-500ms will find all of the available networks, with the default (120ms) I do notice networks being frequently missed.

@UncleGrumpy
Copy link
Collaborator Author

it's the IO.inspect call that crashes, will look into it - used to throwing anything at IO.inspect..

That does sound like a bug, from my limmited understanding of Elixir you should be able to give it just about anything, much like erlang:display/1.

@UncleGrumpy
Copy link
Collaborator Author

{0,[{none,[{rssi,undefined},{authmode,undefined},{channel,undefined}]}]}

You did point me to a problem here, I started testing more boards and realized that even with maximum dwell time no network are being found. I cherry-picked these commits from a different local branch and must have missed something.

@UncleGrumpy UncleGrumpy marked this pull request as draft June 13, 2024 15:10
@UncleGrumpy UncleGrumpy force-pushed the esp32_wifi_scan branch 2 times, most recently from 480b056 to 50a78c7 Compare August 19, 2024 19:18
@UncleGrumpy
Copy link
Collaborator Author

When I split up the working branch I was testing new network features on I mistakenly submitted this PR first, but in order to use the scan function PR #1181 needs to be merged first, and this will need to be rebased.

@UncleGrumpy
Copy link
Collaborator Author

UncleGrumpy commented Aug 19, 2024

{0,[{none,[{rssi,undefined},{authmode,undefined},{channel,undefined}]}]}

I did find a bug that would cause the results to be empty if only a single network was found, during most of my testing I had multiple networks, so this didn't get caught. @petermm, thanks for testing and helping me correct this!

@UncleGrumpy UncleGrumpy marked this pull request as ready for review August 19, 2024 19:43
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Nov 20, 2024
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. If the function is 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 callback now maintains the default behavior of
reconnecting to the last access point if the connection is lost, but if the
user defines a custom callback 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 the current implementation
always starts a connection immediatly and always reconnects when disconnected.

Signed-off-by: Winford <winford@object.stream>
@UncleGrumpy UncleGrumpy changed the base branch from release-0.6 to main November 20, 2024 07:05
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Nov 21, 2024
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. If the function is 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 callback now maintains the default behavior of
reconnecting to the last access point if the connection is lost, but if the
user defines a custom callback 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 the current implementation
always starts a connection immediatly and always reconnects when disconnected.

Signed-off-by: Winford <winford@object.stream>
@UncleGrumpy UncleGrumpy marked this pull request as draft November 21, 2024 21:38
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Dec 22, 2024
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. If the function is 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 callback now maintains the default behavior of
reconnecting to the last access point if the connection is lost, but if the
user defines a custom callback 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 the current implementation
always starts a connection immediatly and always reconnects when disconnected.

Signed-off-by: Winford <winford@object.stream>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 25, 2026
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>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 25, 2026
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>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 26, 2026
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>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 26, 2026
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>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 26, 2026
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>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 26, 2026
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>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 26, 2026
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>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 27, 2026
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>
UncleGrumpy added a commit to UncleGrumpy/AtomVM that referenced this pull request Feb 27, 2026
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>
bettio added a commit that referenced this pull request Feb 27, 2026
Improve connection management for esp32 network driver

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 closed this Mar 2, 2026
@UncleGrumpy
Copy link
Collaborator Author

closed by accident.

@UncleGrumpy UncleGrumpy reopened this Mar 2, 2026
@UncleGrumpy UncleGrumpy force-pushed the esp32_wifi_scan branch 4 times, most recently from cdadd04 to 32969e5 Compare March 2, 2026 09:03
@UncleGrumpy UncleGrumpy requested review from bettio and removed request for bettio March 2, 2026 09:05
@petermm

This comment was marked as outdated.

@petermm

This comment was marked as outdated.

@petermm
Copy link
Contributor

petermm commented Mar 5, 2026

Looking good! - did a full sim test, and we fail on all esp32s2, no crash just error - so will investigate that later on real hw - probably just low mem, which code then seemed to handle, eg. it didn't crash (think solution is to lower results or something, from distant memory)

let's add a check and network:stop() to simtest test_wifi_scan.erl

-module(test_wifi_scan).

-export([start/0]).

start() ->
    {ok, _Pid} = network:start([{sta, [managed]}]),
    {ok, {Num, Networks}} = network:wifi_scan(),
    io:format("network:wifi_scan found ~p networks.~n", [Num]),
    lists:foreach(
        fun(
            _Network =
                #{
                    ssid := SSID,
                    rssi := DBm,
                    authmode := Mode,
                    bssid := BSSID,
                    channel := Number
                }
        ) ->
            io:format(
                "Network: ~p, BSSID: ~p, signal ~p dBm, Security: ~p, channel "
                "~p~n",
                [SSID, binary:encode_hex(BSSID), DBm, Mode, Number]
            )
        end,
        Networks
    ),
    true = lists:any(fun(#{ssid := SSID}) -> SSID =:= <<"Wokwi-GUEST">> end, Networks),
    ok = network:stop(),
    ok.

LLM review: feel free to pick and choose! think most importantly is the ssid data type charlist vs binary?

PR #1165 Review — Add network:wifi_scan/0,1 to ESP32 network driver

Branch: pr/1165
Commits reviewed: feef99b08 (Add network:wifi_scan/0,1) and 44e29c15a (Document new wifi functions)


Must Fix

1. SSIDs should be returned as binaries, not charlists (network_driver.c)

Severity: critical
SSIDs are currently built with term_from_string which creates Erlang charlists. This causes two problems:

  • Elixir compatibility: Elixir uses binaries as default strings. Consuming SSIDs as charlists from Elixir is awkward (~c"MyNetwork" instead of "MyNetwork"). BSSIDs are already returned as binaries, so this is inconsistent.
  • Heap sizing bug: The per-AP heap estimate uses SSID_MAX_SIZE (33) which is the raw byte count. Charlists require TERM_STRING_SIZE(32) (~64 terms) — roughly double. This under-allocation can cause stack-heap overwrite / memory corruption with longer SSIDs.
  • Memory efficiency: Binaries use significantly less heap than charlists (header + word-aligned data vs 2 terms per byte), which matters on memory-constrained chips like esp32s2.

Note: the test file test_wifi_scan.erl already assumes SSIDs are binaries (SSID =:= <<"Wokwi-GUEST">>) — this match will fail against the current charlist return.

Fix: Change term_from_string to term_from_literal_binary for SSIDs in wifi_ap_records_to_list_maybe_gc, and update the heap sizing to use TERM_BINARY_HEAP_SIZE(SSID_MAX_SIZE - 1):

// SSIDs — change from charlist to binary:
ssid = term_from_literal_binary(ap_records[i].ssid, ssid_size, heap, global);

// Heap sizing — update ap_data_size:
size_t ap_data_size = TUPLE_SIZE(2) + term_map_size_in_terms(5)
    + TERM_BINARY_HEAP_SIZE(SSID_MAX_SIZE - 1) + TERM_BINARY_HEAP_SIZE(BSSID_SIZE);

If charlists are preferred for backward compatibility, the heap sizing must still be fixed:

size_t ap_data_size = TUPLE_SIZE(2) + term_map_size_in_terms(5)
    + TERM_STRING_SIZE(SSID_MAX_SIZE - 1) + TERM_BINARY_HEAP_SIZE(BSSID_SIZE);

2. Double-wrapping {error, …} in wifi_scan (network_driver.c)

Severity: high
Several call sites in wifi_scan create an error tuple with port_create_error_tuple(ctx, BADARG_ATOM) and then pass the result to send_scan_error_reply, which internally calls port_create_error_tuple again. This produces {error, {error, badarg}} instead of the intended {error, badarg}.

Affected lines (in wifi_scan):

term error = port_create_error_tuple(ctx, BADARG_ATOM);
send_scan_error_reply(ctx, pid, ref, error);  // double-wraps!

Fix: Pass the bare reason atom directly:

send_scan_error_reply(ctx, pid, ref, BADARG_ATOM);

3. Use-after-free if esp_event_handler_unregister fails (network_driver.c)

Severity: high
In send_scan_results, if esp_event_handler_unregister fails, data (ScanClientData) is still freed. The still-registered handler can then fire again and access freed memory. This affects multiple paths:

  • Success path at end of send_scan_results
  • calloc failure for ap_records (line ~443)
  • esp_wifi_scan_get_ap_records failure (line ~465–466)
  • esp_wifi_scan_start failure in wifi_scan (line ~1488)

Fix: Only free data when esp_event_handler_unregister succeeds. If it fails, intentionally leak data (the lesser evil) and log the error.

4. Missing defensive clamp on num_results after esp_wifi_scan_get_ap_records (network_driver.c)

Severity: medium
esp_wifi_scan_get_ap_records(&num_results, ap_records) updates num_results by reference. If it were to return a value larger than what was allocated for ap_records, subsequent iteration in wifi_ap_records_to_list_maybe_gc would read out of bounds.

Fix: Save the allocated count before the call and clamp afterward:

uint16_t max_results = num_results;
esp_err_t err = esp_wifi_scan_get_ap_records(&num_results, ap_records);
if (num_results > max_results) {
    num_results = max_results;
}

Should Fix

5. Scan result misattribution race (network.erl / network_driver.c)

Severity: medium
All scan requests share the same port Ref. If a scan is cancelled and a new one is started, late results from the old scan arrive with the same Ref and can be delivered to the new scan_receiver.

Fix: Generate a per-scan ref in handle_call({scan,...}), send it to the port, and match on it in handle_info to ignore stale results.

6. Inconsistent cancel message atom (network.erl)

Severity: low
On cancellation, Pid receivers get {scan_done, {error, canceled}} but on success they get {scan_results, Results}. The mismatched wrapper atoms (scan_done vs scan_results) make it harder for receivers to handle both cases uniformly.

Fix: Use {scan_results, {error, canceled}} for Pid receivers on cancellation.


Low Priority

7. Typo in docs: "per-chanel" (network.erl)

Severity: low
Line 478: per-chanelper-channel

8. Typo in docs: "an not" (network.erl)

Severity: low
Line 489: an not in the processand not in the process


Positive Notes

  • Dynamic gen_server:call timeout calculation based on channel count and dwell time is well designed — prevents timeouts on 5 GHz capable chips.
  • Defensive calloc/malloc failure handling throughout the C driver is solid for embedded targets.
  • Immediate freeing of scan_config after esp_wifi_scan_start is correct per ESP-IDF semantics.
  • Good documentation and examples covering both blocking and callback scan modes.

Corrects the type name db() to the more correct `dbm()`, and adds a brief
edoc explanation for the value.

Signed-off-by: Winford <winford@object.stream>
ESP-IDF 5.1 has been end-of-life since December 2025 and will not
receive future security patches. Remove it from the workflow test
matrix.

Signed-off-by: Winford <winford@object.stream>
Signed-off-by: Winford <winford@object.stream>
Adds documentation for `network:wifi_scan/0,1` and updates details for
`network:sta_rssi/0`.

Signed-off-by: Winford <winford@object.stream>
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.

Feature Request: Wifi Scanning

3 participants