Arduino - Web Server Multiple Pages
In this tutorial, we'll find out how to turn an Arduino into a web server that can handle multiple pages at the same time, such as index.html, temperature.html, led.html, error_404.html, and error_405.html... Each page's content, including HTML, CSS, and JavaScript, will be stored in different files on the Arduino IDE. By accessing the Arduino Web Server from a web browser on your PC or smartphone, you will be able to view and control sensors and actuators connected to the Arduino through the web. Additionally, the web server will be designed to accept links with or without the .html extension.
By following this tutorial, you will be able to turn your Arduino into a web server with some cool features:
Multiple web pages are active simultaneously.
The HTML content (including HTML, CSS, and Javascript) for each page is kept separately in its own file.
The HTML content can be dynamically updated with real-time values from sensors, making the web pages dynamic and responsive.
You can access the pages with or without the .html extension. For instance, you can use links like http://192.168.0.2/led or http://192.168.0.2/led.html to reach the same LED control page.
The web server handles HTTP error codes such as 404 Not Found and 405 Method Not Allowed
It might sound complicated, but don't worry! This tutorial provides step-by-step guidance, and the code is designed to be beginner-friendly, ensuring that you can easily comprehend and create your own Arduino web server.
Or you can buy the following sensor kits:
Please note: These are Amazon affiliate links. If you buy the components through these links, We will get a commission at no extra cost to you. We appreciate it.
If you're not familiar with Arduino Uno R4 and Web Server (including pinout, how it works, and programming), you can learn about them through the following tutorials:
When a web browser sends an HTTP request to the Arduino board, the Arduino needs to be programmed to perform the following tasks:
Create a web server that can listen for HTTP requests from the web browser.
Read the first line of the HTTP request header when it receives an HTTP request.
Route the requests based on the first line of the HTTP request to determine which web pages the Arduino should return.
(Optionally) Analyze the HTTP request header to identify control commands sent by the user.
(Optionally) Control the devices connected to the Arduino based on the received control commands.
Send an HTTP response back to the web browser, which includes:
By performing these tasks, the Arduino can effectively handle HTTP requests and provide appropriate responses to the web browser, allowing for web-based control and interaction with the Arduino's connected devices.
The routing function is the most important task, and it will be explained in detail. Other parts are covered in the Arduino - Web Server tutorial. Once you understand the routing algorithm, we will proceed to see the complete code for a web server with multiple pages.
Before writing code for the routing function, you should create a list of web pages and corresponding HTTP methods that will be available on Arduino. In this tutorial, we will only support the GET method. However, you can easily extend it to include other HTTP methods if needed. Here is an example list:
GET home page
GET temperature page
GET door page
GET led page
After that, you need to create a list of the first-line HTTP request headers corresponding to the page list:
GET home page:
GET temperature page
GET door page
GET led page
In summary, we have the following list:
GET /
GET /index.html
GET /temperature.html
GET /door.html
GET /led.html
The following code demonstrates how to implement the routing function for a web server on Arduino.
if (HTTP_req.indexOf("GET") == 0) {
if (HTTP_req.indexOf("GET / ") > -1 || HTTP_req.indexOf("GET /index.html ") > -1) {
Serial.println("home page");
} else if (HTTP_req.indexOf("GET /temperature.html ") > -1) {
Serial.println("temperature page");
} else if (HTTP_req.indexOf("GET /door.html ") > -1) {
Serial.println("door page");
} else if (HTTP_req.indexOf("GET /led.html ") > -1) {
Serial.println("led page");
} else {
Serial.println("404 Not Found");
}
} else {
Serial.println("405 Method Not Allowed");
}
Feel free to modify the code to add or remove pages as needed. Now, let's update the routing function to handle links with or without the .html extension.
if (HTTP_req.indexOf("GET") == 0) {
if (HTTP_req.indexOf("GET / ") > -1 || HTTP_req.indexOf("GET /index ") > -1 || HTTP_req.indexOf("GET /index.html ") > -1) {
Serial.println("home page");
} else if (HTTP_req.indexOf("GET /temperature ") > -1 || HTTP_req.indexOf("GET /temperature.html ") > -1) {
Serial.println("temperature page");
} else if (HTTP_req.indexOf("GET /door ") > -1 || HTTP_req.indexOf("GET /door.html ") > -1) {
Serial.println("door page");
} else if (HTTP_req.indexOf("GET /led ") > -1 || HTTP_req.indexOf("GET /led.html ") > -1) {
Serial.println("led page");
} else {
Serial.println("404 Not Found");
}
} else {
Serial.println("405 Method Not Allowed");
}
Below is the complete Arduino code that creates a web server with multiple pages. To keep it simple, the HTML content for each page is very simple and embedded directly in the Arduino code. In the next part, we will learn how to separate the HTML contents for each page into separate files, making the code more organized and manageable.
#include <WiFiS3.h>
#define PAGE_HOME 0
#define PAGE_TEMPERATURE 1
#define PAGE_DOOR 2
#define PAGE_LED 3
#define PAGE_ERROR_404 -1
#define PAGE_ERROR_405 -2
const char ssid[] = "YOUR_WIFI";
const char pass[] = "YOUR_WIFI_PASSWORD";
int status = WL_IDLE_STATUS;
WiFiServer server(80);
void setup() {
Serial.begin(9600);
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
Serial.println("Please upgrade the firmware");
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
delay(10000);
}
server.begin();
printWifiStatus();
}
void loop() {
WiFiClient client = server.available();
if (client) {
String HTTP_req = "";
while (client.connected()) {
if (client.available()) {
Serial.println("New HTTP Request");
HTTP_req = client.readStringUntil('\n');
Serial.print("<< ");
Serial.println(HTTP_req);
break;
}
}
while (client.connected()) {
if (client.available()) {
String HTTP_header = client.readStringUntil('\n');
if (HTTP_header.equals("\r"))
break;
}
}
int page_id = 0;
if (HTTP_req.indexOf("GET") == 0) {
if (HTTP_req.indexOf("GET / ") > -1 || HTTP_req.indexOf("GET /index ") > -1 || HTTP_req.indexOf("GET /index.html ") > -1) {
Serial.println("home page");
page_id = PAGE_HOME;
} else if (HTTP_req.indexOf("GET /temperature ") > -1 || HTTP_req.indexOf("GET /temperature.html ") > -1) {
Serial.println("temperature page");
page_id = PAGE_TEMPERATURE;
} else if (HTTP_req.indexOf("GET /door ") > -1 || HTTP_req.indexOf("GET /door.html ") > -1) {
Serial.println("door page");
page_id = PAGE_DOOR;
} else if (HTTP_req.indexOf("GET /led ") > -1 || HTTP_req.indexOf("GET /led.html ") > -1) {
Serial.println("led page");
page_id = PAGE_LED;
} else {
Serial.println("404 Not Found");
page_id = PAGE_ERROR_404;
}
} else {
Serial.println("405 Method Not Allowed");
page_id = PAGE_ERROR_405;
}
if (page_id == PAGE_ERROR_404)
client.println("HTTP/1.1 404 Not Found");
if (page_id == PAGE_ERROR_405)
client.println("HTTP/1.1 405 Method Not Allowed");
else
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("<head>");
client.println("<link rel=\"icon\" href=\"data:,\">");
client.println("</head>");
String html;
switch (page_id) {
case PAGE_HOME:
client.println("This is home page");
break;
case PAGE_TEMPERATURE:
client.println("This is temperature page");
break;
case PAGE_DOOR:
client.println("This is door page");
break;
case PAGE_LED:
client.println("This is LED page");
break;
case PAGE_ERROR_404:
client.println("Page Not Found");
break;
case PAGE_ERROR_405:
client.println("Method Not Allowed");
break;
}
client.println("</html>");
client.flush();
client.flush();
delay(10);
client.stop();
}
}
void printWifiStatus() {
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("signal strength (RSSI):");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
}
Copy the above code and open with Arduino IDE
Change the wifi information (SSID and password) in the code to yours
Click Upload button on Arduino IDE to upload code to Arduino
Open the Serial Monitor
Check out the result on Serial Monitor.
Attempting to connect to SSID: YOUR_WIFI
IP Address: 192.168.0.2
signal strength (RSSI):-39 dBm
You will see an IP address on the Serial Monitor, for example: 192.168.0.2
Type the following list one-by-one on the address bar of a web browser on your smartphone or PC.
192.168.0.2
192.168.0.2/index
192.168.0.2/index.html
192.168.0.2/led
192.168.0.2/led.html
192.168.0.2/door
192.168.0.2/door.html
192.168.0.2/temperature
192.168.0.2/temperature.html
192.168.0.2/blabla
192.168.0.2/blabla.html
Please note that you need to change the 192.168.0.2 to the IP address you got on Serial Monitor.
You will see the following pages: home page, led page, door page, temperature page, and Not Found page
You can also check the output on Serial Monitor
The previous code has very simple HTML content for each page. But if we want to make a fancy interface with lots of HTML, the code can get big and messy. To make it simpler, we will learn how to separate the HTML from the Arduino code. This lets us keep the HTML in separate files, making it easier to manage and work with.
Open the Arduino IDE.
Create a new sketch and give it a name, for example, ArduinoWebServer.ino.
Copy the provided code and paste it into that file.
#include <WiFiS3.h>
#include "index.h"
#include "temperature.h"
#include "door.h"
#include "led.h"
#include "error_404.h"
#include "error_405.h"
#define PAGE_HOME 0
#define PAGE_TEMPERATURE 1
#define PAGE_DOOR 2
#define PAGE_LED 3
#define PAGE_ERROR_404 -1
#define PAGE_ERROR_405 -2
const char ssid[] = "YOUR_WIFI";
const char pass[] = "YOUR_WIFI_PASSWORD";
int status = WL_IDLE_STATUS;
WiFiServer server(80);
float getTemperature() {
float temp_x100 = random(0, 10000);
return temp_x100 / 100;
}
void setup() {
Serial.begin(9600);
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
Serial.println("Please upgrade the firmware");
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
delay(10000);
}
server.begin();
printWifiStatus();
}
void loop() {
WiFiClient client = server.available();
if (client) {
String HTTP_req = "";
while (client.connected()) {
if (client.available()) {
Serial.println("New HTTP Request");
HTTP_req = client.readStringUntil('\n');
Serial.print("<< ");
Serial.println(HTTP_req);
break;
}
}
while (client.connected()) {
if (client.available()) {
String HTTP_header = client.readStringUntil('\n');
if (HTTP_header.equals("\r"))
break;
}
}
int page_id = 0;
if (HTTP_req.indexOf("GET") == 0) {
if (HTTP_req.indexOf("GET / ") > -1 || HTTP_req.indexOf("GET /index ") > -1 || HTTP_req.indexOf("GET /index.html ") > -1) {
Serial.println("home page");
page_id = PAGE_HOME;
} else if (HTTP_req.indexOf("GET /temperature ") > -1 || HTTP_req.indexOf("GET /temperature.html ") > -1) {
Serial.println("temperature page");
page_id = PAGE_TEMPERATURE;
} else if (HTTP_req.indexOf("GET /door ") > -1 || HTTP_req.indexOf("GET /door.html ") > -1) {
Serial.println("door page");
page_id = PAGE_DOOR;
} else if (HTTP_req.indexOf("GET /led ") > -1 || HTTP_req.indexOf("GET /led.html ") > -1) {
Serial.println("led page");
page_id = PAGE_LED;
} else {
Serial.println("404 Not Found");
page_id = PAGE_ERROR_404;
}
} else {
Serial.println("405 Method Not Allowed");
page_id = PAGE_ERROR_405;
}
if (page_id == PAGE_ERROR_404)
client.println("HTTP/1.1 404 Not Found");
if (page_id == PAGE_ERROR_405)
client.println("HTTP/1.1 405 Method Not Allowed");
else
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
String html;
switch (page_id) {
case PAGE_HOME:
html = String(HTML_CONTENT_HOME);
break;
case PAGE_TEMPERATURE:
html = String(HTML_CONTENT_TEMPERATURE);
html.replace("TEMPERATURE_MARKER", String(getTemperature(), 2));
break;
case PAGE_DOOR:
html = String(HTML_CONTENT_DOOR);
html.replace("DOOR_STATE_MARKER", "OPENED");
break;
case PAGE_LED:
html = String(HTML_CONTENT_LED);
html.replace("LED_STATE_MARKER", "OFF");
break;
case PAGE_ERROR_404:
html = String(HTML_CONTENT_404);
break;
case PAGE_ERROR_405:
html = String(HTML_CONTENT_405);
break;
}
client.println(html);
client.flush();
delay(10);
client.stop();
}
}
void printWifiStatus() {
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("signal strength (RSSI):");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
}
Change the WiFi information (SSID and password) in the code to yours
Create the index.h file On Arduino IDE by:
const char *HTML_CONTENT_HOME = R""""(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="data:,">
<title>Home Page</title>
</head>
<body>
<h1>Welcome to the Home Page</h1>
<ul>
<li><a href="/led">LED Page</a></li>
<li><a href="/temperature">Temperature Page</a></li>
<li><a href="/door">Door Page</a></li>
</ul>
</body>
</html>
)"""";
const char *HTML_CONTENT_TEMPERATURE = R""""(
<!DOCTYPE html>
<html>
<head>
<title>Arduino - Web Temperature</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=0.7">
<meta charset="utf-8">
<link rel="icon" href="https://diyables.io/images/page/diyables.svg">
<style>
body { font-family: "Georgia"; text-align: center; font-size: width/2pt;}
h1 { font-weight: bold; font-size: width/2pt;}
h2 { font-weight: bold; font-size: width/2pt;}
button { font-weight: bold; font-size: width/2pt;}
</style>
<script>
var cvs_width = 200, cvs_height = 450;
function init() {
var canvas = document.getElementById("cvs");
canvas.width = cvs_width;
canvas.height = cvs_height + 50;
var ctx = canvas.getContext("2d");
ctx.translate(cvs_width/2, cvs_height - 80);
update_view(TEMPERATURE_MARKER);
}
function update_view(temp) {
var canvas = document.getElementById("cvs");
var ctx = canvas.getContext("2d");
var radius = 70;
var offset = 5;
var width = 45;
var height = 330;
ctx.clearRect(-cvs_width/2, -350, cvs_width, cvs_height);
ctx.strokeStyle="blue";
ctx.fillStyle="blue";
var x = -width/2;
ctx.lineWidth=2;
for (var i = 0; i <= 100; i+=5) {
var y = -(height - radius)*i/100 - radius - 5;
ctx.beginPath();
ctx.lineTo(x, y);
ctx.lineTo(x - 20, y);
ctx.stroke();
}
ctx.lineWidth=5;
for (var i = 0; i <= 100; i+=20) {
var y = -(height - radius)*i/100 - radius - 5;
ctx.beginPath();
ctx.lineTo(x, y);
ctx.lineTo(x - 25, y);
ctx.stroke();
ctx.font="20px Georgia";
ctx.textBaseline="middle";
ctx.textAlign="right";
ctx.fillText(i.toString(), x - 35, y);
}
ctx.lineWidth=16;
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.rect(-width/2, -height, width, height);
ctx.stroke();
ctx.beginPath();
ctx.arc(0, -height, width/2, 0, 2 * Math.PI);
ctx.stroke();
ctx.fillStyle="#e6e6ff";
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.rect(-width/2, -height, width, height);
ctx.fill();
ctx.beginPath();
ctx.arc(0, -height, width/2, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle="#ff1a1a";
ctx.beginPath();
ctx.arc(0, 0, radius - offset, 0, 2 * Math.PI);
ctx.fill();
temp = Math.round(temp * 100) / 100;
var y = (height - radius)*temp/100.0 + radius + 5;
ctx.beginPath();
ctx.rect(-width/2 + offset, -y, width - 2*offset, y);
ctx.fill();
ctx.fillStyle="red";
ctx.font="bold 34px Georgia";
ctx.textBaseline="middle";
ctx.textAlign="center";
ctx.fillText(temp.toString() + "°C", 0, 100);
}
window.onload = init;
</script>
</head>
<body>
<h1>Arduino - Web Temperature</h1>
<canvas id="cvs"></canvas>
</body>
</html>
)"""";
const char *HTML_CONTENT_DOOR = R""""(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="data:,">
<title>Door Page</title>
</head>
<body>
<h1>Door Page</h1>
<p>Door State: <span style="color: red;">DOOR_STATE_MARKER</span></p>
</body>
</html>
)"""";
const char *HTML_CONTENT_LED = R""""(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="data:,">
<title>LED Page</title>
</head>
<body>
<h1>LED Page</h1>
<p>LED State: <span style="color: red;">LED_STATE_MARKER</span></p>
</body>
</html>
)"""";
const char *HTML_CONTENT_404 = R""""(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="data:,">
<title>404 - Page Not Found</title>
<style>
h1 {color: #ff4040;}
</style>
</head>
<body>
<h1>404</h1>
<p>Oops! The page you are looking for could not be found on Arduino Web Server.</p>
<p>Please check the URL or go back to the <a href="/">homepage</a>.</p>
<p>Or check <a href="https://arduinogetstarted.com/tutorials/arduino-web-server-multiple-pages"> Arduino Web Server</a> tutorial.</p>
</body>
</html>
)"""";
const char *HTML_CONTENT_405 = R""""(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="data:,">
<title>405 - Method Not Allowed</title>
<style>
h1 {color: #ff4040;}
</style>
</head>
<body>
<h1>405 - Method Not Allowed</h1>
<p>Oops! The requested method is not allowed for this resource.</p>
<p>Please check your request or go back to the <a href="/">homepage</a>.</p>
<p>Or check <a href="https://arduinogetstarted.com/tutorials/arduino-web-server-multiple-pages"> Arduino Web Server</a> tutorial.</p>
</body>
</html>
)"""";
※ NOTE THAT:
If you make changes to the HTML content within the index.h file but don't modify anything in the ArduinoWebServer.ino file, the Arduino IDE won't refresh or update the HTML content when you compile and upload the code to the ESP32.
To force the Arduino IDE to update the HTML content in this situation, you need to make a modification in the ArduinoWebServer.ino file. For example, you can add an empty line or insert a comment. This action triggers the IDE to recognize that there have been changes in the project, ensuring that your updated HTML content gets included in the upload.
※ OUR MESSAGES
You can share the link of this tutorial anywhere. Howerver, please do not copy the content to share on other websites. We took a lot of time and effort to create the content of this tutorial, please respect our work!