The ESP32 family has become a go-to platform for getting started with Zephyr. It is cheap, readily available, and offers integrated WiFi connectivity. This first part of a three-part tutorial series covers establishing a WiFi connection and obtaining an IP address via DHCP.
| Target | ESP32-S3-DevKitC |
| Zephyr version | 4.3.0 |
| Level | Beginner |
Tutorial Series:
- WiFi Connectivity (this part)
- HTTP Client
- HTTPS with TLS
Prerequisites
Hardware:
- ESP32-S3-DevKitC (or compatible ESP32-S3 board)
Software:
- Zephyr RTOS (tested with v4.3.0)
- Zephyr SDK and
westtool - Espressif toolchain and HAL module
- A WiFi network with active DHCP server and internet access
Required Setup:
Before starting, ensure you have a working Zephyr development environment for ESP32:
-
Zephyr Getting Started Guide: Follow the official Getting Started Guide to install Zephyr and the SDK.
-
Espressif (ESP32) Setup: Complete the Espressif ESP32 board setup including:
- Installing the Espressif HAL:
west blobs fetch hal_espressif - Setting up the toolchain
- Installing the Espressif HAL:
-
Verify Installation: Build and flash a simple example like
samples/hello_worldto confirm your environment works.
Project Structure
The project consists of these files:
wifi_app/
├── CMakeLists.txt # Build configuration
├── Kconfig # Custom Kconfig options for WiFi credentials
├── prj.conf # Main Zephyr configuration
├── boards/
│ └── esp32s3_devkitc_procpu.conf # Board-specific settings
└── src/
└── main.c # Application code
Build System Files
Start with CMakeLists.txt:
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(wifi_app)
target_sources(app PRIVATE src/main.c)
Project Configuration
The prj.conf file configures Zephyr’s networking stack for WiFi connectivity:
# Networking
CONFIG_NETWORKING=y
CONFIG_NET_IPV4=y
CONFIG_NET_DHCPV4=y
CONFIG_NET_TCP=y
# WiFi
CONFIG_WIFI=y
CONFIG_NET_L2_WIFI_MGMT=y
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y
CONFIG_NET_MGMT_EVENT_INFO=y
# Stack sizes for ESP32
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_NET_TX_STACK_SIZE=2048
CONFIG_NET_RX_STACK_SIZE=2048
# Network buffers
CONFIG_NET_PKT_RX_COUNT=10
CONFIG_NET_PKT_TX_COUNT=10
CONFIG_NET_BUF_RX_COUNT=20
CONFIG_NET_BUF_TX_COUNT=20
# Logging
CONFIG_LOG=y
CONFIG_NET_LOG=y
# WiFi credentials (edit these)
CONFIG_WIFI_SSID="YourSSID"
CONFIG_WIFI_PSK="YourPassword"
The CONFIG_NET_MGMT* options enable the network management API, which provides callbacks for WiFi and IP address events. The buffer counts and stack sizes are tuned for the ESP32’s memory constraints.
For the WiFi credentials, we define custom Kconfig options in Kconfig:
mainmenu "WiFi HTTP Client Application"
config WIFI_SSID
string "WiFi SSID"
help
SSID of the WiFi network to connect to.
config WIFI_PSK
string "WiFi Password"
help
WPA2-PSK password for the WiFi network.
source "Kconfig.zephyr"
Setting WiFi Credentials
You have two options for configuring the WiFi SSID and password:
Option 1: Edit prj.conf directly
Simply edit the CONFIG_WIFI_SSID and CONFIG_WIFI_PSK values in prj.conf:
CONFIG_WIFI_SSID="MyNetwork"
CONFIG_WIFI_PSK="MyPassword"
Option 2: Use menuconfig
Zephyr’s interactive configuration tool provides a menu-driven interface:
west build -t menuconfig
Navigate to the top-level menu “WiFi HTTP Client Application” to find the SSID and PSK options. Save and exit when done. This updates the build configuration without modifying prj.conf directly.
The ESP32-S3 also needs a board-specific configuration in boards/esp32s3_devkitc_procpu.conf:
CONFIG_ESP32_WIFI_STA_AUTO_DHCPV4=y
Core Concepts
Before diving into the code, let us understand some key Zephyr concepts used in this example.
Network Management Events
Zephyr’s networking stack is event-driven. Operations like WiFi connection and DHCP address assignment happen asynchronously - you initiate an operation, and the system notifies you when it completes (or fails) via callbacks. This is handled by the Network Management API (net_mgmt).
The pattern is:
- Define a callback function to handle specific events
- Register the callback with
net_mgmt_add_event_callback() - Initiate an operation (e.g.,
NET_REQUEST_WIFI_CONNECT) - Your callback fires when the event occurs
Network Interfaces
A network interface (struct net_if) represents a network device - in our case, the ESP32’s WiFi radio. Zephyr abstracts different network technologies (WiFi, Ethernet, cellular) behind this common interface. We use net_if_get_default() to get the primary network interface.
Logging
Zephyr’s logging subsystem (LOG_INF, LOG_ERR, etc.) provides structured logging with module names and severity levels. The LOG_MODULE_REGISTER() macro declares a logging module with a name and default level.
WiFi Connection Code
Create src/main.c:
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/wifi_mgmt.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_event.h>
LOG_MODULE_REGISTER(wifi_app, LOG_LEVEL_INF);
static struct net_mgmt_event_callback wifi_cb;
static struct net_mgmt_event_callback ipv4_cb;
static void wifi_event_handler(struct net_mgmt_event_callback *cb,
uint64_t mgmt_event, struct net_if *iface)
{
if (mgmt_event == NET_EVENT_WIFI_CONNECT_RESULT) {
LOG_INF("WiFi connected");
} else if (mgmt_event == NET_EVENT_WIFI_DISCONNECT_RESULT) {
LOG_INF("WiFi disconnected");
}
}
static void ipv4_event_handler(struct net_mgmt_event_callback *cb,
uint64_t mgmt_event, struct net_if *iface)
{
if (mgmt_event == NET_EVENT_IPV4_ADDR_ADD) {
struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4;
if (ipv4) {
char addr_str[NET_IPV4_ADDR_LEN];
for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) {
if (ipv4->unicast[i].ipv4.is_used) {
net_addr_ntop(AF_INET,
&ipv4->unicast[i].ipv4.address.in_addr,
addr_str, sizeof(addr_str));
LOG_INF("IP Address: %s", addr_str);
}
}
}
}
}
static int connect_wifi(void)
{
struct net_if *iface = net_if_get_default();
struct wifi_connect_req_params params = {
.ssid = CONFIG_WIFI_SSID,
.ssid_length = strlen(CONFIG_WIFI_SSID),
.psk = CONFIG_WIFI_PSK,
.psk_length = strlen(CONFIG_WIFI_PSK),
.channel = WIFI_CHANNEL_ANY,
.band = WIFI_FREQ_BAND_2_4_GHZ,
.security = WIFI_SECURITY_TYPE_PSK,
};
LOG_INF("Connecting to %s...", CONFIG_WIFI_SSID);
return net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, ¶ms, sizeof(params));
}
int main(void)
{
LOG_INF("ESP32-S3 WiFi Example");
/* Register WiFi event callback */
net_mgmt_init_event_callback(&wifi_cb, wifi_event_handler,
NET_EVENT_WIFI_CONNECT_RESULT |
NET_EVENT_WIFI_DISCONNECT_RESULT);
net_mgmt_add_event_callback(&wifi_cb);
/* Register IPv4 event callback */
net_mgmt_init_event_callback(&ipv4_cb, ipv4_event_handler,
NET_EVENT_IPV4_ADDR_ADD);
net_mgmt_add_event_callback(&ipv4_cb);
/* Wait for WiFi driver to initialize */
k_sleep(K_SECONDS(2));
/* Connect to WiFi */
int ret = connect_wifi();
if (ret) {
LOG_ERR("WiFi connect request failed: %d", ret);
}
return 0;
}
Code Walkthrough
Event Callbacks: We register two separate callbacks:
wifi_event_handler- notified when WiFi connects or disconnectsipv4_event_handler- notified when DHCP assigns an IP address
WiFi Parameters: The wifi_connect_req_params structure specifies:
- SSID and password (from Kconfig)
WIFI_CHANNEL_ANY- let the driver find the access pointWIFI_FREQ_BAND_2_4_GHZ- use 2.4 GHz bandWIFI_SECURITY_TYPE_PSK- WPA2-PSK authentication
Asynchronous Flow: The net_mgmt(NET_REQUEST_WIFI_CONNECT, ...) call initiates the connection but returns immediately. The actual connection happens in the background, and our callbacks are invoked when events occur. The main() function returns after initiating the connection - the callbacks continue to fire as events occur.
Building and Flashing
west build -b esp32s3_devkitc/esp32s3/procpu
west flash
west espressif monitor
Expected Output
[00:00:00.000,000] <inf> wifi_app: ESP32-S3 WiFi Example
[00:00:02.000,000] <inf> wifi_app: Connecting to YourSSID...
[00:00:05.xxx,xxx] <inf> wifi_app: WiFi connected
[00:00:06.xxx,xxx] <inf> wifi_app: IP Address: 192.168.x.x
The WiFi connection and DHCP address assignment happen asynchronously after main() returns. The callbacks continue to fire as long as the system is running.
Next Steps
With WiFi connectivity established, proceed to Part 2: HTTP Client to make HTTP requests to a web API.
Further Reading
- Zephyr Networking Guide - comprehensive overview of Zephyr’s network stack
- Zephyr WiFi API Reference - detailed WiFi management API documentation
- Zephyr Network Management - the event callback system used in this tutorial
- ESP32-S3-DevKitC Board Documentation - board-specific details and configuration
- Zephyr Kconfig Reference - understanding the configuration system