Chapter 40: WiFi WPS Implementation

Chapter Objectives

By the end of this chapter, you will be able to:

  • Understand the purpose and functionality of WiFi Protected Setup (WPS).
  • Describe the different WPS methods: Push Button Configuration (PBC) and Personal Identification Number (PIN).
  • Configure the ESP32 to act as a WPS enrollee using ESP-IDF.
  • Implement code to initiate the WPS process, specifically using the PBC method.
  • Handle WPS-specific events to determine connection success, failure, or timeout.
  • Discuss the security implications associated with WPS.

Introduction

Connecting an IoT device to a WiFi network typically requires knowing the network’s SSID and password. While manageable for technical users, this can be cumbersome or intimidating for consumers setting up smart home devices. Manually entering credentials via limited interfaces (like buttons or mobile apps) can also be error-prone.

WiFi Protected Setup (WPS) was developed by the Wi-Fi Alliance as a standard to simplify the process of connecting devices to a secure wireless network without needing to manually enter the password. It provides methods for a quick, user-friendly setup experience. This chapter explores how to implement WPS on the ESP32 using ESP-IDF, allowing your devices to connect to compatible routers with just a button press or a simple PIN entry.

Theory

What is WPS?

WPS is not a security protocol itself but rather a network security standard designed to make the connection process between a client device (like the ESP32, called the Enrollee) and a router (the Registrar) easier and faster. The primary goal is to automate the exchange of network credentials (SSID and password) securely.

WPS operates as an intermediary step before the standard WPA/WPA2/WPA3 authentication occurs. Once WPS successfully completes, the enrollee receives the necessary credentials and then proceeds with the normal WiFi connection and authentication process using those credentials.

WPS Methods

WPS defines several methods for initiating the credential exchange, but two are most common:

  1. Push Button Configuration (PBC):
    • Concept: The user physically presses a WPS button on both the router (Registrar) and the client device (Enrollee) within a short time window (usually 2 minutes).
    • Process: When buttons are pressed, the devices enter a special mode where they search for each other. The router generates the network credentials and securely transmits them to the client device it detects is also in PBC mode.
    • Pros: Extremely simple for the user – no passwords to remember or type.
    • Cons: Security risk if an unauthorized device initiates PBC mode nearby during the active window (“WPS overlap”). Requires physical access to both devices.
  2. Personal Identification Number (PIN):
    • Concept: A PIN code is used to authenticate the connection attempt.
    • Methods:
      • Device PIN: The client device (Enrollee) has a static PIN (often printed on a label). The user enters this PIN into the router’s web interface. The router then finds the device and sends credentials.
      • Router PIN: The router (Registrar) generates a temporary PIN, displayed on its interface. The user enters this PIN into the client device’s interface (less common for simple IoT devices without displays/keypads).
    • Pros: More secure than PBC against accidental overlap if the PIN is kept confidential.
    • Cons: Less user-friendly than PBC. The external registrar PIN method (where the router has a static PIN that the client uses) is known to be highly vulnerable to brute-force attacks and is strongly discouraged or disabled on most modern routers. The device PIN method is generally safer but still requires user interaction with the router’s configuration.
%%{ init: { 'theme': 'base', 'themeVariables': {
  'fontFamily': 'Open Sans, sans-serif',
  'primaryColor': '#DBEAFE',      /* Process Nodes BG */
  'primaryTextColor': '#1E40AF', /* Process Nodes Text */
  'primaryBorderColor': '#2563EB',/* Process Nodes Border */
  'lineColor': '#4B5563',        /* Arrow color */
  'textColor': '#1F2937',        /* Default text color for labels */
  'mainBkg': 'transparent'       /* Diagram background */
}} }%%
graph TD
    A[Start WPS] --> MethodChoice{Choose WPS Method};

    MethodChoice -- "Push Button Configuration (PBC)" --> PBC1["User presses WPS button<br>on Router (Registrar)"];
    PBC1 --> PBC2["User presses WPS button<br>on ESP32 (Enrollee)<br><b>within ~2 minutes</b>"];
    PBC2 --> PBC3["Router & ESP32<br>discover each other"];
    PBC3 --> PBC4["Router securely sends<br>SSID & Password<br>to ESP32"]:::processNode;
    PBC4 --> PBC_Success[/"ESP32 Connects to WiFi<br>using received credentials"/]:::successNode;

    MethodChoice -- "PIN Method (Device PIN)" --> PIN_Dev1["ESP32 (Enrollee) has<br>a static PIN (e.g., on label)"];
    PIN_Dev1 --> PIN_Dev2["User reads PIN from ESP32"];
    PIN_Dev2 --> PIN_Dev3["User enters ESP32's PIN<br>into Router's web interface"];
    PIN_Dev3 --> PIN_Dev4["Router finds ESP32<br>using the PIN"]:::processNode;
    PIN_Dev4 --> PIN_Dev5["Router securely sends<br>SSID & Password<br>to ESP32"]:::processNode;
    PIN_Dev5 --> PIN_Success[/"ESP32 Connects to WiFi<br>using received credentials"/]:::successNode;

    %% PIN Method (Router PIN - Less common for ESP32 enrollee)
    MethodChoice -- "PIN Method (Router PIN)" --> PIN_Reg1["Router (Registrar) generates<br>a temporary PIN (displayed on router)"];
    PIN_Reg1 --> PIN_Reg2["User reads PIN from Router"];
    PIN_Reg2 --> PIN_Reg3["User enters Router's PIN<br>into ESP32 (Enrollee)<br>(Requires ESP32 input method)"];
    PIN_Reg3 --> PIN_Reg4["ESP32 uses PIN to<br>request credentials from Router"]:::processNode;
    PIN_Reg4 --> PIN_Reg5["Router securely sends<br>SSID & Password<br>to ESP32"]:::processNode;
    PIN_Reg5 --> PIN_Success;


    classDef startNode fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6;
    classDef successNode fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46;
    classDef decisionNode fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E;
    classDef processNode fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF;
    classDef checkNode fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B;

    class A startNode
    class MethodChoice decisionNode
    class PBC1 processNode
    class PBC2 processNode
    class PBC3 processNode
    class PIN_Dev1 processNode
    class PIN_Dev2 processNode
    class PIN_Dev3 processNode
    class PIN_Reg1 processNode
    class PIN_Reg2 processNode
    class PIN_Reg3 processNode
    class PBC4 processNode
    class PIN_Dev4 processNode
    class PIN_Dev5 processNode
    class PIN_Reg4 processNode
    class PIN_Reg5 processNode
    class PBC_Success successNode
    class PIN_Success successNode

WPS in ESP-IDF

ESP-IDF provides APIs to enable and manage the WPS process for the ESP32 acting as an enrollee. The key steps involve:

  • Enabling WPS: Configure the system to support WPS, usually via menuconfig.
  • Configuring WPS Type: Specify the desired WPS method (PBC, PIN) using wifi_wps_config_t.
Field in wifi_wps_config_t Type / Sub-fields Description & Usage
wps_type wps_type_t (Enum) Specifies the WPS method to use.
WPS_TYPE_DISABLE: WPS disabled.
WPS_TYPE_PBC: Push Button Configuration.
WPS_TYPE_PIN: Personal Identification Number.
WPS_TYPE_AP_PIN: (For AP mode) AP provides PIN.
Initialized by macros like WIFI_WPS_CONFIG_PBC_SYSTEM_INIT or WIFI_WPS_CONFIG_PIN_SYSTEM_INIT.
pin uint8_t[8] The 8-digit PIN code used if wps_type is WPS_TYPE_PIN. This is the Enrollee’s (ESP32’s) PIN that the user would enter into the router. The last digit is a checksum.
Not used for PBC mode. The macro WIFI_WPS_CONFIG_PIN_SYSTEM_INIT initializes it to a default, but you should set your device-specific PIN.
factory_info wps_factory_information_t (Struct) Optional information about the device manufacturer and model. This information might be displayed on the router during or after WPS.
Sub-fields:
manufacturer[64+1]
model_number[32+1]
model_name[32+1]
device_name[32+1]
The system init macros (WIFI_WPS_CONFIG_XXX_SYSTEM_INIT) provide default Espressif values.
crypto_funcs wps_crypto_funcs_t (Struct) Pointers to cryptographic functions required for WPS.
The system init macros (WIFI_WPS_CONFIG_XXX_SYSTEM_INIT) correctly populate these with default ESP-IDF crypto functions. Typically, users do not need to modify these.
  • Starting WPS: Initiate the WPS process using esp_wifi_wps_start(0). The argument 0 typically indicates an indefinite wait, but timeouts are usually handled by the underlying WPS protocol or can be managed via event handlers.
  • Handling Events: Monitor specific WiFi events related to WPS status:
Event Name (from esp_wifi_types.h) Typical Trigger / Meaning Recommended Action in Handler
WIFI_EVENT_STA_WPS_ER_SUCCESS WPS process completed successfully. The ESP32 has received the network credentials (SSID/password) from the router. Log success. Call esp_wifi_wps_disable(). The WiFi stack will typically attempt to connect automatically using the new credentials. Wait for normal WIFI_EVENT_STA_CONNECTED and IP_EVENT_STA_GOT_IP events.
WIFI_EVENT_STA_WPS_ER_FAILED WPS process failed due to a protocol error, incorrect PIN (if using PIN mode), or other WPS-specific issue. Log failure. Call esp_wifi_wps_disable(). Signal failure to the application (e.g., set event group bit). Consider retry logic or fallback to manual configuration.
WIFI_EVENT_STA_WPS_ER_TIMEOUT WPS process timed out. For PBC, this usually means the WPS button on the router was not pressed within the active window. For PIN, the registrar might not have responded. Log timeout. Call esp_wifi_wps_disable(). Signal failure to the application. Consider retry logic or fallback.
WIFI_EVENT_STA_WPS_ER_PIN Relevant for PIN mode. May indicate the AP (Registrar) is providing its PIN, or requesting the Enrollee’s PIN. The exact usage depends on the PIN method flow. For PBC mode, this event is usually not critical and can be logged for info. For PIN mode, specific handling based on the wifi_event_sta_wps_er_pin_t event data might be needed (e.g., to display AP’s PIN if ESP32 is registrar, or to prompt user for AP’s PIN if ESP32 is enrollee and AP provides PIN). Typically, after this event, the process continues towards success or failure.
WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP WPS PBC overlap detected. Multiple APs in PBC mode were detected simultaneously. WPS is aborted to prevent connecting to the wrong AP. Log the overlap error. Call esp_wifi_wps_disable(). Inform the user about the overlap and suggest trying again in a less congested environment or after waiting a moment.
  • Disabling WPS: Stop the WPS process using esp_wifi_wps_disable(). This should be done after success, failure, or timeout to return the WiFi stack to a normal state.

Security Considerations

  • PBC Overlap: As mentioned, if multiple users initiate PBC simultaneously in close proximity, a device might connect to the wrong network. The window is short (typically 2 minutes), but the risk exists.
  • PIN Vulnerability: The external registrar PIN method (router’s static PIN) is fundamentally flawed and easily brute-forced. Avoid using it. Most modern firmwares disable it by default. Device PIN is better but still relies on the PIN’s secrecy and the router’s security.
  • Recommendation: While convenient, WPS (especially PIN) has known vulnerabilities. For highest security, manually configuring WPA2/WPA3 with a strong password remains the recommended approach. However, WPS PBC can be an acceptable trade-off for ease of use in many consumer scenarios, provided the user understands the short overlap risk. Many modern routers mitigate PBC risks by significantly shortening the active window or requiring additional interaction.

Practical Examples

Let’s implement WPS using the PBC method on the ESP32.

1. Project Setup & Configuration

  • Start with a basic WiFi station project structure.
  • Enable WPS in menuconfig:
    1. Run idf.py menuconfig (or use the VS Code equivalent).
    2. Navigate to Component config -> Wi-Fi.
    3. Ensure WiFi Station Enable is checked.
    4. Enable WPS (Wi-Fi Protected Setup).
    5. You might see options for WPS methods (PBC, PIN); ensure PBC is enabled if selectable.
    6. Save the configuration and exit.
  • Include necessary headers: esp_wifi.h, esp_event.h, esp_log.h, freertos/FreeRTOS.h, freertos/task.h, nvs_flash.h.

2. WPS Event Handling

Modify the WiFi event handler to listen for WPS-specific events.

C
// main.c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"

static const char *TAG = "WPS_EXAMPLE";

// Assume s_wifi_event_group and bits are defined elsewhere
extern EventGroupHandle_t s_wifi_event_group;
extern const int WIFI_CONNECTED_BIT;
extern const int WIFI_FAIL_BIT; // Reuse for general failure/timeout

// --- WPS Configuration ---
// Define the WPS method we want to use
#define ESP_WPS_MODE WPS_TYPE_PBC // Or WPS_TYPE_PIN

// Forward declarations
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data);
static void start_wps(void);

// Modified WiFi Event Handler
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) {
        switch (event_id) {
            case WIFI_EVENT_STA_START:
                ESP_LOGI(TAG, "WIFI_EVENT_STA_START");
                // Optionally start WPS immediately, or trigger via button press
                // start_wps(); // Example: Trigger WPS start here
                break;
            case WIFI_EVENT_STA_CONNECTED:
                 ESP_LOGI(TAG, "WIFI_EVENT_STA_CONNECTED - Should not happen during WPS");
                 // If WPS was successful, it triggers WIFI_EVENT_STA_WPS_ER_SUCCESS first,
                 // then the stack uses the obtained credentials to connect, leading to
                 // normal WIFI_EVENT_STA_CONNECTED and IP_EVENT_STA_GOT_IP events later.
                break;
            case WIFI_EVENT_STA_DISCONNECTED:
                ESP_LOGW(TAG, "WIFI_EVENT_STA_DISCONNECTED");
                // Could happen if connection attempt after successful WPS fails
                // Or if connection drops after successful WPS connection
                is_connected = false; // Assuming global is_connected flag
                if (s_wifi_event_group != NULL) {
                    xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
                    xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
                }
                // Optional: Attempt reconnect or restart WPS?
                // esp_wifi_connect(); // Simple reconnect attempt
                break;

            // --- WPS Specific Events ---
            case WIFI_EVENT_STA_WPS_ER_SUCCESS:
                ESP_LOGI(TAG, "WIFI_EVENT_STA_WPS_ER_SUCCESS: WPS Success! Credentials obtained.");
                // WPS finished successfully. The ESP-IDF WiFi stack should now
                // automatically try to connect using the obtained credentials.
                // We just need to disable the WPS mode.
                ESP_ERROR_CHECK(esp_wifi_wps_disable());
                ESP_LOGI(TAG, "WPS Disabled. Waiting for connection...");
                // Now wait for WIFI_EVENT_STA_CONNECTED and IP_EVENT_STA_GOT_IP
                break;
            case WIFI_EVENT_STA_WPS_ER_FAILED:
                ESP_LOGE(TAG, "WIFI_EVENT_STA_WPS_ER_FAILED: WPS Failed.");
                ESP_ERROR_CHECK(esp_wifi_wps_disable()); // Disable WPS on failure
                if (s_wifi_event_group != NULL) {
                    xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); // Signal failure
                }
                // Retry WPS or enter fallback mode?
                break;
            case WIFI_EVENT_STA_WPS_ER_TIMEOUT:
                ESP_LOGW(TAG, "WIFI_EVENT_STA_WPS_ER_TIMEOUT: WPS Timeout.");
                ESP_ERROR_CHECK(esp_wifi_wps_disable()); // Disable WPS on timeout
                if (s_wifi_event_group != NULL) {
                    xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); // Signal failure
                }
                // Retry WPS or enter fallback mode?
                break;
            case WIFI_EVENT_STA_WPS_ER_PIN:
                 ESP_LOGI(TAG, "WIFI_EVENT_STA_WPS_ER_PIN event received");
                 // Handle PIN logic if using PIN mode (e.g., display AP PIN)
                 // For PBC mode, this event is usually ignored.
                break;
            default:
                 ESP_LOGD(TAG, "Unhandled WIFI_EVENT: %ld", event_id);
                break;
        }
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "IP_EVENT_STA_GOT_IP: Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
        is_connected = true; // Assuming global is_connected flag
         if (s_wifi_event_group != NULL) {
            xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
            xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
         }
        // Connection successful (likely after WPS success)
    } else {
         ESP_LOGD(TAG, "Received unhandled event: base=%s, id=%ld", event_base, event_id);
    }
}

3. Initiating WPS

Create a function to configure and start the WPS process. This function would typically be called when the user initiates the process (e.g., by pressing a button on the ESP32 device).

C
// main.c

// Function to configure and start WPS
static void start_wps(void) {
    ESP_LOGI(TAG, "Starting WPS (%s mode)...", (ESP_WPS_MODE == WPS_TYPE_PBC) ? "PBC" : "PIN");

    // Configure WPS
    wifi_wps_config_t config = WIFI_WPS_CONFIG_PBC_SYSTEM_INIT; // Helper macro for PBC defaults
    // If using PIN, use WIFI_WPS_CONFIG_PIN_SYSTEM_INIT and set config.pin

    // Override defaults if needed:
    config.wps_type = ESP_WPS_MODE;
    // config.factory_info.manufacturer = "ESPRESSIF"; // Optional
    // config.factory_info.model_number = "ESP32";     // Optional
    // config.factory_info.model_name = "ESPRESSIF IOT"; // Optional
    // config.factory_info.device_name = "ESP STATION"; // Optional

    // Enable WPS
    esp_err_t err = esp_wifi_wps_enable(&config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to enable WPS: %s", esp_err_to_name(err));
        return; // Cannot start if enable failed
    }
    ESP_LOGI(TAG, "WPS Enabled.");

    // Start WPS (timeout handled by events)
    err = esp_wifi_wps_start(0); // 0 -> indefinite wait (use events for timeout)
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to start WPS: %s", esp_err_to_name(err));
        esp_wifi_wps_disable(); // Disable if start failed
        return;
    }
    ESP_LOGI(TAG, "WPS Started. Please activate WPS on your router (PBC mode).");
     if (s_wifi_event_group != NULL) {
         // Clear any previous failure bits before starting
         xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
     }
}

// --- Example: Trigger WPS from a task or button handler ---
// This function simulates a button press triggering WPS
void trigger_wps_start(void) {
     // Ensure WiFi station is started before initiating WPS
     // (Assuming wifi_init_sta was called earlier)
     start_wps();
}

4. Main Application (app_main)

Initialize WiFi, register the event handler, and provide a way to trigger WPS (e.g., start it immediately for testing, or set up a GPIO button interrupt).

C
// main.c

// Event group handle defined globally or passed appropriately
EventGroupHandle_t s_wifi_event_group;
const int WIFI_CONNECTED_BIT = BIT0;
const int WIFI_FAIL_BIT = BIT1;
bool is_connected = false; // Simple global state

// Basic WiFi Init (registers event handler)
static void wifi_init(void) {
    s_wifi_event_group = xEventGroupCreate();

    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); // Ensure netif was created

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

    // Register event handlers for ALL WiFi and IP events
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));

    // Set mode to Station
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    // Do NOT set specific SSID/Password here if using WPS for initial setup
    // Do NOT call esp_wifi_connect() here if triggering WPS manually

    // Start the WiFi driver tasks
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "WiFi Initialized. Ready for WPS.");
}


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

    ESP_LOGI(TAG, "ESP32 WPS Example");

    // Initialize WiFi stack and event handlers
    wifi_init();

    // --- Trigger WPS ---
    // For testing, trigger WPS after a short delay.
    // In a real application, trigger this from a button press or other user action.
    vTaskDelay(pdMS_TO_TICKS(5000)); // Wait 5 seconds
    trigger_wps_start();

    // --- Wait for Result ---
    // Example: Wait here for connection or failure using the event group
    ESP_LOGI(TAG, "Waiting for WPS/Connection result...");
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE, // Don't clear on exit
                                           pdFALSE, // Wait for either bit
                                           portMAX_DELAY); // Wait indefinitely

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "Successfully connected via WPS!");
        // Proceed with application logic
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGE(TAG, "WPS or subsequent connection failed.");
        // Handle failure: retry, enter manual config mode, etc.
    } else {
        ESP_LOGE(TAG, "Unexpected event group result.");
    }


    // Keep main task running (or do other things)
    while(1) {
        vTaskDelay(pdMS_TO_TICKS(60000)); // Idle
    }
}

5. Build, Flash, and Observe

  1. Configure: Ensure WPS is enabled in menuconfig.
  2. Build: Use ESP-IDF: Build your project.
  3. Flash: Use ESP-IDF: Flash your project.
  4. Monitor: Open the Serial Monitor (ESP-IDF: Monitor your device).
  5. Test (PBC Mode):
    • The ESP32 will initialize WiFi and log “WiFi Initialized. Ready for WPS.”
    • After the delay (or when you trigger it), it will log “Starting WPS (PBC mode)…” and “WPS Started. Please activate WPS on your router (PBC mode).”
    • Within 2 minutes, press the physical WPS button on your WiFi router.
    • Observe the serial monitor. You should see the router and ESP32 exchange information (often involves many low-level logs if debug level is high).
    • If successful, you should see the WIFI_EVENT_STA_WPS_ER_SUCCESS log, followed by “WPS Disabled. Waiting for connection…”, then normal connection logs (WIFI_EVENT_STA_CONNECTED, IP_EVENT_STA_GOT_IP). The app_main task will log “Successfully connected via WPS!”.
    • If you don’t press the router button in time, you should see the WIFI_EVENT_STA_WPS_ER_TIMEOUT event.
    • If another error occurs, you might see WIFI_EVENT_STA_WPS_ER_FAILED.

Variant Notes

The WPS functionality and the corresponding ESP-IDF APIs (esp_wifi_wps_enable, esp_wifi_wps_start, esp_wifi_wps_disable, related events) are part of the core esp_wifi component and are generally expected to be available and consistent across the ESP32 variants that support WiFi Station mode:

  • ESP32
  • ESP32-S2
  • ESP32-S3
  • ESP32-C3
  • ESP32-C6
  • ESP32-C5
  • ESP32-C61

There are no known major variant-specific differences in the basic WPS API usage within ESP-IDF v5.x. As always, minor behavioral differences or performance nuances related to the underlying radio hardware are possible but unlikely to affect the core WPS logic presented here.

(Note: ESP32-H2 does not support WiFi and therefore does not support WiFi WPS).

Common Mistakes & Troubleshooting Tips

Mistake / Issue Symptom(s) Troubleshooting / Solution
WPS Not Enabled in menuconfig WPS API calls like esp_wifi_wps_enable() return errors or are undefined. WPS functionality is missing. Ensure “WPS (Wi-Fi Protected Setup)” is enabled in menuconfig: Component config -> Wi-Fi -> WPS (Wi-Fi Protected Setup). Rebuild the project after changes.
Timing Issues (PBC Mode) WPS times out (WIFI_EVENT_STA_WPS_ER_TIMEOUT). ESP32 fails to connect. Start WPS on the ESP32 first, then immediately (within 1-2 minutes) press the WPS button on the router. Ensure no other devices are attempting WPS PBC nearby (overlap).
Router WPS Disabled or Incompatible WPS consistently fails or times out, even with correct timing. Check the router’s admin settings: ensure WPS is enabled and supports the chosen method (PBC). Test router’s WPS with another client (e.g., phone) to confirm router functionality. Some older routers may have buggy WPS.
Calling esp_wifi_connect() During WPS WPS process is interrupted or behaves erratically. Connection may fail. Do not call esp_wifi_connect() manually while WPS is active. After WIFI_EVENT_STA_WPS_ER_SUCCESS, the ESP-IDF stack typically handles the connection using the credentials obtained via WPS.
Not Disabling WPS After Completion/Failure Subsequent WiFi operations fail, or new WPS attempts don’t work correctly. WiFi stack might be in an unexpected state. Always call esp_wifi_wps_disable() in the event handlers for WIFI_EVENT_STA_WPS_ER_SUCCESS, WIFI_EVENT_STA_WPS_ER_FAILED, and WIFI_EVENT_STA_WPS_ER_TIMEOUT.
Incorrect PIN Handling (PIN Mode) If using PIN mode, WPS fails with WIFI_EVENT_STA_WPS_ER_FAILED. Ensure the correct PIN is used (either the ESP32’s PIN entered into the router, or the router’s PIN entered into the ESP32, depending on the method). The PIN on the ESP32 is set in wifi_wps_config_t.pin.
WPS PBC Overlap WPS fails with WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP. This means multiple APs (or another client) were in PBC mode simultaneously. Wait a minute and try again, ensuring only your target router and ESP32 are in WPS PBC mode.
WiFi Not Started Before WPS Calls to esp_wifi_wps_enable() or esp_wifi_wps_start() fail. Ensure that esp_wifi_init() and esp_wifi_start() have been called successfully before attempting to initiate WPS. The WiFi station must be started.

Exercises

  1. Button-Triggered WPS: Modify the example code to use a GPIO input pin connected to a push button. Configure the GPIO as an input with an interrupt. When the button is pressed (detected via ISR or a task polling the ISR signal), call the trigger_wps_start() function instead of starting it automatically after a delay. Remember to handle button debouncing.
  2. WPS Retry Logic: Enhance the event handler. If WIFI_EVENT_STA_WPS_ER_FAILED or WIFI_EVENT_STA_WPS_ER_TIMEOUT occurs, wait for a few seconds (e.g., 5-10 seconds using vTaskDelay) and then automatically call start_wps() again to retry the process, perhaps up to 3 times before declaring a permanent failure.

Summary

  • WiFi Protected Setup (WPS) simplifies connecting devices to WiFi by automating credential exchange.
  • Common methods are Push Button Configuration (PBC) and Personal Identification Number (PIN).
  • ESP-IDF provides APIs (esp_wifi_wps_enable, esp_wifi_wps_start, esp_wifi_wps_disable) and specific events (WIFI_EVENT_STA_WPS_ER_...) to manage the WPS process as an enrollee.
  • PBC mode requires enabling WPS in menuconfig, configuring the type, starting the process, and handling success/failure/timeout events.
  • User interaction (pressing the router’s WPS button) is required for PBC within a specific time window.
  • WPS, particularly the PIN method, has known security vulnerabilities, and its use should be considered carefully against manual configuration.
  • Always disable WPS (esp_wifi_wps_disable) after the process completes or fails.

Further Reading

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top