Build an ESP8266 Web Server: The Complete Guide

This tutorial shows you step-by-step how to build a standalone ESP8266 web server that manages two outputs (two LEDs). Any device with a browser on your local network may access this mobile-responsive ESP8266 NodeMCU Web Server.

Part 1: Build an ESP8266 Web Server Using Arduino IDE

This part shows you how to use the Arduino IDE to build a web server that can control two outputs. With the help of this technique, you can create a new ESP8266 web server to meet your requirements.

This tutorial is available in written form (keep reading this page) as well as in video format (watch it below).

Prepare the Arduino IDE

1- On your operating system, download and install the Arduino IDE (some older versions won't work).

2- The Arduino IDE's ESP8266 add-on has to be installed next. Go to File > Preferences to do that.

3- In the “Additional Board Manager URLs” field, enter http://arduino.esp8266.com/stable/package_esp8266com_index.json, as shown in the figure below. Then press the “OK” button.

esp8266 Arduino ide preferences

4- Go to Tools > Board > Boards Manager

Arduino IDE boards manager

5- As shown in the figure below, scroll down and select the ESP8266 board menu, then install “esp8266 by ESP8266 Community“.

installing esp board

6- Choose your ESP8266 board in the Tools > Board menu. Then, restart the Arduino IDE.

Code

Copy the following code to your Arduino IDE, but do not yet upload it. To make it work for you, you'll need to make some changes.

/*********
  LEDEdit PRO
  Complete project details at https://lededitpro.com  
*********/

// Load Wi-Fi library
#include <ESP8266WiFi.h>

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

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

// Variable to store the HTTP request
String header;

// Auxiliar variables to store the current output state
String output5State = "off";
String output4State = "off";

// Assign output variables to GPIO pins
const int output5 = 5;
const int output4 = 4;

// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0; 
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;

void setup() {
  Serial.begin(115200);
  // Initialize the output variables as outputs
  pinMode(output5, OUTPUT);
  pinMode(output4, OUTPUT);
  // Set outputs to LOW
  digitalWrite(output5, LOW);
  digitalWrite(output4, LOW);

  // 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();
}

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

  if (client) {                             // If a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    currentTime = millis();
    previousTime = currentTime;
    while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
      currentTime = millis();         
      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 /5/on") >= 0) {
              Serial.println("GPIO 5 on");
              output5State = "on";
              digitalWrite(output5, HIGH);
            } else if (header.indexOf("GET /5/off") >= 0) {
              Serial.println("GPIO 5 off");
              output5State = "off";
              digitalWrite(output5, LOW);
            } else if (header.indexOf("GET /4/on") >= 0) {
              Serial.println("GPIO 4 on");
              output4State = "on";
              digitalWrite(output4, HIGH);
            } else if (header.indexOf("GET /4/off") >= 0) {
              Serial.println("GPIO 4 off");
              output4State = "off";
              digitalWrite(output4, 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>GPIO 5 - State " + output5State + "</p>");
            // If the output5State is off, it displays the ON button       
            if (output5State=="off") {
              client.println("<p><a href=\"/5/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/5/off\"><button class=\"button button2\">OFF</button></a></p>");
            } 
               
            // Display current state, and ON/OFF buttons for GPIO 4  
            client.println("<p>GPIO 4 - State " + output4State + "</p>");
            // If the output4State is off, it displays the ON button       
            if (output4State=="off") {
              client.println("<p><a href=\"/4/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/4/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("");
  }
}

In order for your ESP8266 to establish a connection with your router, you need to modify the following two variables with your network credentials:

// Replace with your network credentials
const char* ssid = "";
const char* password = "";

Uploading the Sketch

Uploading the Sketch to the ESP-12E

Using an ESP-12E NodeMCU Kit, uploading the sketch is a simple process thanks to the kit's built-in programmer. Connect your board to your computer. Be sure you select the right board and COM port.

In the Arduino IDE, click the Upload button, then wait a few seconds until you see the message “Done uploading“. in the bottom left corner.

done uploading

Uploading Sketch to the ESP-01

As shown in the schematic diagram below, uploading code to the ESP-01 requires establishing serial connectivity between your ESP8266 and an FTDI programmer.

ESP8266 Flasher

Note: An ESP8266-01 serial adapter offers an alternative that is less error-prone and easier to use.

The following table shows the connections between the ESP8266 and the FTDI programmer that you will need to make.

ESP8266FTDI programmer
RXTX
TXRX
CH_PD3.3V
GPIO 0GND
VCC3.3V
GNDGND

You'll probably need to install the FTDI drivers on your Windows PC if you have a brand-new FTDI programmer. You can find the official drivers on this website. (If the COM port in your Arduino IDE is greyed out, you most likely don't have the drivers installed).

You just need to upload the code to the ESP8266 and connect the FTDI programmer to your computer.

Schematics

You'll need the following parts to build the circuit for this tutorial:

You also need an FTDI programmer or a serial adapter if you're using the ESP-01.

You may use the preceding links to find all of the parts you need for your projects at the best possible price!

Connect two LEDs to your ESP8266 as shown in the following schematic diagram: one to GPIO 4 (D2) and the other to GPIO 5 (D1).

esp8266 web server schematics bb

Use the following schematic diagram as a guide if you're using the ESP8266-01, but remember that you need to change the GPIO assignments in the code (to GPIO 2 and GPIO 0).

ESP web server bb

Testing the Web Server

The code will start working right away when you upload it. If you don't, you'll receive an error while trying to upload the file. Make sure you have the right board and COM port chosen. Activate the serial monitor with a 115200 baud rate.

Finding the ESP IP Address

The ESP IP address will be output on the serial display when you press the ESP8266 RESET button.

ESP8266 IP Address

Copy that IP address, because you need it to access the web server.

Accessing the Web Server

You will see the following page if you open your browser and type in the ESP IP address: When you request the ESP8266's IP address, it responds by sending this page.

esp8266 an web server

You can see what is going on in the background if you take a look at the serial monitor. The ESP receives an HTTP request from a brand-new client, in this example, your browser.

You may also see more information about the HTTP format; these fields are called HTTP header fields and define the operating parameters of an HTTP transaction.

http response

Testing the Web Server

The web server should be put to the test. To turn GPIO 5 on, press the button. LED 5 turns on when the ESP receives a request on the /5/on URL.

serial monitor GPIO 5 on

The LED state is also updated on the web page.

esp8266 an web server state

Test the GPIO 4 button and check that it works in a similar way.

How the Code Works

Let's take a closer look at the code to see how it works so that you may modify it to suit your requirements.

Include the ESP8266WiFi library first, since that is all you need to do.

// Load Wi-Fi library
#include <ESP8266WiFi.h>

You need to insert your SSID and password in the following lines inside the double quotes, as previously mentioned.

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

Then, you set your web server to port 80.

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

The following line creates a variable to store the header of the HTTP request:

String header;

To store the current state of your outputs, you create auxiliary variables. You need to create additional variables if you wish to add more outputs and save their states.

// Auxiliar variables to store the current output state
String output5State = "off";
String output4State = "off";

Each of your outputs has to have a GPIO assigned to it as well. We are using GPIOs 4 and 5 in this instance. Any other suitable GPIO may be used.

// Assign output variables to GPIO pins
const int output5 = 5;
const int output4 = 4;

setup()

Let's now enter setup(). When your ESP first launches, the setup() code only runs once. To facilitate debugging, we first start the serial connection at a baud rate of 115200.

Serial.begin(115200);

You also define your GPIOs as OUTPUTs and set them to LOW.

// Initialize the output variables as outputs
pinMode(output5, OUTPUT);
pinMode(output4, OUTPUT);
// Set outputs to LOW
digitalWrite(output5, LOW);
digitalWrite(output4, LOW);

The following lines begin the Wi-Fi connection with WiFi.begin(ssid, password), wait for a successful connection, and print the ESP IP address in the Serial Monitor.

// 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();

loop()

When a new client establishes a connection with the web server, we program what happens in a loop().

This line indicates that the ESP is always listening to incoming clients.

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

We'll save the incoming data as soon as a client request is received. As long as the client stays connected, the while loop that follows will run. If you aren't sure what you're doing, we wouldn't recommend modifying the following part of the code:

if (client) { // If a new client connects,
 Serial.println("New Client."); // print a message out in the serial port
 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();

The next section of if and else statements checks which state was pressed on your web page and controls the outputs accordingly. Depending on the button we press, as we've seen previously, we send a request to different URLs.

// turns the GPIOs on and off
if (header.indexOf("GET /5/on") >= 0) {
  Serial.println("GPIO 5 on");
  output5State = "on";
  digitalWrite(output5, HIGH);
} else if (header.indexOf("GET /5/off") >= 0) {
  Serial.println("GPIO 5 off");
  output5State = "off";
  digitalWrite(output5, LOW);
} else if (header.indexOf("GET /4/on") >= 0) {
  Serial.println("GPIO 4 on");
  output4State = "on";
  digitalWrite(output4, HIGH);
} else if (header.indexOf("GET /4/off") >= 0) {
  Serial.println("GPIO 4 off");
  output4State = "off";
  digitalWrite(output4, LOW);
}

The URL changes to the ESP IP address followed by /5/in, for instance, if you've pressed the GPIO 5 ON button, and we receive that information in the HTTP header. To check whether the header includes the expression GET /5/on, we may do so.

If it does, the code prints a message to the serial monitor, changes the output5State variable to on, and turns on the LED.

This works similarly for the other buttons. If you wish to add other outputs, you need to modify this part of the code to include them.

Displaying the HTML Web Page

Generating the web page is what you need to do next. To display the web page, the ESP8266 will send a response to your browser with HTML text.

Using the client.println() function, the web page is sent to the client. What you wish to communicate to the client should be entered as an argument.

The following line indicates that we are sending HTML and should always be the first text you transmit.

<!DOCTYPE html><html>

The following line makes the page responsive in any web browser after that.

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

To prevent requests relating to the favicon, use the next one. This line is not anything you need to worry about.

client.println("<link rel=\"icon\" href=\"data:,\">");

Styling the Web Page

To style the buttons and the look of the web page, we will now use some CSS. We decide to use the Helvetica font, define the content to be displayed as a block, and center it.

client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");

We style our buttons with some properties to define color, size, border, etc.

on { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");

Then, we define the style for a second button with all of the earlier-defined button's properties but in a different color. This will be the button's style while it is off.

client.println(".button2 {background-color: #77878A;}</style></head>");

Setting the Web Page First Heading

In the next line, you set the first heading of your web page. You can change this text to whatever you like.

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

Displaying the Buttons and Corresponding State

The current state of GPIO 5 is then shown in a paragraph that you write. The state updates instantly when this variable changes, as you can see since we use the output5State variable.

client.println("<p>GPIO 5 - State " + output5State + "</p>");

Then, we display the on or off button, depending on the current state of the GPIO.

if (output5State=="off") {
 client.println("<p><a href=\"/5/on\"><button class=\"button\">ON</button></a></p>");
} else {
 client.println("<p><a href=\"/5/off\"><button class=\"button button2\">OFF</button></a></p>");
}

We use the same procedure for GPIO 4.

Closing the Connection

Finally, when the response ends, we clear the header variable and stop the connection with the client with client.stop().

// Clear the header variable
header = "";
// Close the connection
client.stop();

Taking it Further

You may modify the code to add additional outputs or change your web page now that you know how it works. You may need to know some HTML and CSS to modify your web page.

You may control a relay to practically control any electronic appliance instead of controlling two LEDs.

Part 2: Build an ESP8266 Web Server Using NodeMCU Firmware

Using NodeMCU firmware and the LUA programming language, this part shows how to create a web server to control two outputs. To suit your demands, you may use this method to create a different web server.

esp8266 gpios

First, watch the video demonstration below

Why flash your ESP8266 module with NodeMCU?

With the help of LUA scripts, you can program ESP8266 modules using the NodeMCU firmware. It is quite similar to the way you program your Arduino to program the ESP8266 using LUA and the NodeMCU firmware. A WiFi connection can be established, the ESP8266 GPIOs can be controlled, the ESP8266 can be turned into a web server, and much more can be done with only a few lines of code.

Downloading NodeMCU Flasher for Windows

It is necessary to download the NodeMCU flasher after wiring your circuit. You may download the .exe file using one of the following links:

You can click here to find all the information about the NodeMCU flasher.

Flashing your ESP8266

You only need to plug the ESP8266-12 into your PC if you're using one. You need an FTDI programmer to connect an ESP-01 to your computer if you're using one. as shown in the schematic picture below, to create a serial connection between your ESP8266 and an FTDI programmer.

ESP8266 Flasher

Open the flasher that you just downloaded, and a window should appear (as shown in the following figure).

nodemcu flaser

Press the “Flash” button, and the settings process should start flashing instantly (you may need to adjust some of the settings on the Advanced page). It should look like a green circle with a check icon after finishing this process.

Schematics

You also need an FTDI programmer or a serial adapter if you're using the ESP-01.

If you’re using the ESP8266-01, use the following schematic diagram as a reference:

ESP web server bb

Uploading the Code

To create and save LUA files to your ESP8266, I recommend using 4refr0nt's ESPlorer program. To download and install ESPlorer, follow these instructions:

  1. Click here to download ESPlorer.
  2. Unzip that folder.
  3. Go to the dist folder (here’s the path: ESPlorer-masterESPlorerdist).
  4. Run ESPlorer.jar. It’s a Java program, so you need Java installed on your computer.
  5. Open the Explorer.
esplorer start

You should see a window similar to the one shown in the preceding illustration. To upload an LUA file, follow these instructions:

  1. Connect your FTDI programmer to your computer.
  2. Select your FTDI programmer port.
  3. Press Open/Close.
  4. Select NodeMCU+MicroPtyhon tab.
  5. Create a new file called init.lua.
  6. Press Save to ESP.

The red box has everything you need to worry about or change highlighted.

esplorer tab

Code

Using the preceding software, upload the following code to your ESP8266: You should have a file named “init.lua” in your project. To download the file, click here.

wifi.setmode(wifi.STATION)
wifi.sta.config("YOUR_NETWORK_NAME","YOUR_NETWORK_PASSWORD")
print(wifi.sta.getip())
led1 = 3
led2 = 4
gpio.mode(led1, gpio.OUTPUT)
gpio.mode(led2, gpio.OUTPUT)
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
    conn:on("receive", function(client,request)
        local buf = "";
        local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
        if(method == nil)then
            _, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
        end
        local _GET = {}
        if (vars ~= nil)then
            for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
                _GET[k] = v
            end
        end
        buf = buf.."<h1> ESP8266 Web Server</h1>";
        buf = buf.."<p>GPIO0 <a href=\"?pin=ON1\"><button>ON</button></a> <a href=\"?pin=OFF1\"><button>OFF</button></a></p>";
        buf = buf.."<p>GPIO2 <a href=\"?pin=ON2\"><button>ON</button></a> <a href=\"?pin=OFF2\"><button>OFF</button></a></p>";
        local _on,_off = "",""
        if(_GET.pin == "ON1")then
              gpio.write(led1, gpio.HIGH);
        elseif(_GET.pin == "OFF1")then
              gpio.write(led1, gpio.LOW);
        elseif(_GET.pin == "ON2")then
              gpio.write(led2, gpio.HIGH);
        elseif(_GET.pin == "OFF2")then
              gpio.write(led2, gpio.LOW);
        end
        client:send(buf);
        client:close();
        collectgarbage();
    end)
end)

Note: Don’t forget to replace your WiFi station details in the code above (network name and password).

Accessing your web server

When your ESP8266 restarts, it prints on your serial monitor the IP address of your ESP8266. If you type your ESP8266 IP address in your web browser, you can access your web server.

web server esp8266

Do you have any questions? Leave a comment below!

Oh hi there It’s nice to meet you.

Sign up to receive awesome content in your inbox, every month.

We don’t spam! Read our privacy policy for more info.

Leave a Reply

Your email address will not be published. Required fields are marked *

ESP8266 Home Automation Projects

Leverage the power of this tiny WiFi chip to build exciting smart home projects