ESP-IDF: ESP32 Wi-Fi Scanner (Scan Nearby Networks RSSI)

In this guide, you’ll learn how to setup an ESP32 as a Wi-Fi scanner with ESP-IDF (Espressif IoT Development Framework). The Wi-Fi scanner code allows you to find nearby network access points that your ESP can establish a Wi-Fi connection.

It collects some information about each network, such as RSSI (Received Signal Strength Indicator) to indicate if the ESP32 can establish a connection with a good signal strength. It also returns the Wi-Fi channel, authentication mode (like WPA2/WPA3), and encryption cipher types.

ESP-IDF ESP32 Wi-Fi Scanner Scan Nearby Networks RSSI

Prerequisites

Before following this guide, you need to install the ESP-IDF extension on VS Code IDE (Microsoft Visual Studio Code). Follow the next guide to install it, if you haven’t already:

You will also need an ESP32 development board model of your choice.

Creating an ESP-IDF Template App Project for the ESP32

The ESP-IDF extension provides an easy way to create a project from scratch with all the required files and configurations generated automatically.

To create a new ESP-IDF project on VS Code, follow these steps:

  1. Open the ESP-IDF Espressif extension
  2. Expand the “Advanced” menu
  3. Click the “New Project Wizard” option
  4. Choose the “Use ESP-IDF v5.4.1” to select the framework version
ESP-IDF ESP32 Create Open New Project Wizard Menu

A new window opens, you need to fill in these fields:

  • Project Name: type the desired project name;
  • Enter Project Directory: click the folder icon and select the target folder to save all your project files. You can use any directory. Note: do NOT use a Google Drive / One Drive / Dropbox folder, because it will write/create many files during the building process—if it’s on a cloud folder, this process might be extremely slow;
  • ESP-IDF Target: select the target device chip, I’m using an ESP32 with the esp32s3 chip;
  • ESP-IDF Board: for the esp32s3 chip, I also need to select the configuration: ESP32-S chip (via builtin USB-JTAG);
  • Serial Port: while having your ESP32 board connected to your computer, select the correct COM port number that refers to your ESP32;
  • Choose Template: click the blue button to create a new project using a template.
ESP-IDF ESP32 Create Open New Project Wizard Menu Select Directory Board Template

In the menu, select the “ESP-IDF“, scroll down to the “wifi” section and open the “scan” example. Finally, press the “Create project using template sample project” button.

ESP-IDF ESP32 Wi-Fi-Scanner Example code

Opening the ESP-IDF Project on VS Code

After a few seconds, a notification will appear on a new window on VS Code. You can click “Open Project” to open the newly created ESP-IDF sample project template.

ESP-IDF ESP32 Open New Project Sample

IMPORTANT: if you didn’t see the notification that allows you to automatically open the ESP-IDF project on VS Code, you can easily do it by following these instructions:

Go to File > Open Folder…

ESP-IDF ESP32 Open Project Folder VS Code File Menu

Browse on your computer for the esp-idf-project folder (your project folder name that you’ve previously defined) and “Select Folder“.

ESP-IDF ESP32 Open Project VS Code Select Folder

That’s it! Your new ESP-IDF project example has been successfully created and opened.

The scanner code is in the scan.c file. To open it, follow these instructions:

  1. Open the project explorer by clicking the first icon on the left sidebar and select your project.
  2. Expand the “main” folder.
  3. Open the “scan.c” file.
  4. Your main.c file should load
ESP-IDF ESP32 Wi-Fi Scanner Open scan.c file VS Code

Code: ESP32 Wi-Fi Scanner using ESP-IDF

The ESP32 can scan nearby Wi-Fi networks within its Wi-Fi range. This can be useful to check if the Wi-Fi network you’re trying to connect is within the range of your board or other applications. Your Wi-Fi project may not often work because it may not be able to connect to your router due to insufficient Wi-Fi strength.

ESP32 Scan WiFi Networks

Copy the following code to the main.c file. This code scans for all nearby Wi-Fi networks and indicates their corresponding RSSI.

/* 
    Rui Santos & Sara Santos - Random Nerd Tutorials
    https://RandomNerdTutorials.com/esp-idf-esp32-wi-fi-scanner/
    
    This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "regex.h"

#define DEFAULT_SCAN_LIST_SIZE CONFIG_EXAMPLE_SCAN_LIST_SIZE

#ifdef CONFIG_EXAMPLE_USE_SCAN_CHANNEL_BITMAP
#define USE_CHANNEL_BITMAP 1
#define CHANNEL_LIST_SIZE 3
static uint8_t channel_list[CHANNEL_LIST_SIZE] = {1, 6, 11};
#endif /*CONFIG_EXAMPLE_USE_SCAN_CHANNEL_BITMAP*/

static const char *TAG = "scan";

static void print_auth_mode(int authmode)
{
    switch (authmode) {
    case WIFI_AUTH_OPEN:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_OPEN");
        break;
    case WIFI_AUTH_OWE:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_OWE");
        break;
    case WIFI_AUTH_WEP:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WEP");
        break;
    case WIFI_AUTH_WPA_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_PSK");
        break;
    case WIFI_AUTH_WPA2_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_PSK");
        break;
    case WIFI_AUTH_WPA_WPA2_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_WPA2_PSK");
        break;
    case WIFI_AUTH_ENTERPRISE:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_ENTERPRISE");
        break;
    case WIFI_AUTH_WPA3_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_PSK");
        break;
    case WIFI_AUTH_WPA2_WPA3_PSK:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_WPA3_PSK");
        break;
    case WIFI_AUTH_WPA3_ENTERPRISE:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_ENTERPRISE");
        break;
    case WIFI_AUTH_WPA2_WPA3_ENTERPRISE:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_WPA3_ENTERPRISE");
        break;
    case WIFI_AUTH_WPA3_ENT_192:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_ENT_192");
        break;
    default:
        ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_UNKNOWN");
        break;
    }
}

static void print_cipher_type(int pairwise_cipher, int group_cipher)
{
    switch (pairwise_cipher) {
    case WIFI_CIPHER_TYPE_NONE:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_NONE");
        break;
    case WIFI_CIPHER_TYPE_WEP40:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP40");
        break;
    case WIFI_CIPHER_TYPE_WEP104:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP104");
        break;
    case WIFI_CIPHER_TYPE_TKIP:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP");
        break;
    case WIFI_CIPHER_TYPE_CCMP:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_CCMP");
        break;
    case WIFI_CIPHER_TYPE_TKIP_CCMP:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP");
        break;
    case WIFI_CIPHER_TYPE_AES_CMAC128:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_AES_CMAC128");
        break;
    case WIFI_CIPHER_TYPE_SMS4:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_SMS4");
        break;
    case WIFI_CIPHER_TYPE_GCMP:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_GCMP");
        break;
    case WIFI_CIPHER_TYPE_GCMP256:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_GCMP256");
        break;
    default:
        ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_UNKNOWN");
        break;
    }

    switch (group_cipher) {
    case WIFI_CIPHER_TYPE_NONE:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_NONE");
        break;
    case WIFI_CIPHER_TYPE_WEP40:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP40");
        break;
    case WIFI_CIPHER_TYPE_WEP104:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP104");
        break;
    case WIFI_CIPHER_TYPE_TKIP:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP");
        break;
    case WIFI_CIPHER_TYPE_CCMP:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_CCMP");
        break;
    case WIFI_CIPHER_TYPE_TKIP_CCMP:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP");
        break;
    case WIFI_CIPHER_TYPE_SMS4:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_SMS4");
        break;
    case WIFI_CIPHER_TYPE_GCMP:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_GCMP");
        break;
    case WIFI_CIPHER_TYPE_GCMP256:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_GCMP256");
        break;
    default:
        ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_UNKNOWN");
        break;
    }
}

#ifdef USE_CHANNEL_BITMAP
static void array_2_channel_bitmap(const uint8_t channel_list[], const uint8_t channel_list_size, wifi_scan_config_t *scan_config) {

    for(uint8_t i = 0; i < channel_list_size; i++) {
        uint8_t channel = channel_list[i];
        scan_config->channel_bitmap.ghz_2_channels |= (1 << channel);
    }
}
#endif /*USE_CHANNEL_BITMAP*/


/* Initialize Wi-Fi as sta and set scan method */
static void wifi_scan(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    uint16_t number = DEFAULT_SCAN_LIST_SIZE;
    wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
    uint16_t ap_count = 0;
    memset(ap_info, 0, sizeof(ap_info));


    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());

#ifdef USE_CHANNEL_BITMAP
    wifi_scan_config_t *scan_config = (wifi_scan_config_t *)calloc(1,sizeof(wifi_scan_config_t));
    if (!scan_config) {
        ESP_LOGE(TAG, "Memory Allocation for scan config failed!");
        return;
    }
    array_2_channel_bitmap(channel_list, CHANNEL_LIST_SIZE, scan_config);
    esp_wifi_scan_start(scan_config, true);
    free(scan_config);

#else
    esp_wifi_scan_start(NULL, true);
#endif /*USE_CHANNEL_BITMAP*/

    ESP_LOGI(TAG, "Max AP number ap_info can hold = %u", number);
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
    ESP_LOGI(TAG, "Total APs scanned = %u, actual AP number ap_info holds = %u", ap_count, number);
    for (int i = 0; i < number; i++) {
        ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
        ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
        print_auth_mode(ap_info[i].authmode);
        if (ap_info[i].authmode != WIFI_AUTH_WEP) {
            print_cipher_type(ap_info[i].pairwise_cipher, ap_info[i].group_cipher);
        }
        ESP_LOGI(TAG, "Channel \t\t%d", ap_info[i].primary);
    }
}

void app_main(void)
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK( ret );

    wifi_scan();
}

View raw code

Change the Scan List Size

By default, the scan example tries to find a maximum of 10 networks. Follow these steps to change it:

  1. Click the gear icon to open the SDK Configuration Editor (menu config)
  2. Search for “Max size of scan list
  3. You can edit max size of scan list with another number to show more/less results
  4. Press the “Save” button to apply the changes
ESP-IDF ESP32 Wi-Fi Scanner change max size of scan list configs

How the Code Works

In this section, we’ll do a quick breakdown of the ESP-IDF Wi-Fi scanner code.

Header Includes and Global Configuration Variables

Starting by including the necessary libraries and global configuration variables. As described in the previous section, you can change the DEFAULT_SCAN_LIST_SIZE in the SDK Configuration Editor.

print_auth_mode() and print_cipher_type

There are two helper functions that print info in a readable format:

  • print_auth_mode(int authmode): uses a switch statement to map to values to the authentication mode used by the access point (example: WIFI_AUTH_OPEN, WIFI_AUTH_WPA3_PSK, etc…);
  • print_cipher_type(int pairwise_cipher, int group_cipher): displays both pairwise and group cipher suites used by the access point (example: WIFI_CIPHER_TYPE_CCMP for TKIP).

wifi_scan()

The wifi_scan() function runs once and it’s called in the app_main. It initializes the network interface, default event loop, creates a station interface, and starts the Wi-Fi driver with default configuration.

It uses the ESP_ERROR_CHECK() when executing each function to check for potential errors during the Wi-Fi interface initialization and scan process:

ESP_ERROR_CHECK(esp_wifi_start());

Finally, it loops and logs all the key information in the Terminal window: how many Access Points (APs) were scanned, their SSID, RRSI, authentication mode, encryption cipher types, and Wi-Fi channel.

ESP_LOGI(TAG, "Max AP number ap_info can hold = %u", number);
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
ESP_LOGI(TAG, "Total APs scanned = %u, actual AP number ap_info holds = %u", ap_count, number);
for (int i = 0; i < number; i++) {
    ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
    ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
    print_auth_mode(ap_info[i].authmode);
    if (ap_info[i].authmode != WIFI_AUTH_WEP) {
        print_cipher_type(ap_info[i].pairwise_cipher, ap_info[i].group_cipher);
    }
    ESP_LOGI(TAG, "Channel \t\t%d", ap_info[i].primary);
}

Build and Flash Code to the ESP32 Board

To build and flash ESP-IDF code to the ESP32, you always need to follow this procedure. You need to select the flash method (UART), the COM port number, the target device (ESP32), build the code, and finally, flash it to the board. All these commands are available in the bottom menu bar of VS Code.

Make sure all your options are correct (they may already be properly configured if you used the project wizard).

VS Code ESP-IDF Check All the Configured Settings UART COM Port Target Board

However, if your setup is not correct, follow the next instructions to ensure everything is set up correctly. First, click the “Star” icon and select the flash method as UART.

VS Code ESP-IDF Select Flash UART Option to Program Flash ESP32

While the ESP32 board is connected to your computer, click the COM Port (plug icon) and select the correct port number that refers to your ESP32.

VS Code ESP-IDF Programming ESP32 Board Select Correct COM Port Number

You also need to select the target device. Click on the chip icon at the bottom bar. In my case, I have an ESP32 with the esp32s3 chip.

VS Code Select the ESP32 S3 or Correct Target Device ESP-IDF

For this board, I also need to select the configuration: ESP32-S chip (via builtin USB-JTAG).

VS Code ESP-IDF Select the ESP32 S3 chip via built in USB JTAG Target Device

Finally, your command bar on the bottom of VS Code should have similar options selected.

VS Code ESP-IDF Check All the Configured Settings UART COM Port Target Board

Now, you can build the project by clicking the wrench icon (Build Project) as shown in the image below.

VS Code Build Project Example Code ESP32 ESP-IDF

The first time you build a project, it usually takes a bit more time. Once completed, it should print a similar message in the Terminal menu and show a “Build Successfully” message.

VS Code Build Example Project ESP32 ESP-IDF Success Message

This is the final step. You can now flash the ESP-IDF project to the ESP32 by clicking the “Flash Device” button (thunder icon).

VS Code Flash Hello World Code Project to ESP32 ESP-IDF

Depending on your board, you might need to hold down the on-board BOOT button on your ESP32 to put into flashing mode. Once the process is completed, it will pop-up a info message saying “Flash Done“.

VS Code Flash Hello World Project to ESP32 ESP-IDF Done Success Message

Demonstration

If you followed all the steps, the Wi-Fi scanner example should be running successfully on your board. Open your Terminal window — click the “Monitor Device” tool that is illustrated with a screen icon.

VS Code Open Terminal Window Monitor Device ESP32 ESP-IDF

The terminal should be printing some information regarding each network nearby:

I (3033) scan: SSID             MEO-20------
I (3033) scan: RSSI             -52
I (3033) scan: Authmode         WIFI_AUTH_WPA2_PSK
I (3033) scan: Pairwise Cipher  WIFI_CIPHER_TYPE_CCMP
I (3043) scan: Group Cipher     WIFI_CIPHER_TYPE_CCMP
I (3043) scan: Channel          11
ESP-IDF ESP32 Wi-Fi Scanner Terminal Serial Monitor Demonstration VS Code

You can press the ESP32 on-board RESET button to restart and see new scan results.

Wrapping Up

In this tutorial, you learned how to setup the ESP32 as a Wi-Fi scanner with the ESP-IDF framework using VS Code to find nearby networks and their corresponding signal strenght.

You might find helpful reading other ESP-IDF guides:

Meanwhile, you can check our ESP32 resources (with Arduino IDE) to learn more about the ESP32 board:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.