add fanchars
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "fan_step_response.h"
|
||||
#include "fan_characterize.h"
|
||||
#include "pid_autotuner.h"
|
||||
#include "pid_controller.h"
|
||||
#include "settings_store.h"
|
||||
@@ -11,19 +11,20 @@
|
||||
|
||||
class ThermalController {
|
||||
public:
|
||||
enum class HeaterBlock : uint8_t { None, Cutoff, Corner, Autotune, StepResp };
|
||||
enum class HeaterBlock : uint8_t { None, Cutoff, Corner, Autotune, FanChars };
|
||||
|
||||
ThermalController()
|
||||
: heatPi_(HEAT_PI_KP, HEAT_PI_KI, 0.0f, 0.0f, 100.0f),
|
||||
mixPi_(MIX_PI_KP, MIX_PI_KI, 0.0f, 0.0f, 255.0f),
|
||||
autotuner_(),
|
||||
stepresp_(),
|
||||
fanchars_(),
|
||||
targetTempC_(TARGET_TEMP_C),
|
||||
heaterDutyPercent_(0.0f),
|
||||
heaterAllowancePercent_(100.0f),
|
||||
cornerSpreadC_(0.0f),
|
||||
lastMaxTempC_(0.0f),
|
||||
regulatingFanPwm_(0),
|
||||
stirFanPwm_(FAN_IDLE_PWM),
|
||||
fanPwm_(0),
|
||||
tuningLoaded_(false),
|
||||
fanIdleOverride_(false),
|
||||
@@ -70,9 +71,13 @@ public:
|
||||
}
|
||||
|
||||
SettingsData settings;
|
||||
if (settingsLoad(settings) && settings.targetC >= TARGET_MIN_C &&
|
||||
settings.targetC <= TARGET_MAX_C) {
|
||||
setTarget(settings.targetC, false);
|
||||
if (settingsLoad(settings)) {
|
||||
if (settings.stirFanPwm > 0) {
|
||||
stirFanPwm_ = settings.stirFanPwm;
|
||||
}
|
||||
if (settings.targetC >= TARGET_MIN_C && settings.targetC <= TARGET_MAX_C) {
|
||||
setTarget(settings.targetC, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +145,7 @@ public:
|
||||
bool isTuningLoaded() const { return tuningLoaded_; }
|
||||
|
||||
bool startAutotune(float setpointC) {
|
||||
if (autotuner_.isActive() || stepresp_.isActive()) {
|
||||
if (autotuner_.isActive() || fanchars_.isActive()) {
|
||||
return false;
|
||||
}
|
||||
cutoffActive_ = false;
|
||||
@@ -163,46 +168,65 @@ public:
|
||||
|
||||
float autotunePreheatTargetC() const { return autotuner_.preheatTargetC(); }
|
||||
|
||||
bool startStepResponse(float targetC, float heaterPct) {
|
||||
if (autotuner_.isActive() || stepresp_.isActive()) {
|
||||
bool startFanCharacterize(float maxCornerC, float avgTempC) {
|
||||
if (autotuner_.isActive() || fanchars_.isActive()) {
|
||||
return false;
|
||||
}
|
||||
stopFanTest();
|
||||
cutoffActive_ = false;
|
||||
heatPi_.reset();
|
||||
mixPi_.reset();
|
||||
setTarget(targetC, false);
|
||||
if (!stepresp_.start(targetC, heaterPct)) {
|
||||
setTarget(0.0f, false);
|
||||
return fanchars_.start(maxCornerC, avgTempC);
|
||||
}
|
||||
|
||||
void stopFanCharacterize() {
|
||||
fanchars_.abort();
|
||||
forceHeaterOff();
|
||||
writeFan(0);
|
||||
}
|
||||
|
||||
bool isFanCharacterizeActive() const { return fanchars_.isActive(); }
|
||||
|
||||
uint32_t fanCharacterizeElapsedMs(uint32_t nowMs) const { return fanchars_.elapsedMs(nowMs); }
|
||||
|
||||
const char *fanCharacterizePhaseName() const { return fanchars_.phaseName(); }
|
||||
|
||||
uint8_t fanCharacterizeProfileIndex() const { return fanchars_.profileIndex(); }
|
||||
|
||||
uint8_t fanCharacterizeProfileCount() const { return fanchars_.profileCount(); }
|
||||
|
||||
uint8_t fanCharacterizeFanPwm() const { return fanchars_.currentFanPwm(); }
|
||||
|
||||
bool isFanCharacterizeRefineRun() const { return fanchars_.isRefineRun(); }
|
||||
|
||||
float fanCharacterizeHeaterPct() const { return fanchars_.heaterPct(); }
|
||||
|
||||
uint8_t stirFanPwm() const { return stirFanPwm_; }
|
||||
|
||||
void logFanCharacterizeIfDue(const float *sensorTemps, const bool *sensorValid, uint8_t sensorCount,
|
||||
float avgTempC, float minTempC, float maxTempC, float spreadC,
|
||||
uint32_t nowMs) {
|
||||
fanchars_.logIfDue(sensorTemps, sensorValid, sensorCount, avgTempC, minTempC, maxTempC,
|
||||
spreadC, nowMs);
|
||||
}
|
||||
|
||||
bool saveStirFanFromCharacterize() {
|
||||
if (fanchars_.phase() != FanCharacterize::Phase::Done) {
|
||||
return false;
|
||||
}
|
||||
writeFan(0);
|
||||
const uint8_t winner = fanchars_.winnerFanPwm();
|
||||
if (winner == 0) {
|
||||
return false;
|
||||
}
|
||||
stirFanPwm_ = winner;
|
||||
settingsSaveStirFan(winner);
|
||||
Serial.print(F("stir fan "));
|
||||
Serial.println(winner);
|
||||
fanchars_.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void stopStepResponse() {
|
||||
stepresp_.abort();
|
||||
writeFan(0);
|
||||
}
|
||||
|
||||
bool isStepResponseActive() const { return stepresp_.isActive(); }
|
||||
|
||||
uint32_t stepResponseElapsedMs(uint32_t nowMs) const { return stepresp_.elapsedMs(nowMs); }
|
||||
|
||||
const char *stepResponsePhaseName() const { return stepresp_.phaseName(); }
|
||||
|
||||
uint8_t stepResponseStepIndex() const { return stepresp_.stepIndex(); }
|
||||
|
||||
uint8_t stepResponseStepCount() const { return stepresp_.stepCount(); }
|
||||
|
||||
float stepResponseHeaterPct() const { return stepresp_.heaterPct(); }
|
||||
|
||||
void logStepResponseIfDue(const float *sensorTemps, const bool *sensorValid, uint8_t sensorCount,
|
||||
float avgTempC, float minTempC, float maxTempC, float spreadC,
|
||||
uint32_t nowMs) {
|
||||
stepresp_.logIfDue(sensorTemps, sensorValid, sensorCount, avgTempC, minTempC, maxTempC,
|
||||
spreadC, nowMs);
|
||||
}
|
||||
|
||||
bool commitAutotuneIfDone() {
|
||||
if (autotuner_.phase() != PidAutotuner::Phase::Done) {
|
||||
return false;
|
||||
@@ -331,8 +355,8 @@ public:
|
||||
return "corner";
|
||||
case HeaterBlock::Autotune:
|
||||
return "autotune";
|
||||
case HeaterBlock::StepResp:
|
||||
return "stepresp";
|
||||
case HeaterBlock::FanChars:
|
||||
return "fanchars";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
@@ -352,8 +376,8 @@ public:
|
||||
SPREAD_EMA_ALPHA * cornerSpreadC_ +
|
||||
(1.0f - SPREAD_EMA_ALPHA) * cornerSpreadC;
|
||||
|
||||
if (stepresp_.isActive()) {
|
||||
updateStepResponse(avgTempC, maxTempC, nowMs);
|
||||
if (fanchars_.isActive()) {
|
||||
updateFanCharacterize(avgTempC, maxTempC, nowMs);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -386,7 +410,7 @@ public:
|
||||
heatPi_.reset();
|
||||
mixPi_.reset();
|
||||
autotuner_.abort();
|
||||
stepresp_.abort();
|
||||
fanchars_.abort();
|
||||
}
|
||||
|
||||
void forceHeaterOff() {
|
||||
@@ -425,22 +449,17 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void updateStepResponse(float avgTempC, float maxTempC, uint32_t nowMs) {
|
||||
void updateFanCharacterize(float avgTempC, float maxTempC, uint32_t nowMs) {
|
||||
float duty = 0.0f;
|
||||
uint8_t fan = 0;
|
||||
stepresp_.update(avgTempC, maxTempC, cornerSpreadC_, nowMs, duty, fan);
|
||||
fanchars_.update(avgTempC, maxTempC, cornerSpreadC_, nowMs, duty, fan);
|
||||
|
||||
heaterDutyPercent_ = duty;
|
||||
heaterAllowancePercent_ = duty;
|
||||
heaterBlock_ = duty > 0.0f ? HeaterBlock::StepResp : HeaterBlock::None;
|
||||
heaterBlock_ = duty > 0.0f ? HeaterBlock::FanChars : HeaterBlock::None;
|
||||
applyHeaterBurst(nowMs);
|
||||
writeFan(fan);
|
||||
lastHeaterUpdateMs_ = nowMs;
|
||||
|
||||
if (stepresp_.phase() == FanStepResponse::Phase::Done ||
|
||||
stepresp_.phase() == FanStepResponse::Phase::Failed) {
|
||||
stepresp_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void updateAutotune(float avgTempC, float maxTempC, uint32_t nowMs) {
|
||||
@@ -504,13 +523,28 @@ private:
|
||||
}
|
||||
heaterDutyPercent_ = applyHeaterRamp(duty, avgTempC, nowMs);
|
||||
|
||||
const float mixInput = SPREAD_TARGET_C - cornerSpreadC_;
|
||||
float fanOut = mixPi_.compute(mixInput, nowMs);
|
||||
uint8_t fanPwm = static_cast<uint8_t>(fanOut + 0.5f);
|
||||
if (avgTempC < targetTempC_ - HEAT_UP_BAND_C && fanPwm > FAN_COLD_CAP_PWM) {
|
||||
fanPwm = FAN_COLD_CAP_PWM;
|
||||
uint8_t fanPwm = stirFanPwm_;
|
||||
if (avgTempC < targetTempC_) {
|
||||
mixPi_.reset();
|
||||
} else {
|
||||
const float mixInput = SPREAD_TARGET_C - cornerSpreadC_;
|
||||
float fanOut = mixPi_.compute(mixInput, nowMs);
|
||||
fanPwm = static_cast<uint8_t>(fanOut + 0.5f);
|
||||
if (fanPwm > FAN_MIX_MAX_PWM) {
|
||||
fanPwm = FAN_MIX_MAX_PWM;
|
||||
}
|
||||
}
|
||||
regulatingFanPwm_ = fanPwm;
|
||||
regulatingFanPwm_ = fanWithMinStir(avgTempC, fanPwm);
|
||||
}
|
||||
|
||||
uint8_t fanWithMinStir(float avgTempC, uint8_t pwm) const {
|
||||
if (avgTempC < IDLE_AUTO_FAN_OFF_TEMP_C) {
|
||||
return 0;
|
||||
}
|
||||
if (pwm < stirFanPwm_) {
|
||||
return stirFanPwm_;
|
||||
}
|
||||
return pwm;
|
||||
}
|
||||
|
||||
static float clampPercent(float value) {
|
||||
@@ -633,13 +667,14 @@ private:
|
||||
PidController heatPi_;
|
||||
PidController mixPi_;
|
||||
PidAutotuner autotuner_;
|
||||
FanStepResponse stepresp_;
|
||||
FanCharacterize fanchars_;
|
||||
float targetTempC_;
|
||||
float heaterDutyPercent_;
|
||||
float heaterAllowancePercent_;
|
||||
float cornerSpreadC_;
|
||||
float lastMaxTempC_;
|
||||
uint8_t regulatingFanPwm_;
|
||||
uint8_t stirFanPwm_;
|
||||
uint8_t fanPwm_;
|
||||
bool tuningLoaded_;
|
||||
bool fanIdleOverride_;
|
||||
|
||||
Reference in New Issue
Block a user