voili avec affichage ip et cliquable
Code :
// ESP32 Dashboard avec mode AP pour configuration initiale
#include <WiFi.h>
#include <WebServer.h>
#include <EEPROM.h>
#include <ArduinoJson.h>
// Structure pour stocker la config en EEPROM
struct Config {
char magic[4]; // "RMS" pour vérifier si config valide
char ssid[32];
char password[64];
char dashUser[32]; // Nom d'utilisateur pour le dashboard
char dashPass[32]; // Mot de passe pour le dashboard
char ip1[16];
char ip2[16];
char ip3[16];
char ip4[16];
char name1[32];
char name2[32];
char name3[32];
char name4[32];
};
Config config;
WebServer server(80);
bool isAPMode = false;
// Variables globales pour le test d'IP
String testingIP = "";
bool isTestingConnection = false;
unsigned long testStartTime = 0;
// Page de configuration en mode AP
const char config_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Configuration Dashboard RMS</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background: #1a1a2e;
color: white;
}
.container {
max-width: 600px;
margin: 0 auto;
background: rgba(255,255,255,0.1);
padding: 30px;
border-radius: 10px;
}
h1 {
text-align: center;
color: #4CAF50;
}
.section {
background: rgba(0,0,0,0.3);
padding: 20px;
margin: 20px 0;
border-radius: 5px;
}
h2 {
color: #2196F3;
margin-top: 0;
}
label {
display: block;
margin: 10px 0 5px;
}
input, select {
width: 100%;
padding: 10px;
border: 1px solid #555;
border-radius: 3px;
background: #333;
color: white;
box-sizing: border-box;
}
.ip-group {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 10px;
margin: 10px 0;
}
button {
background: #4CAF50;
color: white;
padding: 12px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
width: 100%;
margin-top: 20px;
}
button:hover {
background: #45a049;
}
.scan-btn {
background: #2196F3;
margin: 10px 0;
}
#scanResults {
max-height: 200px;
overflow-y: auto;
background: rgba(0,0,0,0.5);
padding: 10px;
border-radius: 3px;
margin: 10px 0;
}
.network-item {
padding: 5px;
cursor: pointer;
border-bottom: 1px solid #444;
}
.network-item:hover {
background: rgba(255,255,255,0.1);
}
.loading {
text-align: center;
color: #4CAF50;
}
.info {
background: #ff9800;
color: black;
padding: 10px;
border-radius: 5px;
margin: 10px 0;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h1>Configuration Dashboard RMS</h1>
<div class="info" id="networkInfo">
<!-- L'info sera remplie dynamiquement -->
</div>
<div class="section">
<h2>1. Configuration WiFi</h2>
<div id="currentWifi" style="background: #4CAF50; color: white; padding: 10px; border-radius: 3px; margin-bottom: 10px; display: none;">
? Connecté à : <strong id="wifiName">---</strong>
</div>
<button class="scan-btn" onclick="scanNetworks()">? Scanner les réseaux</button>
<div id="scanResults"></div>
<label>SSID:</label>
<input type="text" id="ssid" placeholder="Nom du réseau WiFi">
<label>Mot de passe:</label>
<input type="password" id="password" placeholder="Mot de passe WiFi">
</div>
<div class="section">
<h2>2. Sécurité du Dashboard</h2>
<label>Nom d'utilisateur:</label>
<input type="text" id="dashUser" placeholder="admin">
<label>Mot de passe:</label>
<input type="password" id="dashPass" placeholder="Mot de passe dashboard">
<p style="color: #aaa; font-size: 12px;">Laissez vide pour désactiver l'authentification</p>
</div>
<div class="section">
<h2>3. Configuration des interfaces RMS</h2>
<p style="color: #aaa;">Configurez entre 2 et 4 interfaces</p>
<div class="ip-group">
<input type="text" id="ip1" placeholder="IP Interface 1 (ex: 192.168.1.100)">
<input type="text" id="name1" placeholder="Nom 1">
</div>
<div class="ip-group">
<input type="text" id="ip2" placeholder="IP Interface 2 (ex: 192.168.1.101)">
<input type="text" id="name2" placeholder="Nom 2">
</div>
<div class="ip-group">
<input type="text" id="ip3" placeholder="IP Interface 3 (optionnel)">
<input type="text" id="name3" placeholder="Nom 3">
</div>
<div class="ip-group">
<input type="text" id="ip4" placeholder="IP Interface 4 (optionnel)">
<input type="text" id="name4" placeholder="Nom 4">
</div>
</div>
<button onclick="saveConfig()">? Sauvegarder et redémarrer</button>
<button style="background: #666; margin-top: 10px;" onclick="window.location.href='/'">
↩️ Retour au Dashboard
</button>
<div id="message"></div>
</div>
<script>
// Charger la config existante au démarrage
window.onload = function() {
// Afficher l'info réseau
fetch('/networkinfo')
.then(response => response.json())
.then(info => {
const networkInfo = document.getElementById('networkInfo');
if (info.isAP) {
networkInfo.innerHTML = 'Mode Configuration - Connectez-vous au WiFi pour continuer';
networkInfo.style.background = '#ff9800';
} else {
networkInfo.innerHTML = `IP réseau : ${info.ip}`;
networkInfo.style.background = '#4CAF50';
}
});
fetch('/getconfig')
.then(response => response.json())
.then(data => {
// Pré-remplir les IPs et noms
if (data.ip1) document.getElementById('ip1').value = data.ip1;
if (data.ip2) document.getElementById('ip2').value = data.ip2;
if (data.ip3) document.getElementById('ip3').value = data.ip3;
if (data.ip4) document.getElementById('ip4').value = data.ip4;
if (data.name1) document.getElementById('name1').value = data.name1;
if (data.name2) document.getElementById('name2').value = data.name2;
if (data.name3) document.getElementById('name3').value = data.name3;
if (data.name4) document.getElementById('name4').value = data.name4;
// AJOUT : Afficher le WiFi actuel si connecté
if (data.ssid && window.location.hostname !== '192.168.4.1') {
document.getElementById('currentWifi').style.display = 'block';
document.getElementById('wifiName').textContent = data.ssid;
}
})
.catch(err => {
console.log('Pas de config existante');
});
};
function scanNetworks() {
document.getElementById('scanResults').innerHTML = '<div class="loading">Scan en cours...</div>';
fetch('/scan')
.then(response => response.json())
.then(data => {
let html = '';
data.networks.forEach(network => {
html += `<div class="network-item" onclick="selectNetwork('${network.ssid}')">
? ${network.ssid} (${network.rssi} dBm)
</div>`;
});
document.getElementById('scanResults').innerHTML = html || 'Aucun réseau trouvé';
});
}
function selectNetwork(ssid) {
document.getElementById('ssid').value = ssid;
}
function saveConfig() {
const config = {
ssid: document.getElementById('ssid').value,
password: document.getElementById('password').value,
dashUser: document.getElementById('dashUser').value,
dashPass: document.getElementById('dashPass').value,
ip1: document.getElementById('ip1').value,
ip2: document.getElementById('ip2').value,
ip3: document.getElementById('ip3').value,
ip4: document.getElementById('ip4').value,
name1: document.getElementById('name1').value || 'Interface 1',
name2: document.getElementById('name2').value || 'Interface 2',
name3: document.getElementById('name3').value || 'Interface 3',
name4: document.getElementById('name4').value || 'Interface 4'
};
// Validation
if (!config.ssid || !config.password) {
alert('Veuillez remplir les informations WiFi');
return;
}
if (!config.ip1 || !config.ip2) {
alert('Veuillez configurer au moins 2 interfaces');
return;
}
document.getElementById('message').innerHTML = '<div class="loading">Sauvegarde en cours...</div>';
fetch('/save', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(config)
})
.then(response => response.json())
.then(data => {
if (data.status === 'testing') {
document.getElementById('message').innerHTML =
'<div class="info">✅ Configuration sauvegardée!</div>' +
'<div class="loading">Test de connexion au réseau...</div>';
// Commencer à vérifier l'IP
checkIPStatus(config.ssid);
}
})
.catch(err => {
document.getElementById('message').innerHTML =
'<div class="info" style="background: #ff4444;">Erreur de sauvegarde: ' + err + '</div>';
});
}
function checkIPStatus(ssid) {
const checkInterval = setInterval(() => {
fetch('/checkip')
.then(response => response.json())
.then(data => {
if (data.status === 'connected' && data.ip) {
clearInterval(checkInterval);
document.getElementById('message').innerHTML =
'<div class="info">✅ Configuration sauvegardée!</div>' +
'<div class="info" style="background: #4CAF50; margin-top: 10px;">' +
'? Nouvelle adresse IP : <a href="http://' + data.ip + '" style="color: white; font-size: 24px; font-weight: bold;">' + data.ip + '</a></div>' +
'<div class="info">Cliquez sur l\'IP ou notez cette adresse! Redémarrage dans 10 secondes...</div>' +
'<div class="info">Connectez-vous au WiFi "' + ssid +
'" et accédez à <a href="http://' + data.ip + '" style="color: white; font-weight: bold;">http://' + data.ip + '</a></div>';
// Redémarrer après 10 secondes
setTimeout(() => {
window.location.href = 'http://' + data.ip;
}, 10000);
} else if (data.status === 'failed') {
clearInterval(checkInterval);
document.getElementById('message').innerHTML =
'<div class="info" style="background: #ff4444;">❌ Impossible de se connecter au réseau!</div>' +
'<div class="info">Vérifiez le mot de passe WiFi et réessayez.</div>' +
'<div class="info">Redémarrage dans 10 secondes...</div>';
}
// Si status est "testing", on continue à vérifier
})
.catch(err => {
// Si erreur, c'est peut-être que l'ESP32 a redémarré
clearInterval(checkInterval);
});
}, 1000); // Vérifier chaque seconde
}
</script>
</body>
</html>
)rawliteral";
// Page de login
const char login_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Dashboard RMS</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background: #1a1a2e;
color: white;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.login-box {
background: rgba(255,255,255,0.1);
padding: 40px;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
width: 300px;
}
h2 {
text-align: center;
color: #4CAF50;
margin-bottom: 30px;
}
input {
width: 100%;
padding: 12px;
margin: 10px 0;
border: 1px solid #555;
border-radius: 5px;
background: #333;
color: white;
box-sizing: border-box;
}
button {
width: 100%;
padding: 12px;
margin-top: 20px;
background: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background: #45a049;
}
.error {
color: #ff4444;
text-align: center;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="login-box">
<h2>? Dashboard RMS</h2>
<form action="/login" method="POST">
<input type="text" name="user" placeholder="Nom d'utilisateur" required>
<input type="password" name="pass" placeholder="Mot de passe" required>
<button type="submit">Se connecter</button>
</form>
<div id="error" class="error"></div>
</div>
<script>
if (window.location.search.includes('error=1')) {
document.getElementById('error').textContent = 'Identifiants incorrects';
}
</script>
</body>
</html>
)rawliteral";
// Page dashboard modifiée avec bouton plein écran
const char dashboard_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard RMS</title>
<style>
body {
margin: 0;
padding: 0;
background: #222;
font-family: Arial, sans-serif;
height: 100vh;
overflow: hidden;
}
#dashboard {
display: grid;
height: 100vh;
gap: 2px;
transition: all 0.3s ease;
}
#dashboard.fullscreen {
display: block !important;
}
.layout-2 {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
}
.layout-3 {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
.layout-3 .frame-container:first-child {
grid-row: span 2;
}
.layout-4 {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
.frame-container {
position: relative;
overflow: hidden;
background: white;
display: none;
}
.frame-container.active {
display: block;
}
.frame-container.fullscreen-active {
display: block !important;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 50;
}
.frame-wrapper {
width: 100%;
height: 100%;
transform-origin: top left;
transition: transform 0.3s ease;
}
iframe {
width: 100%;
height: 100%;
border: none;
background: white;
}
.frame-header {
position: absolute;
top: 5px;
left: 5px;
display: flex;
align-items: center;
gap: 10px;
z-index: 10;
}
.frame-label {
background: rgba(0,0,0,0.8);
color: white;
padding: 5px 10px;
font-size: 12px;
border-radius: 3px;
pointer-events: none;
}
.fullscreen-btn {
background: rgba(0,0,0,0.8);
color: white;
border: none;
padding: 5px 8px;
font-size: 12px;
border-radius: 3px;
cursor: pointer;
transition: background 0.2s;
}
.fullscreen-btn:hover {
background: rgba(0,0,0,0.9);
}
.fullscreen-btn.exit {
background: rgba(220,53,69,0.9);
}
.fullscreen-btn.exit:hover {
background: rgba(220,53,69,1);
}
/* Contrôles de zoom */
.zoom-controls {
position: absolute;
top: 5px;
right: 5px;
background: rgba(0,0,0,0.8);
padding: 5px;
border-radius: 3px;
z-index: 10;
display: flex;
gap: 5px;
}
.zoom-btn {
width: 25px;
height: 25px;
border: none;
background: #444;
color: white;
cursor: pointer;
border-radius: 3px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.zoom-btn:hover {
background: #666;
}
.zoom-value {
color: white;
font-size: 12px;
min-width: 45px;
text-align: center;
display: flex;
align-items: center;
}
.btn-config {
position: fixed;
bottom: 10px;
right: 10px;
background: #2196F3;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
z-index: 100;
}
.btn-config:hover {
background: #1976D2;
}
.hidden {
display: none !important;
}
</style>
</head>
<body>
<div id="dashboard">
<div class="frame-container" id="frame1-container">
<div class="frame-header">
<span class="frame-label" id="label1">Interface 1</span>
<button class="fullscreen-btn" onclick="toggleFullscreen(1)" id="fullscreen-btn-1">⛶</button>
</div>
<div class="zoom-controls">
<button class="zoom-btn" onclick="zoom(1, -0.1)">−</button>
<span class="zoom-value" id="zoom1">100%</span>
<button class="zoom-btn" onclick="zoom(1, 0.1)">+</button>
</div>
<div class="frame-wrapper" id="wrapper1">
<iframe id="frame1"></iframe>
</div>
</div>
<div class="frame-container" id="frame2-container">
<div class="frame-header">
<span class="frame-label" id="label2">Interface 2</span>
<button class="fullscreen-btn" onclick="toggleFullscreen(2)" id="fullscreen-btn-2">⛶</button>
</div>
<div class="zoom-controls">
<button class="zoom-btn" onclick="zoom(2, -0.1)">−</button>
<span class="zoom-value" id="zoom2">100%</span>
<button class="zoom-btn" onclick="zoom(2, 0.1)">+</button>
</div>
<div class="frame-wrapper" id="wrapper2">
<iframe id="frame2"></iframe>
</div>
</div>
<div class="frame-container" id="frame3-container">
<div class="frame-header">
<span class="frame-label" id="label3">Interface 3</span>
<button class="fullscreen-btn" onclick="toggleFullscreen(3)" id="fullscreen-btn-3">⛶</button>
</div>
<div class="zoom-controls">
<button class="zoom-btn" onclick="zoom(3, -0.1)">−</button>
<span class="zoom-value" id="zoom3">100%</span>
<button class="zoom-btn" onclick="zoom(3, 0.1)">+</button>
</div>
<div class="frame-wrapper" id="wrapper3">
<iframe id="frame3"></iframe>
</div>
</div>
<div class="frame-container" id="frame4-container">
<div class="frame-header">
<span class="frame-label" id="label4">Interface 4</span>
<button class="fullscreen-btn" onclick="toggleFullscreen(4)" id="fullscreen-btn-4">⛶</button>
</div>
<div class="zoom-controls">
<button class="zoom-btn" onclick="zoom(4, -0.1)">−</button>
<span class="zoom-value" id="zoom4">100%</span>
<button class="zoom-btn" onclick="zoom(4, 0.1)">+</button>
</div>
<div class="frame-wrapper" id="wrapper4">
<iframe id="frame4"></iframe>
</div>
</div>
</div>
<button class="btn-config" onclick="window.location.href='/config'">
⚙️ Configuration
</button>
<script>
// Stocker les niveaux de zoom et l'état plein écran
const zoomLevels = {1: 1, 2: 1, 3: 1, 4: 1};
let currentFullscreen = null;
// Fonction de basculement plein écran
function toggleFullscreen(frameNum) {
const container = document.getElementById(`frame${frameNum}-container`);
const dashboard = document.getElementById('dashboard');
const btn = document.getElementById(`fullscreen-btn-${frameNum}`);
const configBtn = document.querySelector('.btn-config');
if (currentFullscreen === frameNum) {
// Sortir du plein écran
container.classList.remove('fullscreen-active');
dashboard.classList.remove('fullscreen');
btn.classList.remove('exit');
btn.textContent = '⛶';
configBtn.classList.remove('hidden');
currentFullscreen = null;
// Réafficher toutes les autres vues actives
for (let i = 1; i <= 4; i++) {
const otherContainer = document.getElementById(`frame${i}-container`);
if (otherContainer.classList.contains('active') && i !== frameNum) {
otherContainer.style.display = '';
}
}
} else {
// Entrer en plein écran
// D'abord sortir du plein écran si une autre vue l'est
if (currentFullscreen !== null) {
toggleFullscreen(currentFullscreen);
}
container.classList.add('fullscreen-active');
dashboard.classList.add('fullscreen');
btn.classList.add('exit');
btn.textContent = '✕';
configBtn.classList.add('hidden');
currentFullscreen = frameNum;
// Masquer toutes les autres vues
for (let i = 1; i <= 4; i++) {
if (i !== frameNum) {
const otherContainer = document.getElementById(`frame${i}-container`);
otherContainer.style.display = 'none';
}
}
}
}
// Fonction de zoom individuel
function zoom(frameNum, delta) {
zoomLevels[frameNum] = Math.max(0.5, Math.min(2, zoomLevels[frameNum] + delta));
updateZoom(frameNum);
}
// Mettre à jour l'affichage du zoom
function updateZoom(frameNum) {
const wrapper = document.getElementById(`wrapper${frameNum}`);
const zoomDisplay = document.getElementById(`zoom${frameNum}`);
const scale = zoomLevels[frameNum];
wrapper.style.transform = `scale(${scale})`;
wrapper.style.width = `${100 / scale}%`;
wrapper.style.height = `${100 / scale}%`;
zoomDisplay.textContent = `${Math.round(scale * 100)}%`;
}
// Gestion de la touche Escape pour sortir du plein écran
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && currentFullscreen !== null) {
toggleFullscreen(currentFullscreen);
}
});
// Charger la config depuis l'ESP32
fetch('/getconfig')
.then(response => response.json())
.then(config => {
applyConfig(config);
});
function applyConfig(config) {
const dashboard = document.getElementById('dashboard');
let count = 0;
// Compter les interfaces configurées
for (let i = 1; i <= 4; i++) {
if (config[`ip${i}`]) count++;
}
// Appliquer le layout
dashboard.className = '';
dashboard.classList.add(`layout-${count}`);
// Configurer les frames
let frameIndex = 1;
for (let i = 1; i <= 4; i++) {
if (config[`ip${i}`]) {
const container = document.getElementById(`frame${frameIndex}-container`);
const frame = document.getElementById(`frame${frameIndex}`);
const label = document.getElementById(`label${frameIndex}`);
container.classList.add('active');
frame.src = `http://${config[`ip${i}`]}`;
label.textContent = config[`name${i}`] || `Interface ${i}`;
frameIndex++;
}
}
}
</script>
</body>
</html>
)rawliteral";
void loadConfig() {
EEPROM.begin(sizeof(Config));
EEPROM.get(0, config);
// Vérifier si config valide
if (String(config.magic) != "RMS") {
// Config invalide, mode AP
isAPMode = true;
// Initialiser la config avec des valeurs vides
memset(&config, 0, sizeof(Config));
strcpy(config.magic, "");
strcpy(config.ssid, "");
strcpy(config.password, "");
strcpy(config.dashUser, "");
strcpy(config.dashPass, "");
strcpy(config.ip1, "");
strcpy(config.ip2, "");
strcpy(config.ip3, "");
strcpy(config.ip4, "");
strcpy(config.name1, "");
strcpy(config.name2, "");
strcpy(config.name3, "");
strcpy(config.name4, "");
}
}
void saveConfig() {
strcpy(config.magic, "RMS");
EEPROM.put(0, config);
EEPROM.commit();
}
void clearConfig() {
strcpy(config.magic, "");
EEPROM.put(0, config);
EEPROM.commit();
}
void startAPMode() {
Serial.println("Démarrage en mode AP...");
WiFi.mode(WIFI_AP);
WiFi.softAP("RMS_Dashboard_Config", "rms12345");
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP: ");
Serial.println(IP);
}
void handleConfigPage() {
server.send(200, "text/html", config_html);
}
// Variables globales pour l'authentification
bool isAuthenticated = false;
String sessionID = "";
// Générer un ID de session aléatoire
String generateSessionID() {
String id = "";
for (int i = 0; i < 16; i++) {
id += String(random(0, 16), HEX);
}
return id;
}
// Vérifier l'authentification
bool checkAuth() {
// En mode AP, pas d'authentification
if (isAPMode) {
return true;
}
// Si pas de mot de passe configuré, accès libre
if (strlen(config.dashUser) == 0 && strlen(config.dashPass) == 0) {
return true;
}
// Si seulement un des deux est vide, accès libre aussi (config incomplète)
if (strlen(config.dashUser) == 0 || strlen(config.dashPass) == 0) {
return true;
}
// Vérifier le cookie de session
if (server.hasHeader("Cookie")) {
String cookie = server.header("Cookie");
if (cookie.indexOf("session=" + sessionID) != -1 && sessionID != "") {
return true;
}
}
return false;
}
void handleNetworkInfo() {
String json = "{";
if (isAPMode) {
json += "\"isAP\":true,";
json += "\"ip\":\"192.168.4.1\"";
} else {
json += "\"isAP\":false,";
json += "\"ip\":\"" + WiFi.localIP().toString() + "\"";
}
json += "}";
server.send(200, "application/json", json);
}
void handleLogin() {
if (server.method() == HTTP_POST) {
String user = server.arg("user");
String pass = server.arg("pass");
if (user == config.dashUser && pass == config.dashPass) {
// Login réussi
sessionID = generateSessionID();
server.sendHeader("Set-Cookie", "session=" + sessionID + "; Path=/");
server.sendHeader("Location", "/");
server.send(302);
} else {
// Login échoué
server.sendHeader("Location", "/?error=1");
server.send(302);
}
} else {
server.send(200, "text/html", login_html);
}
}
void handleRoot() {
if (isAPMode) {
server.send(200, "text/html", config_html);
} else {
if (!checkAuth()) {
server.send(200, "text/html", login_html);
} else {
server.send(200, "text/html", dashboard_html);
}
}
}
void handleScan() {
int n = WiFi.scanNetworks();
String json = "{\"networks\":[";
for (int i = 0; i < n; i++) {
if (i > 0) json += ",";
String ssid = WiFi.SSID(i);
// Échapper les caractères spéciaux dans le SSID
ssid.replace("\"", "\\\"");
ssid.replace("\\", "\\\\");
json += "{\"ssid\":\"" + ssid + "\",\"rssi\":" + String(WiFi.RSSI(i)) + "}";
}
json += "]}";
server.send(200, "application/json", json);
}
void handleSave() {
if (server.hasArg("plain")) {
DynamicJsonDocument doc(1024);
deserializeJson(doc, server.arg("plain"));
// Sauvegarder la config
strcpy(config.ssid, doc["ssid"]);
strcpy(config.password, doc["password"]);
strcpy(config.dashUser, doc["dashUser"] | "");
strcpy(config.dashPass, doc["dashPass"] | "");
strcpy(config.ip1, doc["ip1"]);
strcpy(config.ip2, doc["ip2"]);
strcpy(config.ip3, doc["ip3"] | "");
strcpy(config.ip4, doc["ip4"] | "");
strcpy(config.name1, doc["name1"] | "Interface 1");
strcpy(config.name2, doc["name2"] | "Interface 2");
strcpy(config.name3, doc["name3"] | "Interface 3");
strcpy(config.name4, doc["name4"] | "Interface 4");
saveConfig();
// Démarrer le test de connexion
isTestingConnection = true;
testStartTime = millis();
testingIP = "";
// Mode AP+STA
WiFi.mode(WIFI_AP_STA);
WiFi.softAP("RMS_Dashboard_Config", "rms12345");
WiFi.begin(config.ssid, config.password);
Serial.println("Test de connexion au nouveau réseau...");
// Répondre immédiatement
server.send(200, "application/json", "{\"status\":\"testing\"}");
}
}
// Nouvelle route pour vérifier le statut
void handleCheckIP() {
String response = "{";
if (isTestingConnection) {
if (WiFi.status() == WL_CONNECTED && testingIP.isEmpty()) {
testingIP = WiFi.localIP().toString();
Serial.print("IP obtenue: ");
Serial.println(testingIP);
}
if (!testingIP.isEmpty()) {
response += "\"status\":\"connected\",\"ip\":\"" + testingIP + "\"";
} else if (millis() - testStartTime > 10000) {
response += "\"status\":\"failed\"";
isTestingConnection = false;
} else {
response += "\"status\":\"testing\"";
}
} else {
response += "\"status\":\"idle\"";
}
response += "}";
server.send(200, "application/json", response);
}
void handleConfig() {
String json = "{";
json += "\"ssid\":\"" + String(config.ssid) + "\",";
json += "\"ip1\":\"" + String(config.ip1) + "\",";
json += "\"ip2\":\"" + String(config.ip2) + "\",";
json += "\"ip3\":\"" + String(config.ip3) + "\",";
json += "\"ip4\":\"" + String(config.ip4) + "\",";
json += "\"name1\":\"" + String(config.name1) + "\",";
json += "\"name2\":\"" + String(config.name2) + "\",";
json += "\"name3\":\"" + String(config.name3) + "\",";
json += "\"name4\":\"" + String(config.name4) + "\"";
json += "}";
server.send(200, "application/json", json);
}
void handleReset() {
clearConfig();
server.send(200, "text/plain", "Config cleared");
}
void setup() {
Serial.begin(115200);
// Charger la config
loadConfig();
if (isAPMode) {
// Mode AP pour configuration
startAPMode();
} else {
// Mode normal - connexion WiFi
WiFi.begin(config.ssid, config.password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() != WL_CONNECTED) {
// Échec connexion, retour en mode AP
isAPMode = true;
startAPMode();
} else {
Serial.println("\nConnecté!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
}
// Routes
server.on("/", handleRoot);
server.on("/login", handleLogin);
server.on("/config", handleConfigPage); // Page de configuration
server.on("/scan", handleScan);
server.on("/save", HTTP_POST, handleSave);
server.on("/getconfig", handleConfig); // API pour récupérer la config
server.on("/reset", handleReset);
server.on("/checkip", handleCheckIP); // NOUVELLE ROUTE POUR VÉRIFIER L'IP
server.on("/networkinfo", handleNetworkInfo);
server.begin();
Serial.println("Serveur démarré");
}
void loop() {
server.handleClient();
// Si on est en train de tester la connexion, redémarrer après 15 secondes
if (isTestingConnection && millis() - testStartTime > 15000) {
ESP.restart();
}
}
ESP32Wroom, Triac 40A "BTA40", Source UxIx2, Cumulus 300L 3000W.
Sonde temperature sur radiateur triac mise en route ventilateur a 25°
réactivité 30 seuil -50
2 esp32 pour gestion charge batteries
14 panneaux de 410wcc en autoconso micro-onduleur APS DS3
Suivi sur Domoticz
Sonde temperature sur radiateur triac mise en route ventilateur a 25°
réactivité 30 seuil -50
2 esp32 pour gestion charge batteries
14 panneaux de 410wcc en autoconso micro-onduleur APS DS3
Suivi sur Domoticz