Chamber Heater PID (#21156)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
@ -318,6 +318,11 @@ typedef struct SettingsDataStruct {
|
||||
//
|
||||
PID_t bedPID; // M304 PID / M303 E-1 U
|
||||
|
||||
//
|
||||
// PIDTEMPCHAMBER
|
||||
//
|
||||
PID_t chamberPID; // M309 PID / M303 E-2 U
|
||||
|
||||
//
|
||||
// User-defined Thermistors
|
||||
//
|
||||
@ -926,6 +931,25 @@ void MarlinSettings::postprocess() {
|
||||
EEPROM_WRITE(bed_pid);
|
||||
}
|
||||
|
||||
//
|
||||
// PIDTEMPCHAMBER
|
||||
//
|
||||
{
|
||||
_FIELD_TEST(chamberPID);
|
||||
|
||||
const PID_t chamber_pid = {
|
||||
#if DISABLED(PIDTEMPCHAMBER)
|
||||
NAN, NAN, NAN
|
||||
#else
|
||||
// Store the unscaled PID values
|
||||
thermalManager.temp_chamber.pid.Kp,
|
||||
unscalePID_i(thermalManager.temp_chamber.pid.Ki),
|
||||
unscalePID_d(thermalManager.temp_chamber.pid.Kd)
|
||||
#endif
|
||||
};
|
||||
EEPROM_WRITE(chamber_pid);
|
||||
}
|
||||
|
||||
//
|
||||
// User-defined Thermistors
|
||||
//
|
||||
@ -1787,6 +1811,22 @@ void MarlinSettings::postprocess() {
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Heated Chamber PID
|
||||
//
|
||||
{
|
||||
PID_t pid;
|
||||
EEPROM_READ(pid);
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
if (!validating && !isnan(pid.Kp)) {
|
||||
// Scale PID values since EEPROM values are unscaled
|
||||
thermalManager.temp_chamber.pid.Kp = pid.Kp;
|
||||
thermalManager.temp_chamber.pid.Ki = scalePID_i(pid.Ki);
|
||||
thermalManager.temp_chamber.pid.Kd = scalePID_d(pid.Kd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// User-defined Thermistors
|
||||
//
|
||||
@ -2811,6 +2851,16 @@ void MarlinSettings::reset() {
|
||||
thermalManager.temp_bed.pid.Kd = scalePID_d(DEFAULT_bedKd);
|
||||
#endif
|
||||
|
||||
//
|
||||
// Heated Chamber PID
|
||||
//
|
||||
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
thermalManager.temp_chamber.pid.Kp = DEFAULT_chamberKp;
|
||||
thermalManager.temp_chamber.pid.Ki = scalePID_i(DEFAULT_chamberKi);
|
||||
thermalManager.temp_chamber.pid.Kd = scalePID_d(DEFAULT_chamberKd);
|
||||
#endif
|
||||
|
||||
//
|
||||
// User-Defined Thermistors
|
||||
//
|
||||
@ -3386,7 +3436,16 @@ void MarlinSettings::reset() {
|
||||
);
|
||||
#endif
|
||||
|
||||
#endif // PIDTEMP || PIDTEMPBED
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
CONFIG_ECHO_START();
|
||||
SERIAL_ECHOLNPAIR(
|
||||
" M309 P", thermalManager.temp_chamber.pid.Kp
|
||||
, " I", unscalePID_i(thermalManager.temp_chamber.pid.Ki)
|
||||
, " D", unscalePID_d(thermalManager.temp_chamber.pid.Kd)
|
||||
);
|
||||
#endif
|
||||
|
||||
#endif // PIDTEMP || PIDTEMPBED || PIDTEMPCHAMBER
|
||||
|
||||
#if HAS_USER_THERMISTORS
|
||||
CONFIG_ECHO_HEADING("User thermistors:");
|
||||
|
@ -371,10 +371,8 @@ const char str_t_thermal_runaway[] PROGMEM = STR_T_THERMAL_RUNAWAY,
|
||||
#ifdef CHAMBER_MAXTEMP
|
||||
int16_t Temperature::maxtemp_raw_CHAMBER = TEMP_SENSOR_CHAMBER_RAW_HI_TEMP;
|
||||
#endif
|
||||
#if WATCH_CHAMBER
|
||||
chamber_watch_t Temperature::watch_chamber{0};
|
||||
#endif
|
||||
millis_t Temperature::next_chamber_check_ms;
|
||||
TERN_(WATCH_CHAMBER, chamber_watch_t Temperature::watch_chamber{0});
|
||||
IF_DISABLED(PIDTEMPCHAMBER, millis_t Temperature::next_chamber_check_ms);
|
||||
#endif // HAS_HEATED_CHAMBER
|
||||
#endif // HAS_TEMP_CHAMBER
|
||||
|
||||
@ -382,11 +380,6 @@ const char str_t_thermal_runaway[] PROGMEM = STR_T_THERMAL_RUNAWAY,
|
||||
probe_info_t Temperature::temp_probe; // = { 0 }
|
||||
#endif
|
||||
|
||||
// Initialized by settings.load()
|
||||
#if ENABLED(PIDTEMP)
|
||||
//hotend_pid_t Temperature::pid[HOTENDS];
|
||||
#endif
|
||||
|
||||
#if ENABLED(PREVENT_COLD_EXTRUSION)
|
||||
bool Temperature::allow_cold_extrude = false;
|
||||
int16_t Temperature::extrude_min_temp = EXTRUDE_MINTEMP;
|
||||
@ -485,41 +478,44 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
millis_t next_temp_ms = millis(), t1 = next_temp_ms, t2 = next_temp_ms;
|
||||
long t_high = 0, t_low = 0;
|
||||
|
||||
long bias, d;
|
||||
PID_t tune_pid = { 0, 0, 0 };
|
||||
float maxT = 0, minT = 10000;
|
||||
|
||||
const bool isbed = (heater_id == H_BED);
|
||||
const bool ischamber = (heater_id == H_CHAMBER);
|
||||
|
||||
#if HAS_PID_FOR_BOTH
|
||||
#define GHV(B,H) (isbed ? (B) : (H))
|
||||
#define SHV(B,H) do{ if (isbed) temp_bed.soft_pwm_amount = B; else temp_hotend[heater_id].soft_pwm_amount = H; }while(0)
|
||||
#define ONHEATINGSTART() (isbed ? printerEventLEDs.onBedHeatingStart() : printerEventLEDs.onHotendHeatingStart())
|
||||
#define ONHEATING(S,C,T) (isbed ? printerEventLEDs.onBedHeating(S,C,T) : printerEventLEDs.onHotendHeating(S,C,T))
|
||||
#elif ENABLED(PIDTEMPBED)
|
||||
#define GHV(B,H) B
|
||||
#define SHV(B,H) (temp_bed.soft_pwm_amount = B)
|
||||
#define ONHEATINGSTART() printerEventLEDs.onBedHeatingStart()
|
||||
#define ONHEATING(S,C,T) printerEventLEDs.onBedHeating(S,C,T)
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
#define C_TERN(T,A,B) ((T) ? (A) : (B))
|
||||
#else
|
||||
#define GHV(B,H) H
|
||||
#define SHV(B,H) (temp_hotend[heater_id].soft_pwm_amount = H)
|
||||
#define ONHEATINGSTART() printerEventLEDs.onHotendHeatingStart()
|
||||
#define ONHEATING(S,C,T) printerEventLEDs.onHotendHeating(S,C,T)
|
||||
#define C_TERN(T,A,B) (B)
|
||||
#endif
|
||||
#define WATCH_PID BOTH(WATCH_BED, PIDTEMPBED) || BOTH(WATCH_HOTENDS, PIDTEMP)
|
||||
#if ENABLED(PIDTEMPBED)
|
||||
#define B_TERN(T,A,B) ((T) ? (A) : (B))
|
||||
#else
|
||||
#define B_TERN(T,A,B) (B)
|
||||
#endif
|
||||
#define GHV(C,B,H) C_TERN(ischamber, C, B_TERN(isbed, B, H))
|
||||
#define SHV(V) C_TERN(ischamber, temp_chamber.soft_pwm_amount = V, B_TERN(isbed, temp_bed.soft_pwm_amount = V, temp_hotend[heater_id].soft_pwm_amount = V))
|
||||
#define ONHEATINGSTART() C_TERN(ischamber, printerEventLEDs.onChamberHeatingStart(), B_TERN(isbed, printerEventLEDs.onBedHeatingStart(), printerEventLEDs.onHotendHeatingStart()))
|
||||
#define ONHEATING(S,C,T) C_TERN(ischamber, printerEventLEDs.onChamberHeating(S,C,T), B_TERN(isbed, printerEventLEDs.onBedHeating(S,C,T), printerEventLEDs.onHotendHeating(S,C,T)))
|
||||
|
||||
#define WATCH_PID BOTH(WATCH_CHAMBER, PIDTEMPCHAMBER) || BOTH(WATCH_BED, PIDTEMPBED) || BOTH(WATCH_HOTENDS, PIDTEMP)
|
||||
|
||||
#if WATCH_PID
|
||||
#if ALL(THERMAL_PROTECTION_HOTENDS, PIDTEMP, THERMAL_PROTECTION_BED, PIDTEMPBED)
|
||||
#define GTV(B,H) (isbed ? (B) : (H))
|
||||
#elif BOTH(THERMAL_PROTECTION_HOTENDS, PIDTEMP)
|
||||
#define GTV(B,H) (H)
|
||||
#if BOTH(THERMAL_PROTECTION_CHAMBER, PIDTEMPCHAMBER)
|
||||
#define C_GTV(T,A,B) ((T) ? (A) : (B))
|
||||
#else
|
||||
#define GTV(B,H) (B)
|
||||
#define C_GTV(T,A,B) (B)
|
||||
#endif
|
||||
const uint16_t watch_temp_period = GTV(WATCH_BED_TEMP_PERIOD, WATCH_TEMP_PERIOD);
|
||||
const uint8_t watch_temp_increase = GTV(WATCH_BED_TEMP_INCREASE, WATCH_TEMP_INCREASE);
|
||||
const float watch_temp_target = target - float(watch_temp_increase + GTV(TEMP_BED_HYSTERESIS, TEMP_HYSTERESIS) + 1);
|
||||
#if BOTH(THERMAL_PROTECTION_BED, PIDTEMPBED)
|
||||
#define B_GTV(T,A,B) ((T) ? (A) : (B))
|
||||
#else
|
||||
#define B_GTV(T,A,B) (B)
|
||||
#endif
|
||||
#define GTV(C,B,H) C_GTV(ischamber, C, B_GTV(isbed, B, H))
|
||||
const uint16_t watch_temp_period = GTV(WATCH_CHAMBER_TEMP_PERIOD, WATCH_BED_TEMP_PERIOD, WATCH_TEMP_PERIOD);
|
||||
const uint8_t watch_temp_increase = GTV(WATCH_CHAMBER_TEMP_INCREASE, WATCH_BED_TEMP_INCREASE, WATCH_TEMP_INCREASE);
|
||||
const float watch_temp_target = target - float(watch_temp_increase + GTV(TEMP_CHAMBER_HYSTERESIS, TEMP_BED_HYSTERESIS, TEMP_HYSTERESIS) + 1);
|
||||
millis_t temp_change_ms = next_temp_ms + SEC_TO_MS(watch_temp_period);
|
||||
float next_watch_temp = 0.0;
|
||||
bool heated = false;
|
||||
@ -527,7 +523,7 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
|
||||
TERN_(HAS_AUTO_FAN, next_auto_fan_check_ms = next_temp_ms + 2500UL);
|
||||
|
||||
if (target > GHV(BED_MAX_TARGET, temp_range[heater_id].maxtemp - HOTEND_OVERSHOOT)) {
|
||||
if (target > GHV(CHAMBER_MAX_TARGET, BED_MAX_TARGET, temp_range[heater_id].maxtemp - HOTEND_OVERSHOOT)) {
|
||||
SERIAL_ECHOLNPGM(STR_PID_TEMP_TOO_HIGH);
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_TEMP_TOO_HIGH));
|
||||
return;
|
||||
@ -538,10 +534,11 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
disable_all_heaters();
|
||||
TERN_(AUTO_POWER_CONTROL, powerManager.power_on());
|
||||
|
||||
SHV(bias = d = (MAX_BED_POWER) >> 1, bias = d = (PID_MAX) >> 1);
|
||||
long bias = GHV(MAX_CHAMBER_POWER, MAX_BED_POWER, PID_MAX) >> 1, d = bias;
|
||||
SHV(bias);
|
||||
|
||||
#if ENABLED(PRINTER_EVENT_LEDS)
|
||||
const float start_temp = GHV(temp_bed.celsius, temp_hotend[heater_id].celsius);
|
||||
const float start_temp = GHV(temp_chamber.celsius, temp_bed.celsius, temp_hotend[heater_id].celsius);
|
||||
LEDColor color = ONHEATINGSTART();
|
||||
#endif
|
||||
|
||||
@ -557,7 +554,7 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
updateTemperaturesFromRawValues();
|
||||
|
||||
// Get the current temperature and constrain it
|
||||
current_temp = GHV(temp_bed.celsius, temp_hotend[heater_id].celsius);
|
||||
current_temp = GHV(temp_chamber.celsius, temp_bed.celsius, temp_hotend[heater_id].celsius);
|
||||
NOLESS(maxT, current_temp);
|
||||
NOMORE(minT, current_temp);
|
||||
|
||||
@ -572,67 +569,46 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (heating && current_temp > target) {
|
||||
if (ELAPSED(ms, t2 + 5000UL)) {
|
||||
heating = false;
|
||||
SHV((bias - d) >> 1, (bias - d) >> 1);
|
||||
t1 = ms;
|
||||
t_high = t1 - t2;
|
||||
maxT = target;
|
||||
}
|
||||
if (heating && current_temp > target && ELAPSED(ms, t2 + 5000UL)) {
|
||||
heating = false;
|
||||
SHV((bias - d) >> 1);
|
||||
t1 = ms;
|
||||
t_high = t1 - t2;
|
||||
maxT = target;
|
||||
}
|
||||
|
||||
if (!heating && current_temp < target) {
|
||||
if (ELAPSED(ms, t1 + 5000UL)) {
|
||||
heating = true;
|
||||
t2 = ms;
|
||||
t_low = t2 - t1;
|
||||
if (cycles > 0) {
|
||||
const long max_pow = GHV(MAX_BED_POWER, PID_MAX);
|
||||
bias += (d * (t_high - t_low)) / (t_low + t_high);
|
||||
LIMIT(bias, 20, max_pow - 20);
|
||||
d = (bias > max_pow >> 1) ? max_pow - 1 - bias : bias;
|
||||
if (!heating && current_temp < target && ELAPSED(ms, t1 + 5000UL)) {
|
||||
heating = true;
|
||||
t2 = ms;
|
||||
t_low = t2 - t1;
|
||||
if (cycles > 0) {
|
||||
const long max_pow = GHV(MAX_CHAMBER_POWER, MAX_BED_POWER, PID_MAX);
|
||||
bias += (d * (t_high - t_low)) / (t_low + t_high);
|
||||
LIMIT(bias, 20, max_pow - 20);
|
||||
d = (bias > max_pow >> 1) ? max_pow - 1 - bias : bias;
|
||||
|
||||
SERIAL_ECHOPAIR(STR_BIAS, bias, STR_D_COLON, d, STR_T_MIN, minT, STR_T_MAX, maxT);
|
||||
if (cycles > 2) {
|
||||
const float Ku = (4.0f * d) / (float(M_PI) * (maxT - minT) * 0.5f),
|
||||
Tu = float(t_low + t_high) * 0.001f,
|
||||
pf = isbed ? 0.2f : 0.6f,
|
||||
df = isbed ? 1.0f / 3.0f : 1.0f / 8.0f;
|
||||
SERIAL_ECHOPAIR(STR_BIAS, bias, STR_D_COLON, d, STR_T_MIN, minT, STR_T_MAX, maxT);
|
||||
if (cycles > 2) {
|
||||
const float Ku = (4.0f * d) / (float(M_PI) * (maxT - minT) * 0.5f),
|
||||
Tu = float(t_low + t_high) * 0.001f,
|
||||
pf = ischamber ? 0.2f : (isbed ? 0.2f : 0.6f),
|
||||
df = ischamber ? 1.0f / 3.0f : (isbed ? 1.0f / 3.0f : 1.0f / 8.0f);
|
||||
|
||||
SERIAL_ECHOPAIR(STR_KU, Ku, STR_TU, Tu);
|
||||
if (isbed) { // Do not remove this otherwise PID autotune won't work right for the bed!
|
||||
tune_pid.Kp = Ku * 0.2f;
|
||||
tune_pid.Ki = 2 * tune_pid.Kp / Tu;
|
||||
tune_pid.Kd = tune_pid.Kp * Tu / 3;
|
||||
SERIAL_ECHOLNPGM("\n" " No overshoot"); // Works far better for the bed. Classic and some have bad ringing.
|
||||
SERIAL_ECHOLNPAIR(STR_KP, tune_pid.Kp, STR_KI, tune_pid.Ki, STR_KD, tune_pid.Kd);
|
||||
}
|
||||
else {
|
||||
tune_pid.Kp = Ku * pf;
|
||||
tune_pid.Kd = tune_pid.Kp * Tu * df;
|
||||
tune_pid.Ki = 2 * tune_pid.Kp / Tu;
|
||||
SERIAL_ECHOLNPGM("\n" STR_CLASSIC_PID);
|
||||
SERIAL_ECHOLNPAIR(STR_KP, tune_pid.Kp, STR_KI, tune_pid.Ki, STR_KD, tune_pid.Kd);
|
||||
}
|
||||
tune_pid.Kp = Ku * pf;
|
||||
tune_pid.Ki = tune_pid.Kp * 2.0f / Tu;
|
||||
tune_pid.Kd = tune_pid.Kp * Tu * df;
|
||||
|
||||
/**
|
||||
tune_pid.Kp = 0.33 * Ku;
|
||||
tune_pid.Ki = tune_pid.Kp / Tu;
|
||||
tune_pid.Kd = tune_pid.Kp * Tu / 3;
|
||||
SERIAL_ECHOLNPGM(" Some overshoot");
|
||||
SERIAL_ECHOLNPAIR(" Kp: ", tune_pid.Kp, " Ki: ", tune_pid.Ki, " Kd: ", tune_pid.Kd, " No overshoot");
|
||||
tune_pid.Kp = 0.2 * Ku;
|
||||
tune_pid.Ki = 2 * tune_pid.Kp / Tu;
|
||||
tune_pid.Kd = tune_pid.Kp * Tu / 3;
|
||||
SERIAL_ECHOPAIR(" Kp: ", tune_pid.Kp, " Ki: ", tune_pid.Ki, " Kd: ", tune_pid.Kd);
|
||||
*/
|
||||
}
|
||||
SERIAL_ECHOLNPAIR(STR_KU, Ku, STR_TU, Tu);
|
||||
if (ischamber || isbed)
|
||||
SERIAL_ECHOLNPGM(" No overshoot");
|
||||
else
|
||||
SERIAL_ECHOLNPGM(STR_CLASSIC_PID);
|
||||
SERIAL_ECHOLNPAIR(STR_KP, tune_pid.Kp, STR_KI, tune_pid.Ki, STR_KD, tune_pid.Kd);
|
||||
}
|
||||
SHV((bias + d) >> 1, (bias + d) >> 1);
|
||||
cycles++;
|
||||
minT = target;
|
||||
}
|
||||
SHV((bias + d) >> 1);
|
||||
cycles++;
|
||||
minT = target;
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,14 +625,14 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
// Report heater states every 2 seconds
|
||||
if (ELAPSED(ms, next_temp_ms)) {
|
||||
#if HAS_TEMP_SENSOR
|
||||
print_heater_states(isbed ? active_extruder : heater_id);
|
||||
print_heater_states(ischamber ? active_extruder : (isbed ? active_extruder : heater_id));
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
next_temp_ms = ms + 2000UL;
|
||||
|
||||
// Make sure heating is actually working
|
||||
#if WATCH_PID
|
||||
if (BOTH(WATCH_BED, WATCH_HOTENDS) || isbed == DISABLED(WATCH_HOTENDS)) {
|
||||
if (BOTH(WATCH_BED, WATCH_HOTENDS) || isbed == DISABLED(WATCH_HOTENDS) || ischamber == DISABLED(WATCH_HOTENDS)) {
|
||||
if (!heated) { // If not yet reached target...
|
||||
if (current_temp > next_watch_temp) { // Over the watch temp?
|
||||
next_watch_temp = current_temp + watch_temp_increase; // - set the next temp to watch for
|
||||
@ -686,43 +662,47 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
if (cycles > ncycles && cycles > 2) {
|
||||
SERIAL_ECHOLNPGM(STR_PID_AUTOTUNE_FINISHED);
|
||||
|
||||
#if HAS_PID_FOR_BOTH
|
||||
const char * const estring = GHV(PSTR("bed"), NUL_STR);
|
||||
#if EITHER(PIDTEMPBED, PIDTEMPCHAMBER)
|
||||
PGM_P const estring = GHV(PSTR("chamber"), PSTR("bed"), NUL_STR);
|
||||
say_default_(); serialprintPGM(estring); SERIAL_ECHOLNPAIR("Kp ", tune_pid.Kp);
|
||||
say_default_(); serialprintPGM(estring); SERIAL_ECHOLNPAIR("Ki ", tune_pid.Ki);
|
||||
say_default_(); serialprintPGM(estring); SERIAL_ECHOLNPAIR("Kd ", tune_pid.Kd);
|
||||
#elif ENABLED(PIDTEMP)
|
||||
#else
|
||||
say_default_(); SERIAL_ECHOLNPAIR("Kp ", tune_pid.Kp);
|
||||
say_default_(); SERIAL_ECHOLNPAIR("Ki ", tune_pid.Ki);
|
||||
say_default_(); SERIAL_ECHOLNPAIR("Kd ", tune_pid.Kd);
|
||||
#else
|
||||
say_default_(); SERIAL_ECHOLNPAIR("bedKp ", tune_pid.Kp);
|
||||
say_default_(); SERIAL_ECHOLNPAIR("bedKi ", tune_pid.Ki);
|
||||
say_default_(); SERIAL_ECHOLNPAIR("bedKd ", tune_pid.Kd);
|
||||
#endif
|
||||
|
||||
#define _SET_BED_PID() do { \
|
||||
temp_bed.pid.Kp = tune_pid.Kp; \
|
||||
temp_bed.pid.Ki = scalePID_i(tune_pid.Ki); \
|
||||
temp_bed.pid.Kd = scalePID_d(tune_pid.Kd); \
|
||||
}while(0)
|
||||
auto _set_hotend_pid = [](const uint8_t e, const PID_t &in_pid) {
|
||||
#if ENABLED(PIDTEMP)
|
||||
PID_PARAM(Kp, e) = in_pid.Kp;
|
||||
PID_PARAM(Ki, e) = scalePID_i(in_pid.Ki);
|
||||
PID_PARAM(Kd, e) = scalePID_d(in_pid.Kd);
|
||||
updatePID();
|
||||
#else
|
||||
UNUSED(e); UNUSED(in_pid);
|
||||
#endif
|
||||
};
|
||||
|
||||
#define _SET_EXTRUDER_PID() do { \
|
||||
PID_PARAM(Kp, heater_id) = tune_pid.Kp; \
|
||||
PID_PARAM(Ki, heater_id) = scalePID_i(tune_pid.Ki); \
|
||||
PID_PARAM(Kd, heater_id) = scalePID_d(tune_pid.Kd); \
|
||||
updatePID(); }while(0)
|
||||
#if ENABLED(PIDTEMPBED)
|
||||
auto _set_bed_pid = [](const PID_t &in_pid) {
|
||||
temp_bed.pid.Kp = in_pid.Kp;
|
||||
temp_bed.pid.Ki = scalePID_i(in_pid.Ki);
|
||||
temp_bed.pid.Kd = scalePID_d(in_pid.Kd);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
auto _set_chamber_pid = [](const PID_t &in_pid) {
|
||||
temp_chamber.pid.Kp = in_pid.Kp;
|
||||
temp_chamber.pid.Ki = scalePID_i(in_pid.Ki);
|
||||
temp_chamber.pid.Kd = scalePID_d(in_pid.Kd);
|
||||
};
|
||||
#endif
|
||||
|
||||
// Use the result? (As with "M303 U1")
|
||||
if (set_result) {
|
||||
#if HAS_PID_FOR_BOTH
|
||||
if (isbed) _SET_BED_PID(); else _SET_EXTRUDER_PID();
|
||||
#elif ENABLED(PIDTEMP)
|
||||
_SET_EXTRUDER_PID();
|
||||
#else
|
||||
_SET_BED_PID();
|
||||
#endif
|
||||
}
|
||||
if (set_result)
|
||||
GHV(_set_chamber_pid(tune_pid), _set_bed_pid(tune_pid), _set_hotend_pid(heater_id, tune_pid));
|
||||
|
||||
TERN_(PRINTER_EVENT_LEDS, printerEventLEDs.onPidTuningDone(color));
|
||||
|
||||
@ -939,10 +919,11 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
_temp_error(heater_id, PSTR(STR_T_MINTEMP), GET_TEXT(MSG_ERR_MINTEMP));
|
||||
}
|
||||
|
||||
#if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG)
|
||||
bool Temperature::pid_debug_flag; // = 0
|
||||
#endif
|
||||
|
||||
#if HAS_HOTEND
|
||||
#if ENABLED(PID_DEBUG)
|
||||
extern bool pid_debug_flag;
|
||||
#endif
|
||||
|
||||
float Temperature::get_pid_output_hotend(const uint8_t E_NAME) {
|
||||
const uint8_t ee = HOTEND_INDEX;
|
||||
@ -1023,23 +1004,18 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
|
||||
#if ENABLED(PID_DEBUG)
|
||||
if (ee == active_extruder && pid_debug_flag) {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPAIR(STR_PID_DEBUG, ee, STR_PID_DEBUG_INPUT, temp_hotend[ee].celsius, STR_PID_DEBUG_OUTPUT, pid_output);
|
||||
#if DISABLED(PID_OPENLOOP)
|
||||
{
|
||||
SERIAL_ECHOPAIR(
|
||||
STR_PID_DEBUG_PTERM, work_pid[ee].Kp,
|
||||
STR_PID_DEBUG_ITERM, work_pid[ee].Ki,
|
||||
STR_PID_DEBUG_DTERM, work_pid[ee].Kd
|
||||
SERIAL_ECHO_MSG(STR_PID_DEBUG, ee, STR_PID_DEBUG_INPUT, temp_hotend[ee].celsius, STR_PID_DEBUG_OUTPUT, pid_output
|
||||
#if DISABLED(PID_OPENLOOP)
|
||||
, STR_PID_DEBUG_PTERM, work_pid[ee].Kp
|
||||
, STR_PID_DEBUG_ITERM, work_pid[ee].Ki
|
||||
, STR_PID_DEBUG_DTERM, work_pid[ee].Kd
|
||||
#if ENABLED(PID_EXTRUSION_SCALING)
|
||||
, STR_PID_DEBUG_CTERM, work_pid[ee].Kc
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif // PID_DEBUG
|
||||
#endif
|
||||
|
||||
#else // No PID enabled
|
||||
|
||||
@ -1099,13 +1075,76 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
#endif // PID_OPENLOOP
|
||||
|
||||
#if ENABLED(PID_BED_DEBUG)
|
||||
if (pid_debug_flag) {
|
||||
SERIAL_ECHO_MSG(
|
||||
" PID_BED_DEBUG : Input ", temp_bed.celsius, " Output ", pid_output
|
||||
#if DISABLED(PID_OPENLOOP)
|
||||
, STR_PID_DEBUG_PTERM, work_pid.Kp
|
||||
, STR_PID_DEBUG_ITERM, work_pid.Ki
|
||||
, STR_PID_DEBUG_DTERM, work_pid.Kd
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
return pid_output;
|
||||
}
|
||||
|
||||
#endif // PIDTEMPBED
|
||||
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
|
||||
float Temperature::get_pid_output_chamber() {
|
||||
|
||||
#if DISABLED(PID_OPENLOOP)
|
||||
|
||||
static PID_t work_pid{0};
|
||||
static float temp_iState = 0, temp_dState = 0;
|
||||
static bool pid_reset = true;
|
||||
float pid_output = 0;
|
||||
const float max_power_over_i_gain = float(MAX_CHAMBER_POWER) / temp_chamber.pid.Ki - float(MIN_CHAMBER_POWER),
|
||||
pid_error = temp_chamber.target - temp_chamber.celsius;
|
||||
|
||||
if (!temp_chamber.target || pid_error < -(PID_FUNCTIONAL_RANGE)) {
|
||||
pid_output = 0;
|
||||
pid_reset = true;
|
||||
}
|
||||
else if (pid_error > PID_FUNCTIONAL_RANGE) {
|
||||
pid_output = MAX_CHAMBER_POWER;
|
||||
pid_reset = true;
|
||||
}
|
||||
else {
|
||||
if (pid_reset) {
|
||||
temp_iState = 0.0;
|
||||
work_pid.Kd = 0.0;
|
||||
pid_reset = false;
|
||||
}
|
||||
|
||||
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain);
|
||||
|
||||
work_pid.Kp = temp_chamber.pid.Kp * pid_error;
|
||||
work_pid.Ki = temp_chamber.pid.Ki * temp_iState;
|
||||
work_pid.Kd = work_pid.Kd + PID_K2 * (temp_chamber.pid.Kd * (temp_dState - temp_chamber.celsius) - work_pid.Kd);
|
||||
|
||||
temp_dState = temp_chamber.celsius;
|
||||
|
||||
pid_output = constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_CHAMBER_POWER), 0, MAX_CHAMBER_POWER);
|
||||
}
|
||||
|
||||
#else // PID_OPENLOOP
|
||||
|
||||
const float pid_output = constrain(temp_chamber.target, 0, MAX_CHAMBER_POWER);
|
||||
|
||||
#endif // PID_OPENLOOP
|
||||
|
||||
#if ENABLED(PID_CHAMBER_DEBUG)
|
||||
{
|
||||
SERIAL_ECHO_MSG(
|
||||
" PID_BED_DEBUG : Input ", temp_bed.celsius, " Output ", pid_output,
|
||||
" PID_CHAMBER_DEBUG : Input ", temp_chamber.celsius, " Output ", pid_output
|
||||
#if DISABLED(PID_OPENLOOP)
|
||||
STR_PID_DEBUG_PTERM, work_pid.Kp,
|
||||
STR_PID_DEBUG_ITERM, work_pid.Ki,
|
||||
STR_PID_DEBUG_DTERM, work_pid.Kd,
|
||||
, STR_PID_DEBUG_PTERM, work_pid.Kp
|
||||
, STR_PID_DEBUG_ITERM, work_pid.Ki
|
||||
, STR_PID_DEBUG_DTERM, work_pid.Kd
|
||||
#endif
|
||||
);
|
||||
}
|
||||
@ -1114,7 +1153,7 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
return pid_output;
|
||||
}
|
||||
|
||||
#endif // PIDTEMPBED
|
||||
#endif // PIDTEMPCHAMBER
|
||||
|
||||
/**
|
||||
* Manage heating activities for extruder hot-ends and a heated bed
|
||||
@ -1363,42 +1402,47 @@ void Temperature::manage_heater() {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
// PIDTEMPCHAMBER doens't support a CHAMBER_VENT yet.
|
||||
temp_chamber.soft_pwm_amount = WITHIN(temp_chamber.celsius, CHAMBER_MINTEMP, CHAMBER_MAXTEMP) ? (int)get_pid_output_chamber() >> 1 : 0;
|
||||
#else
|
||||
if (ELAPSED(ms, next_chamber_check_ms)) {
|
||||
next_chamber_check_ms = ms + CHAMBER_CHECK_INTERVAL;
|
||||
|
||||
if (WITHIN(temp_chamber.celsius, CHAMBER_MINTEMP, CHAMBER_MAXTEMP)) {
|
||||
if (flag_chamber_excess_heat) {
|
||||
temp_chamber.soft_pwm_amount = 0;
|
||||
#if ENABLED(CHAMBER_VENT)
|
||||
if (!flag_chamber_off) MOVE_SERVO(CHAMBER_VENT_SERVO_NR, temp_chamber.celsius <= temp_chamber.target ? 0 : 90);
|
||||
#endif
|
||||
if (flag_chamber_excess_heat) {
|
||||
temp_chamber.soft_pwm_amount = 0;
|
||||
#if ENABLED(CHAMBER_VENT)
|
||||
if (!flag_chamber_off) MOVE_SERVO(CHAMBER_VENT_SERVO_NR, temp_chamber.celsius <= temp_chamber.target ? 0 : 90);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#if ENABLED(CHAMBER_LIMIT_SWITCHING)
|
||||
if (temp_chamber.celsius >= temp_chamber.target + TEMP_CHAMBER_HYSTERESIS)
|
||||
temp_chamber.soft_pwm_amount = 0;
|
||||
else if (temp_chamber.celsius <= temp_chamber.target - (TEMP_CHAMBER_HYSTERESIS))
|
||||
temp_chamber.soft_pwm_amount = (MAX_CHAMBER_POWER) >> 1;
|
||||
#else
|
||||
temp_chamber.soft_pwm_amount = temp_chamber.celsius < temp_chamber.target ? (MAX_CHAMBER_POWER) >> 1 : 0;
|
||||
#endif
|
||||
#if ENABLED(CHAMBER_VENT)
|
||||
if (!flag_chamber_off) MOVE_SERVO(CHAMBER_VENT_SERVO_NR, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
#if ENABLED(CHAMBER_LIMIT_SWITCHING)
|
||||
if (temp_chamber.celsius >= temp_chamber.target + TEMP_CHAMBER_HYSTERESIS)
|
||||
temp_chamber.soft_pwm_amount = 0;
|
||||
else if (temp_chamber.celsius <= temp_chamber.target - (TEMP_CHAMBER_HYSTERESIS))
|
||||
temp_chamber.soft_pwm_amount = (MAX_CHAMBER_POWER) >> 1;
|
||||
#else
|
||||
temp_chamber.soft_pwm_amount = temp_chamber.celsius < temp_chamber.target ? (MAX_CHAMBER_POWER) >> 1 : 0;
|
||||
#endif
|
||||
#if ENABLED(CHAMBER_VENT)
|
||||
if (!flag_chamber_off) MOVE_SERVO(CHAMBER_VENT_SERVO_NR, 0);
|
||||
#endif
|
||||
temp_chamber.soft_pwm_amount = 0;
|
||||
WRITE_HEATER_CHAMBER(LOW);
|
||||
}
|
||||
}
|
||||
else {
|
||||
temp_chamber.soft_pwm_amount = 0;
|
||||
WRITE_HEATER_CHAMBER(LOW);
|
||||
}
|
||||
|
||||
#if ENABLED(THERMAL_PROTECTION_CHAMBER)
|
||||
tr_state_machine[RUNAWAY_IND_CHAMBER].run(temp_chamber.celsius, temp_chamber.target, H_CHAMBER, THERMAL_PROTECTION_CHAMBER_PERIOD, THERMAL_PROTECTION_CHAMBER_HYSTERESIS);
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO: Implement true PID pwm
|
||||
//temp_bed.soft_pwm_amount = WITHIN(temp_chamber.celsius, CHAMBER_MINTEMP, CHAMBER_MAXTEMP) ? (int)get_pid_output_chamber() >> 1 : 0;
|
||||
}
|
||||
#if ENABLED(THERMAL_PROTECTION_CHAMBER)
|
||||
tr_state_machine[RUNAWAY_IND_CHAMBER].run(temp_chamber.celsius, temp_chamber.target, H_CHAMBER, THERMAL_PROTECTION_CHAMBER_PERIOD, THERMAL_PROTECTION_CHAMBER_HYSTERESIS);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // HAS_HEATED_CHAMBER
|
||||
|
||||
|
@ -210,7 +210,11 @@ struct PIDHeaterInfo : public HeaterInfo {
|
||||
typedef temp_info_t probe_info_t;
|
||||
#endif
|
||||
#if HAS_HEATED_CHAMBER
|
||||
typedef heater_info_t chamber_info_t;
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
typedef struct PIDHeaterInfo<PID_t> chamber_info_t;
|
||||
#else
|
||||
typedef heater_info_t chamber_info_t;
|
||||
#endif
|
||||
#elif HAS_TEMP_CHAMBER
|
||||
typedef temp_info_t chamber_info_t;
|
||||
#endif
|
||||
@ -415,7 +419,7 @@ class Temperature {
|
||||
|
||||
#if HAS_HEATED_CHAMBER
|
||||
TERN_(WATCH_CHAMBER, static chamber_watch_t watch_chamber);
|
||||
static millis_t next_chamber_check_ms;
|
||||
TERN(PIDTEMPCHAMBER,,static millis_t next_chamber_check_ms);
|
||||
#ifdef CHAMBER_MINTEMP
|
||||
static int16_t mintemp_raw_CHAMBER;
|
||||
#endif
|
||||
@ -751,6 +755,11 @@ class Temperature {
|
||||
* Perform auto-tuning for hotend or bed in response to M303
|
||||
*/
|
||||
#if HAS_PID_HEATING
|
||||
|
||||
#if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG)
|
||||
static bool pid_debug_flag;
|
||||
#endif
|
||||
|
||||
static void PID_autotune(const float &target, const heater_id_t heater_id, const int8_t ncycles, const bool set_result=false);
|
||||
|
||||
#if ENABLED(NO_FAN_SLOWING_IN_PID_TUNING)
|
||||
@ -826,11 +835,9 @@ class Temperature {
|
||||
|
||||
static void checkExtruderAutoFans();
|
||||
|
||||
static float get_pid_output_hotend(const uint8_t e);
|
||||
|
||||
TERN_(PIDTEMPBED, static float get_pid_output_bed());
|
||||
|
||||
TERN_(HAS_HEATED_CHAMBER, static float get_pid_output_chamber());
|
||||
TERN_(HAS_HOTEND, static float get_pid_output_hotend(const uint8_t e));
|
||||
TERN_(PIDTEMPBED, static float get_pid_output_bed());
|
||||
TERN_(PIDTEMPCHAMBER, static float get_pid_output_chamber());
|
||||
|
||||
static void _temp_error(const heater_id_t e, PGM_P const serial_msg, PGM_P const lcd_msg);
|
||||
static void min_temp_error(const heater_id_t e);
|
||||
|
Reference in New Issue
Block a user