NodeMCU ESP8266 WebServer Tutorial
- Emmanuel Odunlade
- https://twitter.com/emmaodunlade
- emmaodunlade@gmail.com
- 28.684 Views
- moderate
- Tested
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.
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;
- NodeMCU (ESP8266-12E)
- 220 ohms Resistor(2)
- LED (2)
- Breadboard
- 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.
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.
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.
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!
Thank you for sharing
Hello i tested the project, but if i disconnect and then reconnect again to my network, is impossible to reconnect again .
the only way is to reset the nodemcu.
i try to better explain.
i connect my android and i can see the beb page and use it, but if i close the wi fi on my android and then open again, if i type again the ip address, is impossible to reconnect, to establish the connection again, i need to reset the node mcu .