/* ----------------------------------------------------------------------------
Titre: Serveur HTTP V6a (dossier GENERIQUE)
Mise à jour le 27/4/2026 - amélioration de la documentation et de la syntaxe
Wiki : https://webge.fr/dokuwiki/doku.php?id=web:javascript:serveurhttp
Objectifs: Commander les sorties D0, D1, D2, D3 d'une carte MKR Wifi 1010,
et obtenir les valeurs présentes sur les entrées A0,...,A4 à
partir d'un navigateur (requêtes GET).
URL reconnues:
- http://IP demande "La page d'accueil"
- http://IP/lire/val0 demande la valeur sur A0
- http://IP/lire/val1 demande la valeur sur A1
- http://@IP/lire/vals0et1 demande les valeurs sur les entrées A0 et A1 (json)
- http://IP/lire/val2 demande la valeur sur A2
- http://IP/lire/val3 demande la valeur sur A3
- http://IP/lire/val4 demande la valeur sur A4
- http://IP/lire/all demande toutes les valeurs sur les entrées A0 à An (json)
- http://IP/lire/tablevar demande une table, 24 lignes (0 à 23) x 2 colonnes, de valeurs numériques (0<-A0,1<-A1) et 22constantes (max=50)
- http://IP/ecrire/valA active ou désactive la LED L et D0
- http://IP/ecrire/valB active ou désactive D1
- http://IP/ecrire/valC active ou désactive D2
- http://IP/ecrire/valD active ou désactive D3
Bibliothèques :
- WiFiWebServer, WIFININA, SPI : appelées dans defines.h
- Documentation de WiFiWebServer et WifiNINA
- https://github.com/khoih-prog/WiFiWebServer
- https://www.arduino.cc/en/Reference/WiFiNINA
Carte arduino:
- Module NINA (Arduino MKR WiFi 1010, MKR VIDOR 4000 et UNO WiFi Rev.2)
Fichiers
- define.h, Server_HTTP_V6
----------------------------------------------------------------------------*/
// Définitions
#define LED LED_BUILTIN // LED_BUILTIN (L sur la carte !)
#define D0 0
#define D1 1
#define D2 2
#define D3 3
// Bibliothèques à ajouter
#include "defines.h"
=== 2.2 Page d'accueil du serveur HTTP (HTML, CSS) ===
* **Particularité** : utilisation d'un raw string literal R"(...)" pour éviter tous les échappements **\"** et les **\** de continuation de ligne.
const String paccueil =
const String paccueil = F(R"(
Serveur MKR Wifi 1010 V6a
[Accueil] Serveur HTTP V6a (MKR Wifi 1010)
Remarque : la LED L de la carte doit clignoter quelques secondes après la mise sous tension.
Commandes reconnues
- Ecriture sur les sorties numériques D0 <- valA | D1 <- valB | D2 <- valC | D3 <- valD
=> http://IP/ecrire/valx?val=0 ou 1 avec x=A,B,C ou D
DO D1 D2 D3
- Lecture des entrées analogiques : A0 -> val0, ... , A4 -> val4
- Unique
- http://IP/lire/val0
- http://IP/lire/val1
- http://IP/lire/val2
- http://IP/lire/val3
- http://IP/lire/val4
- Multiples JSON
- http://IP/lire/vals0et1(val0,val1)
- http://IP/lire/all (val0...val4)
- http://IP/lire/tablevar
Table(24 lignes * 2 colonnes) ligne0<-A0, ligne1<-A1 et 22 constantes (valeur max=50)
)");
=== 2.3 Réseau, serveur HTTP et grandeurs physiques===
// Paramètres réseau, cryptage WPA
const char ssid[] = "A compléter"; // nom du SSID
const char pass[] = "A compléter"; // mot de passe du réseau
// Création du serveur HTTP
int status = WL_IDLE_STATUS;
WiFiWebServer server(80);
// Grandeurs physiques à mesurer (simulées par potentiomètres)
unsigned int val0 = 0;
unsigned int val1 = 0;
unsigned int val2 = 0;
unsigned int val3 = 0;
unsigned int val4 = 0;
=== 2.4 Gestionnaire de requêtes ===
- **Chargement de la page d'accueil**
// g0. Renvoi de la page d'accueil du serveur
// Particularité : charset=utf-8 dans le Content-Type : cohérent avec ta déclaration HTML, évite des problèmes d'affichage des accents
void handleRoot() {
server.send(200, F("text/html;charset=utf-8"), paccueil); // Réponse
}
- **Commande de la sortie D0 (même principe pour D1, D2 et D3)**
// g1. Commande de la sortie D0
void ecritureValA() {
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
String val = server.arg(0);
if (val == "0" || val == "1") {
digitalWrite(D0, val == "1" ? HIGH : LOW);
server.send(200, F("text/plain; charset=utf-8"), val);
} else {
server.send(400, F("text/plain; charset=utf-8"), F("Commande <- 0 ou 1 !!!"));
}
}
// Remarque concernant F("Cache-Control"), F("no-cache, no-store, must-revalidate")
// Le jQuery envoie régulièrement des requêtes GET pour lire les valeurs des capteurs ou connaître l'état des sorties. Sans cet en-tête, le navigateur risque de retourner une ancienne réponse → la valeur affichée ne correspond plus à la réalité du microcontrôleur.
- **Transmission de la valeur analogique présente sur A0 (même principe pour A1, ...A4)**
void lectureVal0() {
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
server.send(200, F("text/plain; charset=utf-8"), String(val0));
}
- **Transmission des valeurs analogiques présentes sur A0 et A1 au format JSON**
// g10. Transmission des valeurs analogiques présentes sur A0 et A1 au format JSON
// msgjson: objet JSON
void lectureVals0et1() {
String msgjson = F("{\"val0\":");
msgjson += val0;
msgjson += F(",\"val1\":");
msgjson += val1;
msgjson += F("}");
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
server.send(200, F("application/json; charset=utf-8"), msgjson);
}
- **Mesure et transmission des valeurs analogiques présentes sur A0 à An au format JSON**
// g11. Mesure et transmission des valeurs analogiques présentes sur A0 à An au format JSON
// mmsgtablejson : tableau contenant un objet JSON
void lectureAll() {
String msgjson = "[{\"val0\":";
msgjson += val0;
msgjson += ",";
msgjson += "\"val1\":";
msgjson += val1;
msgjson += ",";
msgjson += "\"val2\":";
msgjson += val2;
msgjson += ",";
msgjson += "\"val3\":";
msgjson += val3;
msgjson += ",";
msgjson += "\"val4\":";
msgjson += val4;
msgjson += "}]";
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
server.send(200, F("application/json; charset=utf-8"), msgjson);
}
- **Mesure de 2 valeurs mises à l'echelle (max 5)**
// g12. Mesure de 2 valeurs mises à l'echelle (max 5)
// et insertion dans une table JSON
// mmsgtablejson : tableau contenant un objet JSON
void lectureTableJSON() {
String msgtablejson = R"([{ "H0": 0, "T0": )" + String(val0 * 0.05) + R"( }, { "H1": 1, "T1": )" + String(val1 * 0.05) +
R"( }, { "H": 1, "T": 11.7 },
{ "H": 2, "T": 12 },
{ "H": 3, "T": 12 },
{ "H": 4, "T": 12 },
{ "H": 5, "T": 10.7 },
{ "H": 6, "T": 10.6 },
{ "H": 7, "T": 10.1 },
{ "H": 8, "T": 9.7 },
{ "H": 9, "T": 10.5 },
{ "H": 10, "T": 11.4 },
{ "H": 11, "T": 13.1 },
{ "H": 12, "T": 15.7 },
{ "H": 13, "T": 19.2 },
{ "H": 14, "T": 20.1 },
{ "H": 15, "T": 20.9 },
{ "H": 16, "T": 20.7 },
{ "H": 17, "T": 20.9 },
{ "H": 18, "T": 20.7 },
{ "H": 19, "T": 19.1 },
{ "H": 20, "T": 17.4 },
{ "H": 21, "T": 16.5 },
{ "H": 22, "T": 16.6 },
{ "H": 23, "T": 15.4 }])";
// Transmission
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
server.send(200, F("application/json; charset=utf-8"), msgtablejson);
}
- **Traitement des requêtes non prises en charge**
- // gn. Traitement des requêtes non prises en charge
void handleNotFound() {
String message = F("File Not Found\n\n");
message += F("URI: ");
message += server.uri();
message += F("\nMethod: ");
message += (server.method() == HTTP_GET) ? F("GET") : F("POST");
message += F("\nArguments: ");
message += server.args();
message += F("\n");
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, F("text/plain"), message);
}
=== 2.5 setup ===
void setup() {
// Configuration des E/S
pinMode(LED, OUTPUT); // Led L de la carte
pinMode(D0, OUTPUT);
pinMode(D1, OUTPUT);
Serial.begin(115200); // Moniteur pour la mise au point
// 1. Vérification de la connexion au module Wifi
if (WiFi.status() == WL_NO_MODULE) {
Serial.println(" La communication avec le module WiFi a échoué!");
// ATTENTION : blocage du programme !!!!
while (true)
;
}
// 2. Connexion au réseau Wifi
while (status != WL_CONNECTED) {
Serial.print("Tentative de connexion au réseau: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass); // si WPA, WPA2
// Attente de 10s avant la prochaine tentative de connexion
delay(10000);
}
// 3. Connecté, inscription des gestionnaires
server.on(F("/"), handleRoot);
server.on(F("/lire/val0"), lectureVal0);
server.on(F("/lire/val1"), lectureVal1);
server.on(F("/lire/val2"), lectureVal2);
server.on(F("/lire/val3"), lectureVal3);
server.on(F("/lire/val4"), lectureVal4);
server.on(F("/lire/vals0et1"), lectureVals0et1);
server.on(F("/lire/all"), lectureAll);
server.on(F("/lire/table"), lectureTableJSON),
server.on(F("/ecrire/valA"), ecritureValA);
server.on(F("/ecrire/valB"), ecritureValB);
server.on(F("/ecrire/valC"), ecritureValC);
server.on(F("/ecrire/valD"), ecritureValD);
server.onNotFound(handleNotFound);
// et démarrage du serveur Web sur le port défini
// lors de l'initialisation
server.begin();
}
=== 2.6 loop ===
// Paramètres de la tâche non bloquante
unsigned long previousMillis = 0; // Temps précédent
const long interval = 1000; // Intervalle en millisecondes (1 seconde)
void loop() {
server.handleClient();
// Mise à jour du timer sans risquer de bug en cas de débordement
if (millis() - previousMillis >= interval) {
previousMillis += interval; // On ajoute l'intervalle plutôt que de copier millis()
// Mesures
val0 = analogRead(A0);
val1 = analogRead(A1);
val2 = analogRead(A2);
val3 = analogRead(A3);
val4 = analogRead(A4);
// Indicateur fonctionnement du programme
digitalWrite(LED, !digitalRead(LED));
}
}
{{ :materiels:capteurs:distance:arduinoico.png?nolink&50|}}
// Réponse de la carte Arduino à la requête .../lire/val1. Transmission de la valeur analogique présente sur l'entrée A1
void lectureVal1() {
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
server.send(200, F("text/plain; charset=utf-8"), String(val1));
}
// Le navigateur initie la requête vers la carte Arduino, via le Raspberry Pi et traite la réponse
$(document).ready(function () {
let delaiMesure = 3000;
let delaiError = 2000;
function loadMeasures() {
$.ajax({
url: 'scripts/lireVal1.php',
method: 'GET',
success: function (data) {
$("#val1").html(data); // Affichage de la valeur sur la page pour les tests
let val1 = parseInt(data) * 50 / 920; // Exemple de mise à l'échelle pour les tests
$('#jaugeCirculaire').jqxGauge({ value: val1 });
},
timeout: delaiError,
error: function () {
// pour aller plus loin : gestion des erreurs
}
})
}
loadMeasures(); // Exécuté une fois au chargement de la page
setInterval(function () { // Boucle infinie, s'exécute
loadMeasures() // toutes les delaiMesure ms
}, delaiMesure);
});
===3.2 Lecture de n valeurs transmises au format JSON ===
* **Utilisation** : carte leaflet, matrice de points , graphique jQwidget
* **Exemple 2** : transfert de 2 valeurs
// Réponse de la carte Arduino à la requête .../lire/vals0et1.
// g10. Transmission des valeurs analogiques présentes sur A0 et A1
// msgjson: objet JSON
void lectureVals0et1() {
String msgjson = F("{\"val0\":");
msgjson += val0;
msgjson += F(",\"val1\":");
msgjson += val1;
msgjson += F("}");
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
server.send(200, F("application/json; charset=utf-8"), msgjson);
}
function loadCoordinates() {
let latitude;
let longitude;
$.ajax({
url: 'scripts/getcoordonnees.php',
method: 'GET',
dataType: 'json', // demande à jQuery de parser le JSON
success: function (data) {
// Pour les tests, on souhaite 0 < val0 < 1023 => 47.07970 < latitude < 47.01328
latitude = -0.0000664 * parseFloat(data.val0) + 47.07970;
// Pour les tests, on souhaite 0 < val1 < 1023 => 2.199468 < longitude < 2.409102
longitude = 0.000210 * parseFloat(data.val1) + 2.199468;
// Afficher les valeurs numériques
$('#latitude').html(latitude);
$('#longitude').html(longitude);
// Afficher un marqueur sur la carte (objet map)
map.flyTo([latitude, longitude], 11, {
animate: true,
duration: 2 // en secondes
});
marker = L.marker([latitude, longitude]).addTo(map);
},
timeout: delaiError,
error: function () {
// pour aller plus loin
}
});
}
loadCoordinates(); // premier appel de la fonction
setInterval(function () { // appel de la fonction toutes les delaiMesure ms
loadCoordinates()
}, delaiMesure);
* **Exemple 3** : lecture de 5 valeurs
// Réponse de la carte Arduino à la requête .../lire/all.
// g11. Mesure et transmission des valeurs analogiques présentes sur A0 à A4 au format JSON
// mmsgjson : objet JSON
void lectureAll() {
String msgjson = F("{\"val0\":");
msgjson += val0;
msgjson += F(",\"val1\":");
msgjson += val1;
msgjson += F(",\"val2\":");
msgjson += val2;
msgjson += F(",\"val3\":");
msgjson += val3;
msgjson += F(",\"val4\":");
msgjson += val4;
msgjson += F("}");
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
server.send(200, F("application/json; charset=utf-8"), msgjson);
}
function loadMeasures() {
$.ajax({
url: 'scripts/lireall.php',
method: 'GET',
dataType: 'json', // demande à jQuery de parser le JSON
success: function(data) {
let vals = [];
vals[0] = parseInt(data.val0);
vals[1] = parseInt(data.val1);
vals[2] = parseInt(data.val2);
vals[3] = parseInt(data.val3);
vals[4] = parseInt(data.val4);
// Utilisation du tableau
...
},
timeout: delaiError,
error: function () {
// pour aller plus loin : gestion des erreurs
}
})
}
* **Exemple 4** : transfert de n valeurs
// Réponse de la carte Arduino à la requête .../lire/tablevar.
// g12. Mesure de 2 valeurs mises à l'echelle (max 5)
// et insertion dans une table JSON
void lectureTableJSON() {
String msgtable = R"([{ "H0": 0, "T0": )" + String(val0 * 0.05) + R"( }, { "H1": 1, "T1": )" + String(val1 * 0.05) +
R"( }, { "H": 1, "T": 11.7 },
{ "H": 2, "T": 12 },
{ "H": 3, "T": 12 },
{ "H": 4, "T": 12 },
{ "H": 5, "T": 10.7 },
...
{ "H": 21, "T": 16.5 },
{ "H": 22, "T": 16.6 },
{ "H": 23, "T": 15.4 }])";
// Transmission
server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
server.send(200, F("application/json; charset=utf-8"), msgtable);
}
function loadMeasures() {
$.ajax({
url: 'scripts/lireTable.php',
method: 'GET',
dataType: 'json', // Format attendu
success: function (data) {
// Configurer la source de données
var source = {
localdata: data,
datatype: "array",
datafields: [
{ name: 'H0', type: 'number' },{ name: 'T0', type: 'number' },
{ name: 'H1', type: 'number' },{ name: 'T1', type: 'number' },
{ name: 'H', type: 'number' },{ name: 'T', type: 'number' }
]
};
var dataAdapter = new $.jqx.dataAdapter(source);
// Configurer jqxChart
var settings = {
title: "Relevé de températures 0 à 23h",
description: "(0h et 1h simulées par des potentiomètres placés sur A0 et A1 [carte MKR1010 & Serveur HTTP V6])",
enableAnimations: false,
showLegend: true,
padding: { left: 10, top: 10, right: 10, bottom: 10 },
titlePadding: { left: 0, top: 0, right: 0, bottom: 10 },
source: dataAdapter,
xAxis: {
dataField: 'H',
displayText: 'H local'
},
colorScheme: 'scheme01',
seriesGroups: [
{
type: 'column', // Type de graphique (ex. : 'column', 'line', 'area', 'bar')
columnsGapPercent: 50,
series: [
{ dataField: 'T0', displayText: 'A0', color: '#FF0000' },
{ dataField: 'T1', displayText: 'A1', color: '#00FF00' },
{ dataField: 'T', displayText: 'Température (°C) le 3/4/2025' , color: '#0000FF'}
]
}
]
};
// Initialiser le jqxChart
$('#jqxChart').jqxChart(settings);
},
timeout: delaiError,
error: function (xhr, status, error) {
console.error('Erreur lors de la récupération des données JSON:', error);
}
});
}
loadMeasures(); // Exécuté une fois au chargement de la page
setInterval(function () { // Boucle infinie, s'exécute
loadMeasures() // toutes les ns
}, delaiMesure);