La caratteristica principale del chip esp32, come per il predecessore esp8266, è sicuramente la possibilità di collegarsi a reti wifi. Come abbiamo visto nel primo articolo di questa serie, il chip esp32 supporta diversi standard (802.11 b/g/n) e diverse modalità di sicurezza (WEP, WPA-PSK…). Oggi vedremo come scrivere un programma per effettuare lo scan delle reti wifi disponibili.
Il programma completo è disponibile nel mio repository su Github; analizziamone nel dettaglio il codice sorgente.
Come sappiamo ogni programma che utilizza il framework esp-idf viene eseguito a partire dal metodo app_main(). Tale metodo inizia con l’inizializzazione dello stack tcp e dell’event handler wifi:
tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); |
Lo stack wifi prevede la definizione di un metodo (= event handler), chiamato ogni volta che avviene un evento legato all’interfaccia wifi (es. collegamento ad una nuova rete…). Parleremo più diffusamente della gestione eventi nel prossimo tutorial; per questo esempio è sufficiente definire un event handler vuoto:
static esp_err_t event_handler(void *ctx, system_event_t *event) { return ESP_OK; } |
Il programma prosegue con la configurazione, l’inizializzazione e l’avvio dello stack wifi:
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()); |
Utilizziamo la configurazione di default con la macro WIFI_INIT_CONFIG_DEFAULT() e attiviamo l’interfaccia wifi in modalità station (WIFI_MODE_STA), ovvero come “client” per il collegamento ad un access point.
Possiamo ora configurare e lanciare il processo di scan:
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)); |
Tramite la variabile scan_config è possibile impostare dei filtri utilizzati dal processo di scansione (es. effettuare lo scan solo di un determinato canale) e indicare se visualizzare o meno gli access points con SSID nascosto. Il valore zero (0) in un campo indica TUTTI (= nessun filtro).
Il processo di scansione può essere eseguito in modalità blocking (true) o non blocking (false) utilizzando il secondo parametro della funzione esp_wifi_scan_start. In questo esempio utilizzo la modalità blocking: l’esecuzione del programma si ferma fino al termine della scansione.
Al termine del processo di scansione è possibile ottenere l’elenco delle reti wireless rilevate:
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)); |
Il metodo esp_wifi_scan_get_ap_records restituisce un array di elementi di tipo wifi_ap_record_t. E’ quindi necessario riservare dello spazio in memoria per ospitare tale array, identificando il massimo numero di elementi che vogliamo ottenere. Nel programma ho quindi definito la costante MAX_APs:
#define MAX_APs 20 |
E’ quindi possibile visualizzare su seriale le reti trovate:
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)); |
Infine creiamo un task vuoto per evitare che il chip sia continuamente resettato:
xTaskCreate(&loop_task, "loop_task", 2048, NULL, 5, NULL); [...] void loop_task(void *pvParameter) { while(1) { vTaskDelay(1000 / portTICK_RATE_MS); } } |
In fase di compilazione del programma, è fondamentale attivare il modulo wifi dal menu di configurazione (Component config – ESP32-specific config):
Ecco l’output del programma:
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