Building a Wi-Fi Network Scanner with ESP32 and ESP-IDF

Learn how to create a Wi-Fi network scanner using the ESP32 microcontroller and ESP-IDF framework. This step-by-step guide walks you through coding a program to scan nearby Wi-Fi networks, logging details like SSIDs, signal strength, and encryption types to the Serial Monitor. Perfect for IoT enthusiasts and embedded developers!

Introduction

The ESP32, a powerful and versatile microcontroller from Espressif Systems, is a favorite among IoT developers due to its dual-core processor, built-in Wi-Fi, and Bluetooth capabilities. One exciting application of the ESP32 is scanning nearby Wi-Fi networks to gather information such as Service Set Identifiers (SSIDs), signal strength (RSSI), and encryption types. This can be useful for network diagnostics, IoT device placement, or even educational projects to understand wireless environments.

In this blog post, we’ll build a Wi-Fi network scanner using the ESP32 and the ESP-IDF (Espressif IoT Development Framework), the official development framework for ESP32. The scanner will periodically scan for nearby Wi-Fi access points and log their details to the Serial Monitor. By the end, you’ll have a working project and a solid understanding of ESP32 Wi-Fi programming. Let’s dive in!

Prerequisites

Before we start, ensure you have the following:

Hardware

  • ESP32 Development Board: Any ESP32-based board (e.g., ESP32-WROOM-32 DevKit) will work.
  • USB Cable: For programming and powering the ESP32.
  • Computer: With a USB port and internet access.

Software

  • ESP-IDF: Install the ESP-IDF framework. It is recommended to use Docker to have a consistent development environment. Check out our guide to setup ESP-IDF with Docker.
  • Serial Monitor: A tool like the ESP-IDF’s built-in monitor (idf.py monitor) or PuTTY to view Serial output.
  • Code Editor: VS Code with the ESP-IDF extension or any text editor.

Knowledge

  • Basic understanding of C programming.
  • Familiarity with ESP32 and ESP-IDF basics (e.g., setting up a project, flashing firmware).
  • Knowledge of Wi-Fi concepts like SSIDs, RSSI, and encryption types.

The Wi-Fi Scanner Code

The core of this project is a C program that uses ESP-IDF’s Wi-Fi APIs to scan for nearby access points. The code initializes the ESP32’s Wi-Fi module in station mode, performs periodic scans, and logs details to the Serial Monitor.

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#define WIFI_SCAN_TASK_STACK_SIZE 4096
#define WIFI_SCAN_TASK_PRIORITY 2

static const char *TAG = "wifi_scanner";

static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Wi-Fi scan completed");
    }
}

static void wifi_scan(void *pvParameters) {
    wifi_scan_config_t scan_config = {
        .ssid = NULL,
        .bssid = NULL,
        .channel = 0,
        .show_hidden = true,
        .scan_type = WIFI_SCAN_TYPE_ACTIVE,
        .scan_time.active.min = 100,
        .scan_time.active.max = 300
    };

    while (1) {
        ESP_LOGI(TAG, "Starting Wi-Fi scan...");
        ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true));

        uint16_t ap_count = 0;
        esp_wifi_scan_get_ap_num(&ap_count);
        wifi_ap_record_t *ap_list = (wifi_ap_record_t *)malloc(ap_count * sizeof(wifi_ap_record_t));
        if (ap_list == NULL) {
            ESP_LOGE(TAG, "Failed to allocate memory for AP list");
            vTaskDelay(5000 / portTICK_PERIOD_MS);
            continue;
        }

        ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_list));
        ESP_LOGI(TAG, "Found %d access points:", ap_count);

        for (uint16_t i = 0; i < ap_count; i++) {
            wifi_ap_record_t *ap = &ap_list[i];
            const char *auth_mode;

            switch (ap->authmode) {
                case WIFI_AUTH_OPEN: auth_mode = "Open"; break;
                case WIFI_AUTH_WEP: auth_mode = "WEP"; break;
                case WIFI_AUTH_WPA_PSK: auth_mode = "WPA-PSK"; break;
                case WIFI_AUTH_WPA2_PSK: auth_mode = "WPA2-PSK"; break;
                case WIFI_AUTH_WPA_WPA2_PSK: auth_mode = "WPA/WPA2-PSK"; break;
                case WIFI_AUTH_WPA3_PSK: auth_mode = "WPA3-PSK"; break;
                case WIFI_AUTH_WPA2_ENTERPRISE: auth_mode = "WPA2-Enterprise"; break;
                default: auth_mode = "Unknown"; break;
            }

            ESP_LOGI(TAG, "SSID: %-32s | RSSI: %d dBm | Channel: %d | Encryption: %s",
                     ap->ssid, ap->rssi, ap->primary, auth_mode);
        }

        free(ap_list);
        vTaskDelay(5000 / portTICK_PERIOD_MS); // Scan every 5 seconds
    }
}

void app_main(void) {
    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);

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

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

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
                                                       &wifi_event_handler, NULL, NULL));

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

    xTaskCreate(&wifi_scan, "wifi_scan_task", WIFI_SCAN_TASK_STACK_SIZE,
                NULL, WIFI_SCAN_TASK_PRIORITY, NULL);
}

The code is provided above. It includes:

  • Initialization of NVS (Non-Volatile Storage) and Wi-Fi.
  • A task to perform Wi-Fi scans every 5 seconds.
  • Logging of SSID, RSSI, channel, and encryption type for each detected access point.

Code Explanation

Let’s break down the key components of the code:

Includes and Definitions

The code starts with necessary headers for FreeRTOS, Wi-Fi, and logging. We define constants for the scan task’s stack size (WIFI_SCAN_TASK_STACK_SIZE) and priority (WIFI_SCAN_TASK_PRIORITY). The TAG is used for logging with ESP_LOGI and ESP_LOGE.

Wi-Fi Event Handler

The wifi_event_handler function handles Wi-Fi events, specifically WIFI_EVENT_SCAN_DONE, which is triggered when a scan completes. It logs a message to indicate the scan is done.

Wi-Fi Scan Task

The wifi_scan function runs in a FreeRTOS task and performs the following:

  • Configures the scan parameters (wifi_scan_config_t) to scan all channels, include hidden SSIDs, and use active scanning.
  • Starts the scan with esp_wifi_scan_start, waiting for it to complete.
  • Retrieves the number of access points with esp_wifi_scan_get_ap_num.
  • Allocates memory for the AP list and retrieves records with esp_wifi_scan_get_ap_records.
  • Iterates through the AP list, mapping the authmode to human-readable encryption types (e.g., WPA2-PSK, Open).
  • Logs each AP’s SSID, RSSI, channel, and encryption type.
  • Frees the AP list and delays for 5 seconds before the next scan.

Main Function

The app_main function is the entry point:

  • Initializes NVS, erasing it if necessary.
  • Sets up the network interface and event loop.
  • Configures and starts the Wi-Fi module in station mode (WIFI_MODE_STA).
  • Registers the event handler and creates the scan task.

Compiling and Flashing the Code

To run the scanner:

  1. Create a Project: In your ESP-IDF workspace, create a new project or use an existing one. Place the provided code in main/main.c.
  2. Configure the Project: Run idf.py menuconfig to ensure the Wi-Fi component is enabled (default in most configurations).
  3. Build the Project: Run idf.py build to compile the code.
  4. Flash the ESP32: Connect your ESP32 to your computer and run idf.py -p PORT flash, replacing PORT with your serial port (e.g., /dev/ttyUSB0 on Linux or COM3 on Windows).
  5. Monitor Output: Run idf.py monitor to view the Serial Monitor output. You should see logs like:
I (1234) wifi_scanner: Starting Wi-Fi scan...
I (1567) wifi_scanner: Wi-Fi scan completed
I (1580) wifi_scanner: Found 5 access points:
I (1590) wifi_scanner: SSID: MyWiFi            | RSSI: -45 dBm | Channel: 6 | Encryption: WPA2-PSK
I (1600) wifi_scanner: SSID: GuestNetwork      | RSSI: -70 dBm | Channel: 1 | Encryption: Open

Understanding the Output

The Serial Monitor displays:

  • SSID: The name of the Wi-Fi network.
  • RSSI: Received Signal Strength Indicator in dBm (e.g., -45 dBm is strong, -90 dBm is weak).
  • Channel: The Wi-Fi channel (1–13 in 2.4 GHz).
  • Encryption: The security protocol (e.g., WPA2-PSK, Open).

This information is useful for:

  • Network Diagnostics: Identifying crowded channels or weak signals.
  • IoT Deployment: Choosing optimal locations for ESP32-based devices.
  • Learning: Understanding Wi-Fi environments and protocols.

Troubleshooting

  • No APs Detected: Ensure you’re in an area with Wi-Fi networks. Check if the ESP32’s antenna is connected (some boards have external antennas).
  • Build Errors: Verify ESP-IDF is correctly set up and the Wi-Fi component is enabled.
  • Serial Monitor Issues: Ensure the correct baud rate (115200) and port are selected.
  • Memory Errors: If malloc fails, increase the task stack size or optimize memory usage.

Potential Extensions

Here are some ideas to enhance the scanner:

  • OLED Display: Add an SSD1306 OLED to display scan results.
  • Web Interface: Serve scan results via a web server on the ESP32.
  • Data Logging: Save results to an SD card or send them to a cloud service.
  • Filter Networks: Add options to filter by SSID, signal strength, or encryption type.

Conclusion

In this blog post, we built a Wi-Fi network scanner using the ESP32 and ESP-IDF, capable of scanning nearby networks and logging their SSIDs, signal strengths, channels, and encryption types to the Serial Monitor. This project demonstrates the power of the ESP32’s Wi-Fi capabilities and the flexibility of the ESP-IDF framework. Whether you’re an IoT enthusiast, a hobbyist, or a professional developer, this scanner is a great starting point for exploring wireless networking with the ESP32.

Try extending the project with your own features, like a graphical display or cloud integration, and share your creations with the community. Happy hacking!