diff --git a/kugelmuehle.ino b/kugelmuehle.ino index a708f27..c3cf037 100644 --- a/kugelmuehle.ino +++ b/kugelmuehle.ino @@ -43,6 +43,7 @@ bool stopMove = false; // Start/Stopp von der Weboberfläche (nur ohne Zeitschaltuhr) bool webRun = false; +bool webStopReq = false; // DEBUG // Laufzeit-Vorgabe per Web (0 = unbegrenzt). @@ -237,12 +238,14 @@ void handleWebCommands() { case WC_RUN: // Nur ohne Zeitschaltuhr erlaubt if (!settings.timerEnabled) { - webRun = (c.v[0] != 0); - if (webRun) { - // v[1] = Dauer in Sekunden (0 = unbegrenzt, max. 30 Tage) + if (c.v[0] != 0) { + webRun = true; uint32_t sec = (uint32_t)constrain(c.v[1], 0, 2592000); webRunStartMs = millis(); webRunDurMs = sec * 1000UL; + } else { + webRun = false; + webStopReq = true; // Stopp-Anforderung aus dem Web vermerken } } break; @@ -672,9 +675,30 @@ void applyMotor(MotorCmd cmd, int32_t speed) { case MOTOR_MANUAL:{ if (speed != lastSpeed) { - stepper->setSpeedInHz(MAX_SPEED_HZ); - stepper->setAcceleration(settings.accelRate); - stepper->moveByAcceleration(speed, true); + if(speed == 1){ + // Plus taste Beschleunigen bis max Speed + stepper->setAcceleration(settings.accelRate); + stepper->setSpeedInHz(MAX_SPEED_HZ); + stepper->runForward(); + }else if(speed == -1){ + if (stepper->isRunning()){ + // Minus Taste bremsen bis fast stillstand + stepper->setAcceleration(settings.accelRate); + stepper->setSpeedInHz(50); // Zeiel Speed 10 + stepper->runForward(); + } + }else{ + // Keine Taste gedrückt Geschwindigkeit halten + uint32_t currentSpeedUs = stepper->getCurrentSpeedInUs(); + if(currentSpeedUs > 0){ + uint32_t currentHz = 1000000/currentSpeedUs; + if(currentHz < 10){ + currentHz = 10; // Absicherunng gegen 0 Hz + } + stepper->setSpeedInHz(currentHz); + stepper->applySpeedAcceleration(); + } + } lastSpeed = speed; lastAutoHz = 0; } @@ -798,9 +822,10 @@ void loop() { motorCmd = MOTOR_STOP; speed = 0; motorCmd = MOTOR_STOP; - } else if (clickStop) { + } else if (clickStop || webStopReq) { manualStopped = true; webRun = false; + webStopReq = false; speed = 0; motorCmd = MOTOR_STOP; } else if (manualStopped) { @@ -809,10 +834,10 @@ void loop() { } speed = 0; // während Sperre: nichts tun } else if (holdPlus) { - speed = settings.accelRate; + speed = 1; webRun = false; } else if (holdMinus) { - speed = -settings.accelRate; + speed = -1; webRun = false; } else if (webRun) { motorCmd = MOTOR_AUTO; // Web-Start: läuft mit autoSpeed diff --git a/web_ui.h b/web_ui.h index 24f90a5..6a710c8 100644 --- a/web_ui.h +++ b/web_ui.h @@ -118,7 +118,8 @@ const $ = id => document.getElementById(id); let ws, last = {}; function connect() { - ws = new WebSocket(`ws://${location.host}/ws`); + const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; + ws = new WebSocket(`${protocol}//${location.host}/ws`); ws.onopen = () => $('conn').classList.add('on'); ws.onclose = () => { $('conn').classList.remove('on'); setTimeout(connect, 2000); }; ws.onmessage = e => render(JSON.parse(e.data)); @@ -130,7 +131,6 @@ function send(cmd, v, sec) { const pad = n => String(n).padStart(2, '0'); -// Sekunden -> "HH:MM:SS" bzw. "2 T HH:MM:SS" function fmt(s) { s = Math.max(0, Math.floor(s)); const d = Math.floor(s / 86400); s %= 86400; @@ -139,16 +139,16 @@ function fmt(s) { return (d > 0 ? d + ' T ' : '') + pad(h) + ':' + pad(m) + ':' + pad(s % 60); } -// Voraussichtliches Ende (Uhr des Browsers) function endText(remainSec) { const end = new Date(Date.now() + remainSec * 1000); const t = pad(end.getHours()) + ':' + pad(end.getMinutes()) + ' Uhr'; if (end.toDateString() === new Date().toDateString()) return t; return end.toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit' }) + ' ' + t; } + function setIfIdle(id, value) { const el = $(id); - if (document.activeElement !== el) el.value = value; // nicht überschreiben, während getippt/gezogen wird + if (document.activeElement !== el) el.value = value; } function render(d) { @@ -165,10 +165,10 @@ function render(d) { ? ' · noch ' + fmt(d.remain) + ' (bis ' + endText(d.remain) + ')' : ''; - $('btnTimed').disabled = d.overheat || d.timerOn; + $('btnTimed').disabled = d.overheat || d.timerOn || d.running; $('timedInfo').textContent = d.timerOn ? 'Gesperrt, solange die Zeitschaltuhr aktiv ist.' - : 'Läuft ab jetzt für die gewählte Dauer und stoppt dann von selbst (max. 30 Tage). Nur per Web möglich.'; + : (d.running ? 'Motor läuft bereits.' : 'Läuft ab jetzt für die gewählte Dauer und stoppt dann von selbst (max. 30 Tage). Nur per Web möglich.'); const st = $('state'); st.className = 'state'; @@ -176,10 +176,12 @@ function render(d) { else if (d.running) { st.textContent = 'Läuft'; st.classList.add('run'); } else { st.textContent = 'Gestoppt'; } + // ZENTRALE ÄNDERUNG: Prüft d.running ODER d.webRun + const active = d.running || d.webRun; const run = $('btnRun'); run.disabled = d.overheat || d.timerOn; - run.textContent = d.webRun ? 'Stopp' : 'Start'; - run.className = d.webRun ? 'stop' : 'go'; + run.textContent = active ? 'Stopp' : 'Start'; + run.className = active ? 'stop' : 'go'; $('runHint').textContent = d.timerOn ? 'Start/Stopp ist gesperrt, solange die Zeitschaltuhr aktiv ist.' : ''; const tm = $('btnTimer'); @@ -194,7 +196,11 @@ function render(d) { setIfIdle('accel', d.accel); } -function toggleRun() { send('run', last.webRun ? 0 : 1); } +// ZENTRALE ÄNDERUNG: Stoppt sobald d.running oder d.webRun aktiv ist +function toggleRun() { + const active = last.running || last.webRun; + send('run', active ? 0 : 1); +} function startTimed() { const sec = Math.round(+$('durVal').value * +$('durUnit').value); @@ -205,6 +211,7 @@ function startTimed() { if (sec > 12 * 3600 && !confirm('Die Mühle läuft dann ' + fmt(sec) + ' lang ohne Aufsicht weiter. Wirklich starten?')) return; send('run', 1, sec); } + function toggleTimer() { send('timer', last.timerOn ? 0 : 1); } function sendWindow() {