diff --git a/src/lib/spoof.ts b/src/lib/spoof.ts index 422134c..93600c4 100644 --- a/src/lib/spoof.ts +++ b/src/lib/spoof.ts @@ -70,7 +70,7 @@ export class SpoofSocket { pwm_off: 4000, pid: 0, kp: 0.6, - ki: 0.1, + ki: 0.03, kd: 0.0, bake_phase: '', bake_remaining: 0 @@ -127,14 +127,16 @@ export class SpoofSocket { private applyMode(m: string): void { const s = this.s; + const wasPid = this.isPidMode(s.mode); s.mode = m; if (m === 'baking') this.bakeStart = Date.now(); if (m === 'manual') { s.relais = 0; this.lastSwitch = Date.now(); } - // Dump windup when the new target is below the current temp (e.g. -> pause) - if (this.isPidMode(m) && this.activeSetpoint() < s.temperature) this.integral = 0; + // Reset integral arriving from a non-PID mode (stale value) or when the + // new target is below the current temp (mirrors firmware applyMode). + if (this.isPidMode(m) && (!wasPid || this.activeSetpoint() < s.temperature)) this.integral = 0; } private handle(cmd: string, value?: number | string): void { @@ -208,7 +210,8 @@ export class SpoofSocket { const satHigh = output >= 100 && error > 0; const satLow = output <= 0 && error < 0; if (!satHigh && !satLow) { - this.integral = Math.max(-100, Math.min(100, this.integral + s.ki * error * dt)); + // integral clamped to ±70 to match the firmware (anti-overshoot) + this.integral = Math.max(-70, Math.min(70, this.integral + s.ki * error * dt)); } output = Math.max(0, Math.min(100, pTerm + this.integral)); s.pid = output; diff --git a/src/main.cpp b/src/main.cpp index a2a0664..6ec3bc2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,7 @@ unsigned long lastWebSocket = 0; const unsigned long WS_INTERVAL = 500; float temperature = 0; -float targetTemp = 370.0f; // default target temp for preheating, adjustable via UI +float targetTemp = 350.0f; // default target temp for preheating, adjustable via UI unsigned long lastSwitch = 0; float pwmSwitchDelayOn = 2000; // 2s @@ -35,8 +35,8 @@ float pwmSwitchDelayOff = 4000; // 4s float power = 100; -float kp = 0.55; -float ki = 0.005; +float kp = 0.6; +float ki = 0.03; float kd = 0.0; // Heating modes. PREHEAT, PAUSE and BAKING are all PI-regulated and only @@ -55,7 +55,11 @@ unsigned long bakeStart = 0; // millis() when BAKING was entered MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO); -PIDController pid(kp, ki, kd, 100.0, 0.0, 100.0); // Kp, Ki, Kd, max, min, max_integral +// integral capped at 70 (not 100): near the setpoint P≈0, so the integral alone +// supplies the holding power. Letting it reach 100 means full power right as we +// hit the setpoint → big overshoot on this high-lag oven. 70 still covers the +// steady-state duty needed at high setpoints. +PIDController pid(kp, ki, kd, 100.0, 0.0, 70.0); // Kp, Ki, Kd, max, min, max_integral static const char *modeToStr(Mode m) { @@ -96,6 +100,7 @@ static float activeSetpoint() // Centralised mode switch so every entry point stays consistent and safe. static void applyMode(Mode m) { + Mode prev = mode; mode = m; if (m == Mode::BAKING) @@ -110,9 +115,12 @@ static void applyMode(Mode m) if (isPidMode(m)) { pid.setSetpoint(activeSetpoint()); - // Dump integral windup when the new target is below the current temp - // (e.g. PREHEAT -> PAUSE) so we don't keep heating into an overshoot. - if (activeSetpoint() < temperature) + // Reset the integral when arriving from a non-PID mode (its value is stale + // and would dump straight into an overshoot) or when the new target is + // below the current temp (e.g. PREHEAT -> PAUSE, or BAKING -> PREHEAT). + // A PID -> PID hand-off at a higher target (e.g. PREHEAT -> BAKING) keeps + // the integral so the boost adds to the existing holding power. + if (!isPidMode(prev) || activeSetpoint() < temperature) pid.reset(); } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e0a12dd..2e9ad61 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -49,7 +49,7 @@ let pwmOff = $state(4); let kp = $state(0.6); - let ki = $state(0.1); + let ki = $state(0.03); let pauseGraphUpdate = $state(false); let darkMode = $state(true);