ESP32 (5) – Wifi scanner

luca 09/01/2017 3

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));
ESP_ERROR_CHECK è una comoda macro, definita in esp_err.h, per controllare l’esito delle diverse chiamate al framework. Se la chiamata restituisce la costante ESP_OK l’esecuzione del programma procede, altrimenti il programma si interrompe e la riga in errore è visualizzata sul collegamento seriale.

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
Può sembrare strano passare la variabile ap_num come riferimento al metodo esp_wifi_scan_get_ap_records. Il motivo è che tale metodo utilizza la variabile in due modi (inout): viene utilizzata in input come massima capacità dell’array che viene passato come secondo parametro e in output per restituire il numero effettivo di reti trovate e memorizzate nell’array.

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):

esp32-wifi01

Ecco l’output del programma:

esp-scan02

3 Comments »

  1. Michele 11/01/2017 at 10:02 - Reply

    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”?

  2. Luciano Veronese 15/01/2017 at 12:43 - Reply

    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

    • luca 16/01/2017 at 08:53 - Reply

      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

Leave A Response »

Questo sito usa i cookie per poterti offrire una migliore esperienza di navigazione maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.

Chiudi