fix tui
This commit is contained in:
@@ -75,13 +75,14 @@ static const float SPREAD_EMA_ALPHA = 0.45f;
|
||||
|
||||
// PID auto-tune (relay method) — run with: autotune 45
|
||||
static const float AUTOTUNE_HYSTERESIS_C = 0.4f;
|
||||
static const float AUTOTUNE_PREHEAT_BAND_C = 5.0f;
|
||||
static const float AUTOTUNE_PREHEAT_BAND_C = 3.0f;
|
||||
static const float AUTOTUNE_PREHEAT_DUTY = 100.0f;
|
||||
static const uint8_t AUTOTUNE_PREHEAT_FAN_PWM = 70; // low fan for entire autotune
|
||||
static const uint8_t AUTOTUNE_CYCLES_REQUIRED = 6;
|
||||
static const uint8_t AUTOTUNE_PREHEAT_FAN_PWM = 0; // fan off — maximize heat-up
|
||||
static const uint8_t AUTOTUNE_CYCLES_REQUIRED = 5;
|
||||
static const uint32_t AUTOTUNE_PREHEAT_TIMEOUT_MS = 1200000UL; // 20 min
|
||||
static const uint32_t AUTOTUNE_SESSION_TIMEOUT_MS = 3600000UL; // 60 min total
|
||||
static const uint32_t AUTOTUNE_RELAY_PERIOD_MAX_MS = 2400000UL; // count periods up to 40 min
|
||||
static const uint32_t AUTOTUNE_RELAY_STALL_MS = 1500000UL; // 25 min in relay, 0 cycles
|
||||
static const uint32_t AUTOTUNE_SESSION_TIMEOUT_MS = 3600000UL; // 60 min total
|
||||
static const uint32_t AUTOTUNE_RELAY_PERIOD_MAX_MS = 2400000UL;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timing
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
spreadSamples_(0),
|
||||
cycleCount_(0),
|
||||
aboveSetpoint_(false),
|
||||
useMaxSensorPv_(false),
|
||||
sessionStartMs_(0),
|
||||
phaseStartMs_(0),
|
||||
resultKp_(PID_KP),
|
||||
@@ -47,12 +48,14 @@ public:
|
||||
|
||||
float preheatTargetC() const { return setpointC_ - AUTOTUNE_PREHEAT_BAND_C; }
|
||||
|
||||
bool usesMaxSensor() const { return useMaxSensorPv_; }
|
||||
|
||||
const char *phaseName() const {
|
||||
switch (phase_) {
|
||||
case Phase::Preheat:
|
||||
return "preheat";
|
||||
case Phase::Relay:
|
||||
return "relay";
|
||||
return useMaxSensorPv_ ? "relay-max" : "relay-avg";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@@ -124,8 +127,8 @@ public:
|
||||
fail(F("autotune: abort — preheat timeout"));
|
||||
return phase_;
|
||||
}
|
||||
if (avgTempC >= preheatTargetC()) {
|
||||
enterRelay(avgTempC, nowMs);
|
||||
if (avgTempC >= preheatTargetC() || maxTempC >= setpointC_ - 2.0f) {
|
||||
enterRelay(avgTempC, maxTempC, spreadC, nowMs);
|
||||
} else {
|
||||
heaterDutyOut = AUTOTUNE_PREHEAT_DUTY;
|
||||
}
|
||||
@@ -137,27 +140,44 @@ public:
|
||||
return phase_;
|
||||
}
|
||||
|
||||
if (nowMs - sessionStartMs_ > AUTOTUNE_SESSION_TIMEOUT_MS) {
|
||||
fail(F("autotune: abort — session timeout"));
|
||||
return phase_;
|
||||
}
|
||||
|
||||
if (cycleCount_ == 0 && nowMs - phaseStartMs_ > AUTOTUNE_RELAY_STALL_MS) {
|
||||
Serial.print(F("autotune: relay stalled — avg "));
|
||||
Serial.print(avgTempC, 1);
|
||||
Serial.print(F("C max "));
|
||||
Serial.print(maxTempC, 1);
|
||||
Serial.println(F("C (spread too large for avg to cross setpoint?)"));
|
||||
fail(F("autotune: abort — no oscillation"));
|
||||
return phase_;
|
||||
}
|
||||
|
||||
const float pv = useMaxSensorPv_ ? maxTempC : avgTempC;
|
||||
|
||||
spreadSum_ += spreadC;
|
||||
++spreadSamples_;
|
||||
|
||||
if (avgTempC > peakSinceCross_) {
|
||||
peakSinceCross_ = avgTempC;
|
||||
if (pv > peakSinceCross_) {
|
||||
peakSinceCross_ = pv;
|
||||
}
|
||||
if (avgTempC < valleySinceCross_) {
|
||||
valleySinceCross_ = avgTempC;
|
||||
if (pv < valleySinceCross_) {
|
||||
valleySinceCross_ = pv;
|
||||
}
|
||||
|
||||
bool heatOn = false;
|
||||
if (avgTempC <= relayLow_) {
|
||||
if (pv <= relayLow_) {
|
||||
heatOn = true;
|
||||
} else if (avgTempC >= relayHigh_) {
|
||||
} else if (pv >= relayHigh_) {
|
||||
heatOn = false;
|
||||
} else {
|
||||
heatOn = !aboveSetpoint_;
|
||||
}
|
||||
heaterDutyOut = heatOn ? 100.0f : 0.0f;
|
||||
|
||||
const bool nowAbove = avgTempC >= setpointC_;
|
||||
const bool nowAbove = pv >= setpointC_;
|
||||
if (nowAbove != aboveSetpoint_) {
|
||||
onSetpointCrossing(nowMs);
|
||||
aboveSetpoint_ = nowAbove;
|
||||
@@ -167,14 +187,18 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void enterRelay(float avgTempC, uint32_t nowMs) {
|
||||
void enterRelay(float avgTempC, float maxTempC, float spreadC, uint32_t nowMs) {
|
||||
phase_ = Phase::Relay;
|
||||
phaseStartMs_ = nowMs;
|
||||
aboveSetpoint_ = avgTempC >= setpointC_;
|
||||
peakSinceCross_ = avgTempC;
|
||||
valleySinceCross_ = avgTempC;
|
||||
useMaxSensorPv_ = spreadC > GOOD_SPREAD_C;
|
||||
const float pv = useMaxSensorPv_ ? maxTempC : avgTempC;
|
||||
aboveSetpoint_ = pv >= setpointC_;
|
||||
peakSinceCross_ = pv;
|
||||
valleySinceCross_ = pv;
|
||||
lastCrossMs_ = 0;
|
||||
Serial.print(F("autotune: relay test started ("));
|
||||
Serial.print(F("autotune: relay "));
|
||||
Serial.print(useMaxSensorPv_ ? F("max-sensor") : F("avg"));
|
||||
Serial.print(F(" ("));
|
||||
Serial.print((nowMs - sessionStartMs_) / 1000UL);
|
||||
Serial.println(F("s preheat)"));
|
||||
}
|
||||
@@ -191,6 +215,7 @@ private:
|
||||
spreadSamples_ = 0;
|
||||
cycleCount_ = 0;
|
||||
aboveSetpoint_ = false;
|
||||
useMaxSensorPv_ = false;
|
||||
}
|
||||
|
||||
void onSetpointCrossing(uint32_t nowMs) {
|
||||
@@ -295,6 +320,7 @@ private:
|
||||
uint16_t spreadSamples_;
|
||||
uint8_t cycleCount_;
|
||||
bool aboveSetpoint_;
|
||||
bool useMaxSensorPv_;
|
||||
uint32_t sessionStartMs_;
|
||||
uint32_t phaseStartMs_;
|
||||
float resultKp_;
|
||||
|
||||
Reference in New Issue
Block a user