refine logging
This commit is contained in:
@@ -51,6 +51,7 @@ static const float APPROACH_BAND_C = 4.0f;
|
||||
// When spread is good, allow hottest corner slightly above target so avg can reach setpoint
|
||||
static const float GOOD_SPREAD_C = 5.0f;
|
||||
static const float BALANCED_MAX_ABOVE_TARGET_C = 2.0f;
|
||||
static const float SPREAD_HEADROOM_FACTOR = 0.5f; // extra max-corner °C per °C of spread
|
||||
|
||||
static const uint16_t HEATER_CYCLE_MS = 3000;
|
||||
|
||||
@@ -71,10 +72,13 @@ 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_DUTY = 80.0f;
|
||||
static const float AUTOTUNE_PREHEAT_DUTY = 100.0f;
|
||||
static const uint8_t AUTOTUNE_PREHEAT_FAN_PWM = 70; // light mixing only while preheating
|
||||
static const float AUTOTUNE_ABORT_ABOVE_C = 15.0f;
|
||||
static const uint8_t AUTOTUNE_CYCLES_REQUIRED = 6;
|
||||
static const uint32_t AUTOTUNE_TIMEOUT_MS = 1800000UL;
|
||||
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
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timing
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
spreadSamples_(0),
|
||||
cycleCount_(0),
|
||||
aboveSetpoint_(false),
|
||||
sessionStartMs_(0),
|
||||
phaseStartMs_(0),
|
||||
resultKp_(PID_KP),
|
||||
resultKi_(PID_KI),
|
||||
@@ -34,6 +35,29 @@ public:
|
||||
|
||||
bool isActive() const { return phase_ == Phase::Preheat || phase_ == Phase::Relay; }
|
||||
|
||||
uint32_t elapsedMs(uint32_t nowMs) const {
|
||||
if (sessionStartMs_ == 0) {
|
||||
return 0;
|
||||
}
|
||||
return nowMs - sessionStartMs_;
|
||||
}
|
||||
|
||||
uint8_t cycleCount() const { return cycleCount_; }
|
||||
uint8_t periodCount() const { return periodCount_; }
|
||||
|
||||
float preheatTargetC() const { return setpointC_ - AUTOTUNE_PREHEAT_BAND_C; }
|
||||
|
||||
const char *phaseName() const {
|
||||
switch (phase_) {
|
||||
case Phase::Preheat:
|
||||
return "preheat";
|
||||
case Phase::Relay:
|
||||
return "relay";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool start(float setpointC) {
|
||||
if (setpointC < 25.0f || setpointC > TARGET_MAX_C) {
|
||||
return false;
|
||||
@@ -44,10 +68,13 @@ public:
|
||||
relayLow_ = setpointC - AUTOTUNE_HYSTERESIS_C;
|
||||
resetMeasurements();
|
||||
phase_ = Phase::Preheat;
|
||||
phaseStartMs_ = millis();
|
||||
sessionStartMs_ = millis();
|
||||
phaseStartMs_ = sessionStartMs_;
|
||||
Serial.print(F("autotune: preheat to "));
|
||||
Serial.print(preheatTargetC(), 1);
|
||||
Serial.print(F("-"));
|
||||
Serial.print(setpointC_, 1);
|
||||
Serial.println(F("C"));
|
||||
Serial.println(F("C avg"));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -56,9 +83,13 @@ public:
|
||||
Serial.println(F("autotune: cancelled"));
|
||||
}
|
||||
phase_ = Phase::Idle;
|
||||
sessionStartMs_ = 0;
|
||||
}
|
||||
|
||||
void reset() { phase_ = Phase::Idle; }
|
||||
void reset() {
|
||||
phase_ = Phase::Idle;
|
||||
sessionStartMs_ = 0;
|
||||
}
|
||||
|
||||
float setpoint() const { return setpointC_; }
|
||||
|
||||
@@ -81,13 +112,23 @@ public:
|
||||
return phase_;
|
||||
}
|
||||
|
||||
if (nowMs - phaseStartMs_ > AUTOTUNE_TIMEOUT_MS) {
|
||||
fail(F("autotune: abort — timeout"));
|
||||
if (nowMs - sessionStartMs_ > AUTOTUNE_SESSION_TIMEOUT_MS) {
|
||||
fail(F("autotune: abort — session timeout"));
|
||||
return phase_;
|
||||
}
|
||||
|
||||
if (phase_ == Phase::Preheat) {
|
||||
if (avgTempC >= setpointC_ - AUTOTUNE_PREHEAT_BAND_C) {
|
||||
fanPwmOut = AUTOTUNE_PREHEAT_FAN_PWM;
|
||||
if (nowMs - phaseStartMs_ > AUTOTUNE_PREHEAT_TIMEOUT_MS) {
|
||||
Serial.print(F("autotune: preheat failed — avg "));
|
||||
Serial.print(avgTempC, 1);
|
||||
Serial.print(F("C after "));
|
||||
Serial.print((nowMs - sessionStartMs_) / 60000UL);
|
||||
Serial.println(F(" min"));
|
||||
fail(F("autotune: abort — preheat timeout"));
|
||||
return phase_;
|
||||
}
|
||||
if (avgTempC >= preheatTargetC()) {
|
||||
enterRelay(avgTempC, nowMs);
|
||||
} else {
|
||||
heaterDutyOut = AUTOTUNE_PREHEAT_DUTY;
|
||||
@@ -132,7 +173,9 @@ private:
|
||||
peakSinceCross_ = avgTempC;
|
||||
valleySinceCross_ = avgTempC;
|
||||
lastCrossMs_ = 0;
|
||||
Serial.println(F("autotune: relay test started"));
|
||||
Serial.print(F("autotune: relay test started ("));
|
||||
Serial.print((nowMs - sessionStartMs_) / 1000UL);
|
||||
Serial.println(F("s preheat)"));
|
||||
}
|
||||
|
||||
void resetMeasurements() {
|
||||
@@ -158,13 +201,18 @@ private:
|
||||
|
||||
Serial.print(F("autotune: cycle "));
|
||||
Serial.print(cycleCount_);
|
||||
Serial.print(F("/"));
|
||||
Serial.print(AUTOTUNE_CYCLES_REQUIRED);
|
||||
Serial.print(F(" amp="));
|
||||
Serial.println(amplitude, 2);
|
||||
Serial.print(amplitude, 2);
|
||||
Serial.print(F("C elapsed="));
|
||||
Serial.print((nowMs - sessionStartMs_) / 1000UL);
|
||||
Serial.println(F("s"));
|
||||
}
|
||||
|
||||
if (lastCrossMs_ > 0) {
|
||||
const uint32_t period = nowMs - lastCrossMs_;
|
||||
if (period > 8000 && period < 900000) {
|
||||
if (period > 8000 && period < AUTOTUNE_RELAY_PERIOD_MAX_MS) {
|
||||
periodSumMs_ += period;
|
||||
++periodCount_;
|
||||
}
|
||||
@@ -173,11 +221,11 @@ private:
|
||||
peakSinceCross_ = valleySinceCross_;
|
||||
|
||||
if (cycleCount_ >= AUTOTUNE_CYCLES_REQUIRED && periodCount_ >= 3 && amplitudeCount_ >= 3) {
|
||||
finish();
|
||||
finish(nowMs);
|
||||
}
|
||||
}
|
||||
|
||||
void finish() {
|
||||
void finish(uint32_t nowMs) {
|
||||
const float avgPeriodSec =
|
||||
static_cast<float>(periodSumMs_ / periodCount_) / 1000.0f;
|
||||
const float avgAmplitude = amplitudeSum_ / static_cast<float>(amplitudeCount_);
|
||||
@@ -212,7 +260,9 @@ private:
|
||||
}
|
||||
|
||||
phase_ = Phase::Done;
|
||||
Serial.println(F("autotune: done"));
|
||||
Serial.print(F("autotune: done in "));
|
||||
Serial.print((nowMs - sessionStartMs_) / 1000UL);
|
||||
Serial.println(F("s"));
|
||||
Serial.print(F(" Kp="));
|
||||
Serial.print(resultKp_, 3);
|
||||
Serial.print(F(" Ki="));
|
||||
@@ -226,6 +276,7 @@ private:
|
||||
void fail(const __FlashStringHelper *reason) {
|
||||
Serial.println(reason);
|
||||
phase_ = Phase::Failed;
|
||||
sessionStartMs_ = 0;
|
||||
}
|
||||
|
||||
Phase phase_;
|
||||
@@ -243,6 +294,7 @@ private:
|
||||
uint16_t spreadSamples_;
|
||||
uint8_t cycleCount_;
|
||||
bool aboveSetpoint_;
|
||||
uint32_t sessionStartMs_;
|
||||
uint32_t phaseStartMs_;
|
||||
float resultKp_;
|
||||
float resultKi_;
|
||||
|
||||
@@ -104,6 +104,16 @@ public:
|
||||
|
||||
bool isAutotuning() const { return autotuner_.isActive(); }
|
||||
|
||||
uint32_t autotuneElapsedMs(uint32_t nowMs) const { return autotuner_.elapsedMs(nowMs); }
|
||||
|
||||
const char *autotunePhaseName() const { return autotuner_.phaseName(); }
|
||||
|
||||
uint8_t autotuneCycleCount() const { return autotuner_.cycleCount(); }
|
||||
|
||||
uint8_t autotunePeriodCount() const { return autotuner_.periodCount(); }
|
||||
|
||||
float autotunePreheatTargetC() const { return autotuner_.preheatTargetC(); }
|
||||
|
||||
bool commitAutotuneIfDone() {
|
||||
if (autotuner_.phase() != PidAutotuner::Phase::Done) {
|
||||
return false;
|
||||
@@ -358,10 +368,22 @@ private:
|
||||
bool isBalancedChamber() const { return cornerSpreadC_ <= GOOD_SPREAD_C; }
|
||||
|
||||
float maxHeatStopTemp(float avgTempC) const {
|
||||
if (isBalancedChamber() && avgTempC < targetTempC_) {
|
||||
return targetTempC_ + BALANCED_MAX_ABOVE_TARGET_C;
|
||||
if (avgTempC >= targetTempC_) {
|
||||
return targetTempC_;
|
||||
}
|
||||
return targetTempC_;
|
||||
|
||||
float stopAt = targetTempC_;
|
||||
if (isBalancedChamber()) {
|
||||
stopAt = targetTempC_ + BALANCED_MAX_ABOVE_TARGET_C;
|
||||
} else {
|
||||
stopAt = targetTempC_ + cornerSpreadC_ * SPREAD_HEADROOM_FACTOR + 1.0f;
|
||||
}
|
||||
|
||||
const float belowCutoff = cutoffThreshold() - 0.1f;
|
||||
if (stopAt > belowCutoff) {
|
||||
stopAt = belowCutoff;
|
||||
}
|
||||
return stopAt;
|
||||
}
|
||||
|
||||
float allowanceFromMaxCorner(float maxTempC, float avgTempC) const {
|
||||
|
||||
Reference in New Issue
Block a user