Friday, November 26, 2021

ESP Webserver tutorial part 1 - textfields

For an index to all my stories click this line

Most of the time we want to control an ESP8266 or ESP32 remotely. Be it from our work or from a lazy chair. The best way to do this is to use the ESP as a webserver.

A webserver builds a webpage and sends that to your computer or smart-phone or tablet. On the webpage there will be text fields that we can fill in, buttons and sliders. As soon as we have filled in the desired action on the webpage the information is send back to the ESP. The ESP analyses what it receives and acts on it by switching a led on or off, dimming a led, setting the angle of a servo,  or altering the color of an RGB led. 

IN ESP-Basic this is all done in an easy way. And there are numerous pages on this weblog that demonstrate this. Unfortunately ESP-Basic is not available for the ESP32 and more important it is no longer maintained as the developer (Mike) has a company to run nowadays. So back to C++ the Arduino language. However in Arduino language this appears much more difficult to realise.

That is untill you realise how it is done. And once you get the hang of it, it is no longer complicated at all.

Therefore I am going to give you a detailed tutorial on how to achieve this.
The first part is showing how to send text and figures to the ESP from a field on a webpage that you can fill in yourself. The next parts will show you how to put buttons, radio buttons, sliders and color pickers on the webpage.This will lead to building the most fantastic websites that can be used for any IOT purpose.

On a sidenote I want to urge you to study HTML, CSS and Javascript as these are the fundamentals that build a webpage. You can find ample information on these subjects on the following websites:
https://www.w3schools.com/
https://www.tutorialspoint.com/index.htm
https://www.edx.org/

At EDX you can follow complete courses. The other two websites are intended for self-study.

How does it work.

There are 4 steps in this process. I already adressed them brief in the beginning of this story.

As soon as the ESP starts it will make a connection with your router. Then it will tell it's IP adress on the Serial Monitor. You can find the IP adress also in your routers webpages.

The ESP then waits till you adress it with a web-browser. That can be any webbrowser on your computer, smart-phone or tablet.
As soon as you point your browser to the ESP's IP number it will send a webpage to the browser.

The webpage appears on your web-browser and the ESP does nothing until you fill in some text fields, press a button or alter the settings of a slider. The web-browser will then send the information back to the ESP.

The ESP receives the information and analyses it. And your program will act on the information received by switching a led on or of, changing the speed of a motor, altering the colors of a ledstring or whatever you want to happen.

To achieve all this you will need a program that handles all the above actions. And here it is.

The ESP webserver

The information provided here will work on the ESP8266 AND on the ESP32. The only thing you will need to change is the part where the libraries are loaded and initialised.


As this will be a long and fairly complicated program I will do a step by step analyse which will shed some light on how this works.


#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

ESP8266WebServer server(80);

// Your routers credentials
const char* ssid = "YourRoutersName";
const char* password = "YourPassWord";

The first part of the program loads the libraries for the ESP8266 that it will need. Then it will start the webserver on port 80 which is the normal port for HTTP communication.
Do not forget to substitute YourRoutersName and YourPassWord with your credentials.



#include <WiFi.h>
#include <WebServer.h>

WebServer server(80);

// Your routers credentials
const char* ssid = "YourRoutersName";
const char* password = "YourPassWord";


The above code is the code for the ESP32. As you can see the changes are minimal. The names of the libraries are different and therefore the way the library is initiated is different. But that is all. The rest of the program is identical for the ESP8266 and the ESP32.


// ===================================================
// Setup
// ===================================================
void setup()
{
  delay(1000);
  Serial.begin(115200);

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid,password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);

  server.begin();
  delay(500);
}

The Setup starts with opening the Serial Port and making a connection to your router. A series of dots (".") will be printed as long as the connection is not established.
When the connection is established the IP number will be printed in the Serial Monitor.
The webserver is started and two handles are defined. The first one is "/" which is the adress of the Homepage on your website. So if you type the IP adress of the ESP in your browser this will direct you to the main page.
If you adress a non-existing addition to the IP adress like 192.168.1.77/test then the server will active the handleNotFound routine.


// ===================================================

// Loop

// ===================================================

void loop()

{ 

  server.handleClient();

  delay(50);

}

As in any normal program the loop will now start. In this loop nothing happens. It just waits till the server receives anything back from the webpage. In a reallife program you would let all kind of things start from here.


// ==================================================
// Handle for page not found
// ==================================================
void handleNotFound()
{
  server.send(200, "text/html", getPage());

If you enter a non existing webpage for the found IP adress like 192.168.1.77/test then this routine catches that error and the server sends the information which is in the getPage() routine


// ===================================================
// Handle root
// ===================================================
void handleRoot() 
{   
  if (server.args() ) 
    {
    handleSubmit();
    } 
  else 
    {
    server.send(200, "text/html", getPage());  
    }
}

If the webpage sends some information back to the ESP this routine recognizes that there is information received and starts the handleSubmit() routine to analyse what has been received.
If the webpages is refreshed but no valid information is send back the server just sends the webpage anew. The webpage is build in the getPage() routine.



// =========================================
// Here is the HTML page
// =========================================
String getPage()
  {
  String page = "<!DOCTYPE HTML>";
  page += "<html>";
  page += "<head>";
  page += "<meta name = \"viewport\" content = \"width = device-width, initial-scale = 1.0 maximum-scale = 2.5, user-scalable=1\">";
  page += "<title>Luc's tekst test demo</title>";
  page += "<style>";
  page += "body { background-color: powderblue}";
  page += "</style>";
  page += "</head>";
  page += "<body>";
  page += "<h1 style='color:red'>Luc's send text to ESP8266</h1>";

The first part of the getPage() routine is where the body of the webpage is being build. The scale is set to one and a maximum enlargement of 2.5 is defined. These settings are especially for mobile devices. You can alter them if you need to. The background color is defined as Powderblue which I use often. The text of the heading is set to red.


  //Form to put the data in
  page += "<FORM action=\"/\" method=\"post\">";
  page += "Fill in the text you want to send<br><br>";
  page += "<input type=\"text\" name=\"textosend\" id=\"textosend\" value=\"Lucs test\">";
  page += "<br><br>";
  page += "<input type=\"submit\" value=\"Send to ESP\">";
  page += "</form>";
  page += "<br>";
  page += "<br>";
  page += "<br>";
  page += "</body>";
  page += "</html>";
  return page;

This is where the action is. A form is created and the method to send the data from the form to the ESP is the POST method.
Next an input field is created of the type text. In this field you can put the data you want to send to the ESP. The name of the field is 'texttosend' and the ID with which we can identify it is also 'texttosend'. A default value is filled in when the page is opened and that default value is 'Luc's test'. All these things are collected in the string with the name page. The last line returns that string to the function that called this routine.

// ==================================================
// Handle submit form
// ==================================================
void handleSubmit()
{
  //Text to show
  if (server.hasArg("textosend"))
      {
      textosend_string = server.arg("textosend");
      Serial.print("The received text is:             ");
      Serial.println(textosend_string);
      Serial.print("If it is an integer its value is: ");
      Serial.println(textosend_string.toInt());
      }

  server.send(200, "text/html", getPage());       //Response to the HTTP request
} 

When you have entered the text you want to send and push the submit button or press enter in the textfield this routine is called.
The if statement test wether the information comes from the field with the ID 'texttosend' If that is the case the received information is put in the variable textosend_string.
The next to lines are just for demonstration purposes. The Serial Monitor prints which information was receved and the information is converted to an integer. If the received information is indeed a figure it will be print6ed in the second line otherwise the result is 0.

That's it. The variable 'textosend_string' contains te information that you filled in on the webpage and with this variable you can start any action you want the ESP to perform.

Adding an extra field

Undoubtedly you want or need to send more then one text or figure. So let us add a second field.
Start with altering the part where the page is build as follows:

  //Form to put the data in
  page += "<FORM action=\"/\" method=\"post\">";
  page += "Fill in the text you want to send<br><br>";
  page += "<input type=\"text\" name=\"textosend\" id=\"textosend\" value=\"Lucs test\">";
  page += "<br><br>";
  page += "<input type=\"submit\" value=\"Send to ESP\">";
  page += "</form>";
  page += "<br>";
  page += "<br>";
  page += "<br>";

  page += "<FORM action=\"/\" method=\"post\">";
  page += "Fill in the second text you want to send<br><br>";
  page += "<input type=\"text\" name=\"textosend2\" id=\"textosend2\" value=\"Something else\">";
  page += "<br><br>";
  page += "<input type=\"submit\" value=\"Send to ESP\">";
  page += "</form>";
  page += "<br>";
  page += "<br>";
  page += "<br>";
  page += "</body>";
  page += "</html>";

There are now 2 forms. The first one is unaltered and the second one is just a copy of the first one. In the second one all references to 'texttosend' are changed in 'texttosend2' This refers the webserver to link the second text field to the second variable.

Now we only have to change the routine where the information that is returned gets processed:


// ==================================================
// Handle submit form
// ==================================================
void handleSubmit()
{
  //Text to show
  if (server.hasArg("textosend"))
      {
      textosend_string = server.arg("textosend");
      Serial.print("The received text is:             ");
      Serial.println(textosend_string);
      Serial.print("If it is an integer its value is: ");
      Serial.println(textosend_string.toInt());
      }

   if (server.hasArg("textosend2"))
      {
      textosend_string2 = server.arg("textosend2");
      Serial.print("The second received text is:             ");
      Serial.println(textosend_string2);
      Serial.print("If it is an integer its value is: ");
      Serial.println(textosend_string2.toInt());
      } 

As you can see the drill is almost the same as where the webpage gets build. We now have a second routine that tests for the new variable and sends it's data to the Serial monitor.

The complete program


// ======================================================
// Webserver program that sends text to an ESP8266
// so we can send commands from a webpage.
// ======================================================

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

ESP8266WebServer server(80);

// Your routers credentials
const char* ssid = "YOURROUTERSNAME";
const char* password = "YOURPASSWORD";


// ==========================================
// initial variables
// ========================================== 

String textosend_string;
String textosend_string2;

// =========================================
// Here is the HTML page
// =========================================
String getPage()
  {
  String page = "<!DOCTYPE HTML>";
  page += "<html>";
  page += "<head>";
  page += "<meta name = \"viewport\" content = \"width = device-width, initial-scale = 1.0 maximum-scale = 2.5, user-scalable=1\">";
  page += "<title>Luc's text test demo</title>";
  page += "<style>";
  page += "body { background-color: powderblue}";
  page += "</style>";
  page += "</head>";
  page += "<body>";
  page += "<h1 style='color:red'>Luc's send text to ESP8266</h1>";

  //Form to put the data in
  page += "<FORM action=\"/\" method=\"post\">";
  page += "Fill in the text you want to send<br><br>";
  page += "<input type=\"text\" name=\"textosend\" id=\"textosend\" value=\"Lucs test\">";
  page += "<br><br>";
  page += "<input type=\"submit\" value=\"Send to ESP\">";
  page += "</form>";
  page += "<br>";
  page += "<br>";
  page += "<br>";

  page += "<FORM action=\"/\" method=\"post\">";
  page += "Fill in the second text you want to send<br><br>";
  page += "<input type=\"text\" name=\"textosend2\" id=\"textosend2\" value=\"Something else\">";
  page += "<br><br>";
  page += "<input type=\"submit\" value=\"Send to ESP\">";
  page += "</form>";
  page += "<br>";
  page += "<br>";
  page += "<br>";
  page += "</body>";
  page += "</html>";

  
  return page;
  }


// ==================================================
// Handle for page not found
// ==================================================
void handleNotFound()
{
  server.send(200, "text/html", getPage());
}


// ==================================================
// Handle submit form
// ==================================================
void handleSubmit()
{
  //Text to show
  if (server.hasArg("textosend"))
      {
      textosend_string = server.arg("textosend");
      Serial.print("The received text is:             ");
      Serial.println(textosend_string);
      Serial.print("If it is an integer its value is: ");
      Serial.println(textosend_string.toInt());
      }

   if (server.hasArg("textosend2"))
      {
      textosend_string2 = server.arg("textosend2");
      Serial.print("The second received text is:             ");
      Serial.println(textosend_string2);
      Serial.print("If it is an integer its value is:        ");
      Serial.println(textosend_string2.toInt());
      }   

  server.send(200, "text/html", getPage());       //Response to the HTTP request
}  


// ===================================================
// Handle root
// ===================================================
void handleRoot() 
{   
  if (server.args() ) 
    {
    handleSubmit();
    } 
  else 
    {
    server.send(200, "text/html", getPage());  
    }
}


// ===================================================
// Setup
// ===================================================
void setup()
{
  delay(1000);
  Serial.begin(115200);

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid,password);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);

  server.begin();
  delay(500);
}


// ===================================================
// Loop
// ===================================================
void loop()
{  
  server.handleClient(); 
  delay(50);
}


And above is the complete program. just copy it into the Arduino IDE and fill in your routers credentials. And as stated in the beginning of this story: replace the first lines of the program with the appropriate libraries and library calls for the ESP8266 or the ESP32.

The result

 
Here you can see how the webpage looks.



And this is how the information is displayed in the Serial monitor.
I entered the default text in the first field and pressed the send button. Next I entered the figure 155 and again pressed the send button. I did the same in field number 2 however here I only entered text.

Next step.

Before we proceed to the next step play around with this program. Add more fields. Alter HTML code and use different background colors and maybe even change the text appearences.

But above all: study the code so you fully understand whats happening.
If you fully understand how this works you can start building fantastic webistes for your IOT projects.
You could remove the second form creating command on the webpage and see what happens when both text fields are treated as one form.
Hint: do not forget to remove the first -- page += "</form>"; -- line.

This is a base for the next articles in which we are going to add all kind of goodies like buttons, radio buttons, sliders, color pickers, etc etc etc.

Till then
Have fun

Luc Volders