Dans ce projet, nous allons construire un serveur Web avec l’ESP32 qui vous permet de contrôler une LED en planifiant une minuterie avec une action ON/OFF pour n’importe quelle durée (secondes, minutes ou heures). La page Web comporte également deux boutons que vous pouvez utiliser pour allumer ou éteindre la LED immédiatement. L’ESP32 sera programmé à l’aide de l’IDE Arduino.

Vous aimerez peut-être lire : Construire un serveur Web ESP32 : le guide complet pour les débutants (Arduino IDE).
Aperçu du projet
L’image suivante montre la page Web que vous allez créer pour ce projet.

Voici les principales fonctionnalités de ce serveur Web :
- Étiquette de texte de l’état actuel de la LED : « GPIO est activé » / « GPIO est désactivé »
- Contrôle instantané des LED : boutons allumer/éteindre
- Formulaire HTML Définir la minuterie : vous pouvez définir l’action (allumer/éteindre), l’unité de temps et la durée de la minuterie.
- État de la minuterie et temps restant pour effectuer l’action sélectionnée
- Bouton Annuler la minuterie
Pour plus de simplicité, nous enregistrerons le HTML et le CSS pour créer la page Web dans une variable du croquis Arduino, mais vous pouvez utiliser le système de fichiers LittleFS.
Conditions préalables
Avant de suivre ce guide, vous devez installer le module complémentaire ESP32 Core dans votre IDE Arduino. Suivez le guide suivant pour l’installer, si ce n’est pas déjà fait :
Vous aurez également besoin d’un modèle de carte de développement ESP32 de votre choix.
Installer les bibliothèques requises dans l’IDE Arduino
Nous allons construire le serveur Web en utilisant les bibliothèques suivantes :
Vous pouvez installer ces bibliothèques dans Arduino Library Manager. Ouvrez le gestionnaire de bibliothèque en cliquant sur l’icône Bibliothèque dans la barre latérale gauche.
Recherchez ESPAsyncWebServer et installez ESPAsyncWebServer par ESP32Async.

Ensuite, installez la bibliothèque AsyncTCP. Recherchez AsyncTCP et installez AsyncTCP par ESP32Async.

Serveur Web ESP32 : Programmation de la minuterie – Arduino Sketch
Le code suivant crée un serveur Web qui dessert une page Web qui vous permet de planifier une minuterie pour allumer/éteindre une LED pendant une durée sélectionnée.
/*********
Rui Santos & Sara Santos - Raspberryme.com
Complete project details at https://Raspberryme.com/esp32-web-server-timer-schedule-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include
#include
#include
#include
#include
// REPLACE WITH YOUR NETWORK CREDENTIALS
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// LED connected to GPIO 5
const int ledPin = 5;
// Global variables
AsyncWebServer server(80);
TimerHandle_t gpioTimer = NULL;
// Timer action: 1 = ON, 0 = OFF
int targetAction = -1;
TickType_t timerStartTick = 0;
uint64_t totalDurationTicks = 0;
// HTML Web Page
const char index_html[] PROGMEM = R"rawliteral(
ESP32 Web Server - Timer Schedule
GPIO is Loading...
TURN ON
TURN OFF
Set Timer
Timer Status: No active timers
CANCEL TIMER
)rawliteral";
// Timer Callback - Turns the LED on or off depending on the action selected
void IRAM_ATTR timerCallback(TimerHandle_t xTimer) {
if (targetAction != -1) {
digitalWrite(ledPin, targetAction ? HIGH : LOW);
Serial.printf("Timer finished: GPIO %d set to %s\n", ledPin, targetAction ? "HIGH (ON)" : "LOW (OFF)");
}
targetAction = -1;
timerStartTick = 0;
totalDurationTicks = 0;
}
void setup() {
Serial.begin(115200);
delay(1000);
// Define ledPin as an OUTPUT and initialize it off (LOW)
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// Create a timer and assign the timerCallback function
gpioTimer = xTimerCreate("GPIO_Timer", pdMS_TO_TICKS(1000), pdFALSE, 0, timerCallback);
// Start the Wi-Fi connection
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi Connected!");
// Print the ESP32 IP Address
Serial.print("Access ESP32 IP Address: http://");
Serial.println(WiFi.localIP());
// Root URL handler
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/html", index_html);
});
// Instant LED control
server.on("/control", HTTP_GET, [](AsyncWebServerRequest *request) {
if (request->hasParam("state")) {
int state = request->getParam("state")->value().toInt();
digitalWrite(ledPin, state ? HIGH : LOW);
Serial.printf("LED set to %s\n", state ? "ON" : "OFF");
}
request->send(200, "text/plain", "OK");
});
// Get current LED state
server.on("/led-state", HTTP_GET, [](AsyncWebServerRequest *request) {
int state = digitalRead(ledPin);
request->send(200, "application/json", "{\"state\":" + String(state) + "}");
});
// Set the timer duration to perform the action selected
server.on("/set-timer", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("action", true) && request->hasParam("duration", true) && request->hasParam("unit", true)) {
targetAction = request->getParam("action", true)->value().toInt();
int dur = request->getParam("duration", true)->value().toInt();
String unit = request->getParam("unit", true)->value();
uint64_t durationSeconds = 0;
if (unit == "s") durationSeconds = (uint64_t)dur;
else if (unit == "m") durationSeconds = (uint64_t)dur * 60ULL;
else if (unit == "h") durationSeconds = (uint64_t)dur * 3600ULL;
if (durationSeconds > 0) {
if (xTimerIsTimerActive(gpioTimer)) xTimerStop(gpioTimer, 0);
uint64_t tempTicks = durationSeconds * (uint64_t)configTICK_RATE_HZ;
TickType_t timerTicks = (tempTicks > 0xFFFFFFFFULL) ? 0xFFFFFFFFULL : (TickType_t)tempTicks;
timerStartTick = xTaskGetTickCount();
totalDurationTicks = tempTicks;
xTimerChangePeriod(gpioTimer, timerTicks, 0);
xTimerStart(gpioTimer, 0);
Serial.printf("Timer scheduled: GPIO %d will be set to %s in %llu seconds\n", ledPin,
targetAction ? "ON" : "OFF", durationSeconds);
}
}
request->redirect("/");
});
// Cancel Timer
server.on("/cancel-timer", HTTP_POST, [](AsyncWebServerRequest *request) {
// Stop the active timer
if (xTimerIsTimerActive(gpioTimer)) {
xTimerStop(gpioTimer, 0);
Serial.println("Timer cancelled");
}
targetAction = -1;
timerStartTick = 0;
totalDurationTicks = 0;
request->send(200, "text/plain", "OK");
});
// Timer Status - Returns the remaining time in seconds
server.on("/timer-status", HTTP_GET, [](AsyncWebServerRequest *request) {
if (targetAction != -1 && xTimerIsTimerActive(gpioTimer) && totalDurationTicks > 0) {
// Calculate how many seconds are left for the timer to end
TickType_t now = xTaskGetTickCount();
uint64_t elapsedTicks = 0;
if (now >= timerStartTick) {
elapsedTicks = now - timerStartTick;
} else {
elapsedTicks = (0xFFFFFFFFULL - timerStartTick) + now + 1;
}
uint64_t remainingTicks = (totalDurationTicks > elapsedTicks) ? totalDurationTicks - elapsedTicks : 0;
uint64_t remainingSeconds = remainingTicks / (uint64_t)configTICK_RATE_HZ;
// Return the amount of seconds left and action that will execute when the timer ends
String json = "{\"status\":\"Setting GPIO "+ String(ledPin) +" to " +
String(targetAction ? "ON" : "OFF") + "\",";
json += "\"active\":true,";
json += "\"remaining_seconds\":" + String((unsigned long)remainingSeconds) + "}";
request->send(200, "application/json", json);
} else {
request->send(200, "application/json", "{\"status\":\"No active timers\",\"active\":false,\"remaining_seconds\":0}");
}
});
server.begin();
Serial.println("ESP32 Web Server Timer Schedule is Ready!");
}
void loop() {
vTaskDelay(pdMS_TO_TICKS(100));
}
Afficher le code brut
Il vous suffit d’insérer vos informations d’identification réseau dans le code et cela fonctionnera immédiatement.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_SSID";
Comment fonctionne le code
Continuez à lire pour savoir comment fonctionne le code ou passez à la section Démonstration.
Y compris les bibliothèques
Tout d’abord, incluez les bibliothèques requises pour vous connecter au Wi-Fi, créer le serveur Web et utiliser les minuteries FreeRTOS.
#include
#include
#include
#include
#include
Informations d’identification réseau
Insérez vos informations d’identification réseau dans les variables suivantes afin que l’ESP32 se connecte à votre réseau.
// REPLACE WITH YOUR NETWORK CREDENTIALS
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_SSID";
Broche LED
Définissez le GPIO qui sera utilisé pour contrôler la LED. Vous pouvez modifier la variable ledPin pour contrôler n’importe quel autre GPIO.
const int ledPin = 5;
Création d’un objet serveur
Créez un objet AsyncWebServer appelé serveur sur le port 80.
AsyncWebServer server(80);
Minuteries
Créez des variables auxiliaires pour exécuter les minuteries.
TimerHandle_t gpioTimer = NULL;
// Timer action: 1 = ON, 0 = OFF
int targetAction = -1;
TickType_t timerStartTick = 0;
uint64_t totalDurationTicks = 0;
En savoir plus sur les minuteries FreeRTOS : ESP32 avec FreeRTOS : minuteries logicielles/interruptions de minuterie (Arduino IDE).
Page web
La variable index_html contient le texte avec le HTML, le CSS et le JavaScript pour créer la page Web.
const char index_html[] PROGMEM = R"rawliteral(
ESP32 Web Server - Timer Schedule
GPIO is Loading...
TURN ON
TURN OFF
Set Timer
Timer Status: No active timers
CANCEL TIMER
)rawliteral";
Dans la page Web, nous créons deux boutons qui effectuent des requêtes sur les chemins d’URL suivants pour contrôler l’allumage/extinction de la LED :
- ALLUMER : /control?state=1
- DÉSACTIVER : /control?state=0
TURN ON
TURN OFF
L’autre section principale de la page Web est le formulaire Web HTML dans lequel vous pouvez sélectionner l’action, l’unité et la durée du minuteur. Il affiche également l’état actuel de la minuterie, le temps restant et un bouton pour annuler la minuterie.
Set Timer
Timer Status: No active timers
CANCEL TIMER
Fonction de rappel de minuterie
La fonction timerCallback() est le gestionnaire de minuterie unique FreeRTOS qui, lorsqu’il a expiré, active/désactive le GPIO en fonction de l’action précédemment sélectionnée sur le serveur Web.
void IRAM_ATTR timerCallback(TimerHandle_t xTimer) {
if (targetAction != -1) {
digitalWrite(ledPin, targetAction ? HIGH : LOW);
Serial.printf("Timer finished: GPIO %d set to %s\n", ledPin, targetAction ? "HIGH (ON)" : "LOW (OFF)");
}
targetAction = -1;
timerStartTick = 0;
totalDurationTicks = 0;
}
installation()
Dans le setup(), nous commençons par initialiser le Serial Monitor et préparer le GPIO.
Serial.begin(115200);
delay(1000);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
Ensuite, créez le gpioTimer et attribuez la fonction timerCallback décrite précédemment qui s’exécute lorsque le minuteur expire.
gpioTimer = xTimerCreate("GPIO_Timer", pdMS_TO_TICKS(1000), pdFALSE, 0, timerCallback);
Démarrez la connexion Wi-Fi et imprimez l’adresse IP ESP32 dans le moniteur série.
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi Connected!");
// Print the ESP32 IP Address
Serial.print("Access ESP32 IP Address: http://");
Serial.println(WiFi.localIP());
URL root /
Ensuite, gérez le serveur Web. Lorsque vous recevez une requête sur l’URL root (/) (cela se produit lorsque vous accédez à l’adresse IP ESP), envoyez le texte HTML stocké dans PROGMEM pour afficher la page Web :
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/html", index_html);
});
Itinéraire de contrôle LED/contrôle
Cette route GET contrôle la LED ON/OFF en fonction du paramètre d’état (1 = HIGH et 0 = LOW).
server.on("/control", HTTP_GET, [](AsyncWebServerRequest *request) {
if (request->hasParam("state")) {
int state = request->getParam("state")->value().toInt();
digitalWrite(ledPin, state ? HIGH : LOW);
Serial.printf("LED set to %s\n", state ? "ON" : "OFF");
}
request->send(200, "text/plain", "OK");
});
GET LED State Route /led-state
Cette route GET lit l’état actuel de la LED et le renvoie sous la forme d’un simple objet JSON contenant la valeur de l’état.
server.on("/led-state", HTTP_GET, [](AsyncWebServerRequest *request) {
int state = digitalRead(ledPin);
request->send(200, "application/json", "{\"state\":" + String(state) + "}");
});
Définir l’itinéraire du minuteur /set-timer
Cette route HTTP POST reçoit les données de la page Web avec l’action, la durée et l’unité de temps sélectionnées. Il convertit la durée en secondes, puis FreeRTOS coche pour démarrer le chronomètre unique. Lorsque le délai expire, l’action s’exécute.
server.on("/set-timer", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("action", true) && request->hasParam("duration", true) && request->hasParam("unit", true)) {
targetAction = request->getParam("action", true)->value().toInt();
int dur = request->getParam("duration", true)->value().toInt();
String unit = request->getParam("unit", true)->value();
uint64_t durationSeconds = 0;
if (unit == "s") durationSeconds = (uint64_t)dur;
else if (unit == "m") durationSeconds = (uint64_t)dur * 60ULL;
else if (unit == "h") durationSeconds = (uint64_t)dur * 3600ULL;
if (durationSeconds > 0) {
if (xTimerIsTimerActive(gpioTimer)) xTimerStop(gpioTimer, 0);
uint64_t tempTicks = durationSeconds * (uint64_t)configTICK_RATE_HZ;
TickType_t timerTicks = (tempTicks > 0xFFFFFFFFULL) ? 0xFFFFFFFFULL : (TickType_t)tempTicks;
timerStartTick = xTaskGetTickCount();
totalDurationTicks = tempTicks;
xTimerChangePeriod(gpioTimer, timerTicks, 0);
xTimerStart(gpioTimer, 0);
Serial.printf("Timer scheduled: GPIO %d will be set to %s in %llu seconds\n", ledPin,
targetAction ? "ON" : "OFF", durationSeconds);
}
}
request->redirect("/");
});
Annuler l’itinéraire du minuteur / annuler le minuteur
Cette route arrête tout minuteur FreeRTOS actif et réinitialise les variables globales du minuteur (timerStartTick, totalDurationTicks) à leur état d’inactivité.
server.on("/cancel-timer", HTTP_POST, [](AsyncWebServerRequest *request) {
// Stop the active timer
if (xTimerIsTimerActive(gpioTimer)) {
xTimerStop(gpioTimer, 0);
Serial.println("Timer cancelled");
}
targetAction = -1;
timerStartTick = 0;
totalDurationTicks = 0;
request->send(200, "text/plain", "OK");
});
Statut du minuteur Itinéraire /statut-minuterie
Cette route vérifie si une minuterie est en cours d’exécution, calcule le temps restant exact en secondes et renvoie un objet JSON avec le message d’état actuel.
server.on("/timer-status", HTTP_GET, [](AsyncWebServerRequest *request) {
if (targetAction != -1 && xTimerIsTimerActive(gpioTimer) && totalDurationTicks > 0) {
// Calculate how many seconds are left for the timer to end
TickType_t now = xTaskGetTickCount();
uint64_t elapsedTicks = 0;
if (now >= timerStartTick) {
elapsedTicks = now - timerStartTick;
} else {
elapsedTicks = (0xFFFFFFFFULL - timerStartTick) + now + 1;
}
uint64_t remainingTicks = (totalDurationTicks > elapsedTicks) ? totalDurationTicks - elapsedTicks : 0;
uint64_t remainingSeconds = remainingTicks / (uint64_t)configTICK_RATE_HZ;
// Return the amount of seconds left and action that will execute when the timer ends
String json = "{\"status\":\"Setting GPIO "+ String(ledPin) +" to " +
String(targetAction ? "ON" : "OFF") + "\",";
json += "\"active\":true,";
json += "\"remaining_seconds\":" + String((unsigned long)remainingSeconds) + "}";
request->send(200, "application/json", json);
} else {
request->send(200, "application/json", "{\"status\":\"No active timers\",\"active\":false,\"remaining_seconds\":0}");
}
});
Initialiser le serveur
Enfin, appelez la méthode Begin() sur l’objet serveur pour initialiser le serveur.
server.begin();
Démonstration
Après avoir inséré vos informations d’identification réseau, téléchargez le code sur la carte ESP32. Après le téléchargement, ouvrez le moniteur série à une vitesse de transmission de 115 200 bauds et appuyez sur le bouton ESP32 RST.

L’adresse IP de l’ESP32 sera imprimée dans le moniteur série.

Avec un ESP32 doté d’une LED connectée au GPIO 5, ouvrez n’importe quel navigateur sur votre réseau local et saisissez l’adresse IP de l’ESP32. La page Web suivante se chargera.

Cliquez sur les boutons TURN ON ou TURN OFF pour contrôler la LED. Vous pouvez également vérifier l’état actuel de la LED sur l’étiquette de texte.

Ensuite, vous disposez d’un formulaire HTML dans lequel vous pouvez sélectionner l’action, l’unité de temps et la durée de la minuterie. Après avoir sélectionné les paramètres de minuterie souhaités, appuyez sur le bouton « START TIMER ».

Au bas de la page Web, l’état du temps affichera l’action en attente actuelle et le temps restant. Il y a aussi un bouton pour annuler la minuterie.

Dans le moniteur série Arduino IDE, vous pouvez vérifier toutes les actions effectuées sur le serveur Web.

Conclusion
Dans ce didacticiel, vous avez appris à créer un serveur Web simple pour créer une minuterie permettant de contrôler vos GPIO ESP32. Vous pouvez maintenant étendre ce projet pour contrôler plus de sorties. Vous pouvez consulter d’autres tutoriels :
Si vous souhaitez en savoir plus sur la construction de serveurs web avec l’ESP32 à partir de zéro, nous vous recommandons de jeter un œil à notre eBook dédié à ce sujet :
Merci d’avoir lu.
Cette vidéo vous emmène dans l’histoire de Raspberry Pi :

-
KEYESTUDIO ESP32 Smart Home IoT Kit Compatible avec Arduino IDE, avec ESP32 Board, Python C, Musical Project, etc.
-
Ulegqin ESP32 NodeMCU mit CH340 Cartes de développement, ESP32 30 Pins ESP ESP-32-WROOM-32D Module 2.4 GHz WiFi + Bluetooth 2-in-1 Faible consommation Typ-C Interface Compatible avec Arduino(3 pcs)
