ESP32 (5) – Wifi scanner

by luca
9 comments

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

Related Posts

9 comments

Michele Wednesday January 11th, 2017 - 10:02 AM

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

Reply
Luciano Veronese Sunday January 15th, 2017 - 12:43 PM

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

Reply
luca Monday January 16th, 2017 - 08:53 AM

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

Reply
Gian Luigi Baldini Wednesday December 6th, 2017 - 10:33 AM

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

Reply
luca Sunday December 10th, 2017 - 05:01 PM

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?

Reply
ciro Friday December 15th, 2017 - 01:12 AM

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…

Reply
luca Wednesday January 3rd, 2018 - 09:28 PM

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

Reply
Paolo Sunday December 24th, 2017 - 12:35 PM

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.

Reply
Rakesh Monday March 26th, 2018 - 06:44 AM

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

Reply

Leave a Comment