add fanchars

This commit is contained in:
2026-07-06 22:19:43 +02:00
parent 55cf03c015
commit 8182b9efd2
9 changed files with 296 additions and 439 deletions

View File

@@ -142,6 +142,10 @@ void printStatus(float avgTemp, float minTemp, float maxTemp) {
Serial.print(F("%)"));
if (thermal.isFanTestActive(millis())) {
Serial.print(F(" TEST"));
} else if (thermal.isFanCharacterizeActive()) {
Serial.print(F("(fanchars-"));
Serial.print(thermal.fanCharacterizePhaseName());
Serial.print(F(")"));
} else if (thermal.isIdle() && thermal.isFanOff()) {
Serial.print(F("(off)"));
} else if (thermal.isIdleCooling()) {
@@ -166,17 +170,29 @@ void printStatus(float avgTemp, float minTemp, float maxTemp) {
Serial.print(F("cyc pre>="));
Serial.print(thermal.autotunePreheatTargetC(), 0);
Serial.print(F("C"));
} else if (thermal.isStepResponseActive()) {
Serial.print(F("stepresp/"));
Serial.print(thermal.stepResponsePhaseName());
} else if (thermal.isFanCharacterizeActive()) {
Serial.print(F("fanchars/"));
Serial.print(thermal.fanCharacterizePhaseName());
Serial.print(F(" "));
Serial.print(thermal.stepResponseElapsedMs(millis()) / 1000UL);
Serial.print(F("s step "));
Serial.print(thermal.stepResponseStepIndex() + 1);
Serial.print(thermal.fanCharacterizeElapsedMs(millis()) / 1000UL);
Serial.print(F("s run "));
const char *fcPhase = thermal.fanCharacterizePhaseName();
if (strcmp(fcPhase, "precool") == 0) {
Serial.print(F("pre"));
} else if (strcmp(fcPhase, "cool") == 0) {
Serial.print(F("n"));
Serial.print(thermal.fanCharacterizeProfileIndex() + 1);
} else if (thermal.isFanCharacterizeRefineRun()) {
Serial.print(F("refine"));
} else {
Serial.print(thermal.fanCharacterizeProfileIndex() + 1);
}
Serial.print(F("/"));
Serial.print(thermal.stepResponseStepCount());
Serial.print(thermal.fanCharacterizeProfileCount());
Serial.print(F(" fan="));
Serial.print(thermal.fanCharacterizeFanPwm());
Serial.print(F(" heat="));
Serial.print(thermal.stepResponseHeaterPct(), 0);
Serial.print(thermal.fanCharacterizeHeaterPct(), 0);
Serial.print(F("%"));
} else {
Serial.print(thermal.regulatingModeName());
@@ -211,8 +227,8 @@ void printHelp() {
Serial.println(F(" fan test N set fan PWM 0-255 for 15s (verify wiring)"));
Serial.println(F(" autotune [C] learn heat PI (default: 40C when idle)"));
Serial.println(F(" autotune stop"));
Serial.println(F(" stepresp [C] [heater%] fan step response (default: 45C 35%)"));
Serial.println(F(" stepresp stop"));
Serial.println(F(" fanchars learn stir fan (30/100/60/80%% + refine)"));
Serial.println(F(" fanchars stop | fanchars save"));
Serial.println(F(" pid show heat + mix PI gains"));
Serial.println(F(" pid default reset all PI to factory"));
Serial.println(F(" pid save write current PI to EEPROM"));
@@ -346,42 +362,41 @@ void processSerialLine(const char *line) {
return;
}
if (strncmp(line, "stepresp", 8) == 0) {
if (strcmp(line, "stepresp stop") == 0) {
thermal.stopStepResponse();
Serial.println(F("OK stepresp cancelled"));
if (strncmp(line, "fanchars", 8) == 0) {
if (strcmp(line, "fanchars stop") == 0) {
thermal.stopFanCharacterize();
Serial.println(F("OK fanchars cancelled"));
return;
}
float tempC = STEPRESP_DEFAULT_TEMP_C;
float heaterPct = STEPRESP_DEFAULT_HEATER_PCT;
if (line[8] == ' ') {
const char *args = line + 9;
tempC = atof(args);
const char *space = strchr(args, ' ');
if (space != nullptr) {
heaterPct = atof(space + 1);
if (strcmp(line, "fanchars save") == 0) {
if (!thermal.saveStirFanFromCharacterize()) {
Serial.println(F("ERR fanchars save — no completed run with winner"));
return;
}
}
if (tempC < 25.0f || tempC > TARGET_MAX_C) {
Serial.println(F("ERR stepresp temperature must be 25-80 C"));
return;
}
if (heaterPct < STEPRESP_MIN_HEATER_PCT || heaterPct > STEPRESP_MAX_HEATER_PCT) {
Serial.print(F("ERR stepresp heater must be "));
Serial.print(STEPRESP_MIN_HEATER_PCT, 0);
Serial.print(F("-"));
Serial.print(STEPRESP_MAX_HEATER_PCT, 0);
Serial.println(F("%"));
Serial.println(F("OK stir fan saved"));
return;
}
if (!thermal.startStepResponse(tempC, heaterPct)) {
Serial.println(F("ERR stepresp already running or autotune active"));
float maxC = FANCHARS_MAX_CORNER_C;
if (line[8] == ' ') {
maxC = atof(line + 9);
}
if (maxC < 45.0f || maxC > EMERGENCY_MAX_TEMP_C - 5.0f) {
Serial.println(F("ERR fanchars max 45-65 C"));
return;
}
Serial.println(F("OK stepresp started — open-loop heater, fan steps, ~25-35 min"));
const float avgTemp = averageValidTemperature();
if (isnan(avgTemp)) {
Serial.println(F("ERR fanchars needs sensors"));
return;
}
if (!thermal.startFanCharacterize(maxC, avgTemp)) {
Serial.println(F("ERR fanchars busy"));
return;
}
Serial.println(F("OK fanchars started"));
return;
}
@@ -527,9 +542,9 @@ void loop() {
sensorValid[i] = sensors[i].valid;
}
const float minTemp = minValidTemperature();
if (thermal.isStepResponseActive() && !isnan(minTemp)) {
thermal.logStepResponseIfDue(sensorTemps, sensorValid, SENSOR_COUNT, avgTemp, minTemp,
maxTemp, spread, now);
if (thermal.isFanCharacterizeActive() && !isnan(minTemp)) {
thermal.logFanCharacterizeIfDue(sensorTemps, sensorValid, SENSOR_COUNT, avgTemp, minTemp,
maxTemp, spread, now);
}
} else {
thermal.enterFailSafe();