Arduino - Serveur Web Multisites
Dans ce tutoriel, nous découvrirons comment transformer un Arduino en serveur web capable de gérer plusieurs pages simultanément, telles que index.html, temperature.html, led.html, error_404.html et error_405.html... Le contenu de chaque page, incluant HTML, CSS et JavaScript, sera stocké dans différents fichiers sur l'IDE Arduino. En accédant au serveur web Arduino depuis un navigateur web sur votre PC ou smartphone, vous pourrez voir et contrôler les capteurs et actionneurs connectés à l'Arduino via le web. De plus, le serveur web sera conçu pour accepter les liens avec ou sans l'extension .html.
En suivant ce tutoriel, vous pourrez transformer votre Arduino en un serveur web avec quelques fonctionnalités intéressantes :
Plusieurs pages web sont actives simultanément.
Le contenu HTML (incluant HTML, CSS et Javascript) pour chaque page est conservé séparément dans son propre fichier.
Le contenu HTML peut être mis à jour dynamiquement avec des valeurs en temps réel provenant de capteurs, rendant les pages web dynamiques et réactives.
Vous pouvez accéder aux pages avec ou sans l'extension .html. Par exemple, vous pouvez utiliser des liens comme http://192.168.0.2/led ou http://192.168.0.2/led.html pour atteindre la même page de contrôle des LED.
Le serveur web gère les codes d'erreur HTTP tels que 404 Non Trouvé et 405 Méthode Non Autorisée
Cela peut sembler compliqué, mais ne vous inquiétez pas ! Ce tutoriel offre des directives étape par étape, et le code est conçu pour être accessible aux débutants, vous assurant ainsi de pouvoir facilement comprendre et créer votre propre serveur web Arduino.
Or you can buy the following sensor kits:
Divulgation : Certains des liens fournis dans cette section sont des liens affiliés Amazon. Nous pouvons recevoir une commission pour tout achat effectué via ces liens, sans coût supplémentaire pour vous. Nous vous remercions de votre soutien.
Si vous n'êtes pas familier avec l'Arduino Uno R4 et le serveur Web (y compris le schéma des broches, son fonctionnement et sa programmation), vous pouvez vous renseigner à leur sujet grâce aux tutoriels suivants :
Avant de rédiger le code pour la fonction de routage, vous devriez créer une liste des pages web et des méthodes HTTP correspondantes qui seront disponibles sur Arduino. Dans ce tutoriel, nous ne prendrons en charge que la méthode GET. Cependant, vous pouvez facilement l'étendre pour inclure d'autres méthodes HTTP si nécessaire. Voici un exemple de liste :
Ensuite, vous devez créer une liste des en-têtes de requête HTTP de première ligne correspondant à la liste des pages :
Obtenir la page d'accueil :
Obtenir la page de température
Obtenir la page de la porte
Obtenir la page des LED
En résumé, nous avons la liste suivante :
GET /
GET /index.html
GET /temperature.html
GET /door.html
GET /led.html
Le code suivant montre comment implémenter la fonction de routage pour un serveur web sur 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");
}
N'hésitez pas à modifier le code pour ajouter ou supprimer des pages selon le besoin. Maintenant, actualisons la fonction de routage pour gérer les liens avec ou sans l'extension .html.
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");
}
Voici le code Arduino complet qui crée un serveur web avec plusieurs pages. Pour simplifier, le contenu HTML de chaque page est très simple et intégré directement dans le code Arduino. Dans la partie suivante, nous apprendrons comment séparer le contenu HTML de chaque page dans des fichiers distincts, rendant le code plus organisé et plus facile à gérer.
#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");
}
Copiez le code ci-dessus et ouvrez-le avec Arduino IDE
Modifiez les informations wifi (SSID et mot de passe) dans le code pour les vôtres
Cliquez sur le bouton Upload sur Arduino IDE pour téléverser le code sur Arduino
Ouvrez le moniteur série
Consultez le résultat sur le moniteur série.
Attempting to connect to SSID: YOUR_WIFI
IP Address: 192.168.0.2
signal strength (RSSI):-39 dBm
Vous verrez une adresse IP sur le moniteur série, par exemple : 192.168.0.2
Tapez les éléments suivants un par un dans la barre d'adresse d'un navigateur web sur votre smartphone ou 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
Veuillez noter que vous devez changer le 192.168.0.2 par l'adresse IP que vous avez obtenue sur le moniteur série.
Vous verrez les pages suivantes : page d'accueil, page des LED, page de la porte, page de la température, et page Non Trouvée.
Vous pouvez également vérifier la sortie sur le moniteur série.
Le code précédent possède un contenu HTML très simple pour chaque page. Mais si nous souhaitons créer une interface élégante avec beaucoup de HTML, le code peut devenir volumineux et désordonné. Pour simplifier, nous allons apprendre à séparer le HTML du code Arduino. Cela nous permet de conserver le HTML dans des fichiers séparés, rendant ainsi la gestion et le travail plus aisés.
Ouvrez l'IDE Arduino.
Créez un nouveau sketch et donnez-lui un nom, par exemple, ArduinoWebServer.ino.
Copiez le code fourni et collez-le dans ce fichier.
#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");
}
Modifiez les informations WiFi (SSID et mot de passe) dans le code par les vôtres
Créez le fichier index.h sur l'Arduino IDE en :
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>
)"""";
Cliquez sur le bouton Upload dans l'IDE Arduino pour téléverser le code sur Arduino
Accédez aux pages web de la carte Arduino via un navigateur web une par une comme auparavant. Vous verrez toutes les pages web comme ci-dessous :
※ NOTE THAT:
Si vous apportez des modifications au contenu HTML dans le fichier index.h mais que vous ne modifiez rien dans le fichier ArduinoWebServer.ino, l'IDE Arduino ne rafraîchira ni ne mettra à jour le contenu HTML lorsque vous compilerez et téléverserez le code sur l'ESP32.
Pour forcer l'IDE Arduino à mettre à jour le contenu HTML dans cette situation, vous devez effectuer une modification dans le fichier ArduinoWebServer.ino. Par exemple, vous pouvez ajouter une ligne vide ou insérer un commentaire. Cette action déclenche la reconnaissance par l'IDE qu'il y a eu des changements dans le projet, garantissant que votre contenu HTML mis à jour est inclus dans le téléversement.