Friday, January 21, 2022

ESP Webserver tutorial part 3 - button with status save and led

For a complete index to all my articles click this text.

This is a series on building a webpage with the ESP8266 and the ESP32. When this series is completed all the information needed for building a webpage with buttons, textfields, sliders and color pickers witch have interaction with the ESP8266 or ESP32 will be discussed and demonstrated.

This third part leans heavy on the previous two parts in this series, so I urge you to read these stories first:

ESP webserver tutorial part 1 - textfields
ESP webserver tutorial part 2 - button

In the previous story I showed you how to build a webpage with buttons. Clicking the button would put a led on the ESP8266 or ESP32 ON or OFF the webpage looks like this:



This is a perfectly valid way to use buttons on a webpage with which you can set leds on and off, start and stop motors, pumps or ventilators, open the garage door etc etc etc. There are however two flaws with this setup.

- When you press the button there is no feedback
- There is no clue about the initial state of the led, motor or whatever.

Wether you think that these issues are severe is a personal preference. When the led is off and you press the OFF button it will stay off. If the led was on it will be put off. So actually it is not a big deal.

However you might be building a project for  non-computer-minded people and then a more visual attractive feedback might be appropriate. Or you just might like the looks of a visual attractive webpage better (I  do !!). Next to that it can be very convenient to see the actual state of the led, motor etc. when the webpage is opened or refreshed.

So in this part I am showing you how to put a circle representing a led on your webpage that will initially show the actual state of the led/motor/pump and that will change its color if you press the button and sending the opposite signal to the ESP.

The webpage will look like this:



The colorscheme I used is as follows. When the IO port which is affected by the button is off (0) the led will be green. When the IO port is on (1) the led will be red. My idea is that green is safe and red is danger. You can alter the colors to your own liking like black for off and white for on. To achive this you only need to alter the colors in the <style> part of the code.

To achive this we will use HTML for building the webpage, CSS for background coloring text coloring and the color of the led, and Javascript for reacting on the buttonpress on the website. This all will be incorporated in an Arduino (C++) program for the ESP series.

This program is intended for the ESP8266. By changing just a few lines in the software and the breadboard setup it will work on the ESP32 as well.

The ESP8266 breadboard setup.

The breadboard setup is the same as in the previous story and as simple as it can be.




Just an ESP8266 (the Wemos D1 mini version) with a led and a current delimiting resistor attached to D5. Any ESP8266 like the NodeMCU will work. You could even adapt this foran ESP8266-01.

The ESP32 breadboard setup

The ESP32 breadboard setup is equally simple.



I am using the ESP32 Devkit board. These boards will not fit a breadboard. Therefore I use 2 breadboards. From one of these breadboards I stripped the power rail as discussed in the story you can read here:
https://lucstechblog.blogspot.com/2018/08/breadboard-hack-for-esp32-and-esp8266.html

The led is attached to GPIO32 through a 220 Ohm current delimiting resistor.

The program

Just like the previous entries in this series the program is written in Arduino language (C++) and is almost identical for the ESP8266 and the ESP32. The changes for the ESP32 are really minimal. I will give you the ESP8266 program as a whole and you can find the few lines which have to be modified for the ESP32 at the end of this story.



// ======================================================
// Webserver program that sends button information
// to ESP8266 and puts a control led on the page
// with save function by Luc Volders
// ======================================================

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

ESP8266WebServer server(80);

// Your routers credentials
const char* ssid = "XXXXXXXXXXXXXXX";
const char* password = "YYYYYYYYYYYYYYYY";

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

String buttonON;
bool ledstate = 0;

// =========================================
// Here we build 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 button demo</title>";
  page += "<body style='background-color:powderblue; onload='myFunction()'>";
  
  page += "<style>";
  page += ".green {";
  page += "height: 25px;";
  page += "width: 25px;";
  page += "background-color: #080;";
  page += "border-radius: 50%;";
  page += "display: inline-block;";
  page += "}";
  page += ".red {";
  page += "height: 25px;";
  page += "width: 25px;";
  page += "background-color: #f00;";
  page += "border-radius: 50%;";
  page += "display: inline-block;";
  page += "}";
  page += "</style>";
  page += "</head>";
  page += "<body>";
  
  page += "<h1 style='color:red'>Luc's web-button for ESP8266</h1>";
  page += "<br>";
  page += "<br>";
  if (ledstate == 1)
  {
    page += "<div id='dot1' class='red'></div>&emsp;&emsp;&emsp;";
  }
  else 
  {
    page += "<div id='dot1' class='green'></div>&emsp;&emsp;&emsp;";  
  }
  page += "<br>";
  page += "<br>";

  page += "<FORM action=\"/\" method=\"post\">";
  page += "<button type=\"submit\" name=\"button1\" id=\"button1\" value=\"but1\" onclick=\'update()\'>CLICK ME</button>";
  page += "</form>";

  page += "<br>";
  page += "<br>";
  
  page += "<script>";
  page += "function update() {";
  page += "if (document.getElementById('dot1').className == 'red'){";
  page += "element = document.getElementById('dot1');";
  page += "element.classList.remove('red');";
  page += "element.classList.add('green');";
  page += "}";
  page += "else{";
  page += "element = document.getElementById('dot1');";
  page += "element.classList.remove('green');";
  page += "element.classList.add('red');";
  page += "}";
  page += "}";
  page += "function myFunction() {";
  // Here we put commands that must be executed when the page is loaded
  page += "}";
  page += "</script>";  
  page += "</body>";
  page += "</html>";
  return page;
  }


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


// ==================================================
// Handle submit form
// ==================================================
void handleSubmit()
{   
   if (server.hasArg("button1"))
      {
      buttonON = server.arg("button1");
      Serial.print("Thereceived button is:             ");
      Serial.println(buttonON);
      Serial.print("If it is an integer its value is:        ");
      Serial.println(buttonON.toInt());
      ledstate= !ledstate;
      digitalWrite(D5, ledstate);
      } 
  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);

  pinMode(D5, OUTPUT);
  digitalWrite(D5,LOW);
}


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

Lets go over this step by step.


#include <ESP8266WiFi.h>

#include <ESP8266WebServer.h>


ESP8266WebServer server(80);


// Your routers credentials

const char* ssid = "XXXXXXXXXX";

const char* password = "YYYYYYYYY";

This is standard material. The necessary libraries are loaded and the wifi credentials are set. Do not forget to alter the XXXXXXXXX and YYYYYYYYY in your routers name and password.


String buttonON;

bool ledstate = 0;

A variable buttonON is created which will receive the data from the webpage. A second variable called ledstate is set to 0. This is used to set the IO pin. for safety reasons it is initially set to 0. So the led or relay or whatever you will attach to the ESP will be set OFF.

lets skip to the handleNotFound routine

 

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

// Handle for page not found

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

void handleNotFound()

{

  server.send(200, "text/html", getPage());

}

This routine just sends the webpage anew when an unknown command is received from the webpage. This is likely never going to happen and therefore just a safety measure.


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

// Handle submit form

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

void handleSubmit()

{  

   if (server.hasArg("button1"))

      {

      buttonON = server.arg("button1");

      Serial.print("Thereceived button is:             ");

      Serial.println(buttonON);

      Serial.print("If it is an integer its value is:        ");

      Serial.println(buttonON.toInt());

      ledstate= !ledstate;

      digitalWrite(D5, ledstate);

      }

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


} 

This part analyses what the ESP receives from the webpage if the button is pressed and acts on it. First the program tests what the webpage has send. In this case it is information from "button1". On this simple website there is only one button. By copying the IF test we can expand this for a multitude of buttons.

Next the buttonON variable gets the value that is send by the webpage. In this program the webpage sends the word "but1" when button1 on the page is pressed. Again you can alter this when using multiple buttons.

The Serial monitor prints then the received information and tries to make an integer value from the recived information. In this case we just receive the string "but1" and as stated this can be altered to many things like a value by altering the code on the webpage.

Next as the button is pressed (otherwise the if is not executed) we negate the ledstate variable so ON will be OFF and the other way round. Then we will write the ledstate to the IO pin so the led on the ESP will go ON or OFF.

And then the getpage() String is called. This rebuilds the webpage.


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

// Handle root

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

void handleRoot()

{  

  if (server.args() )

    {

    handleSubmit();

    }

  else

    {

    server.send(200, "text/html", getPage()); 

    }

}

This routine takes care of refreshing the webpage. If the button on the webpage is pressed the server will get an argument and the before discussed handleSubmit routine is called. If the webpage is simply refreshed or called for the first time then the webpage is simply build and send.

In the setup() routine nothing special happens.
Connection is made with the router like always with programs that use Wifi. Next the webserver is started and the D5 IO pin is set as output and set to OFF.
As the setup routine is only called once when the ESP is powered up or reset the IO pin will be set OFF at the start of the program. This is for avoiding lamps or motors to be put on immediately when the program starts.

The loop() just simply akes sure that the server is activated all the time.


String getPage()

This is where the interesting things happen. Let's look how the webpage is being build.

The first part just sets the width according to the width on which device the webpage is shown. A computer screen is wider as a phone screen or a tablet screen and that is what the vieuwport part takes care off.
Next the name of the page is defined and the background color which is my favorite powderblue.


  page += "<style>";

  page += ".green {";

  page += "height: 25px;";

  page += "width: 25px;";

  page += "background-color: #080;";

  page += "border-radius: 50%;";

  page += "display: inline-block;";

  page += "}";

  page += ".red {";

  page += "height: 25px;";

  page += "width: 25px;";

  page += "background-color: #f00;";

  page += "border-radius: 50%;";

  page += "display: inline-block;";

  page += "}";

  page += "</style>";

This part defines the CSS code for the led on the webpage. There is only one led and it will be green or red. The height and width are defined. The border radius command makes sure the led is round. Put the value 0% in there and the led will be a square.


  if (ledstate == 1)

  {

    page += "<div id='dot1' class='red'></div>&emsp;&emsp;&emsp;";

  }

  else

  {

    page += "<div id='dot1' class='green'></div>&emsp;&emsp;&emsp;"; 

  }

These lines are most important. As I stated at the beginning of this story and in the previous story the previous program from story number 2 had a flaw. The user did not know in which state the led actually is.
These lines take care of that.
The webpage receives the ledstate (being the IO port state) from the ESP. And then the led will get the color as defined by its state. In this case green when the led is off and red when it is on as defined in the CSS <style> lines.
The CSS code for the color and shape is called by the word class.


&emsp;

This is just standard HTML code that puts a fixed space after the led so we can put multiple leds on a line if we want with some space between them.

  page += "<FORM action=\"/\" method=\"post\">";

  page += "<button type=\"submit\" name=\"button1\" id=\"button1\" value=\"but1\" onclick=\'update()\'>CLICK ME</button>";

  page += "</form>";

This code puts the button on the screen. The name of this button is 'button1' and that is what the ESP will react upon. The value that is send when the button is clicked is "but1". If you want multiple buttons just copy this piece of code for eacht button and alter the name and the value. Then alter the code in the handleSubmit() routine accordingly.

  page += "function update() {";

  page += "if (document.getElementById('dot1').className == 'red'){";

  page += "element = document.getElementById('dot1');";

  page += "element.classList.remove('red');";

  page += "element.classList.add('green');";

  page += "}";

  page += "else{";

  page += "element = document.getElementById('dot1');";

  page += "element.classList.remove('green');";

  page += "element.classList.add('red');";

  page += "}";

  page += "}";

  page += "function myFunction() {";

  // Here we put commands that must be executed when the page is loaded

  page += "}";

  page += "</script>";

This part is the Javascript part of the webpage that alters the state of the led oin the screen when the button is pressed. If the led is red the class red is removed and replaced for the calss green and the other way round.

That's all folks...............

Looks complicated and maybe it is. However it will give you much freedom in designing your webpage. You can alter the background color and layout easily. You can alter the shape, size and color of the leds by just altering the CSS <style> codes.
You can even alter the looks of the button if you want.

This will give you the opportunity to build visual attractive websites for manipulating the IO ports on your ESP microcontrollers.

For more info on the HTML, CSS and Javascript used in this program look at the https://www.w3schools.com/ website which offers in-dept information and free courses on these subjects.

The ESP32

With some very small changes we can make the program above suitable for the ESP32.

Change the following lines in the beginning of the program:

#include <ESP8266WiFi.h>

#include <ESP8266WebServer.h>


ESP8266WebServer server(80);

into:

#include <WiFi.h>

#include <WebServer.h>


WebServer server(80);

and in the handleSubmit() routine change:

digitalWrite(D5, ledstate);

into:

digitalWrite(32, ledstate);

In the setup() routine change D5 into 32 for using the right IO pins on the ESP32 so change:



  pinMode(D5, OUTPUT);
  digitalWrite(D5,LOW);
into

  pinMode(32, OUTPUT);
  digitalWrite(32,LOW);
For cosmetics change the following line in the webpage part:


page += "<h1 style='color:red'>Luc's web-button for ESP8266</h1>";

into:


page += "<h1 style='color:red'>Luc's web-button for ESP32</h1>";

That's all

Next steps

So in this 3 parts in this series I covered how to input textfields on a website and send their text to the ESP, put buttons on a webpage and have the ESP react on them and visualise the state of the button and IO port. Next to that you can expand the website by putting sensor readings on it as discussed in earlier stories on this weblog.
All this will allow you to make decent looking webpages to control your ESP from remote locations.

In the next story I will show a very important expansion to the buttons. I will show you how to put sliders on the webpage for controlling RGB leds or the speed of a motor.

So stay tuned and have fun

Luc Volders