docs/en/openthread/openthread_scan.rst
################## OThreadScan Class ##################
The OThreadScan class discovers nearby Thread networks using MLE discover
(otThreadDiscover() / OpenThread CLI discover). Each result is an
OThreadNetworkInfo structure with Thread identity fields (network name,
Extended PAN ID, joinable flag) and IEEE 802.15.4 link fields (extended
address, PAN ID, channel, RSSI, LQI).
This is the same primitive Matter uses to list Thread networks during
commissioning. The global instance is OThreadScan (same pattern as
OThreadUDP and OThreadCoAPServer).
Key Features:
discoverNetworks() — no separate 802.15.4 or energy
scan modes in the public API.scanComplete()), and streaming callbacks
(onResult() / onComplete()).otThreadDiscover() parameters.OT_DISCOVER_RUNNING (-1),
OT_DISCOVER_FAILED (-2).OT_DISCOVER_MAX_RESULTS, default 16) before
each scan so the OpenThread callback path does not heap-allocate while the OT
API lock is held.Use Cases:
Prerequisites:
OThread.begin() and OThread.networkInterfaceUp() — Thread does not
need to be started (matches Matter pre-commission discovery).Include the header:
.. code-block:: arduino
#include <OThreadScan.h>
Each discovery response is stored as OThreadNetworkInfo:
+------------------------+--------------------------------------------------------------+
| Field | Description |
+========================+==============================================================+
| networkName | Thread network name (null-terminated) |
+------------------------+--------------------------------------------------------------+
| extendedPanId | Extended PAN ID (8 bytes) |
+------------------------+--------------------------------------------------------------+
| panId | IEEE 802.15.4 PAN ID |
+------------------------+--------------------------------------------------------------+
| extAddress | Responder extended address (8 bytes) |
+------------------------+--------------------------------------------------------------+
| channel | IEEE 802.15.4 channel (11..26) |
+------------------------+--------------------------------------------------------------+
| rssi | Received signal strength (dBm) |
+------------------------+--------------------------------------------------------------+
| lqi | Link Quality Indicator |
+------------------------+--------------------------------------------------------------+
| threadVersion | Thread version (4-bit MLE value) |
+------------------------+--------------------------------------------------------------+
| joinable | Joining permitted (non-empty Steering Data for MLE discover) |
+------------------------+--------------------------------------------------------------+
| nativeCommissioner | Native Commissioner flag when present |
+------------------------+--------------------------------------------------------------+
Helper methods: networkNameStr(), extendedPanIdStr(),
extAddressStr().
Optional filters for setDiscoverFilters() (defaults match CLI
discover with no extra arguments):
.. code-block:: arduino
OThreadDiscoverFilters filters;
filters.panIdFilter = OT_PANID_BROADCAST; // 0xffff = no PAN filter
filters.joinerOnly = false;
filters.eui64Filter = false;
OThreadScan.setDiscoverFilters(filters);
Configuration
setScanTimeout ^^^^^^^^^^^^^^
.. code-block:: arduino
void setScanTimeout(uint32_t ms);
Maximum time to wait for blocking discovery or async completion. Default:
OT_DISCOVER_DEFAULT_TIMEOUT_MS (30000 ms).
OT_DISCOVER_MAX_RESULTS ^^^^^^^^^^^^^^^^^^^^^^^
Maximum unique networks stored per scan (default 16). Vectors are
pre-reserved to this size before each scan. Define the macro before including
OThreadScan.h to override:
.. code-block:: arduino
#define OT_DISCOVER_MAX_RESULTS 32
#include <OThreadScan.h>
Or pass -DOT_DISCOVER_MAX_RESULTS=32 at compile time. See
ThreadScan_Discover <https://github.com/espressif/arduino-esp32/tree/master/libraries/OpenThread/examples/Native/ThreadScan/ThreadScan_Discover>_
for a sketch example.
setChannel ^^^^^^^^^^
.. code-block:: arduino
void setChannel(uint8_t channel);
Limit discovery to one IEEE 802.15.4 channel (11..26). Pass 0 to scan all
supported channels (channel mask 0 in otThreadDiscover()).
setDiscoverFilters ^^^^^^^^^^^^^^^^^^
.. code-block:: arduino
void setDiscoverFilters(const OThreadDiscoverFilters &filters);
Apply PAN / joiner / EUI-64 filters before the next discoverNetworks()
call.
Callbacks
onResult ^^^^^^^^
.. code-block:: arduino
void onResult(OThreadDiscoverResultCallback callback, void *context = nullptr);
Called for each network as OpenThread receives a Discovery Response. Matches the OpenThread / Matter streaming model.
onComplete ^^^^^^^^^^
.. code-block:: arduino
void onComplete(OThreadDiscoverCompleteCallback callback, void *context = nullptr);
Called when discovery finishes. resultCount is the number of networks
collected, or OT_DISCOVER_FAILED on failure. error is the OpenThread
error from the start request (OT_ERROR_NONE on success).
Discovery
discoverNetworks ^^^^^^^^^^^^^^^^
.. code-block:: arduino
int16_t discoverNetworks(bool async = false);
Start MLE Thread network discovery.
async == false — blocks until complete or setScanTimeout() expires;
returns network count on success.async == true — returns OT_DISCOVER_RUNNING immediately on success.Return values:
>= 0 — number of networks found (blocking mode, or scanComplete() when
done).OT_DISCOVER_RUNNING (-1) — discovery in progress.OT_DISCOVER_FAILED (-2) — failed, timed out, or not started.At most OT_DISCOVER_MAX_RESULTS unique networks (default 16) are stored. Vectors are
pre-reserved before the scan starts so the OpenThread callback does not allocate
while the API lock is held. Multiple discovery responses with the same Extended
PAN ID are merged in storage (strongest RSSI kept). onResult() is still
invoked for every Discovery Response. Additional unique networks beyond the
cap are still delivered through onResult() but are omitted from
getResult() / getResultCount().
scanComplete ^^^^^^^^^^^^
.. code-block:: arduino
int16_t scanComplete();
Poll async discovery state from loop(). Same return semantics as
discoverNetworks() in blocking/async modes. Completion is reported only
after the final discovery callback sets internal state; do not treat
otThreadIsDiscoverInProgress() as a substitute for scanComplete().
scanDelete ^^^^^^^^^^
.. code-block:: arduino
void scanDelete();
Release stored results and free associated memory (internal vector capacity
is released, not just cleared). Call after processing results. This is a no-op
until the final discovery callback has completed, even if OpenThread already
reports discover idle; call again after the scan finishes. After a timeout it
likewise stays a no-op until OpenThread delivers that final callback — retry
once isDiscoverInProgress() returns false.
isDiscoverInProgress ^^^^^^^^^^^^^^^^^^^^
.. code-block:: arduino
bool isDiscoverInProgress() const;
Returns true while an MLE discovery scan is in progress.
Results
Result access (WiFiScan-style) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Indexed result APIs (getResult(), getResultCount(), field getters, and
getActiveScanResult()) are available only after discovery completes:
discoverNetworks() return value >= 0scanComplete() return value >= 0onComplete() has firedWhile a scan is still running, these methods return empty values (count 0,
false, nullptr). Use onResult() to handle networks as they are
discovered. Do not call other OThreadScan methods from inside
onResult() or onComplete() (they run on the OpenThread task with the
API lock held).
getResultCount ^^^^^^^^^^^^^^
.. code-block:: arduino
uint16_t getResultCount() const;
Number of entries from the last completed discovery (saturated at
UINT16_MAX if the internal list is larger). Returns 0 while discovery
is still in progress.
getResult ^^^^^^^^^
.. code-block:: arduino
const OThreadNetworkInfo &getResult(uint16_t index) const;
bool getResult(uint16_t index, OThreadNetworkInfo &info) const;
Access one result by index (0 .. count-1). Internal storage is valid until
scanDelete(). Returns a static empty entry while discovery is still in
progress or if the index is out of range.
Index-based convenience getters (networkName(), panId(),
extendedPanIdStr(), extAddressStr(), channel(), rssi(),
lqi(), isJoinable(), threadVersion(), isNativeCommissioner())
delegate to getResult(index).
getActiveScanResult ^^^^^^^^^^^^^^^^^^^
.. code-block:: arduino
const otActiveScanResult *getActiveScanResult(uint16_t index) const;
Raw OpenThread result for advanced use. Valid until scanDelete(). Returns
nullptr while discovery is still in progress or if the index is out of range.
Blocking (WiFiScan-style)
.. code-block:: arduino
#include <OThread.h>
#include <OThreadScan.h>
void setup() {
Serial.begin(115200);
OThread.begin(false);
OThread.networkInterfaceUp();
int n = OThreadScan.discoverNetworks();
if (n >= 0) {
for (int i = 0; i < n; ++i) {
const OThreadNetworkInfo &net = OThreadScan.getResult(i);
Serial.println(net.networkNameStr());
}
OThreadScan.scanDelete();
}
}
Async poll (WiFiScanAsync-style)
.. code-block:: arduino
OThreadScan.discoverNetworks(true);
void loop() {
int16_t status = OThreadScan.scanComplete();
if (status >= 0) {
// process OThreadScan.getResult(i) ...
OThreadScan.scanDelete();
}
}
Streaming callbacks
.. code-block:: arduino
static void onNetwork(const OThreadNetworkInfo &info, void *) {
Serial.println(info.networkNameStr());
}
static void onDone(int16_t resultCount, otError error, void *) {
// Summary only — do not call scanDelete() here (OT API lock held).
Serial.printf("discovery done: %d\n", resultCount);
}
void setup() {
OThreadScan.onResult(onNetwork);
OThreadScan.onComplete(onDone);
}
void loop() {
OThreadScan.discoverNetworks(true);
while (OThreadScan.scanComplete() == OT_DISCOVER_RUNNING) {
delay(50);
}
OThreadScan.scanDelete();
}
Native API examples (libraries/OpenThread/examples/Native/ThreadScan/):
ThreadScan_Discover <https://github.com/espressif/arduino-esp32/tree/master/libraries/OpenThread/examples/Native/ThreadScan/ThreadScan_Discover>_ — blocking discovery.ThreadScan_Async <https://github.com/espressif/arduino-esp32/tree/master/libraries/OpenThread/examples/Native/ThreadScan/ThreadScan_Async>_ — scanComplete() polling.ThreadScan_Callback <https://github.com/espressif/arduino-esp32/tree/master/libraries/OpenThread/examples/Native/ThreadScan/ThreadScan_Callback>_ — onResult() / onComplete().CLI equivalent: CLI ThreadScan <https://github.com/espressif/arduino-esp32/tree/master/libraries/OpenThread/examples/CLI/ThreadScan>_ (discover command via OThreadCLI).
No networks found
SimpleThreadNetwork LeaderNode <https://github.com/espressif/arduino-esp32/tree/master/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode>_).OThread.networkInterfaceUp() was called before
discoverNetworks().OT_DISCOVER_FAILED
networkInterfaceUp() and increase setScanTimeout() if needed.OT_DISCOVER_RUNNING
scanComplete() to report
completion before starting another scan or calling scanDelete().Wrong network listed
extendedPanId with the target
Leader.openthread — overview and Native vs CLI comparison.openthread_core — OThread.begin(), networkInterfaceUp().openthread_cli — CLI discover command.