ouch finger

This commit is contained in:
2026-07-08 22:17:23 +02:00
parent 2fe53d5a8a
commit d1385baa45
11 changed files with 865 additions and 189 deletions

View File

@@ -42,19 +42,20 @@ static const float PID_KI = HEAT_PI_KI;
static const float PID_KD = 0.0f;
static const float GOOD_SPREAD_C = 5.0f;
// Tiered heater cap during heat-up
static const float HEATER_MAX_DUTY_COLD = 85.0f;
static const float HEATER_MAX_DUTY_MID = 65.0f;
// Tiered heater cap during heat-up (100% until near target)
static const float HEATER_MAX_DUTY_COLD = 100.0f;
static const float HEATER_MAX_DUTY_MID = 75.0f;
static const float HEATER_MAX_DUTY_NEAR = 45.0f;
static const float HEATER_COLD_BELOW_C = 10.0f;
static const float HEATER_WARM_BELOW_C = 3.0f;
static const float CORNER_LIMIT_BAND_C = 10.0f;
// Corner taper only within this band below target (was 10°C — blocked heat-up in uneven chambers)
static const float CORNER_LIMIT_BAND_C = 2.0f;
static const float HEATER_SLEW_UP_PER_S = 18.0f;
static const float MAX_TEMP_HEADROOM_C = 15.0f;
static const uint16_t HEATER_CYCLE_MS = 3000;
// Fan PWM — FAN_IDLE_PWM ≈ 30%; off only while chamber avg is below 40°C
// Fan PWM — stir speed when target > 0; off below 40°C only when idle (target 0)
static const uint8_t FAN_IDLE_PWM = 77;
static const float IDLE_AUTO_FAN_OFF_TEMP_C = 40.0f;
static const uint8_t FAN_MAX_PWM = 255;

View File

@@ -0,0 +1,75 @@
#pragma once
#include <Arduino.h>
#include "config.h"
class FanCharacterize {
public:
enum class Phase : uint8_t { Idle, Precool, Heat, Hold, Cooldown, Done, Failed };
FanCharacterize();
Phase phase() const { return phase_; }
bool isActive() const;
uint32_t elapsedMs(uint32_t nowMs) const;
uint8_t profileIndex() const { return profileIndex_; }
uint8_t profileCount() const { return FANCHARS_COARSE_COUNT + 1; }
uint8_t currentFanPwm() const;
float heaterPct() const { return heaterPct_; }
uint8_t winnerFanPwm() const { return winnerFanPwm_; }
bool isRefineRun() const { return refineRun_ && profileIndex_ >= FANCHARS_COARSE_COUNT; }
const char *phaseName() const;
bool start(float maxCornerC, float avgTempC);
void abort();
void reset();
bool update(float avgTempC, float maxTempC, float spreadC, uint32_t nowMs, float &heaterDutyOut,
uint8_t &fanPwmOut);
void logIfDue(const float *sensorTemps, const bool *sensorValid, uint8_t sensorCount,
float avgTempC, float minTempC, float maxTempC, float spreadC, uint32_t nowMs);
private:
struct ProfileResult {
uint8_t fanPwm;
float meanSpreadC;
};
void resetProfileStats();
void beginProfileHeat(uint32_t nowMs);
void enterHold(uint32_t nowMs);
void finishProfile(uint32_t nowMs);
void skipProfile(uint32_t nowMs, float maxTempC);
void planRefine(uint32_t nowMs);
void finishAll(uint32_t nowMs);
void fail(const __FlashStringHelper *reason);
Phase phase_;
float maxCornerC_;
float coolAvgC_;
float heaterPct_;
uint8_t profileIndex_;
uint8_t refineFanPwm_;
bool refineRun_;
uint32_t sessionStartMs_;
uint32_t phaseStartMs_;
uint32_t lastLogMs_;
float spreadSum_;
uint16_t spreadSamples_;
uint8_t resultCount_;
uint8_t winnerFanPwm_;
ProfileResult results_[FANCHARS_MAX_RESULTS];
};

View File

@@ -456,7 +456,7 @@ private:
void updateAutotune(float avgTempC, float maxTempC, uint32_t nowMs) {
float duty = 0.0f;
uint8_t fan = AUTOTUNE_PREHEAT_FAN_PWM;
uint8_t fan = stirFanPwm_;
autotuner_.update(avgTempC, maxTempC, cornerSpreadC_, nowMs, duty, fan);
heaterDutyPercent_ = duty;
@@ -464,7 +464,7 @@ private:
heaterOn_ = duty >= 50.0f;
digitalWrite(HEATER_PIN, heaterOn_ ? HIGH : LOW);
heaterBlock_ = duty > 0.0f ? HeaterBlock::None : HeaterBlock::Autotune;
writeFan(fan);
writeFan(fanManualActive_ ? fanManualPwm_ : stirFanPwm_);
lastHeaterUpdateMs_ = nowMs;
commitAutotuneIfDone();
}
@@ -513,15 +513,7 @@ private:
}
heaterDutyPercent_ = applyHeaterRamp(duty, avgTempC, nowMs);
const uint8_t fanPwm = fanManualActive_ ? fanManualPwm_ : stirFanPwm_;
regulatingFanPwm_ = fanWithMinStir(avgTempC, fanPwm);
}
uint8_t fanWithMinStir(float avgTempC, uint8_t pwm) const {
if (avgTempC < IDLE_AUTO_FAN_OFF_TEMP_C) {
return 0;
}
return pwm;
regulatingFanPwm_ = fanManualActive_ ? fanManualPwm_ : stirFanPwm_;
}
static float clampPercent(float value) {
@@ -547,6 +539,11 @@ private:
float allowanceFromMaxCorner(float maxTempC, float avgTempC) const {
if (!shouldLimitMaxCorner(avgTempC)) {
// Heat-up: only taper when a hot corner nears the emergency ceiling
if (maxTempC >= EMERGENCY_MAX_TEMP_C - 3.0f) {
const float headroom = EMERGENCY_MAX_TEMP_C - maxTempC;
return clampPercent((headroom / 3.0f) * 100.0f);
}
return 100.0f;
}