In this guide, you’ll learn how to create an ESP32/ESP8266 web server with three input fields to pass values to your ESP using an HTML form. Then, you can use those values as variables in your code. We’ll be using the Arduino IDE to program the boards.

In this tutorial, we’ll build an asynchronous web server using the ESPAsyncWebServer library that displays three input fields to pass values that you can use in your code to update variables.
We’ll take a look at two similar examples. The following figure illustrates how the first example works.

You have a web page with three input fields that you can access with any browser in your network. When you type a new value and press the “Submit” button, your ESP will update a variable with the new value.
If you ever needed to update a variable through an ESP web server, you should follow this project. With this method, you avoid hard coding variables because you can create an input field in a web page to update any variable with a new value. This can be specially useful to set threshold values, set SSID/password, change API Keys, etc…
Later, we’ll also show you how to save those variables permanently using LittleFS and how to access them. Here’s how this second example works.

This web page allows you to enter three types of variables: String, Int and Float. Then, every time you submit a new value, that value is stored in a file in the LittleFS filesystem. This web page also contains a placeholder to show the current values.
Make sure you check all the prerequisites in this section before continuing with the project in order to compile the code.
We’ll program the ESP32 and ESP8266 using Arduino IDE. So, you must have the ESP32 or ESP8266 add-on installed. Follow one of the next tutorials to install the ESP add-on:
To build the web server you need to install the following libraries:
You can install those libraries in the Arduino IDE Library Manager. Go to Sketch > Include Library > Manage Libraries and search for the libraries’ names.
To follow this tutorial you just need an ESP32 or ESP8266 (read ESP32 vs ESP8266). There’s no circuit for this project.
Copy the following code to the Arduino IDE. Then, type your network credentials (SSID and password) to make it work for you.
/********* Rui Santos & Sara Santos - Random Nerd Tutorials Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-input-data-html-form/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. *********/ #include <Arduino.h> #ifdef ESP32 #include <WiFi.h> #include <AsyncTCP.h> #else #include <ESP8266WiFi.h> #include <ESPAsyncTCP.h> #endif #include <ESPAsyncWebServer.h> AsyncWebServer server(80); // REPLACE WITH YOUR NETWORK CREDENTIALS const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD"; const char* PARAM_INPUT_1 = "input1"; const char* PARAM_INPUT_2 = "input2"; const char* PARAM_INPUT_3 = "input3"; // HTML web page to handle 3 input fields (input1, input2, input3) const char index_html[] PROGMEM = R"rawliteral( <!DOCTYPE HTML><html><head> <title>ESP Input Form</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head><body> <form action="/get"> input1: <input type="text" name="input1"> <input type="submit" value="Submit"> </form><br> <form action="/get"> input2: <input type="text" name="input2"> <input type="submit" value="Submit"> </form><br> <form action="/get"> input3: <input type="text" name="input3"> <input type="submit" value="Submit"> </form> </body></html>)rawliteral"; void notFound(AsyncWebServerRequest *request) { request->send(404, "text/plain", "Not found"); } void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); if (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("WiFi Failed!"); return; } Serial.println(); Serial.print("IP Address: "); Serial.println(WiFi.localIP()); // Send web page with input fields to client server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", index_html); }); // Send a GET request to <ESP_IP>/get?input1=<inputMessage> server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { String inputMessage; String inputParam; // GET input1 value on <ESP_IP>/get?input1=<inputMessage> if (request->hasParam(PARAM_INPUT_1)) { inputMessage = request->getParam(PARAM_INPUT_1)->value(); inputParam = PARAM_INPUT_1; } // GET input2 value on <ESP_IP>/get?input2=<inputMessage> else if (request->hasParam(PARAM_INPUT_2)) { inputMessage = request->getParam(PARAM_INPUT_2)->value(); inputParam = PARAM_INPUT_2; } // GET input3 value on <ESP_IP>/get?input3=<inputMessage> else if (request->hasParam(PARAM_INPUT_3)) { inputMessage = request->getParam(PARAM_INPUT_3)->value(); inputParam = PARAM_INPUT_3; } else { inputMessage = "No message sent"; inputParam = "none"; } Serial.println(inputMessage); request->send(200, "text/html", "HTTP GET request sent to your ESP on input field (" + inputParam + ") with value: " + inputMessage + "<br><a href=\"/\">Return to Home Page</a>"); }); server.onNotFound(notFound); server.begin(); } void loop() { }
Let’s take a quick look at the code and see how it works.
First, include the necessary libraries. You include different libraries depending on the board you’re using. If you’re using an ESP32, the code loads the following libraries:
#include <WiFi.h> #include <AsyncTCP.h> #include <ESPAsyncWebServer.h>
If you’re using an ESP8266, you include these libraries:
#include <ESP8266WiFi.h> #include <ESPAsyncTCP.h> #include <ESPAsyncWebServer.h>
Don’t forget to insert your network credentials in the following variables, so that the ESP32 or ESP8266 can connect to your network.
const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD";
First, let’s take a look at the HTML that we need to display the input fields.
In our example, we display three input fields and each field has a “Submit” button. When the user enters data and presses the “Submit” button, that value is sent to the ESP and updates the variable.
For that, create three forms:
<form action="/get"> input1: <input type="text" name="input1"> <input type="submit" value="Submit"> </form><br> <form action="/get"> input2: <input type="text" name="input2"> <input type="submit" value="Submit"> </form><br> <form action="/get"> input3: <input type="text" name="input3"> <input type="submit" value="Submit"> </form>
In HTML, the <form> tag is used to create an HTML form for user input. In our case, the form should contain an input field and a submit button.

Let’s take a look at the first form to see how it works (the other forms work in a similar way).
<form action="/get"> input1: <input type="text" name="input1"> <input type="submit" value="Submit"> </form>
The action attribute specifies where to send the data inserted on the form after pressing submit. In this case, it makes an HTTP GET request to /get?input1=value. The value refers to the text you enter in the input field.
Then, we define two input fields: one text field and one submit button.
The following line defines a one line text input field.
input1: <input type="text" name="input1">The type attribute specifies we want a text input field, and the name attribute specifies the name of the input element.
The next line defines a button for submitting the HTML form data.
<input type="submit" value="Submit">In this case, the type attribute specifies you want a submit button, and the value specifies the text on the button.
In the setup(), connect to your local network.
Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); if (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("WiFi Failed!"); return; } Serial.println(); Serial.print("IP Address: "); Serial.println(WiFi.localIP());
Then, you need to handle the HTTP GET requests.
When, you access the route URL, you send the HTML page to the client. In this case, the HTML text is saved on the index_html variable.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", index_html); });
Then, you need to handle what happens when you receive a request on the /get routes.
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
We create two variables: inputMessage and inputParam to save the input value and the input field.
Then, we need to check whether the HTTP GET request contains the input1, input2, or input3 parameters. These are saved on the PARAM_INPUT_1, PARAM_INPUT_2 and PARAM_INPUT_3 variables.
If the request contains the PARAM_INPUT_1 (i.e. input1), we set the inputMessage to the value inserted in the input1 field.
inputMessage = request->getParam(PARAM_INPUT_1)->value();
Now you have the value you’ve just inserted on the first form saved on the inputMessage variable.
Then, set the inputParam variable to PARAM_INPUT_1 so that we know where the input value comes from.
When you submit the form, you get a message saying the value you’ve inserted and in which field. We also display a link to get back to the route URL (Home Page).
request->send(200, "text/html", "HTTP GET request sent to your ESP on input field (" + inputParam + ") with value: " + inputMessage + "<br><a href=\"/\">Return to Home Page</a>");

If you make a request on an invalid URL, we call the notFound() function, defined at the beginning of the sketch.
void notFound(AsyncWebServerRequest *request) { request->send(404, "text/plain", "Not found"); }
Finally, start the server to handle clients.
server.begin();
After uploading code to your board, open your Arduino IDE Serial Monitor at a baud rate of 115200 to find the ESP IP address.

Then, open your browser and type the IP address. This web page should load:

For example, type 123456 on input1 field, then press the “Submit” button. A new page should load saying that value 123456 was sent to your ESP:

Now, let’s proceed to the second example. This example saves the data inserted on the input fields permanently on LittleFS. We’ve also added placeholders on the web page to show the current values.
Copy the following sketch to Arduino IDE. Then, before uploading type your network credentials (SSID and password).
/********* Rui Santos & Sara Santos - Random Nerd Tutorials Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-input-data-html-form/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. *********/ #include <Arduino.h> #ifdef ESP32 #include <WiFi.h> #include <AsyncTCP.h> #include <LittleFS.h> #else #include <ESP8266WiFi.h> #include <ESPAsyncTCP.h> #include <Hash.h> #include <LittleFS.h> #endif #include <ESPAsyncWebServer.h> AsyncWebServer server(80); // REPLACE WITH YOUR NETWORK CREDENTIALS const char* ssid = "REPLACE_WITH_YOUR_SSID"; const char* password = "REPLACE_WITH_YOUR_PASSWORD"; const char* PARAM_STRING = "inputString"; const char* PARAM_INT = "inputInt"; const char* PARAM_FLOAT = "inputFloat"; // HTML web page to handle 3 input fields (inputString, inputInt, inputFloat) const char index_html[] PROGMEM = R"rawliteral( <!DOCTYPE HTML><html><head> <title>ESP Input Form</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script> function submitMessage() { alert("Saved value to ESP LittleFS"); setTimeout(function(){ document.location.reload(false); }, 500); } </script></head><body> <form action="/get" target="hidden-form"> inputString (current value %inputString%): <input type="text" name="inputString"> <input type="submit" value="Submit" onclick="submitMessage()"> </form><br> <form action="/get" target="hidden-form"> inputInt (current value %inputInt%): <input type="number " name="inputInt"> <input type="submit" value="Submit" onclick="submitMessage()"> </form><br> <form action="/get" target="hidden-form"> inputFloat (current value %inputFloat%): <input type="number " name="inputFloat"> <input type="submit" value="Submit" onclick="submitMessage()"> </form> <iframe style="display:none" name="hidden-form"></iframe> </body></html>)rawliteral"; void notFound(AsyncWebServerRequest *request) { request->send(404, "text/plain", "Not found"); } String readFile(fs::FS &fs, const char * path){ Serial.printf("Reading file: %s\r\n", path); File file = fs.open(path, "r"); if(!file || file.isDirectory()){ Serial.println("- empty file or failed to open file"); return String(); } Serial.println("- read from file:"); String fileContent; while(file.available()){ fileContent+=String((char)file.read()); } file.close(); Serial.println(fileContent); return fileContent; } void writeFile(fs::FS &fs, const char * path, const char * message){ Serial.printf("Writing file: %s\r\n", path); File file = fs.open(path, "w"); if(!file){ Serial.println("- failed to open file for writing"); return; } if(file.print(message)){ Serial.println("- file written"); } else { Serial.println("- write failed"); } file.close(); } // Replaces placeholder with stored values String processor(const String& var){ //Serial.println(var); if(var == "inputString"){ return readFile(LittleFS, "/inputString.txt"); } else if(var == "inputInt"){ return readFile(LittleFS, "/inputInt.txt"); } else if(var == "inputFloat"){ return readFile(LittleFS, "/inputFloat.txt"); } return String(); } void setup() { Serial.begin(115200); // Initialize LittleFS #ifdef ESP32 if(!LittleFS.begin(true)){ Serial.println("An Error has occurred while mounting LittleFS"); return; } #else if(!LittleFS.begin()){ Serial.println("An Error has occurred while mounting LittleFS"); return; } #endif WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); if (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("WiFi Failed!"); return; } Serial.println(); Serial.print("IP Address: "); Serial.println(WiFi.localIP()); // Send web page with input fields to client server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200, "text/html", index_html, processor); }); // Send a GET request to <ESP_IP>/get?inputString=<inputMessage> server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { String inputMessage; // GET inputString value on <ESP_IP>/get?inputString=<inputMessage> if (request->hasParam(PARAM_STRING)) { inputMessage = request->getParam(PARAM_STRING)->value(); writeFile(LittleFS, "/inputString.txt", inputMessage.c_str()); } // GET inputInt value on <ESP_IP>/get?inputInt=<inputMessage> else if (request->hasParam(PARAM_INT)) { inputMessage = request->getParam(PARAM_INT)->value(); writeFile(LittleFS, "/inputInt.txt", inputMessage.c_str()); } // GET inputFloat value on <ESP_IP>/get?inputFloat=<inputMessage> else if (request->hasParam(PARAM_FLOAT)) { inputMessage = request->getParam(PARAM_FLOAT)->value(); writeFile(LittleFS, "/inputFloat.txt", inputMessage.c_str()); } else { inputMessage = "No message sent"; } Serial.println(inputMessage); request->send(200, "text/text", inputMessage); }); server.onNotFound(notFound); server.begin(); } void loop() { // To access your stored values on inputString, inputInt, inputFloat String yourInputString = readFile(LittleFS, "/inputString.txt"); Serial.print("*** Your inputString: "); Serial.println(yourInputString); int yourInputInt = readFile(LittleFS, "/inputInt.txt").toInt(); Serial.print("*** Your inputInt: "); Serial.println(yourInputInt); float yourInputFloat = readFile(LittleFS, "/inputFloat.txt").toFloat(); Serial.print("*** Your inputFloat: "); Serial.println(yourInputFloat); delay(5000); }
This code is very similar with the previous one with a few tweaks. Let’s take a quick look at it and see how it works.
The code loads the following libraries if you’re using the ESP32. You need to load the LittleFS library to write to the LittleFS filesystem.
#include <WiFi.h> #include <ESPAsyncWebServer.h> #include <AsyncTCP.h> #include <LittleFS.h>
If you’re using an ESP8266, you need to include the following libraries instead.
#include <ESP8266WiFi.h> #include <ESPAsyncWebServer.h> #include <ESPAsyncTCP.h> #include <Hash.h> #include <LittleFS.h>
In this example, when you submit the values, a window opens saying the value was saved to LittleFS, instead of being redirected to another page as in the previous example.
For that, we need to a add a JavaScript function, in this case it’s called submitMessage() that pops an alert message saying the value was saved to LittleFS. After that pop up, it reloads the web page so that it displays the current values.
<script> function submitMessage() { alert("Saved value to ESP LittleFS"); setTimeout(function(){ document.location.reload(false); }, 500); } </script>
The forms are also a bit different from the previous ones. Here’s the form for the first input.
<form action="/get" target="hidden-form"> inputString (current value %inputString%): <input type="text" name="inputString"> <input type="submit" value="Submit" onclick="submitMessage()"> </form>
In this case, the target attribute and an <iframe> are used so that you remain on the same page after submitting the form.
The name that shows up for the input field contains a placeholder %inputString% that will then be replaced by the current value of the inputString variable.
The onclick=”submitMessage()” calls the submitMessage() JavaScript function after clicking the “Submit” button.
Then, we have some functions to read and write from LittleFS.
The readFile() reads the content from a file:
String readFile(fs::FS &fs, const char * path){ Serial.printf("Reading file: %s\r\n", path); File file = fs.open(path, "r"); if(!file || file.isDirectory()){ Serial.println("- empty file or failed to open file"); return String(); } Serial.println("- read from file:"); String fileContent; while(file.available()){ fileContent+=String((char)file.read()); } Serial.println(fileContent); return fileContent;
The writeFile() writes content to a file:
void writeFile(fs::FS &fs, const char * path, const char * message){ Serial.printf("Writing file: %s\r\n", path); File file = fs.open(path, "w"); if(!file){ Serial.println("- failed to open file for writing"); return; } if(file.print(message)){ Serial.println("- file written"); } else { Serial.println("- write failed"); } }
The processor() is responsible for searching for placeholders in the HTML text and replacing them with actual values saved on LittleFS.
String processor(const String& var){ //Serial.println(var); if(var == "inputString"){ return readFile(LittleFS, "/inputString.txt"); } else if(var == "inputInt"){ return readFile(LittleFS, "/inputInt.txt"); } else if(var == "inputFloat"){ return readFile(LittleFS, "/inputFloat.txt"); } return String(); }
Handling the HTTP GET requests works the same way as in the previous example, but this time we save the variables on LittleFS.
For example, for the inputString field:
if (request->hasParam(PARAM_STRING)) { inputMessage = request->getParam(PARAM_STRING)->value(); writeFile(LittleFS, "/inputString.txt", inputMessage.c_str()); }
When the request contains inputString (i.e. PARAM_STRING), we set the inputMessage variable to the value submitted on the inputString form.
inputMessage = request->getParam(PARAM_STRING)->value();
Then, save that value to LittleFS.
writeFile(LittleFS, "/inputString.txt", inputMessage.c_str());
A similar process happens for the other forms.
In the loop(), we demonstrate how you can access the variables.
For example, create a String variable called yourInputString that reads the content of the inputString.txt file on LittleFS.
String yourInputString = readFile(LittleFS, "/inputString.txt");
Then, print that variable in the Serial Monitor.
Serial.println(yourInputString);
After uploading code to your board, open your Arduino IDE Serial Monitor at a baud rate of 115200 to find the ESP IP address.

Open your browser and type the IP address. A similar web page should load (at first your current values will be blank).

Type a String in the first input field and press “Submit”, repeat the same process for the Int and Float input values. Every time you press the Submit button for a field, you’ll see an alert message like this:

Press the “OK” button to reload the web page and see the current values updated.
If you have your Arduino IDE Serial Monitor open, you’ll see that the stored values are being printed over and over again:

For a final project, you can delete all those lines in your loop() that print all stored values every 5 seconds, we just left them on purpose for debugging.
Copyright ©2025. All Rights Reserved Emblab THE RAVE INNOVATION