The main feature of the esp32 chip, as it was for its predecessor esp8266, is for sure the ability to connect to wifi networks. I’ve already blogged in a previous article about the different standards (802.11 b/g/n) and security methods (WEP, WPA-PSK…) the chip supports; today I’m going to explain how to develop a program to scan for available wifi networks.
The complete program is available in my Github repository; let’s comment the source code.
As we know, every program using the esp-idf framework is executed starting from the app_main() method. This method starts initializing the tcpip stack and the wifi event handler:
tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); |
The esp-idf wifi stack requires a method (= event handler) that is called every time an event related to the wifi interface is triggered (for example the connection to a new network…). We’ll talk in a future tutorial about event handling, for now it’s enough to define an empty handler method:
static esp_err_t event_handler(void *ctx, system_event_t *event) { return ESP_OK; } |
The program continues configuring, initializing and starting the wifi stack:
wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&wifi_config)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); |
You can create a default configuration with the WIFI_INIT_CONFIG_DEFAULT() macro and start the wifi interface in station mode with the constant WIFI_MODE_STA. Station mode is when the interface acts as a “client”, to connect to an access point.
Now you can configure and start the scan process:
wifi_scan_config_t scan_config = { .ssid = 0, .bssid = 0, .channel = 0, .show_hidden = true }; ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true)); |
Using the scan_config variable you can configure some filters the scanning process will use (for example scan only a given channel) and ask to display or not the access points with hidden SSID. A zero value for each field means ALL (= no filter).
The scanning process can be executed in blocking (true) or non blocking (false) mode changing the second parameter of the function esp_wifi_scan_start. In this example I’m using the blocking mode: program execution is halted until the scan is completed.
When the scan is complete, you can obtain the list of the detected networks:
uint16_t ap_num = MAX_APs; wifi_ap_record_t ap_records[MAX_APs]; ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, ap_records)); |
The esp_wifi_scan_get_ap_records method returns an array of wifi_ap_record_t elements. You have to reserve some memory space to store that array, defining the maximum number of elements you want to retrieve. In my program, I defined therefore the MAX_APs constant:
#define MAX_APs 20 |
You can now print on the serial connection the details of the networks found:
printf("Found %d access points:\n", ap_num); for(int i = 0; i < ap_num; i++) printf("%32s | %7d | %4d | %12s\n", (char *)ap_records[i].ssid, ap_records[i].primary, ap_records[i].rssi, getAuthModeName(ap_records[i].authmode)); |
At the end, create an empty task to avoid a continuous reset of the chip:
xTaskCreate(&loop_task, "loop_task", 2048, NULL, 5, NULL); [...] void loop_task(void *pvParameter) { while(1) { vTaskDelay(1000 / portTICK_RATE_MS); } } |
During the program compilation, remember to activate the wifi module in the menuconfig (Component config – ESP32-specific config):
Here’s the output of the program:
Salve, Un piccolo dubbio , sulla funzione che crea un nuovo task:
xTaskCreate(&loop_task, “loop_task”, 2048, NULL, 5, NULL);
2048 è lo usStackDepth, mi sono spempre chiesto come va dimensionato?
Bisogna considerare tutte la variabili dentro il task “loop_task”, ma bisogna anche considerare variabili esterne dichiarate globali e che vengono usate all’interno di “loop_task”?
Articolo molto interessante Luca, grazie!
Oltre a questa funzione, sarebbe possibile verificare se un determinato MAC address è collegato a una specifica rete wifi ?
Questo permetterebbe di verificare anche da remoto se un determinato dispositivo è fisicamente presente nel raggio della rete wifi.
Esempi?
Grazie 1000
Luciano
Ciao Luciano, per sapere se un MAC è collegato ad una rete wifi dovresti interrogare l’access point che gestisce quella wifi o – solo se il device fa “traffico” – intercettarlo. Non mi pare però che il chip esp32 attualmente consenta di attivare la modalità “promiscua” per analizzare il traffico, mi dispiace
Buongiorno Luca, sto iniziando ora con l’ESP32 e sto seguendo passo passo i tuoi ottimi tutorial.
Nell’articolo dici che è fondamentale attivare il modulo wifi, ma quando vado in Component config – ESP32-specific config, nel menu non trovo la voce WiFi (e neppure BT).
In seguito, il programma trova un errore nella esp_wifi_init e va in reboot continuo.
Manca qualche configurazione del mio modulo?
Grazie
Ciao! Se stai utilizzando l’ultima versione (3.0) del framework non è più necessario attivare la wifi da menuconfig… sto aggiornando tutti gli articoli. Che errore ti da la wifi_init?
Ciao, anche a me da lo stesso errore:
I (0) cpu_start: Starting scheduler on APP CPU.
W (22) wifi: wifi nvs_open fail ret=4353
E (22) wifi: wifi_init 1408 ret=4353
ESP_ERROR_CHECK failed: esp_err_t 0x1101 at 0x40106a5e
0x40106a5e: app_main at /home/pi/esp/esp32-tutorial/01_wifi_scanner/main/./main.c:43 (discriminator 1)
file: “/home/pi/esp/esp32-tutorial/01_wifi_scanner/main/./main.c” line 43
func: app_main
expression: esp_wifi_init(&wifi_config)
Backtrace: 0x4008751c:0x3ffba550 0x40087889:0x3ffba570 0x40106a5e:0x3ffba590 0x400d0e7a:0x3ffbab20
0x4008751c: invoke_abort at /home/pi/esp/esp-idf/components/esp32/./panic.c:572
0x40087889: _esp_error_check_failed at /home/pi/esp/esp-idf/components/esp32/./panic.c:584
0x40106a5e: app_main at /home/pi/esp/esp32-tutorial/01_wifi_scanner/main/./main.c:43 (discriminator 1)
0x400d0e7a: main_task at /home/pi/esp/esp-idf/components/esp32/./cpu_start.c:456
Rebooting…
ciao, ho appena sistemato l’esempio su github per renderlo compatibile con le nuove versioni del framework
Ciao Luca, sto seguendo i tuoi tutorial per muovere i primi passi con l’ esp32. Provando questo esempio avevo un errore nella wifi_init, come successo a Gian Luigi.
Per risolvere ho aggiunto la parte di inizializzazione dell’NVS prima della scansione.
#include “nvs_flash.h”
…….
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
così facendo tutto funziona correttamente.
Hello,
It’s really a fantastic work, I really appreciate you for what you’ve done..!
I have a question, can this code be modified in a way that we can do periodic scan of surrounding Wi-Fi networks? I tried by creating a new task and putting these lines inside the while loop.
// initialize the tcp stack
tcpip_adapter_init();
// initialize the wifi event handler
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
// configure, initialize and start the wifi driver
wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&wifi_config));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
// configure and run the scan process in blocking mode
wifi_scan_config_t scan_config = {
.ssid = 0,
.bssid = 0,
.channel = 0,
.show_hidden = true
};
printf(“Start scanning…”);
ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true));
printf(” completed!\n”);
printf(“\n”);
// get the list of APs found in the last scan
uint16_t ap_num = MAX_APs;
wifi_ap_record_t ap_records[MAX_APs];
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, ap_records));
// print the list
printf(“Found %d access points:\n”, ap_num);
printf(“\n”);
printf(” SSID | Channel | RSSI | Auth Mode \n”);
printf(“—————————————————————-\n”);
for(int i = 0; i < ap_num; i++)
{
printf("%32s | %7d | %4d | %12s\n", (char *)ap_records[i].ssid, ap_records[i].primary, ap_records[i].rssi, getAuthModeName(ap_records[i].authmode));
}
printf("—————————————————————-\n");
But the build shows a lot of errors. I understand that I tried calling some undeclared variables but I cannot figure it out properly. Can you just reply how it could be done in a better way?
Many thanks
Rakesh