Chapter 81: DHCP Server Implementation
Chapter Objectives
By the end of this chapter, you will be able to:
- Understand the fundamentals of the Dynamic Host Configuration Protocol (DHCP).
- Explain the role and operation of a DHCP server.
- Configure an ESP32 as a Wi-Fi Access Point (AP) with a DHCP server.
- Set custom IP address ranges, lease times, and other DHCP options.
- Handle DHCP server events, such as IP address assignments.
- Implement static IP address assignments based on MAC addresses.
Introduction
In the previous chapter, we explored how an ESP32 can act as a DHCP client to obtain an IP address automatically. Now, we turn the tables and investigate how an ESP32 can function as a DHCP server. This capability is crucial when the ESP32 operates as a Wi-Fi Access Point (AP), creating its own local network. In such scenarios, the ESP32 AP needs to provide IP addresses and other network configuration details (like gateway and DNS server addresses) to connecting client devices (e.g., smartphones, laptops, or other IoT devices).
Understanding and implementing a DHCP server on your ESP32 allows you to create self-contained networks, essential for applications like direct device-to-device communication, local control systems without an existing network infrastructure, or provisioning networks for other devices. This chapter will guide you through the theory of DHCP and the practical steps to enable and configure a DHCP server using ESP-IDF v5.x.
Theory
What is DHCP?
DHCP (Dynamic Host Configuration Protocol) is a network management protocol used on IP networks whereby a DHCP server dynamically assigns an IP address and other network configuration parameters to each device (client) on a network so they can communicate with other IP networks. Without DHCP, IP addresses would need to be configured manually on each device, which is cumbersome and error-prone, especially in larger networks.
Key Roles in DHCP:
Role | Description | Relevance to ESP32 AP |
---|---|---|
DHCP Server | A networked device that centrally manages and assigns IP addresses and network configuration information to clients. | When an ESP32 is in Access Point (AP) mode, it typically acts as a DHCP server for the devices connecting to its Wi-Fi network. |
DHCP Client | A device that requests network configuration information from a DHCP server. | Smartphones, laptops, or other IoT devices connecting to the ESP32’s AP network act as DHCP clients. An ESP32 in Station (STA) mode also acts as a DHCP client. |
DHCP Relay Agent | A host or router that forwards DHCP messages between clients and servers on different subnets. | Not typically relevant for a simple ESP32 AP setup creating its own isolated local network. More common in larger, segmented networks. |
- DHCP Server: A networked device (e.g., a router, a dedicated server, or an ESP32 in AP mode) that centrally manages and assigns IP addresses and network configuration information to clients.
- DHCP Client: A device (e.g., a computer, smartphone, or another ESP32) that requests network configuration information from a DHCP server.
- DHCP Relay Agent: A host or router that listens for DHCP client messages being broadcast on a subnet and then forwards them to a DHCP server on a different subnet. This is not typically relevant for a simple ESP32 AP setup.
DHCP Message Exchange (DORA)
The core DHCP process involves a four-step message exchange between a client and a server, often referred to by the acronym DORA:
%%{init: {"theme": "base", "themeVariables": { "primaryColor": "#DBEAFE", "primaryTextColor": "#1E40AF", "primaryBorderColor": "#2563EB", "lineColor": "#6B7280", "textColor": "#1F2937", "fontSize": "14px", "fontFamily": "Open Sans" }}}%% graph TD A["<b>Client:</b> Needs IP<br>(Initial State)"] ==>|"Broadcasts<br>DHCPDISCOVER"| B("<b>Server(s):</b> Receive Discover"); B ==>|"Sends<br>DHCPOFFER"| C{"<b>Client:</b> Receives Offer(s)<br>Selects one"}; C ==>|"Broadcasts<br>DHCPREQUEST"| D(<b>Selected Server:</b> Receive Request); D ==>|"Sends<br>DHCPACK"| E[<b>Client:</b> Configured!<br>IP Address Leased]; style A fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6 style B fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF style C fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E style D fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF style E fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46
- Discover: The DHCP client broadcasts a
DHCPDISCOVER
message on the network subnet to find available DHCP servers. - Offer: DHCP servers that receive the
DHCPDISCOVER
message may respond with aDHCPOFFER
message, offering an IP address lease and other configuration parameters. - Request: The client selects one of the offers (usually the first one it receives) and broadcasts a
DHCPREQUEST
message, requesting the offered IP address and parameters from the selected server. - Acknowledge (ACK): The selected DHCP server finalizes the lease and sends a
DHCPACK
message to the client, confirming the IP address and other parameters. The client can now use the assigned IP address.
[Insert diagram illustrating the DORA process: Client (broadcast DHCPDISCOVER) -> Server (unicast/broadcast DHCPOFFER) -> Client (broadcast DHCPREQUEST) -> Server (unicast/broadcast DHCPACK)]
Other DHCP Message Types:
- DHCPNAK (Negative Acknowledge): Sent by the server to the client if the client’s requested IP address is incorrect (e.g., already in use or client moved to a new subnet).
- DHCPRELEASE: Sent by the client to the server to terminate its lease and release the IP address.
- DHCPINFORM: Used by clients that have already obtained an IP address (e.g., manually configured) to request other local configuration parameters from a DHCP server.
- DHCPDECLINE: Sent by the client to the server if it determines the offered configuration parameters are invalid.
IP Address Allocation Methods
DHCP servers can allocate IP addresses in three ways:
Method | Description | ESP32 DHCP Server Usage | Lease Type |
---|---|---|---|
Dynamic Allocation | The DHCP server assigns an IP address from a predefined pool for a limited period (lease time). | This is the primary method used by the ESP32 DHCP server. You define an IP pool (e.g., 192.168.10.100 – 192.168.10.150 ) and a lease time. |
Temporary (leased) |
Automatic Allocation | The DHCP server permanently assigns an IP address to a client from a pool, often remembering the first IP it assigned to a MAC address. | The default LwIP DHCP server on ESP32 might exhibit behavior similar to automatic allocation if a client reconnects and requests its previous IP, but it’s primarily dynamic. True permanent assignment without MAC-based rules is less common for ESP32 APs. | Effectively permanent (but still managed by DHCP) |
Static (Manual) Allocation / DHCP Reservation | The DHCP server assigns a specific, pre-configured IP address based on the client’s MAC address. | Supported by ESP32. You can use esp_netif_dhcps_option() with ESP_NETIF_STATIC_IP_ADDR to reserve an IP for a specific MAC address. This ensures a device always gets the same IP. |
Reserved (permanent for that MAC) |
Lease Time
When a DHCP server assigns an IP address, it’s typically for a specific duration called the lease time. Before the lease expires, the client usually attempts to renew it. If the lease expires and is not renewed, the IP address is returned to the pool for reallocation.
DHCP Options
Besides an IP address, a DHCP server can provide other configuration parameters to clients, known as DHCP options. Common options include:
Option Number | Option Name | Purpose | ESP-IDF esp_netif_dhcps_opt_id_t (if applicable) |
---|---|---|---|
1 | Subnet Mask | Defines the network portion and host portion of an IP address for the client. | Typically derived from AP’s static IP netmask (esp_netif_set_ip_info ). Set via ESP_NETIF_SUBNET_MASK . |
3 | Router/Gateway Address | The IP address of the router that clients should use to reach external networks. For an ESP32 AP, this is usually the AP’s own IP address. | Typically AP’s own IP. Set via ESP_NETIF_ROUTER_SOLICITATION_ADDRESS (or implicitly by AP IP). |
6 | DNS Server Address | The IP address(es) of Domain Name System (DNS) servers for resolving hostnames. | ESP_NETIF_DOMAIN_NAME_SERVER |
51 | IP Address Lease Time | The duration (in seconds) for which the assigned IP address is valid for the client. | ESP_NETIF_IP_ADDRESS_LEASE_TIME |
15 | Domain Name | The domain name for the client to use (e.g., “local.lan”). | ESP_NETIF_DOMAIN_NAME (less commonly configured for simple APs) |
N/A (Conceptual) | IP Address Pool | Defines the range of IP addresses the DHCP server can lease out. | ESP_NETIF_REQUESTED_IP_ADDRESS (used with dhcps_lease_t to set start/end IPs) |
N/A (Conceptual) | Static IP Assignment | Assigns a fixed IP address to a specific MAC address. | ESP_NETIF_STATIC_IP_ADDR (used with dhcps_static_lease_t ) |
ESP32 as a DHCP Server
When an ESP32 is configured in Wi-Fi Access Point (AP) mode, it creates its own Wi-Fi network. To allow other devices to connect and communicate on this network, the ESP32 needs to provide them with IP addresses. This is where the built-in DHCP server functionality of the ESP-IDF comes into play. The DHCP server runs on the ESP32’s AP network interface (esp_netif
).
%%{init: {"theme": "base", "themeVariables": { "primaryColor": "#DBEAFE", "primaryTextColor": "#1E40AF", "primaryBorderColor": "#2563EB", "lineColor": "#6B7280", "textColor": "#1F2937", "fontSize": "14px", "fontFamily": "Open Sans" }}}%% graph LR subgraph ESP32_Device ["ESP32 (acting as Wi-Fi AP)"] direction LR esp_module["<b>ESP32 Microcontroller</b>"] subgraph Software_Stack ["Software Stack"] direction TB app_code["Application Code"] esp_idf["ESP-IDF Framework"] netif["esp_netif (AP Interface)"] dhcp_server["<b>DHCP Server Module</b><br>(Manages 192.168.10.x)"] wifi_driver["Wi-Fi Driver (AP Mode)"] end esp_module --> app_code; app_code --> esp_idf; esp_idf --> netif; netif --- dhcp_server; esp_idf --> wifi_driver; end subgraph Client_Devices ["Client Devices"] direction TB client1["📱<br>Smartphone"] client2["💻<br>Laptop"] client3["💡<br>IoT Device"] end wifi_driver -.->|"<br>Wi-Fi Signal (SSID: ESP32_AP_DHCP)"| client1; wifi_driver -.->|"<br>Wi-Fi Signal (SSID: ESP32_AP_DHCP)"| client2; wifi_driver -.->|"<br>Wi-Fi Signal (SSID: ESP32_AP_DHCP)"| client3; dhcp_server --"IP: 192.168.10.100<br>GW: 192.168.10.1<br>DNS: 192.168.10.1"--> client1; dhcp_server --"IP: 192.168.10.101<br>GW: 192.168.10.1<br>DNS: 192.168.10.1"--> client2; dhcp_server --"IP: 192.168.10.102<br>GW: 192.168.10.1<br>DNS: 192.168.10.1"--> client3; style ESP32_Device fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6 style dhcp_server fill:#DBEAFE,stroke:#2563EB,stroke-width:2px,color:#1E40AF,font-weight:bold style Client_Devices fill:#E0F2FE,stroke:#0EA5E9,stroke-width:1px,color:#0369A1 style client1 fill:#FFFFFF,stroke:#6B7280 style client2 fill:#FFFFFF,stroke:#6B7280 style client3 fill:#FFFFFF,stroke:#6B7280 %% Note: Mermaid doesn't directly render external images in all environments. %% Using placeholders or FontAwesome icons if images don't render. %% For example, using fa:fa-mobile-alt for smartphone, fa:fa-laptop for laptop. %% The placeholder images are used as a fallback.
The ESP-IDF provides APIs through esp_netif
to start, stop, and configure the DHCP server associated with a network interface (typically the AP interface). By default, when you initialize the AP interface and Wi-Fi, the DHCP server is started with a default configuration. However, you can customize its behavior, such as the range of IP addresses it leases (the IP pool), the lease duration, and DNS server information.
Practical Examples
This section demonstrates how to configure an ESP32 as a Wi-Fi AP and enable its DHCP server with custom settings.
Prerequisites
- ESP-IDF v5.x installed and configured.
- VS Code with the Espressif IDF Extension.
- An ESP32 development board.
- A Wi-Fi client device (e.g., smartphone or laptop) to test the DHCP server.
Project Setup
- Open VS Code.
- Press
F1
and typeESP-IDF: New Project
. - Choose a project name (e.g.,
esp32_dhcp_server
). - Select your ESP32 target board.
- Choose a template (e.g.,
esp-idf-template
). - Select a location to save the project.
Code Implementation
Replace the content of your main/main.c
file with the following code:
#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 "lwip/inet.h" // For inet_pton
#include "lwip/err.h"
#include "lwip/sys.h"
#include "esp_netif.h"
// Wi-Fi AP Configuration
#define EXAMPLE_ESP_WIFI_SSID "ESP32_AP_DHCP"
#define EXAMPLE_ESP_WIFI_PASS "password123"
#define EXAMPLE_ESP_WIFI_CHANNEL 1
#define EXAMPLE_MAX_STA_CONN 4
// Static IP configuration for the AP interface
#define AP_STATIC_IP_ADDR "192.168.10.1"
#define AP_STATIC_NETMASK_ADDR "255.255.255.0"
#define AP_STATIC_GW_ADDR "192.168.10.1" // AP itself is the gateway
// DHCP Server Configuration
#define DHCP_LEASE_START_IP "192.168.10.100"
#define DHCP_LEASE_END_IP "192.168.10.150"
#define DHCP_LEASE_TIME_MIN 120 // Lease time in minutes
#define DNS_SERVER_IP "192.168.10.1" // AP itself as DNS or use 8.8.8.8 for Google DNS
static const char *TAG = "dhcp_server_example";
// Event handler for Wi-Fi events
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI(TAG, "Station "MACSTR" joined, AID=%d",
MAC2STR(event->mac), event->aid);
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
ESP_LOGI(TAG, "Station "MACSTR" left, AID=%d",
MAC2STR(event->mac), event->aid);
}
}
// Event handler for IP events (specifically for IP assignment)
static void ip_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_id == IP_EVENT_AP_STAIPASSIGNED) {
ip_event_ap_staipassigned_t* event = (ip_event_ap_staipassigned_t*) event_data;
ESP_LOGI(TAG, "Assigned IP address to station: " IPSTR, IP2STR(&event->ip));
}
}
void wifi_init_softap(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
assert(ap_netif);
// 1. Stop DHCP server (it might be started by default)
ESP_LOGI(TAG, "Stopping DHCP server...");
ESP_ERROR_CHECK(esp_netif_dhcps_stop(ap_netif));
// 2. Configure static IP for the AP interface
ESP_LOGI(TAG, "Configuring static IP for AP interface...");
esp_netif_ip_info_t ip_info;
memset(&ip_info, 0, sizeof(esp_netif_ip_info_t));
inet_pton(AF_INET, AP_STATIC_IP_ADDR, &ip_info.ip);
inet_pton(AF_INET, AP_STATIC_GW_ADDR, &ip_info.gw);
inet_pton(AF_INET, AP_STATIC_NETMASK_ADDR, &ip_info.netmask);
ESP_ERROR_CHECK(esp_netif_set_ip_info(ap_netif, &ip_info));
ESP_LOGI(TAG, "AP Static IP configured: IP=" IPSTR ", GW=" IPSTR ", Mask=" IPSTR,
IP2STR(&ip_info.ip), IP2STR(&ip_info.gw), IP2STR(&ip_info.netmask));
// 3. Configure DHCP server options
ESP_LOGI(TAG, "Configuring DHCP server options...");
// Set IP address lease pool
dhcps_lease_t lease_opt;
memset(&lease_opt, 0, sizeof(dhcps_lease_t));
inet_pton(AF_INET, DHCP_LEASE_START_IP, &lease_opt.start_ip);
inet_pton(AF_INET, DHCP_LEASE_END_IP, &lease_opt.end_ip);
esp_err_t opt_ret = esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, &lease_opt, sizeof(lease_opt));
if (opt_ret == ESP_OK) {
ESP_LOGI(TAG, "DHCP IP Pool configured: %s - %s", DHCP_LEASE_START_IP, DHCP_LEASE_END_IP);
} else {
ESP_LOGE(TAG, "Failed to set DHCP IP Pool (err=0x%x)", opt_ret);
}
// Set IP address lease time (in minutes, converted to seconds for LwIP)
// Note: LwIP DHCP server expects lease time in seconds. The ESP_NETIF_IP_ADDRESS_LEASE_TIME option takes minutes.
// Let's check the esp_netif_dhcps_option documentation or source for expected unit.
// According to esp_netif_lwip.c, it takes seconds for ESP_NETIF_IP_ADDRESS_LEASE_TIME
// However, older comments and Kconfig might mention minutes. Let's assume seconds based on LwIP.
// The header `esp_netif_types.h` says "The IP address lease time (in seconds)"
uint32_t lease_time_sec = DHCP_LEASE_TIME_MIN * 60;
opt_ret = esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_IP_ADDRESS_LEASE_TIME, &lease_time_sec, sizeof(lease_time_sec));
if (opt_ret == ESP_OK) {
ESP_LOGI(TAG, "DHCP Lease Time configured: %d minutes (%d seconds)", DHCP_LEASE_TIME_MIN, lease_time_sec);
} else {
ESP_LOGE(TAG, "Failed to set DHCP Lease Time (err=0x%x)", opt_ret);
}
// Set DNS Server (Router option is usually AP's IP, set by default)
esp_netif_dns_info_t dns_info;
memset(&dns_info, 0, sizeof(esp_netif_dns_info_t));
inet_pton(AF_INET, DNS_SERVER_IP, &dns_info.ip.u_addr.ip4);
dns_info.ip.type = ESP_IPADDR_TYPE_V4;
// Option ESP_NETIF_DOMAIN_NAME_SERVER configures DHCP option 6 (DNS)
opt_ret = esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dns_info, sizeof(dns_info));
if (opt_ret == ESP_OK) {
ESP_LOGI(TAG, "DHCP DNS Server configured: %s", DNS_SERVER_IP);
} else {
ESP_LOGE(TAG, "Failed to set DHCP DNS Server (err=0x%x)", opt_ret);
}
// 4. Start DHCP server
ESP_LOGI(TAG, "Starting DHCP server...");
ESP_ERROR_CHECK(esp_netif_dhcps_start(ap_netif));
ESP_LOGI(TAG, "DHCP server started.");
// Wi-Fi Configuration
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// Register event handlers
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_AP_STAIPASSIGNED,
&ip_event_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA2_PSK, // Changed from WIFI_AUTH_WPA_WPA2_PSK for simplicity if issues arise
.pmf_cfg = {
.required = false,
},
},
};
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
ESP_LOGI(TAG, "Connect to AP and observe DHCP lease assignments.");
}
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, "ESP_WIFI_MODE_AP with DHCP Server");
wifi_init_softap();
}
Explanation:
- Includes and Definitions:
- Necessary headers for FreeRTOS, Wi-Fi, networking, events, logging, and NVS are included.
- Constants for AP SSID, password, channel, maximum connections, static IP configuration for the AP, and DHCP server parameters (IP pool, lease time, DNS server) are defined.
- Event Handlers (
wifi_event_handler
,ip_event_handler
):wifi_event_handler
: Logs when stations connect to or disconnect from the ESP32 AP.ip_event_handler
: Specifically listens forIP_EVENT_AP_STAIPASSIGNED
. When a client successfully obtains an IP address from our DHCP server, this event is triggered, and we log the assigned IP.
wifi_init_softap(void)
Function:- Network Stack Initialization:
esp_netif_init()
initializes the underlying TCP/IP stack, andesp_event_loop_create_default()
sets up the default event loop. - AP Network Interface:
esp_netif_create_default_wifi_ap()
creates the network interface for the AP. - Stop DHCP Server:
esp_netif_dhcps_stop(ap_netif)
ensures any default DHCP server instance is stopped before custom configuration. This is crucial because the DHCP server’s range is typically based on the interface’s IP, which we are about to set. - Set Static IP for AP:
- An
esp_netif_ip_info_t
structure is populated with the desired static IP address, netmask, and gateway for the AP itself. esp_netif_set_ip_info(ap_netif, &ip_info)
applies this static IP configuration to the AP interface. The DHCP server will offer IPs from this subnet.
- An
- Configure DHCP Server Options: This is done using
esp_netif_dhcps_option()
:- IP Address Pool (
ESP_NETIF_REQUESTED_IP_ADDRESS
): Adhcps_lease_t
structure defines the start and end IP addresses of the pool that the DHCP server will manage. This option effectively sets the range of IPs to be leased out. - Lease Time (
ESP_NETIF_IP_ADDRESS_LEASE_TIME
): Sets the duration for which an IP address is leased to a client. The value is provided in seconds. Theesp_netif_types.h
specifies the unit as seconds. - DNS Server (
ESP_NETIF_DOMAIN_NAME_SERVER
): Anesp_netif_dns_info_t
structure is used to specify the DNS server IP address that will be provided to clients. Here, we use the AP’s own IP address, meaning clients will query the AP for DNS. For actual internet resolution, the AP would need to perform DNS forwarding or clients use a public DNS like8.8.8.8
if the AP has internet connectivity itself (e.g., in APSTA mode with NAT).
- IP Address Pool (
- Start DHCP Server:
esp_netif_dhcps_start(ap_netif)
starts the DHCP server with the configured static IP for the AP and the custom DHCP options. - Wi-Fi Initialization & Configuration:
WIFI_INIT_CONFIG_DEFAULT()
gets the default Wi-Fi initialization configuration.esp_wifi_init()
initializes the Wi-Fi driver.- Event handlers for Wi-Fi and IP events are registered.
- A
wifi_config_t
structure is populated with AP settings (SSID, password, channel, authentication mode, max connections). esp_wifi_set_mode(WIFI_MODE_AP)
sets the ESP32 to AP mode.esp_wifi_set_config(WIFI_IF_AP, &wifi_config)
applies the AP configuration.esp_wifi_start()
starts the Wi-Fi AP.
- Network Stack Initialization:
app_main(void)
Function:- Initializes NVS (Non-Volatile Storage), which is required by the Wi-Fi driver.
- Calls
wifi_init_softap()
to set up and start the AP with the DHCP server.
%%{init: {"theme": "base", "themeVariables": { "primaryColor": "#DBEAFE", "primaryTextColor": "#1E40AF", "primaryBorderColor": "#2563EB", "lineColor": "#6B7280", "textColor": "#1F2937", "fontSize": "14px", "fontFamily": "Open Sans" }}}%% graph TD A["Start: wifi_init_softap()"] --> B{"Initialize Netif & Event Loop?"}; B -- Yes --> C["Create Default Wi-Fi AP Netif<br><code class='mermaid-code'>esp_netif_create_default_wifi_ap()</code>"]; C --> D["Stop DHCP Server (if running)<br><code class='mermaid-code'>esp_netif_dhcps_stop()</code>"]; D --> E["Configure Static IP for AP<br><code class='mermaid-code'>esp_netif_set_ip_info()</code>"]; E --> F{"Static IP Set Successfully?"}; F -- Yes --> G["Configure DHCP Server Options<br>- IP Pool (ESP_NETIF_REQUESTED_IP_ADDRESS)<br>- Lease Time (ESP_NETIF_IP_ADDRESS_LEASE_TIME)<br>- DNS Server (ESP_NETIF_DOMAIN_NAME_SERVER)<br><code class='mermaid-code'>esp_netif_dhcps_option()</code>"]; G --> H{"Options Set Successfully?"}; H -- Yes --> I["Start DHCP Server<br><code class='mermaid-code'>esp_netif_dhcps_start()</code>"]; I --> J{"DHCP Server Started?"}; J -- Yes --> K["Initialize Wi-Fi & Register Handlers<br><code class='mermaid-code'>esp_wifi_init()</code>, <code class='mermaid-code'>esp_event_handler_instance_register()</code>"]; K --> L["Configure Wi-Fi AP Settings<br><code class='mermaid-code'>esp_wifi_set_mode(WIFI_MODE_AP)</code><br><code class='mermaid-code'>esp_wifi_set_config()</code>"]; L --> M["Start Wi-Fi AP<br><code class='mermaid-code'>esp_wifi_start()</code>"]; M --> N[End: AP & DHCP Server Running]; F -- No --> Err1[Error: Setting Static IP Failed]; H -- No --> Err2[Error: Setting DHCP Options Failed]; J -- No --> Err3[Error: DHCP Server Start Failed]; B -- No --> Err4[Error: Netif/Event Loop Init Failed]; Err1 --> X[Handle Error / Log]; Err2 --> X; Err3 --> X; Err4 --> X; classDef startEnd fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6,font-weight:bold classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF classDef decision fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E classDef success fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46,font-weight:bold classDef errorNode fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B class A,N startEnd; class C,D,E,G,I,K,L,M process; class B,F,H,J decision; class Err1,Err2,Err3,Err4,X errorNode;
CMakeLists.txt
Ensure your main/CMakeLists.txt
file includes the necessary components:
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")
# Required components for this example
# esp_wifi for Wi-Fi functionalities
# esp_event for event handling
# esp_netif for network interface management (including DHCP server)
# esp_log for logging
# nvs_flash for NVS initialization
# lwip for TCP/IP stack utilities like inet_pton
The build system typically infers dependencies, but explicitly listing esp_wifi
, esp_event
, esp_netif
, esp_log
, nvs_flash
, and lwip
in your project’s main CMakeLists.txt
(or ensuring they are public dependencies of other components you use) is good practice.
Usually, you only need to add your source files:
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")
The ESP-IDF build system will link the required libraries like esp_netif
, esp_wifi
, etc., based on the esp_interface.h
includes.
Build Instructions
- Open VS Code and ensure your project (
esp32_dhcp_server
) is loaded. - Select your ESP32 target (e.g., ESP32, ESP32-S3) using the
ESP-IDF: Set ESP-IDF Target
command or the button in the status bar. - Build the project using the
ESP-IDF: Build your project
command (or the build icon).
Run/Flash/Observe Steps
- Connect your ESP32 board to your computer via USB.
- Select the correct serial port using
ESP-IDF: Select Port to Use (COM, ttyS)
if not automatically detected. - Flash the project using
ESP-IDF: Flash your project
(or the flash icon). - Open the ESP-IDF Monitor using
ESP-IDF: Monitor your device
(or the monitor icon) to view log messages. You should see logs indicating the AP has started, the static IP of the AP, and the DHCP server configuration.I (XXX) dhcp_server_example: ESP_WIFI_MODE_AP with DHCP Server
I (XXX) dhcp_server_example: Stopping DHCP server...
I (XXX) dhcp_server_example: Configuring static IP for AP interface...
I (XXX) dhcp_server_example: AP Static IP configured: IP=192.168.10.1, GW=192.168.10.1, Mask=255.255.255.0
I (XXX) dhcp_server_example: Configuring DHCP server options...
I (XXX) dhcp_server_example: DHCP IP Pool configured: 192.168.10.100 - 192.168.10.150
I (XXX) dhcp_server_example: DHCP Lease Time configured: 120 minutes (7200 seconds)
I (XXX) dhcp_server_example: DHCP DNS Server configured: 192.168.10.1
I (XXX) dhcp_server_example: Starting DHCP server...
I (XXX) dhcp_server_example: DHCP server started.
...
I (XXX) dhcp_server_example: wifi_init_softap finished. SSID:ESP32_AP_DHCP password:password123 channel:1
I (XXX) dhcp_server_example: Connect to AP and observe DHCP lease assignments.
- On your Wi-Fi client device (e.g., smartphone):
- Scan for Wi-Fi networks.
- Connect to the SSID
ESP32_AP_DHCP
using the passwordpassword123
.
- Observe the ESP32 monitor output. You should see messages like:
I (XXX) dhcp_server_example: Station xx:xx:xx:xx:xx:xx joined, AID=y I (YYY) dhcp_server_example: Assigned IP address to station: 192.168.10.100
The assigned IP address should be within the192.168.10.100
to192.168.10.150
range you configured. - On your client device, check its Wi-Fi connection details. It should show the assigned IP address, subnet mask (e.g., 255.255.255.0), router/gateway (192.168.10.1), and DNS server (192.168.10.1).
Tip: If you encounter issues with clients not connecting or getting IPs, double-check the AP password, the
authmode
, and ensure the DHCP server settings (especially the IP pool relative to the AP’s static IP and netmask) are correct.
Variant Notes
The DHCP server implementation using esp_netif
is largely consistent across ESP32 variants that support Wi-Fi AP mode. This includes:
- ESP32: Full support.
- ESP32-S2: Full support.
- ESP32-S3: Full support.
- ESP32-C3: Full support.
- ESP32-C6: Full support (Wi-Fi 6 capabilities, but DHCP server operation is standard).
- ESP32-H2: Supports Wi-Fi, so DHCP server on Wi-Fi AP interface is applicable. (Note: ESP32-H2 also supports 802.15.4 Thread/Zigbee, which use different mechanisms for address assignment, not typically DHCPv4).
Key Considerations:
- Resource Availability: While the DHCP server itself is lightweight, the maximum number of connected clients (
EXAMPLE_MAX_STA_CONN
) and overall network performance can be influenced by the specific ESP32 variant’s RAM and processing power. For typical applications with a few clients, all listed variants are capable. - API Consistency: ESP-IDF
esp_netif
abstraction ensures that the C code provided in the examples will work across these variants without modification for the DHCP server functionality. - Default Configurations: Default Kconfig settings related to LwIP and DHCP server might vary slightly between chip targets initially, but the runtime configuration shown in the example overrides these for specific parameters like IP pool and lease time. Always verify your project’s
sdkconfig
if you rely on default behaviors.
No significant differences in the DHCP server API or its core functionality are expected for these Wi-Fi enabled variants when using ESP-IDF v5.x. The primary differences would lie in other chip features (CPU core, peripherals, radio type like Wi-Fi 6 for ESP32-C6).
Common Mistakes & Troubleshooting Tips
Mistake / Issue | Symptom(s) | Troubleshooting / Solution |
---|---|---|
DHCP Server Not Starting or Clients Not Getting IPs |
– ESP32 log shows errors related to DHCP server start. – Wi-Fi clients connect to AP but don’t receive an IP address (stuck on “Obtaining IP address…”). – Clients might self-assign an APIPA address (e.g., 169.254.x.x). |
– Ensure AP Static IP: Verify esp_netif_set_ip_info() is called for the AP esp_netif_t handle before configuring or starting the DHCP server. The AP needs a fixed IP (e.g., 192.168.10.1 ).– Correct Subnet for Pool: The DHCP IP pool (e.g., DHCP_LEASE_START_IP , DHCP_LEASE_END_IP ) must be on the same subnet as the AP’s static IP and netmask. For AP IP 192.168.10.1 / 255.255.255.0 , a pool like 192.168.10.100 - 192.168.10.150 is valid. A pool like 192.168.5.100 would be invalid.– AP IP Not in Pool: The AP’s own static IP address should not be part of the dynamic lease pool. |
Incorrect DHCP Options Applied |
– Clients receive wrong DNS server, gateway, or lease time. – ESP32 log might show errors from esp_netif_dhcps_option() (check return code).
|
– Verify Option IDs: Use correct esp_netif_dhcps_opt_id_t values (e.g., ESP_NETIF_IP_ADDRESS_LEASE_TIME , ESP_NETIF_DOMAIN_NAME_SERVER ).– Correct Data Types & Units: Ensure data passed to esp_netif_dhcps_option() matches expected type and units. Lease time (ESP_NETIF_IP_ADDRESS_LEASE_TIME ) expects a pointer to uint32_t representing seconds.– Log Return Values: Check the esp_err_t returned by esp_netif_dhcps_option() for success (ESP_OK ) or specific error codes.
|
IP Address Conflicts |
– Intermittent connectivity issues for clients. – Multiple devices claiming the same IP address. |
– Exclusive DHCP Pool: Ensure the DHCP server’s IP address pool is unique and does not overlap with any statically assigned IP addresses on the AP network. – AP IP Excluded: The AP’s own static IP should not be included in the range of IPs leased by the DHCP server. |
IP_EVENT_AP_STAIPASSIGNED Not Triggering |
– Client connects to AP and gets an IP, but the ESP32 log doesn’t show the “Assigned IP address to station” message. |
– Event Handler Registration: Verify esp_event_handler_instance_register() is correctly called for IP_EVENT base and IP_EVENT_AP_STAIPASSIGNED event ID.– Wi-Fi Connection: Ensure basic Wi-Fi connectivity is stable. If the client fails authentication or disconnects quickly, the DHCP process might not complete. – Client-Side Issues: Rarely, the client device itself might have issues with its DHCP client. Test with multiple client devices. |
Forgetting to Stop DHCP Server Before Reconfiguration |
– Unpredictable behavior after changing AP’s static IP or DHCP pool settings. – DHCP server might operate with old parameters or fail. |
– Stop-Configure-Start Sequence: Always call esp_netif_dhcps_stop(ap_netif) before changing the AP interface’s IP address (with esp_netif_set_ip_info() ) or significantly altering DHCP pool settings using esp_netif_dhcps_option() . After all configurations are applied, call esp_netif_dhcps_start(ap_netif) .
|
Wi-Fi Authentication Issues |
– Clients fail to connect to the AP (password incorrect, unsupported auth mode). – No WIFI_EVENT_AP_STACONNECTED event.
|
– Check SSID & Password: Ensure the client is using the correct SSID and password defined in EXAMPLE_ESP_WIFI_SSID and EXAMPLE_ESP_WIFI_PASS .– Authentication Mode: Verify wifi_config.ap.authmode . WIFI_AUTH_WPA2_PSK is common. If password is empty, it should be WIFI_AUTH_OPEN .– Client Compatibility: Some older clients might have issues with WPA3 or mixed WPA2/WPA3 modes if configured. Start with WIFI_AUTH_WPA2_PSK for broad compatibility.
|
Exercises
- Modify DHCP Server Configuration:
- Change the AP’s static IP address to
10.0.0.1
. - Configure the DHCP server to lease IP addresses in the range
10.0.0.50
to10.0.0.70
. - Set the lease time to 30 minutes.
- Set the DNS server provided to clients to Google’s public DNS (
8.8.8.8
). - Test by connecting a client and verifying its received IP configuration.
- Change the AP’s static IP address to
- Log DHCP Leases:
- Modify the
ip_event_handler
forIP_EVENT_AP_STAIPASSIGNED
. - When an IP is assigned, log the client’s MAC address (available from
WIFI_EVENT_AP_STACONNECTED
if you map AID or store it) along with the assigned IP address and the current timestamp (e.g., fromesp_timer_get_time()
). - Advanced: Store these lease details (MAC, IP, timestamp) in NVS for persistence.
- Modify the
- Implement Static IP Assignment (DHCP Reservation):
- Identify the MAC address of a specific client device you want to assign a static IP.
- Use the
esp_netif_dhcps_option()
function with the option IDESP_NETIF_STATIC_IP_ADDR
and thedhcps_static_lease_t
structure to reserve a specific IP address (e.g.,192.168.10.200
from the example’s subnet) for that MAC address.
// Example snippet for Exercise 3 (place appropriately after AP netif is created and before dhcps_start)
// Assume ap_netif is your ESP_Netif handle for the AP
dhcps_static_lease_t static_lease;
// Convert MAC string to uint8_t array, e.g., "AA:BB:CC:DD:EE:FF"
// sscanf("AA:BB:CC:DD:EE:FF", "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
// &static_lease.mac[0], &static_lease.mac[1], &static_lease.mac[2],
// &static_lease.mac[3], &static_lease.mac[4], &static_lease.mac[5]);
// Or manually:
static_lease.mac[0] = 0xAA; static_lease.mac[1] = 0xBB; /* ... and so on */
inet_pton(AF_INET, "192.168.10.200", &static_lease.ip); // The IP to reserve
esp_err_t err = esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_STATIC_IP_ADDR, &static_lease, sizeof(static_lease));
if (err == ESP_OK) {
ESP_LOGI(TAG, "Static IP 192.168.10.200 reserved for MAC %02X:%02X:%02X:%02X:%02X:%02X",
static_lease.mac[0], static_lease.mac[1], static_lease.mac[2],
static_lease.mac[3], static_lease.mac[4], static_lease.mac[5]);
} else {
ESP_LOGE(TAG, "Failed to set static IP lease (err=0x%x)", err);
}
- Connect the client device and verify it receives the reserved IP address. Ensure the reserved IP is outside the dynamic pool or that the DHCP server handles such reservations correctly (LwIP usually does).
Summary
- DHCP automates IP address and network configuration for clients.
- The DORA process (Discover, Offer, Request, Acknowledge) is the core of DHCP communication.
- ESP32 can act as a DHCP server when in AP mode, providing IPs to connected clients.
- The
esp_netif
API is used to manage network interfaces and the DHCP server in ESP-IDF v5.x. - Key steps: Initialize AP
esp_netif
, stop default DHCP, set static IP for AP, configure DHCP options (pool, lease time, DNS), and start DHCP server. - DHCP options like IP pool (
ESP_NETIF_REQUESTED_IP_ADDRESS
), lease time (ESP_NETIF_IP_ADDRESS_LEASE_TIME
), and DNS server (ESP_NETIF_DOMAIN_NAME_SERVER
) can be configured usingesp_netif_dhcps_option()
. - Static IP assignments for specific MAC addresses can be configured using
ESP_NETIF_STATIC_IP_ADDR
. - The
IP_EVENT_AP_STAIPASSIGNED
event indicates successful IP assignment to a client. - Functionality is consistent across ESP32, S2, S3, C3, C6, and H2 (for Wi-Fi AP).
Further Reading
- ESP-IDF Wi-Fi Documentation:
- Wi-Fi SoftAP + DHCP Server Example: Official Espressif example. (Adapt branch for v5.x)
- ESP-IDF Wi-Fi API Guide
- ESP-IDF Networking (
esp_netif
) Documentation:- esp_netif API Reference: Details on network interface management, including DHCP server functions.
- Check the
esp_netif_dhcps_option()
documentation and availableopt_id
values.
- LwIP DHCP Server Documentation:
- While
esp_netif
provides an abstraction, the underlying DHCP server is often based on LwIP. Understanding LwIP’s DHCP server can provide deeper insights: https://savannah.nongnu.org/projects/lwip/ (client-side, but general concepts apply) andapps/dhcpserver/dhcpserver.h
.
- While
- RFC 2131: Dynamic Host Configuration Protocol
- RFC 2132: DHCP Options and BOOTP Vendor Extensions
