From 3c70f2c0a1afcd2bdd18d3666d2eecb81e3fcc04 Mon Sep 17 00:00:00 2001 From: scayac Date: Wed, 17 Dec 2025 22:03:10 +0100 Subject: [PATCH] =?UTF-8?q?R=C3=A9actualisation=20interface=20reload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/app.js | 76 +++++++++++++++++++++++++++++++++++++++++++++++-- data/index.html | 6 ++-- src/main.cpp | 39 ++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 7 deletions(-) diff --git a/data/app.js b/data/app.js index 121baec..9c6e587 100644 --- a/data/app.js +++ b/data/app.js @@ -18,6 +18,34 @@ function connectWS() { powerBar.value = pct; powerBarValue.textContent = pct + '%'; } + + // Mettre à jour l'état des boutons selon le mode (manuel/auto) + if (typeof data.auto !== 'undefined' || typeof data.manual !== 'undefined') { + try { + updateButton(btnPreheat, !!data.manual); + updateButton(btnProfil, !!data.auto); + } catch (e) { + // ignore si les boutons ne sont pas encore disponibles + } + } + + // Si mode automatique, appliquer la classe 'active' sur la row correspondante + if (typeof data.step !== 'undefined') { + const mapping = {0: 'row-preheat', 1: 'row-soak', 2: 'row-reflow'}; + // Retirer la classe active de toutes + Object.values(mapping).forEach(id => { + const el = document.getElementById(id); + if (el) el.classList.remove('active'); + }); + // Si auto activé, ajouter la classe sur la step active + if (data.auto) { + const target = mapping[data.step]; + if (target) { + const el = document.getElementById(target); + if (el) el.classList.add('active'); + } + } + } } catch { if (tempDiv) tempDiv.textContent = e.data; } @@ -26,6 +54,47 @@ function connectWS() { connectWS(); +// Récupérer l'état initial depuis le serveur (après reload de la page) +function refreshState() { + fetch('/state').then(r => { + if (!r.ok) throw new Error('no state'); + return r.json(); + }).then(data => { + const btnPreheatEl = document.getElementById('validateChauffeManuelle'); + const btnProfilEl = document.getElementById('validateChauffeAuto'); + if (btnPreheatEl) updateButton(btnPreheatEl, !!data.manual); + if (btnProfilEl) updateButton(btnProfilEl, !!data.auto); + const powerBar = document.getElementById('power-bar'); + const powerBarValue = document.getElementById('power-bar-value'); + if (powerBar && typeof data.power !== 'undefined') { + let pct = Math.max(0, Math.min(100, Math.round(data.power))); + powerBar.value = pct; + if (powerBarValue) powerBarValue.textContent = pct + '%'; + } + // Ajuster la jauge de préchauffage sur la valeur de setpoint si présente + const gauge = document.getElementById('preheat-gauge'); + const gaugeValue = document.getElementById('preheat-gauge-value'); + if (gauge && gaugeValue && typeof data.setpoint !== 'undefined') { + gauge.value = Math.round(data.setpoint); + gaugeValue.textContent = gauge.value + '°C'; + } + + // Gérer la classe 'active' sur les lignes (si mode auto) + const mapping = {0: 'row-preheat', 1: 'row-soak', 2: 'row-reflow'}; + Object.values(mapping).forEach(id => { + const el = document.getElementById(id); + if (el) el.classList.remove('active'); + }); + if (data.auto && typeof data.step !== 'undefined') { + const target = mapping[data.step]; + if (target) { + const el = document.getElementById(target); + if (el) el.classList.add('active'); + } + } + }).catch(err => console.log('refreshState failed', err)); +} + // Fonction utilitaire pour modifier l'aspect d'un bouton function updateButton(btn, isActive) { if (isActive) { @@ -37,9 +106,8 @@ function updateButton(btn, isActive) { } } -// Initialisation : aucun mode actif -updateButton(btnPreheat, false); -updateButton(btnProfil, false); +// L'état initial est chargé depuis le serveur + btnPreheat.onclick = function() { if (btnPreheat.className === 'stop') { @@ -124,4 +192,6 @@ document.addEventListener('DOMContentLoaded', () => { } }; } + // Après que le DOM est prêt, récupérer et appliquer l'état initial + refreshState(); }); diff --git a/data/index.html b/data/index.html index c61c680..55b43ca 100644 --- a/data/index.html +++ b/data/index.html @@ -26,17 +26,17 @@

Chauffe automatique :

-
+
°C s
-
+
°C s
-
+
°C s diff --git a/src/main.cpp b/src/main.cpp index eebcfcd..5878373 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,7 @@ int preheatTemp = 0, preheatTime = 0; int soakTemp = 0, soakTime = 0; int reflowTemp = 0, reflowTime = 0; bool autoProfileActive = false; +bool manualPreheatActive = false; unsigned long profileStartTime = 0; int profileStep = 0; @@ -134,6 +135,7 @@ void setup() { server.on("/action/stop", HTTP_POST, [](AsyncWebServerRequest *request){ setpoint = 0; autoProfileActive = false; + manualPreheatActive = false; profileStep = 0; Serial.println("Arrêt demandé : setpoint PID à 0°C"); request->send(200, "text/plain", "OK"); @@ -152,6 +154,7 @@ void setup() { Serial.println("Mode automatique désactivé (chauffe manuelle)"); } setpoint = request->arg("temp").toDouble(); + manualPreheatActive = true; Serial.printf("Chauffe manuelle demandée : %.1f°C (setpoint PID fixé)\n", setpoint); request->send(200, "text/plain", "OK"); }); @@ -172,6 +175,7 @@ void setup() { reflowTime = request->arg("reflowTime").toInt(); // Démarrage du profil automatique autoProfileActive = true; + manualPreheatActive = false; profileStep = 0; profileStartTime = millis(); setpoint = preheatTemp; @@ -180,6 +184,32 @@ void setup() { request->send(200, "text/plain", "OK"); }); + // Endpoint pour renvoyer l'état actuel (utile pour réactualiser l'UI après reload) + server.on("/state", HTTP_GET, [](AsyncWebServerRequest *request){ + int pct = 0; + if (output > 0) { + pct = (int)round((output * 100) / PWM_PERIOD); + if (pct > 100) pct = 100; + if (pct < 0) pct = 0; + } + int preheatProgress = 0; + if (autoProfileActive && profileStep == 0 && preheatTime > 0) { + unsigned long elapsed = (millis() - profileStartTime) / 1000; + unsigned long prog = (elapsed * 100UL) / (unsigned long)preheatTime; + if (prog > 100) prog = 100; + preheatProgress = (int)prog; + } + String json = String("{") + + "\"manual\":" + (manualPreheatActive ? "true" : "false") + + ",\"auto\":" + (autoProfileActive ? "true" : "false") + + ",\"power\":" + String(pct) + + ",\"setpoint\":" + String(setpoint, 1) + + ",\"preheatProgress\":" + String(preheatProgress) + + ",\"step\":" + String(profileStep) + + "}"; + request->send(200, "application/json", json); + }); + // Connexion WiFi ou démarrage AP si ssid/pwd vides if (strlen(ssid) == 0 || strlen(password) == 0) { Serial.println("SSID ou mot de passe vide, démarrage en mode AP"); @@ -299,6 +329,7 @@ void loop() { if (elapsed >= (unsigned long)reflowTime) { profileStep = 3; autoProfileActive = false; + setpoint = 0; // Reflow terminé -> couper la chauffe Serial.println("Profil terminé"); } } @@ -328,7 +359,13 @@ void loop() { pct = (int)round((output * 100) / PWM_PERIOD); if (pct > 100) pct = 100; } - String json = String("{\"temp\":") + String(input, 1) + ",\"setpoint\":" + String(setpoint, 1) + ",\"output\":" + String(pct) + "}"; + String json = String("{\"temp\":") + String(input, 1) + + ",\"setpoint\":" + String(setpoint, 1) + + ",\"output\":" + String(pct) + + ",\"manual\":" + (manualPreheatActive ? "true" : "false") + + ",\"auto\":" + (autoProfileActive ? "true" : "false") + + ",\"step\":" + String(profileStep) + + "}"; Serial.println("Température: " + String(input, 1) + " °C | Setpoint: " + String(setpoint, 1) + " | Output PID: " + String(output) + " | Puissance: " + String(pct) + "%"); ws.textAll(json); }