Simple ESP32 Web Server LED Control - ESP-IDF

In this tutorial I will show you how to create Web Server using ESP32 Module and ESP-IDF. You will create how to create a HTML Web Page with Buttons for 2 LEDs. We will control LEDs based on pressed buttons.


ESP32 LED Control using Web Server

In this tutorial we will use HTTP protocol for running Web Server on ESP32. Lets first understand what is HTTP.


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

Before proceeding further make sure you have installed VS Code with ESP-IDF extension. You can follow steps mentioned at this link to install and configure ESP-IDF in VS Code.


Hardware Used

In this tutorial we are using following hardware,
  1. ESP32-C6 Dev Kit (You can use any ESP32 based board with this code)ESP32-C3-DevKitC-02 is an entry-level development board based on ESP32-C3-WROOM-02 or ESP32-C3-WROOM-02U, general-purpose modules with 4 MB SPI flash. This board integrates complete Wi-Fi and Bluetooth® Low Energy functions.
  2. 5mm Through Hole LEDs
    Through Hole Red and GreenLED
  3. 470 Ohm Through Hole Resistors
    470 Ohm through hole resistor
  4. Connecting Wires
    Connecting Wires, Jumper Wires
  5. Bread Board
    Bread Board for Connecting Circuit


Create Example Project ESP-IDF


Open VS Code and Press CTRL+SHIFT+P or Click on View > Command Palette to Open Command Palette option. Select ESP-IDF: Show Example Projects Option to view all available examples from ESP-IDF.



Select captive_portal under http_server section, then Select location at which you want to create example project.


After project is created you will see project folder open in VS Code.


ESP-IDF Web-Server Code

You need to replace 2 files with provided code for each of them below.
  1. main.c
    /* Captive Portal Example
    
        This example code is in the Public Domain (or CC0 licensed, at your option.)
    
        Unless required by applicable law or agreed to in writing, this
        software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
        CONDITIONS OF ANY KIND, either express or implied.
    */
    
    #include <sys/param.h>
    
    #include "esp_event.h"
    #include "esp_log.h"
    #include "esp_mac.h"
    
    #include "nvs_flash.h"
    #include "esp_wifi.h"
    #include "esp_netif.h"
    #include "lwip/inet.h"
    #include "driver/gpio.h"
    
    #include "esp_http_server.h"
    #include "dns_server.h"
    
    #define EXAMPLE_ESP_WIFI_SSID               "EmbeTronics"
    #define EXAMPLE_ESP_WIFI_PASS               "12345678"
    #define EXAMPLE_MAX_STA_CONN                5
    
    #define RED_LED_PIN                         2
    #define GREEN_LED_PIN                       3
    
    
    extern const char root_start[] asm("_binary_root_html_start");
    extern const char root_end[] asm("_binary_root_html_end");
    
    static const char *TAG = "example";
    
    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 " join, 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 " leave, AID=%d, reason=%d",
                     MAC2STR(event->mac), event->aid, event->reason);
        }
    }
    
    static void wifi_init_softap(void)
    {
        wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
        ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    
        ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
    
        wifi_config_t wifi_config = {
            .ap = {
                .ssid = EXAMPLE_ESP_WIFI_SSID,
                .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
                .password = EXAMPLE_ESP_WIFI_PASS,
                .max_connection = EXAMPLE_MAX_STA_CONN,
                .authmode = WIFI_AUTH_WPA_WPA2_PSK
            },
        };
        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(ESP_IF_WIFI_AP, &wifi_config));
        ESP_ERROR_CHECK(esp_wifi_start());
    
        esp_netif_ip_info_t ip_info;
        esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);
    
        char ip_addr[16];
        inet_ntoa_r(ip_info.ip.addr, ip_addr, 16);
        ESP_LOGI(TAG, "Set up softAP with IP: %s", ip_addr);
    
        ESP_LOGI(TAG, "wifi_init_softap finished. SSID
    :'%s' password:'%s'",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    }
    
    #ifdef CONFIG_ESP_ENABLE_DHCP_CAPTIVEPORTAL
    static void dhcp_set_captiveportal_url(void) {
        // get the IP of the access point to redirect to
        esp_netif_ip_info_t ip_info;
        esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);
    
        char ip_addr[16];
        inet_ntoa_r(ip_info.ip.addr, ip_addr, 16);
        ESP_LOGI(TAG, "Set up softAP with IP: %s", ip_addr);
    
        // turn the IP into a URI
        char* captiveportal_uri = (char*) malloc(32 * sizeof(char));
        assert(captiveportal_uri && "Failed to allocate captiveportal_uri");
        strcpy(captiveportal_uri, "http://");
        strcat(captiveportal_uri, ip_addr);
    
        // get a handle to configure DHCP with
        esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
    
        // set the DHCP option 114
        ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(netif));
        ESP_ERROR_CHECK(esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, ESP_NETIF_CAPTIVEPORTAL_URI, captiveportal_uri, strlen(captiveportal_uri)));
        ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(netif));
    }
    #endif // CONFIG_ESP_ENABLE_DHCP_CAPTIVEPORTAL
    
    /* HTTP GET Handlers */ 
    static esp_err_t root_get_handler(httpd_req_t *req)
    {
        const uint32_t root_len = root_end - root_start;
    
        ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
        httpd_resp_set_type(req, "text/html");
        httpd_resp_send(req, root_start, root_len);
    
        return ESP_OK;
    }
    
    static esp_err_t root_get_handler_red_led_on(httpd_req_t *req)
    {
        ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
        gpio_set_level(RED_LED_PIN, 1);
        root_get_handler(req);
    
        return ESP_OK;
    }
    
    static esp_err_t root_get_handler_red_led_off(httpd_req_t *req)
    {
        ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
        gpio_set_level(RED_LED_PIN, 0);
        root_get_handler(req);
    
        return ESP_OK;
    }
    
    static esp_err_t root_get_handler_green_led_on(httpd_req_t *req)
    {
        ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
        gpio_set_level(GREEN_LED_PIN, 1);
        root_get_handler(req);
    
        return ESP_OK;
    }
    
    static esp_err_t root_get_handler_green_led_off(httpd_req_t *req)
    {
        ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
        gpio_set_level(GREEN_LED_PIN
    , 0);
        root_get_handler(req);
    
        return ESP_OK;
    }
    
    static const httpd_uri_t root = {
        .uri = "/",
        .method = HTTP_GET,
        .handler = root_get_handler
    };
    
    static const httpd_uri_t root_red_led_on = {
        .uri = "/RED_LED_ON",
        .method = HTTP_GET,
        .handler = root_get_handler_red_led_on
    };
    
    static const httpd_uri_t root_red_led_off = {
        .uri = "/RED_LED_OFF",
        .method = HTTP_GET,
        .handler = root_get_handler_red_led_off
    };
    
    static const httpd_uri_t root_green_led_on = {
        .uri = "/GREEN_LED_ON",
        .method = HTTP_GET,
        .handler = root_get_handler_green_led_on
    };
    
    static const httpd_uri_t root_green_led_off = {
        .uri = "/GREEN_LED_OFF",
        .method = HTTP_GET,
        .handler = root_get_handler_green_led_off
    };
    
    // HTTP Error (404) Handler - Redirects all requests to the root page
    esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
    {
        // Set status
        httpd_resp_set_status(req, "302 Temporary Redirect");
        // Redirect to the "/" root directory
        httpd_resp_set_hdr(req, "Location", "/");
        // iOS requires content in the response to detect a captive portal, simply redirecting is not sufficient.
        httpd_resp_send(req, "Redirect to the captive portal", HTTPD_RESP_USE_STRLEN);
    
        ESP_LOGI(TAG, "Redirecting to root");
        return ESP_OK;
    }
    
    static httpd_handle_t start_webserver(void)
    {
        httpd_handle_t server = NULL;
        httpd_config_t config = HTTPD_DEFAULT_CONFIG();
        config.max_open_sockets = 13;
        config.lru_purge_enable = true;
    
        // Start the httpd server
        ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
        if (httpd_start(&server, &config) == ESP_OK) {
            // Set URI handlers
            ESP_LOGI(TAG, "Registering URI handlers");
            httpd_register_uri_handler(server, &root);
            httpd_register_uri_handler(server, &root_red_led_on);
            httpd_register_uri_handler(server, &root_red_led_off);
            httpd_register_uri_handler(server, &root_green_led_on);
            httpd_register_uri_handler(server, &root_green_led_off);
            httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, http_404_error_handler);
        }
        return server;
    }
    
    void app_main(void)
    {
        /*
            Turn of warnings from HTTP server as redirecting traffic will yield
            lots of invalid requests
        */
        esp_log_level_set("httpd_uri", ESP_LOG_ERROR);
        esp_log_level_set("httpd_txrx", ESP_LOG_ERROR);
        esp_log_level_set("httpd_parse", ESP_LOG_ERROR);
    
        /* Init LED GPIO Pins */
        gpio_config_t io_conf = {
            .pin_bit_mask = ((1ULL << RED_LED_PIN) | (1ULL << GREEN_LED_PIN)),      // Select Led GPIO Pins
            .mode = GPIO_MODE_OUTPUT,            // Set as output
            .pull_up_en = GPIO_PULLUP_DISABLE,  // Disable pull-up
            .pull_down_en = GPIO_PULLDOWN_DISABLE,  // Disable pull-down
            .intr_type = GPIO_INTR_DISABLE             // Disable interrupts
        };
    
        gpio_config(&io_conf);
    
        // Initialize networking stack
        ESP_ERROR_CHECK(esp_netif_init());
    
        // Create default event loop needed by the  main app
        ESP_ERROR_CHECK(esp_event_loop_create_default());
    
        // Initialize NVS needed by Wi-Fi
        ESP_ERROR_CHECK(nvs_flash_init());
    
        // Initialize Wi-Fi including netif with default config
        esp_netif_create_default_wifi_ap();
    
        // Initialise ESP32 in SoftAP mode
        wifi_init_softap();
    
        // Configure DNS-based captive portal, if configured
        #ifdef CONFIG_ESP_ENABLE_DHCP_CAPTIVEPORTAL
            dhcp_set_captiveportal_url();
        #endif
    
        // Start the server for the first time
        start_webserver();
    
        // Start the DNS server that will redirect all queries to the softAP IP
        dns_server_config_t config = DNS_SERVER_CONFIG_SINGLE("*" /* all A queries */, "WIFI_AP_DEF" /* softAP netif ID */);
        start_dns_server(&config);
    }
    
  2. root.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>EmbeTronics LED Control</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                display: flex;
                justify-content: center;
                align-items: center;
                min-height: 100vh;
                background-color: #f0f0f0;
                margin: 0;
            }
            .container {
                background-color: #2e5077;
                padding: 20px;
                border-radius: 8px;
                box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
                text-align: center;
            }
            a {
              color: #ffffff;
              font-weight: bold;
            }
            h1 {
                color: #ffffff;
            }
            h2 {
                color: #ffffff;
            }
            .led-control {
                margin: 15px 0;
            }
            .led-control button {
                padding: 10px 20px;
                margin: 5px;
                border: none;
                border-radius: 5px;
                cursor: pointer;
                font-size: 16px;
                color: white;
            }
            .led-control .green-on { background-color: #28a745; }
            .led-control .green-off { background-color: #dc3545; }
            .led-control .red-on { background-color: #dc3545; }
            .led-control .red-off { background-color: #28a745; }
        </style>
    </head>
    <body>
        <div class="container">
    
          <img alt="Embedded Image"
              src="data:image/png;base64,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">
    
            <h1>EmbeTronics LED Control Panel</h1>
            <a href="https://www.embetronics.com/">Visit embetronics.com for More information !!</a>
    
            <div class="led-control">
                <h2>Green LED</h2>
                <!-- <button class="green-on" onclick="controlLed('green', 'on')">Turn On</button> -->
                <a href=GREEN_LED_ON><button class="green-on" >Turn On</button></a>
                <a href=GREEN_LED_OFF><button class="green-off" >Turn Off</button></a>
            </div>
    
            <div class="led-control">
                <h2>Red LED</h2>
                <a href=RED_LED_ON><button class="red-on" >Turn On</button></a>
                <a href=RED_LED_OFF><button class="red-off" >Turn Off</button></a>
            </div>
        </div>
    
    </body>
    </html>


Working Of Code

  1. Connect 2 LEDs with Board on GPIO Pins 2 and 3 through 470 Ohm series resistor.
  2. Now replace code for above two files in project folder, then build and flash code on ESP32 Board.
  3. As soon as program starts running on board, It will create Wi-Fi Access Point with below credentials.
    SSID : EmbeTronics
    PASS : 12345678
  4. 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.
  5. If Webpage didn't open automatically in Web Browser, Use IP Address "192.168.4.1" to open Web Page manually.
  6. From Webpage you can control both LEDs by pressing On and Off Button.


Video

You can watch video for working of project at below link.



Full Project Code

You can directly download and use whole project from below GitHub link.

GitHub Repo Link


Explanation of Code

Let us understand blocks of code and see how it works.

First we will include all required header files. Header files for FreeRTOS, WiFi, MQTT and Logging are included.
#include <sys/param.h>

#include "esp_event.h"
#include "esp_log.h"
#include "esp_mac.h"

#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "lwip/inet.h"
#include "driver/gpio.h"

#include "esp_http_server.h"
#include "dns_server.h"


Then Wi-Fi Credentials to used with Access Point are set.
#define EXAMPLE_ESP_WIFI_SSID               "EmbeTronics"
#define EXAMPLE_ESP_WIFI_PASS               "12345678"
#define EXAMPLE_MAX_STA_CONN                5

Pins for Red and Green LED are set, You can replace pins number with your configuration. 
#define RED_LED_PIN                         2
#define GREEN_LED_PIN                       3

HTML file for Web Page is embedded into project. It is specified using EMBED_FILES into CMakeLists.txt file.
idf_compo
nent_register(SRCS main.c
                       EMBED_FILES root.html)

HTML file content is accessed from code via below variables.
extern const char root_start[] asm("_binary_root_html_start");
extern const char root_end[] asm("_binary_root_html_end");

TAG to be used when ESP Log print is defined. You can modify as per your requirement.
extern const char root_start[] asm("_binary_root_html_start");
/* TAG for ESP Log Print */
static const char *TAG = "example";

wifi_event_handler handler function to handle all Wi-Fi related events is defined. It handles Wi-Fi Connect, Disconnect and IP Got events. It will try to connect with provided Wi-Fi network with defined number of retries.
/* WiFi Event Handler Function */
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    /* WiFi Connect Retry Count */
    static int s_retry_num = 0;

    /* If WiFi Station Mode Start Event Received */
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    }
    /* If WiFi Disconnect Event Received */
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        /* If WiFi Cconnect Retry Count not Completed,Try Connecting */
        if (s_retry_num < EXAMPLE_MAXIMUM_RETRY) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
            xEventGroupSetBits(s_network_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    }
    /* If IP Got Event Received */
    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, "static ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_network_event_group, CONNECTED_BIT);
    }
}

wifi_init_softap function initializes Wi-Fi in Soft Access Point Mode.
static void wifi_init_softap(void)
{
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

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

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    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(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    esp_netif_ip_info_t ip_info;
    esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);

    char ip_addr[16];
    inet_ntoa_r(ip_info.ip.addr, ip_addr, 16);
    ESP_LOGI(TAG, "Set up softAP with IP: %s", ip_addr);

    ESP_LOGI(TAG, "wifi_init_softap finished. SSID:'%s' password:'%s'",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
}

dhcp_set_captiveportal_url function enables DHCP server on Access Point.
static void dhcp_set_captiveportal_url(void) {
    // get the IP of the access point to redirect to
    esp_netif_ip_info_t ip_info;
    esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);

    char ip_addr[16];
    inet_ntoa_r(ip_info.ip.addr, ip_addr, 16);
    ESP_LOGI(TAG, "Set up softAP with IP: %s", ip_addr);

    // turn the IP into a URI
    char* captiveportal_uri = (char*) malloc(32 * sizeof(char));
    assert(captiveportal_uri && "Failed to allocate captiveportal_uri");
    strcpy(captiveportal_uri, "http://");
    strcat(captiveportal_uri, ip_addr);

    // get a handle to configure DHCP with
    esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");

    // set the DHCP option 114
    ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(netif));
    ESP_ERROR_CHECK(esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, ESP_NETIF_CAPTIVEPORTAL_URI, captiveportal_uri, strlen(captiveportal_uri)));
    ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(netif));
}

Then, HTTP request handler functions are defined as below. We have defined individual handler for Webpage root request and LED On/Off requests.
/* HTTP GET Handlers */ 
static esp_err_t root_get_handler(httpd_req_t *req)
{
    const uint32_t root_len = root_end - root_start;

    ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, root_start, root_len);

    return ESP_OK;
}

static esp_err_t root_get_handler_red_led_on(httpd_req_t *req)
{
    ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
    gpio_set_level(RED_LED_PIN, 1);
    root_get_handler(req);

    return ESP_OK;
}

static esp_err_t root_get_handler_red_led_off(httpd_req_t *req)
{
    ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
    gpio_set_level(RED_LED_PIN, 0);
    root_get_handler(req);

    return ESP_OK;
}

static esp_err_t root_get_handler_green_led_on(httpd_req_t *req)
{
    ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
    gpio_set_level(GREEN_LED_PIN, 1);
    root_get_handler(req);

    return ESP_OK;
}

static esp_err_t root_get_handler_green_led_off(httpd_req_t *req)
{
    ESP_LOGI(TAG, "Serve root, Req %s",req->uri);
    gpio_set_level(GREEN_LED_PIN, 0);
    root_get_handler(req);

    return ESP_OK;
}

All structures as shown below are used to register each handler function is defined.
static const httpd_uri_t root = {
    .uri = "/",
    .method = HTTP_GET,
    .handler = root_get_handler
};

static const httpd_uri_t root_red_led_on = {
    .uri = "/RED_LED_ON",
    .method = HTTP_GET,
    .handler = root_get_handler_red_led_on
};

static const httpd_uri_t root_red_led_off = {
    .uri = "/RED_LED_OFF",
    .method = HTTP_GET,
    .handler = root_get_handler_red_led_off
};

static const httpd_uri_t root_green_led_on = {
    .uri = "/GREEN_LED_ON",
    .method = HTTP_GET,
    .handler = root_get_handler_green_led_on
};

static const httpd_uri_t root_green_led_off = {
    .uri = "/GREEN_LED_OFF",
    .method = HTTP_GET,
    .handler = root_get_handler_green_led_off
};

http_404_error_handler handler function is used to handle HTTP 404 Error.
// HTTP Error (404) Handler - Redirects all requests to the root page
esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
{
    // Set status
    httpd_resp_set_status(req, "302 Temporary Redirect");
    // Redirect to the "/" root directory
    httpd_resp_set_hdr(req, "Location", "/");
    // iOS requires content in the response to detect a captive portal, simply redirecting is not sufficient.
    httpd_resp_send(req, "Redirect to the captive portal", HTTPD_RESP_USE_STRLEN);

    ESP_LOGI(TAG, "Redirecting to root");
    return ESP_OK;
}

start_webserver function initializes and starts HTTP Web Server, and registers all handlers functions (root, led on and off requests) with it.
static httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.max_open_sockets = 13;
    config.lru_purge_enable = true;

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &root);
        httpd_register_uri_handler(server, &root_red_led_on);
        httpd_register_uri_handler(server, &root_red_led_off);
        httpd_register_uri_handler(server, &root_green_led_on);
        httpd_register_uri_handler(server, &root_green_led_off);
        httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, http_404_error_handler);
    }
    return server;
}

At last app_main function of our code is defined. It performs below mentioned things, 
  • Set Log level for HTTP modules
  • Initializes GPIO pins for Red and Green LEDs.
  • Initializes network stack with Soft-AP Wi-Fi Access Point
  • Initializes NVS flash memory
  • Initializes DHCP server for Captive Portal
  • Start Web Server over HTTP
  • Start DNS Server
void app_main(void)
{
    /*
        Turn of warnings from HTTP server as redirecting traffic will yield
        lots of invalid requests
    */
    esp_log_level_set("httpd_uri", ESP_LOG_ERROR);
    esp_log_level_set("httpd_txrx", ESP_LOG_ERROR);
    esp_log_level_set("httpd_parse", ESP_LOG_ERROR);

    /* Init LED GPIO Pins */
    gpio_config_t io_conf = {
        .pin_bit_mask = ((1ULL << RED_LED_PIN) | (1ULL << GREEN_LED_PIN)),      // Select Led GPIO Pins
        .mode = GPIO_MODE_OUTPUT,            // Set as output
        .pull_up_en = GPIO_PULLUP_DISABLE,  // Disable pull-up
        .pull_down_en = GPIO_PULLDOWN_DISABLE,  // Disable pull-down
        .intr_type = GPIO_INTR_DISABLE             // Disable interrupts
    };

    gpio_config(&io_conf);

    // Initialize networking stack
    ESP_ERROR_CHECK(esp_netif_init());

    // Create default event loop needed by the  main app
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // Initialize NVS needed by Wi-Fi
    ESP_ERROR_CHECK(nvs_flash_init());

    // Initialize Wi-Fi including netif with default config
    esp_netif_create_default_wifi_ap();

    // Initialise ESP32 in SoftAP mode
    wifi_init_softap();

    // Configure DNS-based captive portal, if configured
    #ifdef CONFIG_ESP_ENABLE_DHCP_CAPTIVEPORTAL
        dhcp_set_captiveportal_url();
    #endif

    // Start the server for the first time
    start_webserver();

    // Start the DNS server that will redirect all queries to the softAP IP
    dns_server_config_t config = DNS_SERVER_CONFIG_SINGLE("*" /* all A queries */, "WIFI_AP_DEF" /* softAP netif ID */);
    start_dns_server(&config);
}

Wrapping Up

In todays, Tutorial you have learned how to control leds from web browser using ESP32 Board.

Please leave a comment if you have a question or you found this helpful.

Post a Comment

0 Comments