ESP32 webserver from SPIFFS

zazas321
Posts: 231
Joined: Mon Feb 01, 2021 9:41 am

ESP32 webserver from SPIFFS

Postby zazas321 » Mon Jan 10, 2022 11:50 am

Hello. I am learning about ESP32 WiFi and ability to create a webserver.

My wifi functions:

Code: Select all





static esp_err_t index_page_handler(httpd_req_t *req)
{
    esp_err_t error;
    ESP_LOGI(TAG_WEBSERVER,"This is index page");
    const char* resp_str = (const char*) req->user_ctx;
    error = httpd_resp_send(req,resp_str,strlen(resp_str));
    if(error !=ESP_OK){
        ESP_LOGI(TAG_WEBSERVER,"Error %d while sending response \n",error);
    }
    else{
        ESP_LOGI(TAG_WEBSERVER,"response sent sucesfully \n");
    }
    return error;
}


static const httpd_uri_t index_page = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = index_page_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = "<!DOCTYPE html>\
<html>\
<body>\
<h2>ESP32 WIFI CREDENTIALS</h2>\
<form action=\"set_wifi.php\">\
  <label for=\"fname\">SSID:</label><br>\
  <input type=\"text\" id=\"fname\" name=\"fname\" value=\"Enter SSID here\"><br>\
  <label for=\"lname\">Password:</label><br>\
  <input type=\"text\" id=\"lname\" name=\"lname\" value=\"Enter Password here\"><br><br>\
  <input type=\"submit\" value=\"Submit\">\
</form>\
<p>If you click the \"Submit\" button, the form-data will be sent to a page called \"/action_page.php\".</p>\
</body>\
</html>"

};





esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
{

    /* For any other URI send 404 and close socket */
    httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Some 404 error message");
    return ESP_FAIL;
}





static httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.lru_purge_enable = true;

    // Start the httpd server
    ESP_LOGI(TAG_WEBSERVER, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG_WEBSERVER, "Registering URI handlers");
        httpd_register_uri_handler(server, &index_page);
        return server;
    }

    ESP_LOGI(TAG_WEBSERVER, "Error starting server!");
    return NULL;
}

static void stop_webserver(httpd_handle_t server)
{
    // Stop the httpd server
    httpd_stop(server);
}

void disconnect_handler(void* arg, esp_event_base_t event_base,
                               int32_t event_id, void* event_data)
{
    httpd_handle_t* server = (httpd_handle_t*) arg;
    if (*server) {
        ESP_LOGI(TAG_WEBSERVER, "Stopping webserver");
        stop_webserver(*server);
        *server = NULL;
    }
}

void connect_handler(void* arg, esp_event_base_t event_base,
                            int32_t event_id, void* event_data)
{
    httpd_handle_t* server = (httpd_handle_t*) arg;
    if (*server == NULL) {
        ESP_LOGI(TAG_WEBSERVER, "Starting webserver");
        *server = start_webserver();
    }
}




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_WEBSERVER, "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_WEBSERVER, "station "MACSTR" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
}



I have simply created an event when someone tries to connect to index page :
httpd_register_uri_handler(server, &index_page);


Then the below code is responsible for displaying html elements on the webserver.

Code: Select all

static const httpd_uri_t index_page = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = index_page_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx  = "<!DOCTYPE html>\
<html>\
<body>\
<h2>ESP32 WIFI CREDENTIALS</h2>\
<form action=\"set_wifi.php\">\
  <label for=\"fname\">SSID:</label><br>\
  <input type=\"text\" id=\"fname\" name=\"fname\" value=\"Enter SSID here\"><br>\
  <label for=\"lname\">Password:</label><br>\
  <input type=\"text\" id=\"lname\" name=\"lname\" value=\"Enter Password here\"><br><br>\
  <input type=\"submit\" value=\"Submit\">\
</form>\
<p>If you click the \"Submit\" button, the form-data will be sent to a page called \"/action_page.php\".</p>\
</body>\
</html>"

};




I would like to know how can I keep my html files in SPIFFS instead of typing them in the .user_ctx.

Imagine I have placed index.html inside my SPIFFS and whenever the index page is executed, I want to display that html so the .user_ctx needs to read that html file. Could someone point me in the right direction what is the proper way of doing this?

zazas321
Posts: 231
Joined: Mon Feb 01, 2021 9:41 am

Re: ESP32 webserver from SPIFFS

Postby zazas321 » Mon Jan 10, 2022 1:17 pm

So far, the only alternative method I have found is to to place index.html in my project/main and then in CMakeLists.txt add :

Code: Select all

target_add_binary_data(my_project.elf "main/index.html" TEXT)
Then in my code I can declare this as :

Code: Select all

extern const uint8_t html_file_array[] asm("_binary_index_html_start");

And use it as following:

Code: Select all

static const httpd_uri_t index_page = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = index_page_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx = html_file_array,

};
and it seems to be working fine. Could someone help me understand what is the difference of doing it this way vs using SPIFFS to keep all html files and serve the web from SPIFFS? I assume in SPIFFS, the data will be put in flash whereas doing it this way, the data is actually kept in ram. Is that correct?
Last edited by zazas321 on Tue Jan 11, 2022 6:27 am, edited 1 time in total.

ESP_Sprite
Posts: 9731
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 webserver from SPIFFS

Postby ESP_Sprite » Tue Jan 11, 2022 1:10 am

No, it's not: files included this way will also be put in flash. There's two restrictions using this vs spiffs: first of all, files included this way cannot be changed while the app is running; you can only change them by recompiling and reflashing, and secondly, there's a size limit when including files like this: the total of files plus the rodata section of your app cannot exceed 4MiB. It's a lot simpler and faster than spiffs, though, so if you only have a small amount of fixed files, it may be preferable.

zazas321
Posts: 231
Joined: Mon Feb 01, 2021 9:41 am

Re: ESP32 webserver from SPIFFS

Postby zazas321 » Tue Jan 11, 2022 7:00 am

Thank you very much. Could you help me understand how to read an input from the webserver on my ESP32 ?

I have the following index page:
https://ibb.co/MGCcqNr

Code: Select all

<!DOCTYPE HTML>
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0">
<title>ESP32 Demo</title>
<style>
body { background-color: #0067B3  ; font-family: Arial, Helvetica, Sans-Serif; Color: #FFFFFF; }
</style>
</head>
<body>
<center>
<h1 style="color:#FFFFFF; font-family:verdana;font-family: verdana;padding-top: 10px;padding-bottom: 10px;font-size: 36px">ESP32 Captive Portal</h1>
<h2 style="color:#FFFFFF;font-family: Verdana;font: caption;font-size: 27px;padding-top: 10px;padding-bottom: 10px;">Give Your WiFi Credentials</h2>
<FORM action="/" method="post">
<P ><label style="font-family:Times New Roman">SSID</label><br><input maxlength="30px" type='text' id="ssid_wifi" name="ssid" placeholder='Enter WiFi SSID' style="width: 400px; padding: 5px 10px ; margin: 8px 0; border : 2px solid #3498db; border-radius: 4px; box-sizing:border-box" ><br></P>
<P><label style="font-family:Times New Roman">PASSKEY</label><br><input maxlength="30px" type = "text" id="pass_wifi" name="passkey"  placeholder = "Enter WiFi PASSKEY" style="width: 400px; padding: 5px 10px ; margin: 8px 0; border : 2px solid #3498db; border-radius: 4px; box-sizing:border-box" ><br><P>
<input type="checkbox" name="configure" value="change"> Change IP Settings </P>
<BR>
<INPUT type="submit"><INPUT type="reset">
<style>
input[type="reset"]{background-color: #3498DB; border: none; color: white; padding:  15px 48px; text-align: center; text-decoration: none;display: inline-block; font-size: 16px;}
input[type="submit"]{background-color: #3498DB; border: none; color: white; padding:  15px 48px;text-align: center; text-decoration: none;display: inline-block;font-size: 16px;}
</style>
</FORM>
</center>
</body>

As you can see from above, I have a form with 2 inputs SSID and password. I am going to input my wifi credentials here and click button "submit". Once the button is clicked, I want to be able to know what SSID and password was inputed on the webserver so my ESP32 device can attempt to connect to the WiFi. Could you point me in the right direction how can this be done? I have only managed to find examples for Arduino IDE.

ESP_Sprite
Posts: 9731
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 webserver from SPIFFS

Postby ESP_Sprite » Tue Jan 11, 2022 9:50 am

Suggest you look here. There's a basic example here, but there are more in the parent directory. You'd need to do the plumbing to WiFi yourself. Or you could simply use an existing solution for this.

zazas321
Posts: 231
Joined: Mon Feb 01, 2021 9:41 am

Re: ESP32 webserver from SPIFFS

Postby zazas321 » Tue Jan 11, 2022 10:35 am

Thank you very much. That was very helpful. I just want to confirm 1 more thing. If I want to have multiple pages performing POST requests, how can I determine in my post handler which page just performed post request?
For example:

Code: Select all

static const httpd_uri_t action_page1 = {
    .uri       = "/action_page1",
    .method    = HTTP_POST,
    .handler   = post_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx = NULL,

};

static const httpd_uri_t action_page2 = {
    .uri       = "/action_page2",
    .method    = HTTP_POST,
    .handler   = post_handler,
    /* Let's pass response string in user
     * context to demonstrate it's usage */
    .user_ctx = NULL,

};
I have action_page1.html and action_page2.html and both perform HTTP_POST method. In my post_handler function:

Code: Select all

esp_err_t post_handler(httpd_req_t *req)
{
    printf("post handler \n");
    /* Destination buffer for content of HTTP POST request.
     * httpd_req_recv() accepts char* only, but content could
     * as well be any binary data (needs type casting).
     * In case of string data, null termination will be absent, and
     * content length would give length of string */
    char content[100];

    /* Truncate if content length larger than the buffer */
    size_t recv_size = MIN(req->content_len, sizeof(content));

    int ret = httpd_req_recv(req, content, recv_size);
    printf("content received = %s \n",content);
    if (ret <= 0) {  /* 0 return value indicates connection closed */
        /* Check if timeout occurred */
        if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
            /* In case of timeout one can choose to retry calling
             * httpd_req_recv(), but to keep it simple, here we
             * respond with an HTTP 408 (Request Timeout) error */
            httpd_resp_send_408(req);
        }
        /* In case of error, returning ESP_FAIL will
         * ensure that the underlying socket is closed */
        return ESP_FAIL;
    }

    /* Send a simple response */
    const char resp[] = "URI POST Response";
    httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}
How can I know if the post request came from action_page1 or action_page2?

ESP_Sprite
Posts: 9731
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 webserver from SPIFFS

Postby ESP_Sprite » Tue Jan 11, 2022 3:24 pm

You could do that by using a different user_ctx for each page.

Craige Hales
Posts: 94
Joined: Tue Sep 07, 2021 12:07 pm

Re: ESP32 webserver from SPIFFS

Postby Craige Hales » Wed Jan 12, 2022 3:35 am

Craige

Momin786
Posts: 13
Joined: Mon Jul 11, 2022 7:52 am
Contact:

Re: ESP32 webserver from SPIFFS

Postby Momin786 » Wed Jan 11, 2023 3:51 am

I create ESP32 tutorials and Projects using ESP-IDF https://esp32tutorials.com/

User avatar
mbratch
Posts: 303
Joined: Fri Jun 11, 2021 1:51 pm

Re: ESP32 webserver from SPIFFS

Postby mbratch » Wed Jan 11, 2023 4:05 am

When you send an http requests to the ESP32 the request data that the request handler receives contains the request url. So you can check it for action_page1 or action_page2.

Who is online

Users browsing this forum: axellin and 88 guests