diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index 3234a73eda..1c98168236 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -447,6 +447,10 @@
#define TEMP_BED_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer
#define TEMP_BED_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target
+#define TEMP_CHAMBER_RESIDENCY_TIME 10 // (seconds) Time to wait for chamber to "settle" in M191
+#define TEMP_CHAMBER_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer
+#define TEMP_CHAMBER_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target
+
// Below this temperature the heater will be switched off
// because it probably indicates a broken thermistor wire.
#define HEATER_0_MINTEMP 5
@@ -458,6 +462,7 @@
#define HEATER_6_MINTEMP 5
#define HEATER_7_MINTEMP 5
#define BED_MINTEMP 5
+#define CHAMBER_MINTEMP 5
// Above this temperature the heater will be switched off.
// This can protect components from overheating, but NOT from shorts and failures.
@@ -471,6 +476,7 @@
#define HEATER_6_MAXTEMP 275
#define HEATER_7_MAXTEMP 275
#define BED_MAXTEMP 150
+#define CHAMBER_MAXTEMP 60
//===========================================================================
//============================= PID Settings ================================
@@ -544,7 +550,52 @@
// FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles.
#endif // PIDTEMPBED
-#if EITHER(PIDTEMP, PIDTEMPBED)
+//===========================================================================
+//==================== PID > Chamber Temperature Control ====================
+//===========================================================================
+
+/**
+ * PID Chamber Heating
+ *
+ * If this option is enabled set PID constants below.
+ * If this option is disabled, bang-bang will be used and CHAMBER_LIMIT_SWITCHING will enable
+ * hysteresis.
+ *
+ * The PID frequency will be the same as the extruder PWM.
+ * If PID_dT is the default, and correct for the hardware/configuration, that means 7.689Hz,
+ * which is fine for driving a square wave into a resistive load and does not significantly
+ * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 200W
+ * heater. If your configuration is significantly different than this and you don't understand
+ * the issues involved, don't use chamber PID until someone else verifies that your hardware works.
+ */
+//#define PIDTEMPCHAMBER
+
+//#define CHAMBER_LIMIT_SWITCHING
+
+/**
+ * Max Chamber Power
+ * Applies to all forms of chamber control (PID, bang-bang, and bang-bang with hysteresis).
+ * When set to any value below 255, enables a form of PWM to the chamber heater that acts like a divider
+ * so don't use it unless you are OK with PWM on your heater. (See the comment on enabling PIDTEMPCHAMBER)
+ */
+#define MAX_CHAMBER_POWER 255 // limits duty cycle to chamber heater; 255=full current
+
+#if ENABLED(PIDTEMPCHAMBER)
+ #define MIN_CHAMBER_POWER 0
+ //#define PID_CHAMBER_DEBUG // Sends debug data to the serial port.
+
+ // Lasko "MyHeat Personal Heater" (200w) modified with a Fotek SSR-10DA to control only the heating element
+ // and placed inside the small Creality printer enclosure tent.
+ //
+ #define DEFAULT_chamberKp 37.04
+ #define DEFAULT_chamberKi 1.40
+ #define DEFAULT_chamberKd 655.17
+ // M309 P37.04 I1.04 D655.17
+
+ // FIND YOUR OWN: "M303 E-2 C8 S50" to run autotune on the chamber at 50 degreesC for 8 cycles.
+#endif // PIDTEMPCHAMBER
+
+#if ANY(PIDTEMP, PIDTEMPBED, PIDTEMPCHAMBER)
//#define PID_DEBUG // Sends debug data to the serial port. Use 'M303 D' to toggle activation.
//#define PID_OPENLOOP // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX
//#define SLOW_PWM_HEATERS // PWM with very low frequency (roughly 0.125Hz=8s) and minimum state time of approximately 1s useful for heaters driven by a relay
@@ -1624,11 +1675,13 @@
#define PREHEAT_1_LABEL "PLA"
#define PREHEAT_1_TEMP_HOTEND 180
#define PREHEAT_1_TEMP_BED 70
+#define PREHEAT_1_TEMP_CHAMBER 35
#define PREHEAT_1_FAN_SPEED 0 // Value from 0 to 255
#define PREHEAT_2_LABEL "ABS"
#define PREHEAT_2_TEMP_HOTEND 240
#define PREHEAT_2_TEMP_BED 110
+#define PREHEAT_2_TEMP_CHAMBER 35
#define PREHEAT_2_FAN_SPEED 0 // Value from 0 to 255
/**
diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
index 987ac293b2..50f17d1b9e 100644
--- a/Marlin/Configuration_adv.h
+++ b/Marlin/Configuration_adv.h
@@ -143,12 +143,19 @@
//
// Heated Chamber options
//
+#if DISABLED(PIDTEMPCHAMBER)
+ #define CHAMBER_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control
+ #if ENABLED(CHAMBER_LIMIT_SWITCHING)
+ #define CHAMBER_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > CHAMBER_HYSTERESIS
+ #endif
+#endif
+
#if TEMP_SENSOR_CHAMBER
- #define CHAMBER_MINTEMP 5
- #define CHAMBER_MAXTEMP 60
- #define TEMP_CHAMBER_HYSTERESIS 1 // (°C) Temperature proximity considered "close enough" to the target
- //#define CHAMBER_LIMIT_SWITCHING
- //#define HEATER_CHAMBER_PIN 44 // Chamber heater on/off pin
+ // Make sure you define where your heater is connected, the following works on a BTT SKR 1.4 Turbo
+ // using the secondary tool heater output. (FAN1 by default).
+ //#define FAN1_PIN -1 // Remove the fan signal on pin P2_04 (SKR 1.4 Turbo specific)
+ //#define HEATER_CHAMBER_PIN P2_04 // Chamber heater on/off pin (HE1 connector on SKR 1.4 Turbo)
+
//#define HEATER_CHAMBER_INVERTING false
//#define CHAMBER_FAN // Enable a fan on the chamber
diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h
index 923ad903cb..6024e9be3e 100644
--- a/Marlin/src/core/language.h
+++ b/Marlin/src/core/language.h
@@ -221,7 +221,7 @@
// temperature.cpp strings
#define STR_PID_AUTOTUNE_START "PID Autotune start"
-#define STR_PID_BAD_EXTRUDER_NUM "PID Autotune failed! Bad extruder number"
+#define STR_PID_BAD_HEATER_ID "PID Autotune failed! Bad heater id"
#define STR_PID_TEMP_TOO_HIGH "PID Autotune failed! Temperature too high"
#define STR_PID_TIMEOUT "PID Autotune failed! timeout"
#define STR_BIAS " bias: "
diff --git a/Marlin/src/feature/leds/printer_event_leds.cpp b/Marlin/src/feature/leds/printer_event_leds.cpp
index 3a6b91a258..32c6862704 100644
--- a/Marlin/src/feature/leds/printer_event_leds.cpp
+++ b/Marlin/src/feature/leds/printer_event_leds.cpp
@@ -77,6 +77,19 @@ PrinterEventLEDs printerEventLEDs;
pel_set_rgb(red, 0, 255);
}
}
+
+#endif
+
+#if HAS_HEATED_CHAMBER
+
+ void PrinterEventLEDs::onChamberHeating(const float &start, const float ¤t, const float &target) {
+ const uint8_t green = pel_intensity(start, current, target);
+ if (green != old_intensity) {
+ old_intensity = green;
+ pel_set_rgb(255, green, 255);
+ }
+ }
+
#endif
#endif // PRINTER_EVENT_LEDS
diff --git a/Marlin/src/feature/leds/printer_event_leds.h b/Marlin/src/feature/leds/printer_event_leds.h
index 86ec292aa3..668c9c969b 100644
--- a/Marlin/src/feature/leds/printer_event_leds.h
+++ b/Marlin/src/feature/leds/printer_event_leds.h
@@ -55,7 +55,12 @@ public:
static void onBedHeating(const float &start, const float ¤t, const float &target);
#endif
- #if HAS_TEMP_HOTEND || HAS_HEATED_BED
+ #if HAS_HEATED_CHAMBER
+ static inline LEDColor onChamberHeatingStart() { old_intensity = 127; return leds.get_color(); }
+ static void onChamberHeating(const float &start, const float ¤t, const float &target);
+ #endif
+
+ #if HAS_TEMP_HOTEND || HAS_HEATED_BED || HAS_HEATED_CHAMBER
static inline void onHeatingDone() { leds.set_white(); }
static inline void onPidTuningDone(LEDColor c) { leds.set_color(c); }
#endif
diff --git a/Marlin/src/gcode/config/M309.cpp b/Marlin/src/gcode/config/M309.cpp
new file mode 100644
index 0000000000..2247481b25
--- /dev/null
+++ b/Marlin/src/gcode/config/M309.cpp
@@ -0,0 +1,48 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(PIDTEMPCHAMBER)
+
+#include "../gcode.h"
+#include "../../module/temperature.h"
+
+/**
+ * M309 - Set and/or Report the current Chamber PID values
+ *
+ * P - Set the P value
+ * I - Set the I value
+ * D - Set the D value
+ */
+void GcodeSuite::M309() {
+ if (parser.seen('P')) thermalManager.temp_chamber.pid.Kp = parser.value_float();
+ if (parser.seen('I')) thermalManager.temp_chamber.pid.Ki = scalePID_i(parser.value_float());
+ if (parser.seen('D')) thermalManager.temp_chamber.pid.Kd = scalePID_d(parser.value_float());
+
+ SERIAL_ECHO_START();
+ SERIAL_ECHOLNPAIR(" p:", thermalManager.temp_chamber.pid.Kp,
+ " i:", unscalePID_i(thermalManager.temp_chamber.pid.Ki),
+ " d:", unscalePID_d(thermalManager.temp_chamber.pid.Kd));
+}
+
+#endif // PIDTEMPCHAMBER
diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp
index 77ac1fbff8..a410ad90a4 100644
--- a/Marlin/src/gcode/gcode.cpp
+++ b/Marlin/src/gcode/gcode.cpp
@@ -682,6 +682,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
case 304: M304(); break; // M304: Set bed PID parameters
#endif
+ #if ENABLED(PIDTEMPCHAMBER)
+ case 309: M309(); break; // M309: Set chamber PID parameters
+ #endif
+
#if ENABLED(PHOTO_GCODE)
case 240: M240(); break; // M240: Trigger a camera
#endif
diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h
index 7fd8d6904a..123b648f90 100644
--- a/Marlin/src/gcode/gcode.h
+++ b/Marlin/src/gcode/gcode.h
@@ -197,6 +197,7 @@
* M303 - PID relay autotune S sets the target temperature. Default 150C. (Requires PIDTEMP)
* M304 - Set bed PID parameters P I and D. (Requires PIDTEMPBED)
* M305 - Set user thermistor parameters R T and P. (Requires TEMP_SENSOR_x 1000)
+ * M309 - Set chamber PID parameters P I and D. (Requires PIDTEMPCHAMBER)
* M350 - Set microstepping mode. (Requires digital microstepping pins.)
* M351 - Toggle MS1 MS2 pins directly. (Requires digital microstepping pins.)
* M355 - Set Case Light on/off and set brightness. (Requires CASE_LIGHT_PIN)
@@ -711,6 +712,8 @@ private:
TERN_(HAS_USER_THERMISTORS, static void M305());
+ TERN_(PIDTEMPCHAMBER, static void M309());
+
#if HAS_MICROSTEPS
static void M350();
static void M351();
diff --git a/Marlin/src/gcode/temp/M303.cpp b/Marlin/src/gcode/temp/M303.cpp
index a066ddc88d..159a52bf26 100644
--- a/Marlin/src/gcode/temp/M303.cpp
+++ b/Marlin/src/gcode/temp/M303.cpp
@@ -40,19 +40,15 @@
* C Number of times to repeat the procedure. (Minimum: 3, Default: 5)
* U Flag to apply the result to the current PID values
*
- * With PID_DEBUG:
+ * With PID_DEBUG, PID_BED_DEBUG, or PID_CHAMBER_DEBUG:
* D Toggle PID debugging and EXIT without further action.
*/
-#if ENABLED(PID_DEBUG)
- bool pid_debug_flag = 0;
-#endif
-
void GcodeSuite::M303() {
- #if ENABLED(PID_DEBUG)
+ #if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG)
if (parser.seen('D')) {
- pid_debug_flag = !pid_debug_flag;
+ thermalManager.pid_debug_flag ^= true;
SERIAL_ECHO_START();
SERIAL_ECHOPGM("PID Debug ");
serialprintln_onoff(pid_debug_flag);
@@ -60,25 +56,34 @@ void GcodeSuite::M303() {
}
#endif
- #define SI TERN(PIDTEMPBED, H_BED, H_E0)
- #define EI TERN(PIDTEMP, HOTENDS - 1, H_BED)
- const heater_id_t e = (heater_id_t)parser.intval('E');
- if (!WITHIN(e, SI, EI)) {
- SERIAL_ECHOLNPGM(STR_PID_BAD_EXTRUDER_NUM);
- TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_BAD_EXTRUDER_NUM));
- return;
+ const heater_id_t hid = (heater_id_t)parser.intval('E');
+ int16_t default_temp;
+ switch (hid) {
+ #if ENABLED(PIDTEMP)
+ case 0 ... HOTENDS - 1: default_temp = PREHEAT_1_TEMP_HOTEND; break;
+ #endif
+ #if ENABLED(PIDTEMPBED)
+ case H_BED: default_temp = PREHEAT_1_TEMP_BED; break;
+ #endif
+ #if ENABLED(PIDTEMPCHAMBER)
+ case H_CHAMBER: default_temp = PREHEAT_1_TEMP_CHAMBER; break;
+ #endif
+ default:
+ SERIAL_ECHOLNPGM(STR_PID_BAD_HEATER_ID);
+ TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_BAD_EXTRUDER_NUM));
+ return;
}
+ const int16_t temp = parser.celsiusval('S', default_temp);
const int c = parser.intval('C', 5);
const bool u = parser.boolval('U');
- const int16_t temp = parser.celsiusval('S', e < 0 ? PREHEAT_1_TEMP_BED : PREHEAT_1_TEMP_HOTEND);
#if DISABLED(BUSY_WHILE_HEATING)
KEEPALIVE_STATE(NOT_BUSY);
#endif
LCD_MESSAGEPGM(MSG_PID_AUTOTUNE);
- thermalManager.PID_autotune(temp, e, c, u);
+ thermalManager.PID_autotune(temp, hid, c, u);
ui.reset_status();
}
diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h
index f8360767d9..585d48de8c 100644
--- a/Marlin/src/inc/Conditionals_post.h
+++ b/Marlin/src/inc/Conditionals_post.h
@@ -1992,27 +1992,31 @@
#define BED_OVERSHOOT 10
#endif
#define BED_MAX_TARGET (BED_MAXTEMP - (BED_OVERSHOOT))
+#else
+ #undef PIDTEMPBED
#endif
+
#if HAS_HEATED_BED || HAS_TEMP_CHAMBER
#define BED_OR_CHAMBER 1
#endif
#if HAS_TEMP_HOTEND || BED_OR_CHAMBER || HAS_TEMP_PROBE
#define HAS_TEMP_SENSOR 1
#endif
+
#if HAS_TEMP_CHAMBER && PIN_EXISTS(HEATER_CHAMBER)
#define HAS_HEATED_CHAMBER 1
+ #ifndef CHAMBER_OVERSHOOT
+ #define CHAMBER_OVERSHOOT 10
+ #endif
+ #define CHAMBER_MAX_TARGET (CHAMBER_MAXTEMP - (CHAMBER_OVERSHOOT))
+#else
+ #undef PIDTEMPCHAMBER
#endif
// PID heating
-#if !HAS_HEATED_BED
- #undef PIDTEMPBED
-#endif
-#if EITHER(PIDTEMP, PIDTEMPBED)
+#if ANY(PIDTEMP, PIDTEMPBED, PIDTEMPCHAMBER)
#define HAS_PID_HEATING 1
#endif
-#if BOTH(PIDTEMP, PIDTEMPBED)
- #define HAS_PID_FOR_BOTH 1
-#endif
// Thermal protection
#if BOTH(HAS_HEATED_BED, THERMAL_PROTECTION_BED)
@@ -2346,6 +2350,9 @@
* Heated chamber requires settings
*/
#if HAS_HEATED_CHAMBER
+ #ifndef MIN_CHAMBER_POWER
+ #define MIN_CHAMBER_POWER 0
+ #endif
#ifndef MAX_CHAMBER_POWER
#define MAX_CHAMBER_POWER 255
#endif
diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h
index c1b0290ac1..ba69b4792e 100644
--- a/Marlin/src/inc/SanityCheck.h
+++ b/Marlin/src/inc/SanityCheck.h
@@ -1203,6 +1203,13 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
#error "To use BED_LIMIT_SWITCHING you must disable PIDTEMPBED."
#endif
+/**
+ * Chamber Heating Options - PID vs Limit Switching
+ */
+#if BOTH(PIDTEMPCHAMBER, CHAMBER_LIMIT_SWITCHING)
+ #error "To use CHAMBER_LIMIT_SWITCHING you must disable PIDTEMPCHAMBER."
+#endif
+
/**
* Kinematics
*/
diff --git a/Marlin/src/lcd/menu/menu_advanced.cpp b/Marlin/src/lcd/menu/menu_advanced.cpp
index cb7827168b..aab93c3bdd 100644
--- a/Marlin/src/lcd/menu/menu_advanced.cpp
+++ b/Marlin/src/lcd/menu/menu_advanced.cpp
@@ -177,18 +177,25 @@ void menu_backlash();
#if ENABLED(PIDTEMPBED)
int16_t autotune_temp_bed = PREHEAT_1_TEMP_BED;
#endif
+ #if ENABLED(PIDTEMPCHAMBER)
+ int16_t autotune_temp_chamber = PREHEAT_1_TEMP_CHAMBER;
+ #endif
#include "../../gcode/queue.h"
- void _lcd_autotune(const int16_t e) {
+ void _lcd_autotune(const heater_id_t hid) {
char cmd[30];
- sprintf_P(cmd, PSTR("M303 U1 E%i S%i"), e,
- #if HAS_PID_FOR_BOTH
- e < 0 ? autotune_temp_bed : autotune_temp[e]
- #else
- TERN(PIDTEMPBED, autotune_temp_bed, autotune_temp[e])
+ int16_t tune_temp;
+ switch (hid) {
+ #if ENABLED(PIDTEMPBED)
+ case H_BED: tune_temp = autotune_temp_bed; break;
#endif
- );
+ #if ENABLED(PIDTEMPCHAMBER)
+ case H_CHAMBER: tune_temp = autotune_temp_chamber; break;
+ #endif
+ default: tune_temp = autotune_temp[hid]; break;
+ }
+ sprintf_P(cmd, PSTR("M303 U1 E%i S%i"), hid, tune_temp);
queue.inject(cmd);
ui.return_to_status();
}
@@ -225,7 +232,7 @@ void menu_backlash();
#if ENABLED(PID_AUTOTUNE_MENU)
#define DEFINE_PIDTEMP_FUNCS(N) \
_DEFINE_PIDTEMP_BASE_FUNCS(N); \
- void lcd_autotune_callback_E##N() { _lcd_autotune(N); }
+ void lcd_autotune_callback_E##N() { _lcd_autotune(heater_id_t(N)); }
#else
#define DEFINE_PIDTEMP_FUNCS(N) _DEFINE_PIDTEMP_BASE_FUNCS(N);
#endif
@@ -269,56 +276,70 @@ void menu_backlash();
//
#if ENABLED(PID_EDIT_MENU)
- #define __PID_BASE_MENU_ITEMS(N) \
- raw_Ki = unscalePID_i(TERN(PID_BED_MENU_SECTION, thermalManager.temp_bed.pid.Ki, PID_PARAM(Ki, N))); \
- raw_Kd = unscalePID_d(TERN(PID_BED_MENU_SECTION, thermalManager.temp_bed.pid.Kd, PID_PARAM(Kd, N))); \
- EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_P_E, &TERN(PID_BED_MENU_SECTION, thermalManager.temp_bed.pid.Kp, PID_PARAM(Kp, N)), 1, 9990); \
+ #define _PID_EDIT_ITEMS_TMPL(N,T) \
+ raw_Ki = unscalePID_i(T.pid.Ki); \
+ raw_Kd = unscalePID_d(T.pid.Kd); \
+ EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_P_E, &T.pid.Kp, 1, 9990); \
+ EDIT_ITEM_FAST_N(float52sign, N, MSG_PID_I_E, &raw_Ki, 0.01f, 9990, []{ copy_and_scalePID_i(N); }); \
+ EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_D_E, &raw_Kd, 1, 9990, []{ copy_and_scalePID_d(N); })
+
+ #define __PID_HOTEND_MENU_ITEMS(N) \
+ raw_Ki = unscalePID_i(PID_PARAM(Ki, N)); \
+ raw_Kd = unscalePID_d(PID_PARAM(Kd, N)); \
+ EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_P_E, &PID_PARAM(Kp, N), 1, 9990); \
EDIT_ITEM_FAST_N(float52sign, N, MSG_PID_I_E, &raw_Ki, 0.01f, 9990, []{ copy_and_scalePID_i(N); }); \
EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_D_E, &raw_Kd, 1, 9990, []{ copy_and_scalePID_d(N); })
#if ENABLED(PID_EXTRUSION_SCALING)
- #define _PID_BASE_MENU_ITEMS(N) \
- __PID_BASE_MENU_ITEMS(N); \
+ #define _PID_HOTEND_MENU_ITEMS(N) \
+ __PID_HOTEND_MENU_ITEMS(N); \
EDIT_ITEM_N(float4, N, MSG_PID_C_E, &PID_PARAM(Kc, N), 1, 9990)
#else
- #define _PID_BASE_MENU_ITEMS(N) __PID_BASE_MENU_ITEMS(N)
+ #define _PID_HOTEND_MENU_ITEMS(N) __PID_HOTEND_MENU_ITEMS(N)
#endif
#if ENABLED(PID_FAN_SCALING)
- #define _PID_EDIT_MENU_ITEMS(N) \
- _PID_BASE_MENU_ITEMS(N); \
+ #define _HOTEND_PID_EDIT_MENU_ITEMS(N) \
+ _PID_HOTEND_MENU_ITEMS(N); \
EDIT_ITEM_N(float4, N, MSG_PID_F_E, &PID_PARAM(Kf, N), 1, 9990)
#else
- #define _PID_EDIT_MENU_ITEMS(N) _PID_BASE_MENU_ITEMS(N)
+ #define _HOTEND_PID_EDIT_MENU_ITEMS(N) _PID_HOTEND_MENU_ITEMS(N)
#endif
#else
- #define _PID_EDIT_MENU_ITEMS(N) NOOP
+ #define _HOTEND_PID_EDIT_MENU_ITEMS(N) NOOP
#endif
#if ENABLED(PID_AUTOTUNE_MENU)
- #define PID_EDIT_MENU_ITEMS(N) \
- _PID_EDIT_MENU_ITEMS(N); \
- EDIT_ITEM_FAST_N(int3, N, MSG_PID_AUTOTUNE_E, &autotune_temp[N], 150, thermalManager.heater_maxtemp[N] - HOTEND_OVERSHOOT, []{ _lcd_autotune(MenuItemBase::itemIndex); });
+ #define HOTEND_PID_EDIT_MENU_ITEMS(N) \
+ _HOTEND_PID_EDIT_MENU_ITEMS(N); \
+ EDIT_ITEM_FAST_N(int3, N, MSG_PID_AUTOTUNE_E, &autotune_temp[N], 150, thermalManager.heater_maxtemp[N] - HOTEND_OVERSHOOT, []{ _lcd_autotune(heater_id_t(MenuItemBase::itemIndex)); });
#else
- #define PID_EDIT_MENU_ITEMS(N) _PID_EDIT_MENU_ITEMS(N);
+ #define HOTEND_PID_EDIT_MENU_ITEMS(N) _HOTEND_PID_EDIT_MENU_ITEMS(N);
#endif
- PID_EDIT_MENU_ITEMS(0);
+ HOTEND_PID_EDIT_MENU_ITEMS(0);
#if ENABLED(PID_PARAMS_PER_HOTEND)
- REPEAT_S(1, HOTENDS, PID_EDIT_MENU_ITEMS)
+ REPEAT_S(1, HOTENDS, HOTEND_PID_EDIT_MENU_ITEMS)
#endif
#if ENABLED(PIDTEMPBED)
#if ENABLED(PID_EDIT_MENU)
- #define PID_BED_MENU_SECTION
- __PID_BASE_MENU_ITEMS(-1);
- #undef PID_BED_MENU_SECTION
+ _PID_EDIT_ITEMS_TMPL(H_BED, thermalManager.temp_bed);
#endif
#if ENABLED(PID_AUTOTUNE_MENU)
- EDIT_ITEM_FAST_N(int3, -1, MSG_PID_AUTOTUNE_E, &autotune_temp_bed, PREHEAT_1_TEMP_BED, BED_MAX_TARGET, []{ _lcd_autotune(-1); });
+ EDIT_ITEM_FAST_N(int3, H_BED, MSG_PID_AUTOTUNE_E, &autotune_temp_bed, PREHEAT_1_TEMP_BED, BED_MAX_TARGET, []{ _lcd_autotune(H_BED); });
+ #endif
+ #endif
+
+ #if ENABLED(PIDTEMPCHAMBER)
+ #if ENABLED(PID_EDIT_MENU)
+ _PID_EDIT_ITEMS_TMPL(H_CHAMBER, thermalManager.temp_chamber);
+ #endif
+ #if ENABLED(PID_AUTOTUNE_MENU)
+ EDIT_ITEM_FAST_N(int3, H_CHAMBER, MSG_PID_AUTOTUNE_E, &autotune_temp_chamber, PREHEAT_1_TEMP_CHAMBER, CHAMBER_MAX_TARGET, []{ _lcd_autotune(H_CHAMBER); });
#endif
#endif
diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp
index 7fb378191e..f4b4bf0b8b 100644
--- a/Marlin/src/module/settings.cpp
+++ b/Marlin/src/module/settings.cpp
@@ -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:");
diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp
index edd76df3b7..58b0a965c9 100644
--- a/Marlin/src/module/temperature.cpp
+++ b/Marlin/src/module/temperature.cpp
@@ -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
diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h
index 75da232874..1019c10fad 100644
--- a/Marlin/src/module/temperature.h
+++ b/Marlin/src/module/temperature.h
@@ -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 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);