In this tutorial I will show you how to create Web Server using ESP32 Module based on Zephyr RTOS. You will learn how to create a HTML Web Page with Buttons for 2 LEDs. We will control LEDs based on pressed buttons.
What is Zephyr RTOS?
Zephyr is small RTOS (Real Time Operating System) which is designed for Resource-Constrained and Embedded Devices. It is provided with open-source permissively licensed using the Apache 2.0 license.
For a Linux developer, Zephyr RTOS offers a smooth transition into embedded systems by leveraging familiar Linux tools (like Kconfig, Device Tree) for powerful, scalable, and secure IoT development.
It supports multiple architectures like,
- ARCv2 (EM and HS) and ARCv3 (HS6X)
- ARMv6-M, ARMv7-M, and ARMv8-M (Cortex-M)
- ARMv7-A and ARMv8-A (Cortex-A, 32- and 64-bit)
- ARMv7-R, ARMv8-R (Cortex-R, 32- and 64-bit)
- Intel x86 (32- and 64-bit)
- MIPS (MIPS32 Release 1 specification)
- Renesas RX
- RISC-V (32- and 64-bit)
- SPARC V8
- Tensilica Xtensa
Key Features,
- A small kernel
- Highly configurable / Modular for flexibility
- Supports Variety of Boards with Different CPU Architecture
- Implements Memory Protection
- Compile-time resource definition reduces code size
- Consistent Device Driver Support
- Uses Device tree Concept from Linux
- Has Native Support for Multiple Protocols
- Host PC Development support for Linux, macOS and Windows OS
- Native sim allows to run as Linux application for Simulation purposes.
HTTP
HTTP which stands for Hypertext Transfer Protocol is the foundation of the World Wide Web (WWW) is used to load Web Pages including our WebSite. It is standard which is used between Web browser and Web Server, It defines rules to transfer data.
It works on Client server model, Client requests data from Server.
Features
- Stateless
Server is not required to store any information between requests. - Connectionless
It is considered connectionless as connection is dropped after every data transfers. - Media Independent
Multiple type of data can be shared using HTTP. - Client-Server Model
Follows client-server model for requesting and serving data. - Requests Methods
Supports multiple methods like GET, POST, PUT and DELETE for different actions.
Prerequisite
Zephyr RTOS Installation Step
Hardware Used
- ESP32-C6 Dev Kit (You can use any ESP32 based board with this code)
- 5mm Through Hole LEDs
- 470 Ohm Through Hole Resistors
- Connecting Wires
- Bread Board
Full Project Code
GitHub Repo Link
How to use Build and Flash Project using Zephyr RTOS
- First of all, Setup Zephyr RTOS on your host pc. To setup Zephyr RTOS on windows system I have created tutorial at,
Zephyr RTOS Installation Step
- Download full project code from provided GitHub repo link above.
- Copy downloaded project folder "ESP32_Zephyr_WebServer_LED_Control" into Zephyr RTOS folder at path "zephyrproject\zephyr\applications\" .
Full path for reference, "C:\Users\embetronics\zephyrproject\zephyr\applications\ESP32_Zephyr_WebServer_LED_Control" - To build project use below command,
west build -p=auto -b esp32c3_devkitm .\applications\ESP32_Zephyr_WebServer_LED_Control\ - Flash build project and open com port monitor using below commands,
west flash west espressif monitor
Working Of Code
- Connect 2 LEDs with Board on GPIO Pins 2 and 3 through 470 Ohm series resistor.
- Build and Flash provided project on board using steps mentioned in above steps.
- As soon as program starts running on board, It will create Wi-Fi Access Point with below credentials.
SSID : EmbeTronics PASS : 12345678 - From Laptop or Mobile, Connect with above Access Point using provided credentials. As soon as device is connected with access point of ESP32, It will automatically open webpage in browser.
- If Webpage didn't open automatically in Web Browser, Use IP Address "192.168.4.1" to open Web Page manually.
- From Webpage you can control both LEDs by pressing On and Off Button.
Explanation of Code
main.c
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/wifi_mgmt.h>
#include <zephyr/net/dhcpv4_server.h>
#include <zephyr/net/http/service.h>
#include <zephyr/drivers/gpio.h>LOG_MODULE_REGISTER(MAIN);/* The devicetree node identifier for the "led0" alias. */
#define RED_NODE DT_ALIAS(redled)
#define GREEN_NODE DT_ALIAS(greenled)static const struct gpio_dt_spec redled = GPIO_DT_SPEC_GET(RED_NODE, gpios);
static const struct gpio_dt_spec greenled = GPIO_DT_SPEC_GET(GREEN_NODE, gpios);
#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X"
#define NET_EVENT_WIFI_MASK \
(NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT | \
NET_EVENT_WIFI_AP_ENABLE_RESULT | NET_EVENT_WIFI_AP_DISABLE_RESULT | \
NET_EVENT_WIFI_AP_STA_CONNECTED | NET_EVENT_WIFI_AP_STA_DISCONNECTED)
static struct net_if *ap_iface;
static struct net_if *sta_iface;static struct wifi_connect_req_params ap_config;
static struct wifi_connect_req_params sta_config;static struct net_mgmt_event_callback cb;/* Check necessary definitions */
BUILD_ASSERT(sizeof(CONFIG_WIFI_SAMPLE_AP_SSID) > 1,
"CONFIG_WIFI_SAMPLE_AP_SSID is empty. Please set it in conf file.");
BUILD_ASSERT(sizeof(CONFIG_WIFI_SAMPLE_SSID) > 1,
"CONFIG_WIFI_SAMPLE_SSID is empty. Please set it in conf file.");
#if WIFI_SAMPLE_DHCPV4_START
BUILD_ASSERT(sizeof(CONFIG_WIFI_SAMPLE_AP_IP_ADDRESS) > 1,
"CONFIG_WIFI_SAMPLE_AP_IP_ADDRESS is empty. Please set it in conf file.");
BUILD_ASSERT(sizeof(CONFIG_WIFI_SAMPLE_AP_NETMASK) > 1,
"CONFIG_WIFI_SAMPLE_AP_NETMASK is empty. Please set it in conf file.");
#endifstatic uint8_t root_html_gz[] = {
#include "root.html.gz.inc"
};static int default_handler(struct http_client_ctx *client, enum http_data_status status,
const struct http_request_ctx *request_ctx,
struct http_response_ctx *response_ctx, void *user_data)
{
// LOG_INF("%s,%d, req %s", __func__, __LINE__, client->url_buffer);
if(strstr(client->url_buffer,"GREEN_LED_ON"))
{
gpio_pin_set_dt(&greenled,1);
}
else if(strstr(client->url_buffer,"GREEN_LED_OFF"))
{
gpio_pin_set_dt(&greenled,0);
}
else if(strstr(client->url_buffer,"RED_LED_ON"))
{
gpio_pin_set_dt(&redled,1);
}
else if(strstr(client->url_buffer,"RED_LED_OFF"))
{
gpio_pin_set_dt(&redled,0);
}
if (status == HTTP_SERVER_DATA_FINAL) {
response_ctx->status = HTTP_200_OK;
response_ctx->body = root_html_gz;
response_ctx->body_len = sizeof(root_html_gz) - 1;
response_ctx->final_chunk = true;
}
return 0;
}static struct http_resource_detail_dynamic default_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_DYNAMIC,
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
},
.cb = default_handler,
.user_data = NULL,
};
HTTP_SERVICE_DEFINE(http_service, "0.0.0.0", &http_service_port, 1, 10, NULL, &default_detail, NULL);struct http_resource_detail_static root_html_gz_resource_detail = {
.common = {
.type = HTTP_RESOURCE_TYPE_STATIC,
.bitmask_of_supported_http_methods = BIT(HTTP_GET),
// .content_encoding = "gzip",
},
.static_data = root_html_gz,
.static_data_len = sizeof(root_html_gz),
};
HTTP_RESOURCE_DEFINE(root_html_gz_resource, http_service, "/",&root_html_gz_resource_detail);
static void wifi_event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_event,
struct net_if *iface)
{
switch (mgmt_event) {
case NET_EVENT_WIFI_CONNECT_RESULT: {
LOG_INF("Connected to %s", CONFIG_WIFI_SAMPLE_SSID);
break;
}
case NET_EVENT_WIFI_DISCONNECT_RESULT: {
LOG_INF("Disconnected from %s", CONFIG_WIFI_SAMPLE_SSID);
break;
}
case NET_EVENT_WIFI_AP_ENABLE_RESULT: {
LOG_INF("AP Mode is enabled. Waiting for station to connect");
break;
}
case NET_EVENT_WIFI_AP_DISABLE_RESULT: {
LOG_INF("AP Mode is disabled.");
break;
}
case NET_EVENT_WIFI_AP_STA_CONNECTED: {
struct wifi_ap_sta_info *sta_info = (struct wifi_ap_sta_info *)cb->info;
LOG_INF("station: " MACSTR " joined ", sta_info->mac[0], sta_info->mac[1],
sta_info->mac[2], sta_info->mac[3], sta_info->mac[4], sta_info->mac[5]);
break;
}
case NET_EVENT_WIFI_AP_STA_DISCONNECTED: {
struct wifi_ap_sta_info *sta_info = (struct wifi_ap_sta_info *)cb->info;
LOG_INF("station: " MACSTR " leave ", sta_info->mac[0], sta_info->mac[1],
sta_info->mac[2], sta_info->mac[3], sta_info->mac[4], sta_info->mac[5]);
break;
}
default:
break;
}
}
#if CONFIG_WIFI_SAMPLE_DHCPV4_START
static void enable_dhcpv4_server(void)
{
static struct net_in_addr addr;
static struct net_in_addr netmaskAddr;
if (net_addr_pton(NET_AF_INET, CONFIG_WIFI_SAMPLE_AP_IP_ADDRESS, &addr)) {
LOG_ERR("Invalid address: %s", CONFIG_WIFI_SAMPLE_AP_IP_ADDRESS);
return;
}
if (net_addr_pton(NET_AF_INET, CONFIG_WIFI_SAMPLE_AP_NETMASK, &netmaskAddr)) {
LOG_ERR("Invalid netmask: %s", CONFIG_WIFI_SAMPLE_AP_NETMASK);
return;
}
net_if_ipv4_set_gw(ap_iface, &addr);
if (net_if_ipv4_addr_add(ap_iface, &addr, NET_ADDR_DHCP, 0) == NULL) {
LOG_ERR("unable to set IP address for AP interface");
}
if (!net_if_ipv4_set_netmask_by_addr(ap_iface, &addr, &netmaskAddr)) {
LOG_ERR("Unable to set netmask for AP interface: %s",
CONFIG_WIFI_SAMPLE_AP_NETMASK);
}
addr.s4_addr[3] += 10; /* Starting IPv4 address for DHCPv4 address pool. */
if (net_dhcpv4_server_start(ap_iface, &addr) != 0) {
LOG_ERR("DHCP server is not started for desired IP");
return;
}
LOG_INF("DHCPv4 server started...\n");
}
#endifstatic int enable_ap_mode(void)
{
if (!ap_iface) {
LOG_INF("AP: is not initialized");
return -EIO;
}
LOG_INF("Turning on AP Mode");
ap_config.ssid = (const uint8_t *)CONFIG_WIFI_SAMPLE_AP_SSID;
ap_config.ssid_length = sizeof(CONFIG_WIFI_SAMPLE_AP_SSID) - 1;
ap_config.psk = (const uint8_t *)CONFIG_WIFI_SAMPLE_AP_PSK;
ap_config.psk_length = sizeof(CONFIG_WIFI_SAMPLE_AP_PSK) - 1;
ap_config.channel = WIFI_CHANNEL_ANY;
ap_config.band = WIFI_FREQ_BAND_2_4_GHZ;
if (sizeof(CONFIG_WIFI_SAMPLE_AP_PSK) == 1) {
ap_config.security = WIFI_SECURITY_TYPE_NONE;
} else {
ap_config.security = WIFI_SECURITY_TYPE_PSK;
}
#if CONFIG_WIFI_SAMPLE_DHCPV4_START
enable_dhcpv4_server();
#endif
int ret = net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, ap_iface, &ap_config,
sizeof(struct wifi_connect_req_params));
if (ret) {
LOG_ERR("NET_REQUEST_WIFI_AP_ENABLE failed, err: %d", ret);
}
return ret;
}
static int connect_to_wifi(void)
{
if (!sta_iface) {
LOG_INF("STA: interface no initialized");
return -EIO;
}
sta_config.ssid = (const uint8_t *)CONFIG_WIFI_SAMPLE_SSID;
sta_config.ssid_length = sizeof(CONFIG_WIFI_SAMPLE_SSID) - 1;
sta_config.psk = (const uint8_t *)CONFIG_WIFI_SAMPLE_PSK;
sta_config.psk_length = sizeof(CONFIG_WIFI_SAMPLE_PSK) - 1;
sta_config.security = WIFI_SECURITY_TYPE_PSK;
sta_config.channel = WIFI_CHANNEL_ANY;
sta_config.band = WIFI_FREQ_BAND_2_4_GHZ;
LOG_INF("Connecting to SSID: %s\n", sta_config.ssid);
int ret = net_mgmt(NET_REQUEST_WIFI_CONNECT, sta_iface, &sta_config,
sizeof(struct wifi_connect_req_params));
if (ret) {
LOG_ERR("Unable to Connect to (%s)", CONFIG_WIFI_SAMPLE_SSID);
}
return ret;
}
- Initialize GPIO pins for both LEDs in Output mode, using device node from Device tree overlay.
- Register Wi-Fi event handler function.
- Initialize Wi-Fi Access Point and Station Mode.
- Start http server.
int main(void)
{
// k_sleep(K_SECONDS(5));
/* Init LED GPIO Pins */
int ret;
if (!gpio_is_ready_dt(&redled)) {
LOG_ERR("RED LED gpio not ready !!");
return 0;
}
if (!gpio_is_ready_dt(&greenled)) {
LOG_ERR("GREEN LED gpio not ready !!");
return 0;
}
ret = gpio_pin_configure_dt(&redled, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
ret = gpio_pin_configure_dt(&greenled, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
// ret = gpio_pin_toggle_dt(&redled);
// ret = gpio_pin_toggle_dt(&greenled);
// k_sleep(K_SECONDS(2));
// ret = gpio_pin_toggle_dt(&redled);
// ret = gpio_pin_toggle_dt(&greenled);
// k_sleep(K_SECONDS(2));
// ret = gpio_pin_toggle_dt(&redled);
// ret = gpio_pin_toggle_dt(&greenled);
net_mgmt_init_event_callback(&cb, wifi_event_handler, NET_EVENT_WIFI_MASK);
net_mgmt_add_event_callback(&cb);
/* Get AP interface in AP-STA mode. */
ap_iface = net_if_get_wifi_sap();
/* Get STA interface in AP-STA mode. */
sta_iface = net_if_get_wifi_sta();
enable_ap_mode();
connect_to_wifi();
http_server_start();
return 0;
}
Device Logs
ESP-ROM:esp32c3-api1-20210207
Build:Feb 7 2021
rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fc8c540,len:0x7454
load:0x40380000,len:0xc530
SHA-256 comparison failed:
Calculated: cb0ed82a57c19be673d7b9a8a85823ddaa5e0b6eb06a3a3443f0314522230864
Expected: 0000000040c60000000000000000000000000000000000000000000000000000
Attempting to boot anyway...
entry 0x4038328c
I (75) soc_init: ESP Simple boot
I (75) soc_init: compile time Jan 3 2026 12:58:13
I (75) soc_init: chip revision: v0.3
I (75) flash_init: SPI Speed : 80MHz
I (78) flash_init: SPI Mode : DIO
I (82) flash_init: SPI Flash Size : 4MB
I (85) boot: DRAM : lma=00000020h vma=3fc8c540h size=07454h ( 29780)
I (91) boot: IRAM : lma=0000747ch vma=40380000h size=0c530h ( 50480)
I (97) boot: IROM : lma=00020000h vma=42000000h size=670e8h (422120)
I (103) boot: DROM : lma=00090000h vma=3c070000h size=0e898h ( 59544)
I (121) boot: libc heap size 87 kB.
I (121) spi_flash: detected chip: generic
I (121) spi_flash: flash io: dio
[00:00:00.003,000] <inf> wifi_init: rx ba win: 6
*** Booting Zephyr OS build v4.3.0-2760-g72360f821a61 ***
[00:00:00.005,000] <inf> MAIN: Turning on AP Mode
[00:00:00.005,000] <dbg> net_dhcpv4_server: net_dhcpv4_server_start: Started DHCPv4 server, address pool:
[00:00:00.005,000] <dbg> net_dhcpv4_server: net_dhcpv4_server_start: 0: 192.168.4.11
[00:00:00.005,000] <dbg> net_dhcpv4_server: net_dhcpv4_server_start: 1: 192.168.4.12
[00:00:00.005,000] <dbg> net_dhcpv4_server: net_dhcpv4_server_start: 2: 192.168.4.13
[00:00:00.005,000] <dbg> net_dhcpv4_server: net_dhcpv4_server_start: 3: 192.168.4.14
[00:00:00.006,000] <inf> MAIN: DHCPv4 server started...
[00:00:00.912,000] <inf> MAIN: AP Mode is enabled. Waiting for station to connect
[00:00:00.912,000] <inf> MAIN: Connecting to SSID: SAMPLE_SSID
[00:00:03.750,000] <inf> MAIN: Disconnected from SAMPLE_SSID
[00:00:10.620,000] <inf> MAIN: station: 28:C5:D2:4D:0C:2E joined
[00:00:11.361,000] <dbg> net_dhcpv4_server: dhcpv4_handle_discover: DHCPv4 processing Discover - reserved 192.168.4.11
[00:00:12.377,000] <dbg> net_dhcpv4_server: dhcpv4_handle_request: DHCPv4 processing Request - allocated 192.168.4.11
0 Comments