Chamber Heater PID (#21156)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
		
				
					committed by
					
						 Scott Lahteine
						Scott Lahteine
					
				
			
			
				
	
			
			
			
						parent
						
							c051a2ecae
						
					
				
				
					commit
					8d05a823e9
				
			| @@ -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 | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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: " | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										48
									
								
								Marlin/src/gcode/config/M309.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Marlin/src/gcode/config/M309.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #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<pval> - Set the P value | ||||
|  *  I<ival> - Set the I value | ||||
|  *  D<dval> - 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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -197,6 +197,7 @@ | ||||
|  * M303 - PID relay autotune S<temperature> 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(); | ||||
|   | ||||
| @@ -40,19 +40,15 @@ | ||||
|  *  C<cycles>       Number of times to repeat the procedure. (Minimum: 3, Default: 5) | ||||
|  *  U<bool>         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(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|  */ | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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