ESP32 (5) – Wifi scanner

luca 09/01/2017 9

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));
ESP_ERROR_CHECK is a handy macro, defined in esp_err.h, to check the result of the different framework methods. If a call returns the constant ESP_OK, the execution of the program can proceed; if not the program is stopped and the line in error is displayed on the serial output.

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
It may sound strange to pass the ap_num variable by reference to the esp_wifi_scan_get_ap_records method. The reason is that the method uses the variable in two ways (inout): as input to find the max capacity of the array passed as the second parameter and as output to return the effective number of networks found and stored in the array.

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

esp32-wifi01

Here’s the output of the program:

esp-scan02

9 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

  3. Gian Luigi Baldini 06/12/2017 at 10:33 - Reply

    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

    • luca 10/12/2017 at 17:01 - Reply

      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?

  4. ciro 15/12/2017 at 01:12 - Reply

    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…

    • luca 03/01/2018 at 21:28 - Reply

      ciao, ho appena sistemato l’esempio su github per renderlo compatibile con le nuove versioni del framework

  5. Paolo 24/12/2017 at 12:35 - Reply

    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.

  6. Rakesh 26/03/2018 at 06:44 - Reply

    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

Leave A Response »

This website uses cookies to ensure you get the best experience on our website 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