NodeMCU ESP8266 WebServer Tutorial

From the Wemos D1 mini to the NodeMCU, the ESP8266 based boards are by far the most popular platform, among makers and hobbyist, for building WiFi based projects due to their low cost and general ease of use.  For today’s tutorial, we will continue our exploration of the most popular of the ESP8266 based boards; the NodeMCU development board and we will explore how to build a web server using it. Through the web server, we will be able to control anything connected to the GPIO pins of the board. We will not limit ourselves to just controlling LEDs, but by expanding this project you can build a full-blown home automation system or any other project that involves controlling or obtaining data from the NodeMCU’s GPIO remotely.

NodeMCU Development Board

The NodeMCU development board is an open source platform for developing WiFi based embedded devices. It is based on the ESP8266 WiFi Module and runs the Lua based NodeMCU firmware. The NodeMCU was born out of the desire to overcome all the bottlenecks associated with the first versions of the ESP8266 module which was not compatible with breadboards, difficult to power and even more difficult to program. Its ease of use and low cost, quickly endeared it to the heart of makers and it is one of the most popular boards today.

At the end of today’s tutorial, you would know how to use the NodeMCU as a web server and how to control the GPIO pins of your NodeMCU/ESP8266 board from a webpage.

Required Components

The following components are required to build this project;

  1. NodeMCU (ESP8266-12E)
  2. 220 ohms Resistor(2)
  3. LED (2)
  4. Breadboard
  5. Jumper Wire

Schematics

The schematic for this project is quite simple. As mentioned earlier, we will toggle LEDs to demonstrate what can be achieved using the NodeMCU Web server. While LEDs are being used here, you can decide to use more useful components like a relay, which could then be used to control appliances in your home.

Connect the components as shown in the schematics below.

Schematics

The positive legs of the green and red LEDs are connected to digital pins 1 and 2 of the NodeMCU (respectively), while their negative legs are connected to ground via a 220 ohms resistor to limit the amount of current flowing through the LEDs.

With the schematics done, we can now move to the code for the project.

Code

One of the easiest ways to program the NodeMCU is via the Arduino IDE. However, this requires setting up the Arduino IDE by installing the board support file for NodeMCU. If you are using the Arduino IDE to program the NodeMCU for the first time, you need to do this first before proceeding with the tutorial. Follow this detailed tutorial to learn how to set up your Arduino to program ESP8266 based boards.

With that done, we can now move to the code. The main driver behind today’s tutorial is the ESP8266WiFi Library. This library contains cool functions to implement WiFi based activities and project on the NodeMCU. It contains all we need to create a WiFi access point or join up with an existing access point and also create a server and client which are all important for today’s project. The libraries come attached with the NodeMCU board files for the Arduino, so there is no need to install them once the board files have been installed.

As mentioned above, our goal is to create a web server through which the GPIOs of the NodeMCU can be controlled. The Web server should be accessible via a browser on any device on the same network as the NodeMCU.

The code for this project is a modified version of the code by Rui Santos (credits for him). It’s a little bit complex and might be difficult to follow for those without the knowledge of HTML but I will do my best to break it down.

To start,  as usual, we include the library that will be used for the code which in this case, is the ESP8266wifi library.



#include <ESP8266WiFi.h>

Next, add the credentials of the WiFi access point to which the NodeMCU will be connected. Ensure the username and password are in-between the double quotes. We also specify the port through which the system will communicate and create a variable to hold requests.

const char* ssid     = "WIFI_SSID";
const char* password = "WIFI_PASSWORD";

WiFiServer server(80);// Set port to 80


String header; // This storees the HTTP request

Next, we declare the pins of the Nodemcu to which the red and green LED will be connected and create variables to hold the state of the LEDs.

int greenled = D1;
int redled = D2; 

String greenstate = "off";// state of green LED
String redstate = "off";// state of red LED

With this done, we move to the void setup() function.

We start by initializing the serial monitor (as it will be used for debugging later on) and setting the pinModes of the pins to which the LEDs are connected as output. We then set the pins “LOW” to ensure the system starts at a neutral state.

void setup() {
  Serial.begin(115200);
 // Set the pinmode of the pins to which the LEDs are connected and turn them low to prevent flunctuations
  pinMode(greenled, OUTPUT);
  pinMode(redled, OUTPUT);
  digitalWrite(greenled, LOW);
  digitalWrite(redled, LOW);

Next, we connect to the access point using the credentials as arguments to the WiFi.begin() function and we use the WiFi.status() function to check if connection was successful.

WiFi.begin(ssid, password);
Serial.print("Connecting to ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}

If the connection is successful, a text is printed on the serial monitor to indicate that, and the IP address of the web server is also displayed. This IP address becomes the web address for server and it is what will be entered on any web browser on the same network to access the server.

Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());// this will display the Ip address of the Pi which should be entered into your browser

With that done, we start the server using the server.begin() function and proceed to the void loop( ) function.

server.begin();

The void loop() function is where the majority of the work is done. We start by using the server.available() function to listen for incoming connection by clients (web browsers). When a client is available and connected, we read the client request and send the header as a response.

WiFiClient client = server.available();   // Listen for incoming clients

  if (client) {                             // If a new client connects,
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

Next, we check if the client’s request indicates a button press on the web page, to turn any of the pins “on/off” starting with the green LED. If the request indicates “on”, the pin is turned high and the state variable is updated accordingly and vice versa.

// turns the GPIOs on and off
           if (header.indexOf("GET /green/on") >= 0) {
             Serial.println("green on");
             greenstate = "on";
             digitalWrite(greenled, HIGH);
           } else if (header.indexOf("GET /green/off") >= 0) {
             Serial.println("green off");
             greenstate = "off";
             digitalWrite(greenled, LOW);
           } else if (header.indexOf("GET /red/on") >= 0) {
             Serial.println("red on");
             redstate = "on";
             digitalWrite(redled, HIGH);
           } else if (header.indexOf("GET /red/off") >= 0) {
             Serial.println("red off");
             redstate = "off";
             digitalWrite(redled, LOW);
           }

Next, we create the webpage that will be displayed and updated by the NodeMCU as the user interacts with it. The key function for this is the Client.println() function which is used to send HTML scripts line by line to the client (browser).

we start by using the “doctype” to indicate that the next few texts to be printed are HTML lines.

client.println("<!DOCTYPE html><html>");

Next, we add the lines below to make webpage responsive irrespective of the browser being used

client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");

We also throw in some bits of CSS to the client to make the page user friendly. You can edit this to add your own color, font style etc.

client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #77878A;}</style></head>");

Next, the webpage header is sent alongside the buttons which are set to display on or off based on the current state of the LED. It will display off if the current state is ON and vice versa.

client.println("<body><h1>ESP8266 Web Server</h1>");

// Display current state, and ON/OFF buttons for GPIO 5  
client.println("<p>green - State " + greenstate + "</p>");
// If the green LED is off, it displays the ON button       
if (greenstate == "off") {
  client.println("<p><a href=\"/green/on\"><button class=\"button\">ON</button></a></p>");
} else {
  client.println("<p><a href=\"/green/off\"><button class=\"button button2\">OFF</button></a></p>");
} 
   
// Display current state, and ON/OFF buttons for GPIO 4  
client.println("<p>red - State " + redstate + "</p>");
// If the red LED is off, it displays the ON button       
if (redstate == "off") {
  client.println("<p><a href=\"/red/on\"><button class=\"button\">ON</button></a></p>");
} else {
  client.println("<p><a href=\"/red/off\"><button class=\"button button2\">OFF</button></a></p>");
}
client.println("</body></html>");

Next, we close the connection and the loop goes over again.

// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");

The complete code for the project is shown below and also available for download under the download section at the end of the tutorial.

#include <ESP8266WiFi.h>

// Add wifi access point credentiaals
const char* ssid     = "WIFI_SSID";
const char* password = "WIFI_PASSWORD";

WiFiServer server(80);// Set port to 80


String header; // This storees the HTTP request

// Declare the pins to which the LEDs are connected 
int greenled = D1;
int redled = D2; 

String greenstate = "off";// state of green LED
String redstate = "off";// state of red LED


void setup() {
  Serial.begin(115200);
 // Set the pinmode of the pins to which the LEDs are connected and turn them low to prevent flunctuations
  pinMode(greenled, OUTPUT);
  pinMode(redled, OUTPUT);
  digitalWrite(greenled, LOW);
  digitalWrite(redled, LOW);
  //connect to access point
  WiFi.begin(ssid, password);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  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());// this will display the Ip address of the Pi which should be entered into your browser
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   // Listen for incoming clients

  if (client) {                             // If a new client connects,
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            
            // turns the GPIOs on and off
            if (header.indexOf("GET /green/on") >= 0) {
              Serial.println("green on");
              greenstate = "on";
              digitalWrite(greenled, HIGH);
            } else if (header.indexOf("GET /green/off") >= 0) {
              Serial.println("green off");
              greenstate = "off";
              digitalWrite(greenled, LOW);
            } else if (header.indexOf("GET /red/on") >= 0) {
              Serial.println("red on");
              redstate = "on";
              digitalWrite(redled, HIGH);
            } else if (header.indexOf("GET /red/off") >= 0) {
              Serial.println("red off");
              redstate = "off";
              digitalWrite(redled, LOW);
            }
       
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #77878A;}</style></head>");
            
            // Web Page Heading
            client.println("<body><h1>ESP8266 Web Server</h1>");
            
            // Display current state, and ON/OFF buttons for GPIO 5  
            client.println("<p>green - State " + greenstate + "</p>");
            // If the green LED is off, it displays the ON button       
            if (greenstate == "off") {
              client.println("<p><a href=\"/green/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/green/off\"><button class=\"button button2\">OFF</button></a></p>");
            } 
               
            // Display current state, and ON/OFF buttons for GPIO 4  
            client.println("<p>red - State " + redstate + "</p>");
            // If the red LED is off, it displays the ON button       
            if (redstate == "off") {
              client.println("<p><a href=\"/red/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/red/off\"><button class=\"button button2\">OFF</button></a></p>");
            }
            client.println("</body></html>");
            
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Demo

Upload the code to your NodeMCU. Ensure everything is connected as described under the schematics section. After uploading the code, open the serial monitor, you should see the IP address of your web server displayed, as shown below.

The IP Address

Copy the IP address and paste in a web browser on a device connected to the same network as the NodeMCU. You should see the web page and be able to toggle the LEDs by clicking on the buttons.

Demo: Click the buttons to change the states of the LEDs

That’s it for this tutorial, Thanks for reading.
As mentioned above, this tutorial could serve as a building block for complex web servers and IoT solutions. What will you build?

If you have any question about today’s tutorial, please feel free to share via the comment section.

Till Next time!

Comments

  • Tự Động Hóa Sinh Viên

    Thank you for sharing

    4 months ago

Leave a Reply

RELATED PROJECTS

By continuing to use the site, you agree to the use of cookies. more info

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close