WebServers on ESP32

0 53486 Easy

Run a WebServer on ESP32, which can be accessed on the IP address of the ESP32. Access the page with IP address, Local Domain, or even Cloud!

 

Things used in the Project:-

Espressif ESP32 Development Board - Developer Edition

SparkFun Atmospheric Sensor Breakout - BME280

Arduino IDE

 

Get PCBs for Your Projects Manufactured

projectImage

You must check out ​PCBWAY for ordering PCBs online for cheap!

 

You get 10 good-quality PCBs manufactured and shipped to your doorstep for cheap. You will also get a discount on shipping on your first order. Upload your Gerber files onto PCBWAY to get them manufactured with good quality and quick turnaround time. PCBWay now could provide a complete product solution, from design to enclosure production. Check out their online Gerber viewer function. With reward points, you can get free stuff from their gift shop.

Overview

We will make a couple of projects under this one category, i.e. Web Servers -

1. 'Hello World' on WebPage (hosted from ESP32)

2. Display LIVE Data from BME280 (environment sensor) connected with ESP32

3. Make ESP32 as Access Point (HotSpot) and view the Web Page by connecting with ESP32 directly

4. Using SPIFFS (File Management within ESP32)

5. Use SPIFFS to host pages from HTML, CSS, and JS files (including images, etc)

6. Use mDNS to use custom domain names and access the WebServer

7. Use NGROK to tunnel the server and host it LIVE on the Internet

Now, you have a full-fledged working webserver on that small embedded system board.

 

 

 

Libraries Required/Used

Wire - This library allows you to communicate with I2C / TWI devices. In ESP32, SDA -> D21 and SCL -> D22. Used to set I2C communication with the BME280 sensor.

SparkFunBME280 - This library allows the user to: Read pressure in Pa, Read temperature in C and F, Read humidity in %RH and Read Altitude in feet and meters. more details

Adafruit Sensor - To use the BME280 library, you also need to install the Adafruit_Sensor library. Follow the next steps to install the library in your Arduino IDE: Go to Sketch > Include Library > Manage Libraries and type “Adafruit Unified Sensor” in the search box. Scroll all the way down to find the library and install it.

WiFi - This library lets the board connect to the internet, and wifi service. more details

WebServer - Supports in creating a webserver and running an independent simple webserver. more details

ESPAsyncWebServer - Creates Async HTTP and WebSocket Server on ESP32. Requires AsyncTCP to use with ESP32. more details

SPIFFS - It lets you access the flash memory like you would do in a normal filesystem in your computer, but simpler and more limited. You can read, write, close, and delete files. more details

ESP32mDNS - mDNS is a multicast UDP service that is used to provide local network service and host discovery. It is installed by default on most operating systems or is available as a separate package. more details

 

 

 

Hello World WebPage

In this project, we will host a server on the ESP32 dev board, which will display a header with 'Hello World' on it. We can view this page, from the IP address of the ESP32 Dev Board when it is connected to the same wifi/network like the one I am connected with.

Hardware Required

ESP32 Dev Board ONLY.

Code

Use the below libraries -

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

Enter the WiFi name and Password here -

CODE
/*Put your SSID & Password*/
const char* ssid = "<wifi_name>";    // Enter SSID here
const char* password = "<password>"; // Enter Password here

Here, we are opening port 80 for the webserver to be accessed.

WebServer server(80);

Inside the void setup(), we will first connect to the wifi network with available SSID and password using the WiFi.h library's function. Once the connection is established, print the IP address that was designated to the ESP32 by the wifi router, on the Serial monitor of Arduino IDE.

CODE
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP());
server.on("/", handle_OnConnect);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}

Using the below functions, we are creating server paths that can be accessed from the browser once the IP address is tried to be accessed from that device. It is the URL path that will/must be requested by the device trying to request data from the ESP32's Web Server.

These are the Endpoints that will help us in controlling the ESP32 from the URL. Basically, we are making a REST server locally that does not require internet.

server.on() function has 2 parameters, first part is the endpoint waiting from the clientside. and 2nd part is a function that gets executed upon the endpoint being triggered. The function is defined in the same code file.

CODE
server.on("/", handle_OnConnect);
server.onNotFound(handle_NotFound);

Using the below function, the server is started.

CODE
server.begin();
Serial.println("HTTP server started");

Now, let us define the response (HTML Page) that will be sent back to the device/user who sent the request. The function handles the server that has been started and controls all the endpoint functions when receiving a request.

When the request is, https://<ip_address>/ i.e. - "/" -> Response will be the function handle_OnConnect()

CODE
void handle_OnConnect() {
server.send(200, "text/html", SendHTML());
}

Here we can see, that the server will send a response code '200' with content as 'text/html' and finally the main HTML text. Below is the way HTML text is responded to -

CODE
String SendHTML(){
    String ptr = "<!DOCTYPE html> <html>\n";
    ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
    ptr +="<title>ESP32 Hello World</title>\n";
    ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
    ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
    ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
    ptr +="</style>\n";
    ptr +="</head>\n";
    ptr +="<body>\n";
    ptr +="<div id=\"webpage\">\n";
    ptr +="<h1>Hello World !!</h1>\n";
    ptr +="</div>\n";
    ptr +="</body>\n";
    ptr +="</html>\n";
    return ptr;    
}

When the request is, ANYTHING ELSE -> Response will be the function handle_NotFound()

CODE
void handle_NotFound(){
    server.send(404, "text/plain", "Not found");
}
CODE
#include <WiFi.h>
#include <WebServer.h>

/*Put your SSID & Password*/
const char* ssid = "<wifi_name>";  // Enter SSID here
const char* password = "<password>";  //Enter Password here

WebServer server(80);             
 
void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.println("Connecting to ");
  Serial.println(ssid);

  //connect to your local wi-fi network
  WiFi.begin(ssid, password);

  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  
  Serial.println(WiFi.localIP());

  server.on("/", handle_OnConnect);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("HTTP server started");
}
void loop() {
  server.handleClient();
}

void handle_OnConnect() {

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

void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

String SendHTML(){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>ESP32 Hello World</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
  ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<div id=\"webpage\">\n";
  ptr +="<h1>Hello World !!</h1>\n";
  ptr +="</div>\n";
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

LIVE Display of Data from ESP32

 

In this project, we will host a server on the ESP32 dev board, which will display Temperature, Humidity, Altitude, and Pressure from the BME280 sensor module (You can use any other sensor to get the environment data / Or use a randomized value to view data here). We can view this page, from the IP address of the ESP32 Dev Board when it is connected to the same wifi/network like the one I am connected with.

Hardware Required

1 x ESP32 Dev Board1 x Any sensor module (I am using BME280)

Code

Use below libraries (MUST) -

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

Additionally, I am using libraries for the BME280 module -

CODE
#include <Wire.h>
#include"SparkFunBME280.h"

Next, we need to read the data from the sensors and store it in a variable, when the client(user) opens the Page on Browser -

CODE
void handle_OnConnect() {
    temperature = bme.readTempC();
    humidity = bme.readFloatHumidity();
    pressure = bme.readFloatPressure() / 100.0F;
    altitude = bme.readFloatAltitudeFeet();
    server.send(200, "text/html", SendHTML(temperature,humidity,pressure,altitude));
    Serial.print(temperature);
    Serial.print(humidity);
    Serial.print(pressure);
    Serial.println(altitude);
}

Now that we have the data, we can push the float variables to the HTML page to view it -

CODE
String SendHTML(float temperature,float humidity,float pressure,float altitude){
    String ptr = "<!DOCTYPE html> <html>\n";
    ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
    ptr +="<title>ESP32 Weather Station</title>\n";
    ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
    ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
    ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
    ptr +="</style>\n";
    ptr +="</head>\n";
    ptr +="<body>\n";
    ptr +="<div id=\"webpage\">\n";
    ptr +="<h1>ESP32 Weather Station</h1>\n";
    ptr +="<p>Temperature: ";
    ptr +=temperature;
    ptr +="&deg;C</p>";
    ptr +="<p>Humidity: ";
    ptr +=humidity;
    ptr +="%</p>";
    ptr +="<p>Pressure: ";
    ptr +=pressure;
    ptr +="hPa</p>";
    ptr +="<p>Altitude: ";
    ptr +=altitude;
    ptr +="m</p>";
    ptr +="</div>\n";
    ptr +="</body>\n";
    ptr +="</html>\n";
    return ptr;
}

Now, let us go through the FINAL CODE

CODE
#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include "SparkFunBME280.h" 

#define SEALEVELPRESSURE_HPA (1013.25)

BME280 bme;

float temperature, humidity, pressure, altitude;

/*Put your SSID & Password*/
const char* ssid = "<wifi_name>";  // Enter SSID here
const char* password = "<password>";  //Enter Password here

WebServer server(80);             
 
void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.println("Connecting to ");
  Serial.println(ssid);

  //connect to your local wi-fi network
  WiFi.begin(ssid, password);

  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  
  Serial.println(WiFi.localIP());

  server.on("/", handle_OnConnect);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("HTTP server started");
Wire.begin();

  if (bme.beginI2C() == false) //Begin communication over I2C
  {
    Serial.println("The sensor did not respond. Please check wiring.");
    while(1); //Freeze
  }
}
void loop() {
  server.handleClient();
}

void handle_OnConnect() {
  temperature = bme.readTempC();
  humidity = bme.readFloatHumidity();
  pressure = bme.readFloatPressure() / 100.0F;
  altitude = bme.readFloatAltitudeFeet();
  server.send(200, "text/html", SendHTML(temperature,humidity,pressure,altitude)); 
  Serial.print(temperature);
  Serial.print(humidity);
  Serial.print(pressure);
  Serial.println(altitude);
}

void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

String SendHTML(float temperature,float humidity,float pressure,float altitude){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>ESP32 Weather Station</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
  ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<div id=\"webpage\">\n";
  ptr +="<h1>ESP32 Weather Station</h1>\n";
  ptr +="<p>Temperature: ";
  ptr +=temperature;
  ptr +="&deg;C</p>";
  ptr +="<p>Humidity: ";
  ptr +=humidity;
  ptr +="%</p>";
  ptr +="<p>Pressure: ";
  ptr +=pressure;
  ptr +="hPa</p>";
  ptr +="<p>Altitude: ";
  ptr +=altitude;
  ptr +="m</p>";
  ptr +="</div>\n";
  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}
projectImage

ESP32 as Access Point (HotSpot)

 

Here, the ESP32 will itself create a Hotspot, with below SSID and Password -

CODE
const char* ssid = "WeatherBoy;       //Set your own name for ESP32
const char* password = "PASSWORD123";  //Set the password of SSID

Below are the network configurations we need to make, to start a network and open the network for connections from other clients. Since the IP and Gateway are the same (Gateway must be x.x.x.1), we are hardcoding the device to be the Host here -

CODE
IPAddress local_ip(192,168,1,1);
IPAddress gateway(192,168,1,1);
IPAddres subnet(255,255,255,0);

In the above configurations, the network components may not be clear, but you can go through CCNA or other Computer Network Configuration articles/courses to learn and understand these components.

CODE
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(local_ip, gateway, subnet);
WiFi.softAP(ssid, password);

In the above section, we have selected the mode of WiFi i.e. AP and therefore using the softAP() function to configure and create a WiFi Hotspot on the ESP32 dev board.

projectImage

And that's it! Type http://192.168.1.1/ in the Browser and view the HTML page -

projectImage

SPIFFS (Serial Peripheral Interface Flash File System)

 

Having a File System within the board is definitely an advantage -

Create files to save small amounts of data instead of using a microSD card;

Save HTML and CSS files to build a web server;

Save images, figures, and icons

In the Flash Memory of 4 MB, around 1MB is reserved for storage by the users. To use it along with Arduino, we need to install the File Uploader Plugin on Arduino IDE.

Then follow the below steps -

Follow the next steps to install the filesystem uploader:

 

1) Download the ZIP file from the Github Repo Releases Page. Reach out to the latest release. (ESP32FS-1.0.zip)

 

2) Go to the Arduino IDE directory, and open the Tools folder.

projectImage

3) Unzip the downloaded .zip folder to the Tools folder. You should have a similar folder structure:

CODE
../Arduino-Directory/tools/ESP32FS/tool/esp32fs.jar
projectImage

4) Now, restart the Arduino IDE. We'll see the uploader plugin visible in the Tools section.

projectImage

Select the Partition Scheme and choose the amount of space you require for your SPIFFS Data.

projectImage

By Default, select 1.5MB SPIFFS (FAT32 is meant for SD Card modules). Now we are READY and SET to upload data to the ESP32.

Next, let us upload sample data to the ESP32 board.

To upload files to the ESP32 filesystem follow the next instructions -

1) Create an Arduino sketch and save it. For demonstration purposes, we can use the below sketch -

CODE
#include "SPIFFS.h"
 
void setup() {
  Serial.begin(115200);
  
  if(!SPIFFS.begin(true)){
    Serial.println("SPIFFS Not Found");
    return;
  }
  
  File file = SPIFFS.open("/test_example.txt");
  if(!file){
    Serial.println("Unable to Open/Read File");
    return;
  }
  
  Serial.println("File Content:");
  while(file.available()){
    Serial.write(file.read());
  }
  file.close();
}
 
void loop() {

}

2) Then, open the sketch folder. You can go to Sketch > Show Sketch Folder. The folder where your sketch is saved should open.

projectImage

3) Inside that folder, create a new folder called data.

 

projectImage

4) Inside the data folder is where you should put the files you want to be saved into the ESP32 filesystem. As an example, create a .txt file with some text called sample.txt.

projectImage

5)Then, to upload the files, in the Arduino IDE, you just need to go to Tools > ESP32 Sketch Data Upload

projectImage

After the Data is uploaded, the below response will be visible in the console (verbose).

projectImage

Note: in some ESP32 development boards you need to keep the ESP32 on-board “BOOT” button pressed while it’s uploading the files. When you see the “Connecting …….____……” message, you need to press the ESP32 on-board “BOOT” button.

Now, let’s just check if the file was actually saved into the ESP32 filesystem -

Upload the Code to the ESP32 this time, and view the Serial Monitor

projectImage

HTML, CSS and JS using SPIFFS

 

In this project, we will host a server on the ESP32 dev board, which will display Temperature, Humidity, Altitude, and Pressure from the BME280 sensor module. It is the same as the LIVE Display of Data on ESP32but this time the file for the Web Page can be easily scripted in HTML, CSS, and JS for all the functionalities.

Here, we shall upload the data (HTML, CSS, images - files) to the ESP32 board using SPIFFS first.

projectImage

The data folder components are present in the Code Section

Next, use the below code to extract SPIFFS data and use it on the WebServer as the client requests for information, and ESP32 (Server) responses with data from the file system (Database).

CODE
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
#include <Wire.h>
#include "SparkFunBME280.h"

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

#define SEALEVELPRESSURE_HPA (1013.25)

BME280 bme;
String temperature, humid, pressr, alt ;

// Create AsyncWebServer object on port 80
AsyncWebServer server (80);

// Replaces placeholder with value
String processor(const String& var){
  Serial.print(var);
  if(var == "humid"){
    humid = bme.readFloatHumidity();
    Serial.println(humid);
    return humid;
  }
  else if(var == "temp"){
    temperature = bme.readTempC();
    Serial.println(temperature);
    return temperature;
  }
  else if(var == "pressr"){
    pressr = bme.readFloatPressure()/100;
    Serial.println(pressr);
    return pressr;
  }
  else if(var == "alt"){
    alt = bme.readFloatAltitudeFeet();
    Serial.println(alt);
    return alt;
  }
  return String();
}
 
void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);
  //pinMode(ledPin, OUTPUT);

  // Initialize SPIFFS
  if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP32 Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  
  // Route to load style.css file
  server.on("/main.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/main.css", "text/css");
  });

  server.on("/bgimage.jpg", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/bgimage.jpg", "image/jpg");
  });

  // Start server
  server.begin();

  Wire.begin();

  if (bme.beginI2C() == false) //Begin communication over I2C
  {
    Serial.println("The sensor did not respond. Please check wiring.");
    while(1); //Freeze
  }
}
 
void loop(){
  
}

What we have over here, is a FULL-FLEDGED Web System (Local) - Without any OS running the whole thing. Even though it is small scale and meant for only single runtime data, we at least have an understanding of the whole system.

projectImage

Custom Domains for WebServers

In this project, we will assign a Domain name to the IP Address and access the WebServer on ESP32 not using IP Address, but with a name directly. So, it won't matter what the IP Address is, it can always be ACCESSED with the Domain Name,

We will use the mDNS library for this, and assign a name to the IP Address.

CODE
#include <ESPmDNS.h>

Next, let us create a 'host' variable which will be the hostname to access the IP Address -

CODE
const char* host = "weatherboy";

Inside the void setup() function, just insert the below block of code -

CODE
if (!MDNS.begin(host)) { //http://<hostname>.local
    Serial.println("Error setting up MDNS responder!");
    while (1) {
    delay(1000);
    }
}
Serial.println("mDNS responder started"
projectImage

As you can see above, I was able to access my site using http://weatherboy.local and this opens endless possibilities for the projects we can make on WebServers.

NGROK to host on Cloud

In this project, we will host the page we created from the server on ESP32 to the cloud. The IP Address is tunneled and the port is made available on the hosting server of NGROK.

1)First, sign up and log in to ngrok.com -

projectImage

2) Once you are Logged IN to the dashboard, download and unzip the file -

projectImage

3) Now, run the ngrok.exe [http://ngrok.exe/] file. Go to Dashboard and copy the command with Auth Token. Use the command on the terminal that opened on opening the ngrok application.

projectImage

This will save the auth token to the configurations ngrok.yml [http://ngrok.yml/] file for future uses.

4) The very last thing, is to run the tunnel service from this terminal -

projectImage

We can also use the IP Address or the hostname here. But to make sure we also need to mention the port we used to create the webserver.> ngrok http <host/IP-Address>:port number

There we go, this will tunnel the IP Address and make it publicly available - for Free (temporary features)

projectImage

That's it we have the service running on the URL Address on the 'Forwarding' key.

projectImage

Do not worry about the inability to copy the URL Link, go to the Dashboard > Endpoints and both the HTTP and HTTPS links will be available and clickable there.

 

THERE WE GOOOO !! Control/Monitor your ESP32 through the Internet, from any corner of the world. Now you have a RealTime WebServer that is physically present with you, and you'd be able to access the website on the Device from the NGROK's link.

We are at the END of the Documentation on WebServers on ESP32. I have shared ALL the possibilities I am aware of, related to the WebServer. There are 2 separate ways to build a Web Server Page though, one of them runs/works inside the void loop() function instead of setup() as an endpoint. (Similar to Backend technologies)

License
All Rights
Reserved
licensBg
0