My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

temperature.h 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #pragma once
  23. /**
  24. * temperature.h - temperature controller
  25. */
  26. #include "thermistor/thermistors.h"
  27. #include "../inc/MarlinConfig.h"
  28. #if ENABLED(AUTO_POWER_CONTROL)
  29. #include "../feature/power.h"
  30. #endif
  31. #if ENABLED(AUTO_REPORT_TEMPERATURES)
  32. #include "../libs/autoreport.h"
  33. #endif
  34. #if HAS_FANCHECK
  35. #include "../feature/fancheck.h"
  36. #endif
  37. #ifndef SOFT_PWM_SCALE
  38. #define SOFT_PWM_SCALE 0
  39. #endif
  40. #define HOTEND_INDEX TERN(HAS_MULTI_HOTEND, e, 0)
  41. #define E_NAME TERN_(HAS_MULTI_HOTEND, e)
  42. // Element identifiers. Positive values are hotends. Negative values are other heaters or coolers.
  43. typedef enum : int8_t {
  44. H_REDUNDANT = HID_REDUNDANT,
  45. H_COOLER = HID_COOLER,
  46. H_PROBE = HID_PROBE,
  47. H_BOARD = HID_BOARD,
  48. H_CHAMBER = HID_CHAMBER,
  49. H_BED = HID_BED,
  50. H_E0 = HID_E0, H_E1, H_E2, H_E3, H_E4, H_E5, H_E6, H_E7
  51. } heater_id_t;
  52. // PID storage
  53. typedef struct { float Kp, Ki, Kd; } PID_t;
  54. typedef struct { float Kp, Ki, Kd, Kc; } PIDC_t;
  55. typedef struct { float Kp, Ki, Kd, Kf; } PIDF_t;
  56. typedef struct { float Kp, Ki, Kd, Kc, Kf; } PIDCF_t;
  57. typedef
  58. #if BOTH(PID_EXTRUSION_SCALING, PID_FAN_SCALING)
  59. PIDCF_t
  60. #elif ENABLED(PID_EXTRUSION_SCALING)
  61. PIDC_t
  62. #elif ENABLED(PID_FAN_SCALING)
  63. PIDF_t
  64. #else
  65. PID_t
  66. #endif
  67. hotend_pid_t;
  68. #if ENABLED(PID_EXTRUSION_SCALING)
  69. typedef IF<(LPQ_MAX_LEN > 255), uint16_t, uint8_t>::type lpq_ptr_t;
  70. #endif
  71. #define PID_PARAM(F,H) _PID_##F(TERN(PID_PARAMS_PER_HOTEND, H, 0 & H)) // Always use 'H' to suppress warning
  72. #define _PID_Kp(H) TERN(PIDTEMP, Temperature::temp_hotend[H].pid.Kp, NAN)
  73. #define _PID_Ki(H) TERN(PIDTEMP, Temperature::temp_hotend[H].pid.Ki, NAN)
  74. #define _PID_Kd(H) TERN(PIDTEMP, Temperature::temp_hotend[H].pid.Kd, NAN)
  75. #if ENABLED(PIDTEMP)
  76. #define _PID_Kc(H) TERN(PID_EXTRUSION_SCALING, Temperature::temp_hotend[H].pid.Kc, 1)
  77. #define _PID_Kf(H) TERN(PID_FAN_SCALING, Temperature::temp_hotend[H].pid.Kf, 0)
  78. #else
  79. #define _PID_Kc(H) 1
  80. #define _PID_Kf(H) 0
  81. #endif
  82. /**
  83. * States for ADC reading in the ISR
  84. */
  85. enum ADCSensorState : char {
  86. StartSampling,
  87. #if HAS_TEMP_ADC_0
  88. PrepareTemp_0, MeasureTemp_0,
  89. #endif
  90. #if HAS_TEMP_ADC_BED
  91. PrepareTemp_BED, MeasureTemp_BED,
  92. #endif
  93. #if HAS_TEMP_ADC_CHAMBER
  94. PrepareTemp_CHAMBER, MeasureTemp_CHAMBER,
  95. #endif
  96. #if HAS_TEMP_ADC_COOLER
  97. PrepareTemp_COOLER, MeasureTemp_COOLER,
  98. #endif
  99. #if HAS_TEMP_ADC_PROBE
  100. PrepareTemp_PROBE, MeasureTemp_PROBE,
  101. #endif
  102. #if HAS_TEMP_ADC_BOARD
  103. PrepareTemp_BOARD, MeasureTemp_BOARD,
  104. #endif
  105. #if HAS_TEMP_ADC_REDUNDANT
  106. PrepareTemp_REDUNDANT, MeasureTemp_REDUNDANT,
  107. #endif
  108. #if HAS_TEMP_ADC_1
  109. PrepareTemp_1, MeasureTemp_1,
  110. #endif
  111. #if HAS_TEMP_ADC_2
  112. PrepareTemp_2, MeasureTemp_2,
  113. #endif
  114. #if HAS_TEMP_ADC_3
  115. PrepareTemp_3, MeasureTemp_3,
  116. #endif
  117. #if HAS_TEMP_ADC_4
  118. PrepareTemp_4, MeasureTemp_4,
  119. #endif
  120. #if HAS_TEMP_ADC_5
  121. PrepareTemp_5, MeasureTemp_5,
  122. #endif
  123. #if HAS_TEMP_ADC_6
  124. PrepareTemp_6, MeasureTemp_6,
  125. #endif
  126. #if HAS_TEMP_ADC_7
  127. PrepareTemp_7, MeasureTemp_7,
  128. #endif
  129. #if HAS_JOY_ADC_X
  130. PrepareJoy_X, MeasureJoy_X,
  131. #endif
  132. #if HAS_JOY_ADC_Y
  133. PrepareJoy_Y, MeasureJoy_Y,
  134. #endif
  135. #if HAS_JOY_ADC_Z
  136. PrepareJoy_Z, MeasureJoy_Z,
  137. #endif
  138. #if ENABLED(FILAMENT_WIDTH_SENSOR)
  139. Prepare_FILWIDTH, Measure_FILWIDTH,
  140. #endif
  141. #if ENABLED(POWER_MONITOR_CURRENT)
  142. Prepare_POWER_MONITOR_CURRENT,
  143. Measure_POWER_MONITOR_CURRENT,
  144. #endif
  145. #if ENABLED(POWER_MONITOR_VOLTAGE)
  146. Prepare_POWER_MONITOR_VOLTAGE,
  147. Measure_POWER_MONITOR_VOLTAGE,
  148. #endif
  149. #if HAS_ADC_BUTTONS
  150. Prepare_ADC_KEY, Measure_ADC_KEY,
  151. #endif
  152. SensorsReady, // Temperatures ready. Delay the next round of readings to let ADC pins settle.
  153. StartupDelay // Startup, delay initial temp reading a tiny bit so the hardware can settle
  154. };
  155. // Minimum number of Temperature::ISR loops between sensor readings.
  156. // Multiplied by 16 (OVERSAMPLENR) to obtain the total time to
  157. // get all oversampled sensor readings
  158. #define MIN_ADC_ISR_LOOPS 10
  159. #define ACTUAL_ADC_SAMPLES _MAX(int(MIN_ADC_ISR_LOOPS), int(SensorsReady))
  160. #if HAS_PID_HEATING
  161. #define PID_K2 (1-float(PID_K1))
  162. #define PID_dT ((OVERSAMPLENR * float(ACTUAL_ADC_SAMPLES)) / TEMP_TIMER_FREQUENCY)
  163. // Apply the scale factors to the PID values
  164. #define scalePID_i(i) ( float(i) * PID_dT )
  165. #define unscalePID_i(i) ( float(i) / PID_dT )
  166. #define scalePID_d(d) ( float(d) / PID_dT )
  167. #define unscalePID_d(d) ( float(d) * PID_dT )
  168. #endif
  169. #if ENABLED(G26_MESH_VALIDATION) && EITHER(HAS_LCD_MENU, EXTENSIBLE_UI)
  170. #define G26_CLICK_CAN_CANCEL 1
  171. #endif
  172. // A temperature sensor
  173. typedef struct TempInfo {
  174. uint16_t acc;
  175. int16_t raw;
  176. celsius_float_t celsius;
  177. inline void reset() { acc = 0; }
  178. inline void sample(const uint16_t s) { acc += s; }
  179. inline void update() { raw = acc; }
  180. } temp_info_t;
  181. #if HAS_TEMP_REDUNDANT
  182. // A redundant temperature sensor
  183. typedef struct RedundantTempInfo : public TempInfo {
  184. temp_info_t* target;
  185. } redundant_info_t;
  186. #endif
  187. // A PWM heater with temperature sensor
  188. typedef struct HeaterInfo : public TempInfo {
  189. celsius_t target;
  190. uint8_t soft_pwm_amount;
  191. } heater_info_t;
  192. // A heater with PID stabilization
  193. template<typename T>
  194. struct PIDHeaterInfo : public HeaterInfo {
  195. T pid; // Initialized by settings.load()
  196. };
  197. #if ENABLED(PIDTEMP)
  198. typedef struct PIDHeaterInfo<hotend_pid_t> hotend_info_t;
  199. #else
  200. typedef heater_info_t hotend_info_t;
  201. #endif
  202. #if HAS_HEATED_BED
  203. #if ENABLED(PIDTEMPBED)
  204. typedef struct PIDHeaterInfo<PID_t> bed_info_t;
  205. #else
  206. typedef heater_info_t bed_info_t;
  207. #endif
  208. #endif
  209. #if HAS_TEMP_PROBE
  210. typedef temp_info_t probe_info_t;
  211. #endif
  212. #if HAS_HEATED_CHAMBER
  213. #if ENABLED(PIDTEMPCHAMBER)
  214. typedef struct PIDHeaterInfo<PID_t> chamber_info_t;
  215. #else
  216. typedef heater_info_t chamber_info_t;
  217. #endif
  218. #elif HAS_TEMP_CHAMBER
  219. typedef temp_info_t chamber_info_t;
  220. #endif
  221. #if HAS_TEMP_BOARD
  222. typedef temp_info_t board_info_t;
  223. #endif
  224. #if EITHER(HAS_COOLER, HAS_TEMP_COOLER)
  225. typedef heater_info_t cooler_info_t;
  226. #endif
  227. // Heater watch handling
  228. template <int INCREASE, int HYSTERESIS, millis_t PERIOD>
  229. struct HeaterWatch {
  230. celsius_t target;
  231. millis_t next_ms;
  232. inline bool elapsed(const millis_t &ms) { return next_ms && ELAPSED(ms, next_ms); }
  233. inline bool elapsed() { return elapsed(millis()); }
  234. inline bool check(const celsius_t curr) { return curr >= target; }
  235. inline void restart(const celsius_t curr, const celsius_t tgt) {
  236. if (tgt) {
  237. const celsius_t newtarget = curr + INCREASE;
  238. if (newtarget < tgt - HYSTERESIS - 1) {
  239. target = newtarget;
  240. next_ms = millis() + SEC_TO_MS(PERIOD);
  241. return;
  242. }
  243. }
  244. next_ms = 0;
  245. }
  246. };
  247. #if WATCH_HOTENDS
  248. typedef struct HeaterWatch<WATCH_TEMP_INCREASE, TEMP_HYSTERESIS, WATCH_TEMP_PERIOD> hotend_watch_t;
  249. #endif
  250. #if WATCH_BED
  251. typedef struct HeaterWatch<WATCH_BED_TEMP_INCREASE, TEMP_BED_HYSTERESIS, WATCH_BED_TEMP_PERIOD> bed_watch_t;
  252. #endif
  253. #if WATCH_CHAMBER
  254. typedef struct HeaterWatch<WATCH_CHAMBER_TEMP_INCREASE, TEMP_CHAMBER_HYSTERESIS, WATCH_CHAMBER_TEMP_PERIOD> chamber_watch_t;
  255. #endif
  256. #if WATCH_COOLER
  257. typedef struct HeaterWatch<WATCH_COOLER_TEMP_INCREASE, TEMP_COOLER_HYSTERESIS, WATCH_COOLER_TEMP_PERIOD> cooler_watch_t;
  258. #endif
  259. // Temperature sensor read value ranges
  260. typedef struct { int16_t raw_min, raw_max; } raw_range_t;
  261. typedef struct { celsius_t mintemp, maxtemp; } celsius_range_t;
  262. typedef struct { int16_t raw_min, raw_max; celsius_t mintemp, maxtemp; } temp_range_t;
  263. #define THERMISTOR_ABS_ZERO_C -273.15f // bbbbrrrrr cold !
  264. #define THERMISTOR_RESISTANCE_NOMINAL_C 25.0f // mmmmm comfortable
  265. #if HAS_USER_THERMISTORS
  266. enum CustomThermistorIndex : uint8_t {
  267. #if TEMP_SENSOR_0_IS_CUSTOM
  268. CTI_HOTEND_0,
  269. #endif
  270. #if TEMP_SENSOR_1_IS_CUSTOM
  271. CTI_HOTEND_1,
  272. #endif
  273. #if TEMP_SENSOR_2_IS_CUSTOM
  274. CTI_HOTEND_2,
  275. #endif
  276. #if TEMP_SENSOR_3_IS_CUSTOM
  277. CTI_HOTEND_3,
  278. #endif
  279. #if TEMP_SENSOR_4_IS_CUSTOM
  280. CTI_HOTEND_4,
  281. #endif
  282. #if TEMP_SENSOR_5_IS_CUSTOM
  283. CTI_HOTEND_5,
  284. #endif
  285. #if TEMP_SENSOR_BED_IS_CUSTOM
  286. CTI_BED,
  287. #endif
  288. #if TEMP_SENSOR_PROBE_IS_CUSTOM
  289. CTI_PROBE,
  290. #endif
  291. #if TEMP_SENSOR_CHAMBER_IS_CUSTOM
  292. CTI_CHAMBER,
  293. #endif
  294. #if TEMP_SENSOR_COOLER_IS_CUSTOM
  295. CTI_COOLER,
  296. #endif
  297. #if TEMP_SENSOR_BOARD_IS_CUSTOM
  298. CTI_BOARD,
  299. #endif
  300. #if TEMP_SENSOR_REDUNDANT_IS_CUSTOM
  301. CTI_REDUNDANT,
  302. #endif
  303. USER_THERMISTORS
  304. };
  305. // User-defined thermistor
  306. typedef struct {
  307. bool pre_calc; // true if pre-calculations update needed
  308. float sh_c_coeff, // Steinhart-Hart C coefficient .. defaults to '0.0'
  309. sh_alpha,
  310. series_res,
  311. res_25, res_25_recip,
  312. res_25_log,
  313. beta, beta_recip;
  314. } user_thermistor_t;
  315. #endif
  316. #if HAS_AUTO_FAN || HAS_FANCHECK
  317. #define HAS_FAN_LOGIC 1
  318. #endif
  319. class Temperature {
  320. public:
  321. #if HAS_HOTEND
  322. static hotend_info_t temp_hotend[HOTENDS];
  323. static const celsius_t hotend_maxtemp[HOTENDS];
  324. static celsius_t hotend_max_target(const uint8_t e) { return hotend_maxtemp[e] - (HOTEND_OVERSHOOT); }
  325. #endif
  326. #if HAS_HEATED_BED
  327. static bed_info_t temp_bed;
  328. #endif
  329. #if HAS_TEMP_PROBE
  330. static probe_info_t temp_probe;
  331. #endif
  332. #if HAS_TEMP_CHAMBER
  333. static chamber_info_t temp_chamber;
  334. #endif
  335. #if HAS_TEMP_COOLER
  336. static cooler_info_t temp_cooler;
  337. #endif
  338. #if HAS_TEMP_BOARD
  339. static board_info_t temp_board;
  340. #endif
  341. #if HAS_TEMP_REDUNDANT
  342. static redundant_info_t temp_redundant;
  343. #endif
  344. #if EITHER(AUTO_POWER_E_FANS, HAS_FANCHECK)
  345. static uint8_t autofan_speed[HOTENDS];
  346. #endif
  347. #if ENABLED(AUTO_POWER_CHAMBER_FAN)
  348. static uint8_t chamberfan_speed;
  349. #endif
  350. #if ENABLED(AUTO_POWER_COOLER_FAN)
  351. static uint8_t coolerfan_speed;
  352. #endif
  353. #if ENABLED(FAN_SOFT_PWM)
  354. static uint8_t soft_pwm_amount_fan[FAN_COUNT],
  355. soft_pwm_count_fan[FAN_COUNT];
  356. #endif
  357. #if BOTH(FAN_SOFT_PWM, USE_CONTROLLER_FAN)
  358. static uint8_t soft_pwm_controller_speed;
  359. #endif
  360. #if ENABLED(PREVENT_COLD_EXTRUSION)
  361. static bool allow_cold_extrude;
  362. static celsius_t extrude_min_temp;
  363. static bool tooCold(const celsius_t temp) { return allow_cold_extrude ? false : temp < extrude_min_temp - (TEMP_WINDOW); }
  364. static bool tooColdToExtrude(const uint8_t E_NAME) { return tooCold(wholeDegHotend(HOTEND_INDEX)); }
  365. static bool targetTooColdToExtrude(const uint8_t E_NAME) { return tooCold(degTargetHotend(HOTEND_INDEX)); }
  366. #else
  367. static bool tooColdToExtrude(const uint8_t) { return false; }
  368. static bool targetTooColdToExtrude(const uint8_t) { return false; }
  369. #endif
  370. static bool hotEnoughToExtrude(const uint8_t e) { return !tooColdToExtrude(e); }
  371. static bool targetHotEnoughToExtrude(const uint8_t e) { return !targetTooColdToExtrude(e); }
  372. #if EITHER(SINGLENOZZLE_STANDBY_TEMP, SINGLENOZZLE_STANDBY_FAN)
  373. #if ENABLED(SINGLENOZZLE_STANDBY_TEMP)
  374. static celsius_t singlenozzle_temp[EXTRUDERS];
  375. #endif
  376. #if ENABLED(SINGLENOZZLE_STANDBY_FAN)
  377. static uint8_t singlenozzle_fan_speed[EXTRUDERS];
  378. #endif
  379. static void singlenozzle_change(const uint8_t old_tool, const uint8_t new_tool);
  380. #endif
  381. #if HEATER_IDLE_HANDLER
  382. // Heater idle handling. Marlin creates one per hotend and one for the heated bed.
  383. typedef struct {
  384. millis_t timeout_ms;
  385. bool timed_out;
  386. inline void update(const millis_t &ms) { if (!timed_out && timeout_ms && ELAPSED(ms, timeout_ms)) timed_out = true; }
  387. inline void start(const millis_t &ms) { timeout_ms = millis() + ms; timed_out = false; }
  388. inline void reset() { timeout_ms = 0; timed_out = false; }
  389. inline void expire() { start(0); }
  390. } heater_idle_t;
  391. // Indices and size for the heater_idle array
  392. enum IdleIndex : int8_t {
  393. _II = -1
  394. #define _IDLE_INDEX_E(N) ,IDLE_INDEX_E##N
  395. REPEAT(HOTENDS, _IDLE_INDEX_E)
  396. #undef _IDLE_INDEX_E
  397. OPTARG(HAS_HEATED_BED, IDLE_INDEX_BED)
  398. , NR_HEATER_IDLE
  399. };
  400. // Convert the given heater_id_t to idle array index
  401. static IdleIndex idle_index_for_id(const int8_t heater_id) {
  402. TERN_(HAS_HEATED_BED, if (heater_id == H_BED) return IDLE_INDEX_BED);
  403. return (IdleIndex)_MAX(heater_id, 0);
  404. }
  405. static heater_idle_t heater_idle[NR_HEATER_IDLE];
  406. #endif // HEATER_IDLE_TIMER
  407. #if HAS_ADC_BUTTONS
  408. static uint32_t current_ADCKey_raw;
  409. static uint16_t ADCKey_count;
  410. #endif
  411. #if ENABLED(PID_EXTRUSION_SCALING)
  412. static int16_t lpq_len;
  413. #endif
  414. #if HAS_FAN_LOGIC
  415. static constexpr millis_t fan_update_interval_ms = TERN(HAS_PWMFANCHECK, 5000, TERN(HAS_FANCHECK, 1000, 2500));
  416. #endif
  417. private:
  418. #if ENABLED(WATCH_HOTENDS)
  419. static hotend_watch_t watch_hotend[HOTENDS];
  420. #endif
  421. #if ENABLED(PID_EXTRUSION_SCALING)
  422. static int32_t last_e_position, lpq[LPQ_MAX_LEN];
  423. static lpq_ptr_t lpq_ptr;
  424. #endif
  425. #if HAS_HOTEND
  426. static temp_range_t temp_range[HOTENDS];
  427. #endif
  428. #if HAS_HEATED_BED
  429. #if ENABLED(WATCH_BED)
  430. static bed_watch_t watch_bed;
  431. #endif
  432. IF_DISABLED(PIDTEMPBED, static millis_t next_bed_check_ms);
  433. static int16_t mintemp_raw_BED, maxtemp_raw_BED;
  434. #endif
  435. #if HAS_HEATED_CHAMBER
  436. #if ENABLED(WATCH_CHAMBER)
  437. static chamber_watch_t watch_chamber;
  438. #endif
  439. TERN(PIDTEMPCHAMBER,,static millis_t next_chamber_check_ms);
  440. static int16_t mintemp_raw_CHAMBER, maxtemp_raw_CHAMBER;
  441. #endif
  442. #if HAS_COOLER
  443. #if ENABLED(WATCH_COOLER)
  444. static cooler_watch_t watch_cooler;
  445. #endif
  446. static millis_t next_cooler_check_ms, cooler_fan_flush_ms;
  447. static int16_t mintemp_raw_COOLER, maxtemp_raw_COOLER;
  448. #endif
  449. #if HAS_TEMP_BOARD && ENABLED(THERMAL_PROTECTION_BOARD)
  450. static int16_t mintemp_raw_BOARD, maxtemp_raw_BOARD;
  451. #endif
  452. #if MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED > 1
  453. static uint8_t consecutive_low_temperature_error[HOTENDS];
  454. #endif
  455. #if MILLISECONDS_PREHEAT_TIME > 0
  456. static millis_t preheat_end_time[HOTENDS];
  457. #endif
  458. #if HAS_FAN_LOGIC
  459. static millis_t fan_update_ms;
  460. static void manage_extruder_fans(millis_t ms) {
  461. if (ELAPSED(ms, fan_update_ms)) { // only need to check fan state very infrequently
  462. const millis_t next_ms = ms + fan_update_interval_ms;
  463. #if HAS_PWMFANCHECK
  464. #define FAN_CHECK_DURATION 100
  465. if (fan_check.is_measuring()) {
  466. fan_check.compute_speed(ms + FAN_CHECK_DURATION - fan_update_ms);
  467. fan_update_ms = next_ms;
  468. }
  469. else
  470. fan_update_ms = ms + FAN_CHECK_DURATION;
  471. fan_check.toggle_measuring();
  472. #else
  473. TERN_(HAS_FANCHECK, fan_check.compute_speed(next_ms - fan_update_ms));
  474. fan_update_ms = next_ms;
  475. #endif
  476. TERN_(HAS_AUTO_FAN, update_autofans()); // Needed as last when HAS_PWMFANCHECK to properly force fan speed
  477. }
  478. }
  479. #endif
  480. #if ENABLED(PROBING_HEATERS_OFF)
  481. static bool paused_for_probing;
  482. #endif
  483. public:
  484. /**
  485. * Instance Methods
  486. */
  487. void init();
  488. /**
  489. * Static (class) methods
  490. */
  491. #if HAS_USER_THERMISTORS
  492. static user_thermistor_t user_thermistor[USER_THERMISTORS];
  493. static void M305_report(const uint8_t t_index, const bool forReplay=true);
  494. static void reset_user_thermistors();
  495. static celsius_float_t user_thermistor_to_deg_c(const uint8_t t_index, const int16_t raw);
  496. static bool set_pull_up_res(int8_t t_index, float value) {
  497. //if (!WITHIN(t_index, 0, USER_THERMISTORS - 1)) return false;
  498. if (!WITHIN(value, 1, 1000000)) return false;
  499. user_thermistor[t_index].series_res = value;
  500. return true;
  501. }
  502. static bool set_res25(int8_t t_index, float value) {
  503. if (!WITHIN(value, 1, 10000000)) return false;
  504. user_thermistor[t_index].res_25 = value;
  505. user_thermistor[t_index].pre_calc = true;
  506. return true;
  507. }
  508. static bool set_beta(int8_t t_index, float value) {
  509. if (!WITHIN(value, 1, 1000000)) return false;
  510. user_thermistor[t_index].beta = value;
  511. user_thermistor[t_index].pre_calc = true;
  512. return true;
  513. }
  514. static bool set_sh_coeff(int8_t t_index, float value) {
  515. if (!WITHIN(value, -0.01f, 0.01f)) return false;
  516. user_thermistor[t_index].sh_c_coeff = value;
  517. user_thermistor[t_index].pre_calc = true;
  518. return true;
  519. }
  520. #endif
  521. #if HAS_HOTEND
  522. static celsius_float_t analog_to_celsius_hotend(const int16_t raw, const uint8_t e);
  523. #endif
  524. #if HAS_HEATED_BED
  525. static celsius_float_t analog_to_celsius_bed(const int16_t raw);
  526. #endif
  527. #if HAS_TEMP_PROBE
  528. static celsius_float_t analog_to_celsius_probe(const int16_t raw);
  529. #endif
  530. #if HAS_TEMP_CHAMBER
  531. static celsius_float_t analog_to_celsius_chamber(const int16_t raw);
  532. #endif
  533. #if HAS_TEMP_COOLER
  534. static celsius_float_t analog_to_celsius_cooler(const int16_t raw);
  535. #endif
  536. #if HAS_TEMP_BOARD
  537. static celsius_float_t analog_to_celsius_board(const int16_t raw);
  538. #endif
  539. #if HAS_TEMP_REDUNDANT
  540. static celsius_float_t analog_to_celsius_redundant(const int16_t raw);
  541. #endif
  542. #if HAS_FAN
  543. static uint8_t fan_speed[FAN_COUNT];
  544. #define FANS_LOOP(I) LOOP_L_N(I, FAN_COUNT)
  545. static void set_fan_speed(const uint8_t fan, const uint16_t speed);
  546. #if ENABLED(REPORT_FAN_CHANGE)
  547. static void report_fan_speed(const uint8_t fan);
  548. #endif
  549. #if EITHER(PROBING_FANS_OFF, ADVANCED_PAUSE_FANS_PAUSE)
  550. static bool fans_paused;
  551. static uint8_t saved_fan_speed[FAN_COUNT];
  552. #endif
  553. #if ENABLED(ADAPTIVE_FAN_SLOWING)
  554. static uint8_t fan_speed_scaler[FAN_COUNT];
  555. #endif
  556. static uint8_t scaledFanSpeed(const uint8_t fan, const uint8_t fs) {
  557. UNUSED(fan); // Potentially unused!
  558. return (fs * uint16_t(TERN(ADAPTIVE_FAN_SLOWING, fan_speed_scaler[fan], 128))) >> 7;
  559. }
  560. static uint8_t scaledFanSpeed(const uint8_t fan) {
  561. return scaledFanSpeed(fan, fan_speed[fan]);
  562. }
  563. static constexpr inline uint8_t pwmToPercent(const uint8_t speed) { return ui8_to_percent(speed); }
  564. static uint8_t fanSpeedPercent(const uint8_t fan) { return ui8_to_percent(fan_speed[fan]); }
  565. static uint8_t scaledFanSpeedPercent(const uint8_t fan) { return ui8_to_percent(scaledFanSpeed(fan)); }
  566. #if ENABLED(EXTRA_FAN_SPEED)
  567. typedef struct { uint8_t saved, speed; } extra_fan_t;
  568. static extra_fan_t extra_fan_speed[FAN_COUNT];
  569. static void set_temp_fan_speed(const uint8_t fan, const uint16_t command_or_speed);
  570. #endif
  571. #if EITHER(PROBING_FANS_OFF, ADVANCED_PAUSE_FANS_PAUSE)
  572. void set_fans_paused(const bool p);
  573. #endif
  574. #endif // HAS_FAN
  575. static void zero_fan_speeds() {
  576. #if HAS_FAN
  577. FANS_LOOP(i) set_fan_speed(i, 0);
  578. #endif
  579. }
  580. /**
  581. * Called from the Temperature ISR
  582. */
  583. static void isr();
  584. static void readings_ready();
  585. /**
  586. * Call periodically to manage heaters
  587. */
  588. static void manage_heater() _O2; // Added _O2 to work around a compiler error
  589. /**
  590. * Preheating hotends
  591. */
  592. #if MILLISECONDS_PREHEAT_TIME > 0
  593. static bool is_preheating(const uint8_t E_NAME) {
  594. return preheat_end_time[HOTEND_INDEX] && PENDING(millis(), preheat_end_time[HOTEND_INDEX]);
  595. }
  596. static void start_preheat_time(const uint8_t E_NAME) {
  597. preheat_end_time[HOTEND_INDEX] = millis() + MILLISECONDS_PREHEAT_TIME;
  598. }
  599. static void reset_preheat_time(const uint8_t E_NAME) {
  600. preheat_end_time[HOTEND_INDEX] = 0;
  601. }
  602. #else
  603. #define is_preheating(n) (false)
  604. #endif
  605. //high level conversion routines, for use outside of temperature.cpp
  606. //inline so that there is no performance decrease.
  607. //deg=degreeCelsius
  608. static celsius_float_t degHotend(const uint8_t E_NAME) {
  609. return TERN0(HAS_HOTEND, temp_hotend[HOTEND_INDEX].celsius);
  610. }
  611. static celsius_t wholeDegHotend(const uint8_t E_NAME) {
  612. return TERN0(HAS_HOTEND, static_cast<celsius_t>(temp_hotend[HOTEND_INDEX].celsius + 0.5f));
  613. }
  614. #if ENABLED(SHOW_TEMP_ADC_VALUES)
  615. static int16_t rawHotendTemp(const uint8_t E_NAME) {
  616. return TERN0(HAS_HOTEND, temp_hotend[HOTEND_INDEX].raw);
  617. }
  618. #endif
  619. static celsius_t degTargetHotend(const uint8_t E_NAME) {
  620. return TERN0(HAS_HOTEND, temp_hotend[HOTEND_INDEX].target);
  621. }
  622. #if HAS_HOTEND
  623. static void setTargetHotend(const celsius_t celsius, const uint8_t E_NAME) {
  624. const uint8_t ee = HOTEND_INDEX;
  625. #if MILLISECONDS_PREHEAT_TIME > 0
  626. if (celsius == 0)
  627. reset_preheat_time(ee);
  628. else if (temp_hotend[ee].target == 0)
  629. start_preheat_time(ee);
  630. #endif
  631. TERN_(AUTO_POWER_CONTROL, if (celsius) powerManager.power_on());
  632. temp_hotend[ee].target = _MIN(celsius, hotend_max_target(ee));
  633. start_watching_hotend(ee);
  634. }
  635. static bool isHeatingHotend(const uint8_t E_NAME) {
  636. return temp_hotend[HOTEND_INDEX].target > temp_hotend[HOTEND_INDEX].celsius;
  637. }
  638. static bool isCoolingHotend(const uint8_t E_NAME) {
  639. return temp_hotend[HOTEND_INDEX].target < temp_hotend[HOTEND_INDEX].celsius;
  640. }
  641. #if HAS_TEMP_HOTEND
  642. static bool wait_for_hotend(const uint8_t target_extruder, const bool no_wait_for_cooling=true
  643. OPTARG(G26_CLICK_CAN_CANCEL, const bool click_to_cancel=false)
  644. );
  645. #if ENABLED(WAIT_FOR_HOTEND)
  646. static void wait_for_hotend_heating(const uint8_t target_extruder);
  647. #endif
  648. #endif
  649. static bool still_heating(const uint8_t e) {
  650. return degTargetHotend(e) > TEMP_HYSTERESIS && ABS(wholeDegHotend(e) - degTargetHotend(e)) > TEMP_HYSTERESIS;
  651. }
  652. static bool degHotendNear(const uint8_t e, const celsius_t temp) {
  653. return ABS(wholeDegHotend(e) - temp) < (TEMP_HYSTERESIS);
  654. }
  655. // Start watching a Hotend to make sure it's really heating up
  656. static void start_watching_hotend(const uint8_t E_NAME) {
  657. UNUSED(HOTEND_INDEX);
  658. #if WATCH_HOTENDS
  659. watch_hotend[HOTEND_INDEX].restart(degHotend(HOTEND_INDEX), degTargetHotend(HOTEND_INDEX));
  660. #endif
  661. }
  662. #endif // HAS_HOTEND
  663. #if HAS_HEATED_BED
  664. #if ENABLED(SHOW_TEMP_ADC_VALUES)
  665. static int16_t rawBedTemp() { return temp_bed.raw; }
  666. #endif
  667. static celsius_float_t degBed() { return temp_bed.celsius; }
  668. static celsius_t wholeDegBed() { return static_cast<celsius_t>(degBed() + 0.5f); }
  669. static celsius_t degTargetBed() { return temp_bed.target; }
  670. static bool isHeatingBed() { return temp_bed.target > temp_bed.celsius; }
  671. static bool isCoolingBed() { return temp_bed.target < temp_bed.celsius; }
  672. // Start watching the Bed to make sure it's really heating up
  673. static void start_watching_bed() { TERN_(WATCH_BED, watch_bed.restart(degBed(), degTargetBed())); }
  674. static void setTargetBed(const celsius_t celsius) {
  675. TERN_(AUTO_POWER_CONTROL, if (celsius) powerManager.power_on());
  676. temp_bed.target = _MIN(celsius, BED_MAX_TARGET);
  677. start_watching_bed();
  678. }
  679. static bool wait_for_bed(const bool no_wait_for_cooling=true
  680. OPTARG(G26_CLICK_CAN_CANCEL, const bool click_to_cancel=false)
  681. );
  682. static void wait_for_bed_heating();
  683. static bool degBedNear(const celsius_t temp) {
  684. return ABS(wholeDegBed() - temp) < (TEMP_BED_HYSTERESIS);
  685. }
  686. #endif // HAS_HEATED_BED
  687. #if HAS_TEMP_PROBE
  688. #if ENABLED(SHOW_TEMP_ADC_VALUES)
  689. static int16_t rawProbeTemp() { return temp_probe.raw; }
  690. #endif
  691. static celsius_float_t degProbe() { return temp_probe.celsius; }
  692. static celsius_t wholeDegProbe() { return static_cast<celsius_t>(degProbe() + 0.5f); }
  693. static bool isProbeBelowTemp(const celsius_t target_temp) { return wholeDegProbe() < target_temp; }
  694. static bool isProbeAboveTemp(const celsius_t target_temp) { return wholeDegProbe() > target_temp; }
  695. static bool wait_for_probe(const celsius_t target_temp, bool no_wait_for_cooling=true);
  696. #endif
  697. #if HAS_TEMP_CHAMBER
  698. #if ENABLED(SHOW_TEMP_ADC_VALUES)
  699. static int16_t rawChamberTemp() { return temp_chamber.raw; }
  700. #endif
  701. static celsius_float_t degChamber() { return temp_chamber.celsius; }
  702. static celsius_t wholeDegChamber() { return static_cast<celsius_t>(degChamber() + 0.5f); }
  703. #if HAS_HEATED_CHAMBER
  704. static celsius_t degTargetChamber() { return temp_chamber.target; }
  705. static bool isHeatingChamber() { return temp_chamber.target > temp_chamber.celsius; }
  706. static bool isCoolingChamber() { return temp_chamber.target < temp_chamber.celsius; }
  707. static bool wait_for_chamber(const bool no_wait_for_cooling=true);
  708. #endif
  709. #endif
  710. #if HAS_HEATED_CHAMBER
  711. static void setTargetChamber(const celsius_t celsius) {
  712. temp_chamber.target = _MIN(celsius, CHAMBER_MAX_TARGET);
  713. start_watching_chamber();
  714. }
  715. // Start watching the Chamber to make sure it's really heating up
  716. static void start_watching_chamber() { TERN_(WATCH_CHAMBER, watch_chamber.restart(degChamber(), degTargetChamber())); }
  717. #endif
  718. #if HAS_TEMP_COOLER
  719. #if ENABLED(SHOW_TEMP_ADC_VALUES)
  720. static int16_t rawCoolerTemp() { return temp_cooler.raw; }
  721. #endif
  722. static celsius_float_t degCooler() { return temp_cooler.celsius; }
  723. static celsius_t wholeDegCooler() { return static_cast<celsius_t>(temp_cooler.celsius + 0.5f); }
  724. #if HAS_COOLER
  725. static celsius_t degTargetCooler() { return temp_cooler.target; }
  726. static bool isLaserHeating() { return temp_cooler.target > temp_cooler.celsius; }
  727. static bool isLaserCooling() { return temp_cooler.target < temp_cooler.celsius; }
  728. static bool wait_for_cooler(const bool no_wait_for_cooling=true);
  729. #endif
  730. #endif
  731. #if HAS_TEMP_BOARD
  732. #if ENABLED(SHOW_TEMP_ADC_VALUES)
  733. static int16_t rawBoardTemp() { return temp_board.raw; }
  734. #endif
  735. static celsius_float_t degBoard() { return temp_board.celsius; }
  736. static celsius_t wholeDegBoard() { return static_cast<celsius_t>(temp_board.celsius + 0.5f); }
  737. #endif
  738. #if HAS_TEMP_REDUNDANT
  739. #if ENABLED(SHOW_TEMP_ADC_VALUES)
  740. static int16_t rawRedundantTemp() { return temp_redundant.raw; }
  741. static int16_t rawRedundanTargetTemp() { return (*temp_redundant.target).raw; }
  742. #endif
  743. static celsius_float_t degRedundant() { return temp_redundant.celsius; }
  744. static celsius_float_t degRedundantTarget() { return (*temp_redundant.target).celsius; }
  745. static celsius_t wholeDegRedundant() { return static_cast<celsius_t>(temp_redundant.celsius + 0.5f); }
  746. static celsius_t wholeDegRedundantTarget() { return static_cast<celsius_t>((*temp_redundant.target).celsius + 0.5f); }
  747. #endif
  748. #if HAS_COOLER
  749. static void setTargetCooler(const celsius_t celsius) {
  750. temp_cooler.target = constrain(celsius, COOLER_MIN_TARGET, COOLER_MAX_TARGET);
  751. start_watching_cooler();
  752. }
  753. // Start watching the Cooler to make sure it's really cooling down
  754. static void start_watching_cooler() { TERN_(WATCH_COOLER, watch_cooler.restart(degCooler(), degTargetCooler())); }
  755. #endif
  756. /**
  757. * The software PWM power for a heater
  758. */
  759. static int16_t getHeaterPower(const heater_id_t heater_id);
  760. /**
  761. * Switch off all heaters, set all target temperatures to 0
  762. */
  763. static void disable_all_heaters();
  764. /**
  765. * Cooldown, as from the LCD. Disables all heaters and fans.
  766. */
  767. static void cooldown() {
  768. zero_fan_speeds();
  769. disable_all_heaters();
  770. }
  771. #if ENABLED(PRINTJOB_TIMER_AUTOSTART)
  772. /**
  773. * Methods to check if heaters are enabled, indicating an active job
  774. */
  775. static bool auto_job_over_threshold();
  776. static void auto_job_check_timer(const bool can_start, const bool can_stop);
  777. #endif
  778. /**
  779. * Perform auto-tuning for hotend or bed in response to M303
  780. */
  781. #if HAS_PID_HEATING
  782. #if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG)
  783. static bool pid_debug_flag;
  784. #endif
  785. static void PID_autotune(const celsius_t target, const heater_id_t heater_id, const int8_t ncycles, const bool set_result=false);
  786. #if ENABLED(NO_FAN_SLOWING_IN_PID_TUNING)
  787. static bool adaptive_fan_slowing;
  788. #elif ENABLED(ADAPTIVE_FAN_SLOWING)
  789. static constexpr bool adaptive_fan_slowing = true;
  790. #endif
  791. /**
  792. * Update the temp manager when PID values change
  793. */
  794. #if ENABLED(PIDTEMP)
  795. static void updatePID() {
  796. TERN_(PID_EXTRUSION_SCALING, last_e_position = 0);
  797. }
  798. #endif
  799. #endif
  800. #if ENABLED(PROBING_HEATERS_OFF)
  801. static void pause_heaters(const bool p);
  802. #endif
  803. #if HEATER_IDLE_HANDLER
  804. static void reset_hotend_idle_timer(const uint8_t E_NAME) {
  805. heater_idle[HOTEND_INDEX].reset();
  806. start_watching_hotend(HOTEND_INDEX);
  807. }
  808. #if HAS_HEATED_BED
  809. static void reset_bed_idle_timer() {
  810. heater_idle[IDLE_INDEX_BED].reset();
  811. start_watching_bed();
  812. }
  813. #endif
  814. #endif // HEATER_IDLE_HANDLER
  815. #if HAS_TEMP_SENSOR
  816. static void print_heater_states(const uint8_t target_extruder
  817. OPTARG(HAS_TEMP_REDUNDANT, const bool include_r=false)
  818. );
  819. #if ENABLED(AUTO_REPORT_TEMPERATURES)
  820. struct AutoReportTemp { static void report(); };
  821. static AutoReporter<AutoReportTemp> auto_reporter;
  822. #endif
  823. #endif
  824. #if HAS_HOTEND && HAS_STATUS_MESSAGE
  825. static void set_heating_message(const uint8_t e);
  826. #else
  827. static void set_heating_message(const uint8_t) {}
  828. #endif
  829. #if HAS_LCD_MENU && HAS_TEMPERATURE
  830. static void lcd_preheat(const uint8_t e, const int8_t indh, const int8_t indb);
  831. #endif
  832. private:
  833. // Reading raw temperatures and converting to Celsius when ready
  834. static volatile bool raw_temps_ready;
  835. static void update_raw_temperatures();
  836. static void updateTemperaturesFromRawValues();
  837. static bool updateTemperaturesIfReady() {
  838. if (!raw_temps_ready) return false;
  839. updateTemperaturesFromRawValues();
  840. raw_temps_ready = false;
  841. return true;
  842. }
  843. // MAX Thermocouples
  844. #if HAS_MAX_TC
  845. #define MAX_TC_COUNT COUNT_ENABLED(TEMP_SENSOR_0_IS_MAX_TC, TEMP_SENSOR_1_IS_MAX_TC, TEMP_SENSOR_REDUNDANT_IS_MAX_TC)
  846. #if MAX_TC_COUNT > 1
  847. #define HAS_MULTI_MAX_TC 1
  848. #define READ_MAX_TC(N) read_max_tc(N)
  849. #else
  850. #define READ_MAX_TC(N) read_max_tc()
  851. #endif
  852. static int16_t read_max_tc(TERN_(HAS_MULTI_MAX_TC, const uint8_t hindex=0));
  853. #endif
  854. #if HAS_AUTO_FAN
  855. #if ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN)
  856. static bool autofans_on;
  857. #endif
  858. static void update_autofans();
  859. #endif
  860. #if HAS_HOTEND
  861. static float get_pid_output_hotend(const uint8_t e);
  862. #endif
  863. #if ENABLED(PIDTEMPBED)
  864. static float get_pid_output_bed();
  865. #endif
  866. #if ENABLED(PIDTEMPCHAMBER)
  867. static float get_pid_output_chamber();
  868. #endif
  869. static void _temp_error(const heater_id_t e, FSTR_P const serial_msg, FSTR_P const lcd_msg);
  870. static void min_temp_error(const heater_id_t e);
  871. static void max_temp_error(const heater_id_t e);
  872. #define HAS_THERMAL_PROTECTION ANY(THERMAL_PROTECTION_HOTENDS, THERMAL_PROTECTION_CHAMBER, THERMAL_PROTECTION_BED, THERMAL_PROTECTION_COOLER)
  873. #if HAS_THERMAL_PROTECTION
  874. // Indices and size for the tr_state_machine array. One for each protected heater.
  875. enum RunawayIndex : int8_t {
  876. _RI = -1
  877. #if ENABLED(THERMAL_PROTECTION_HOTENDS)
  878. #define _RUNAWAY_IND_E(N) ,RUNAWAY_IND_E##N
  879. REPEAT(HOTENDS, _RUNAWAY_IND_E)
  880. #undef _RUNAWAY_IND_E
  881. #endif
  882. OPTARG(THERMAL_PROTECTION_BED, RUNAWAY_IND_BED)
  883. OPTARG(THERMAL_PROTECTION_CHAMBER, RUNAWAY_IND_CHAMBER)
  884. OPTARG(THERMAL_PROTECTION_COOLER, RUNAWAY_IND_COOLER)
  885. , NR_HEATER_RUNAWAY
  886. };
  887. // Convert the given heater_id_t to runaway state array index
  888. static RunawayIndex runaway_index_for_id(const int8_t heater_id) {
  889. TERN_(THERMAL_PROTECTION_CHAMBER, if (heater_id == H_CHAMBER) return RUNAWAY_IND_CHAMBER);
  890. TERN_(THERMAL_PROTECTION_COOLER, if (heater_id == H_COOLER) return RUNAWAY_IND_COOLER);
  891. TERN_(THERMAL_PROTECTION_BED, if (heater_id == H_BED) return RUNAWAY_IND_BED);
  892. return (RunawayIndex)_MAX(heater_id, 0);
  893. }
  894. enum TRState : char { TRInactive, TRFirstHeating, TRStable, TRRunaway
  895. OPTARG(THERMAL_PROTECTION_VARIANCE_MONITOR, TRMalfunction)
  896. };
  897. typedef struct {
  898. millis_t timer = 0;
  899. TRState state = TRInactive;
  900. float running_temp;
  901. #if ENABLED(THERMAL_PROTECTION_VARIANCE_MONITOR)
  902. millis_t variance_timer = 0;
  903. celsius_float_t last_temp = 0.0, variance = 0.0;
  904. #endif
  905. void run(const_celsius_float_t current, const_celsius_float_t target, const heater_id_t heater_id, const uint16_t period_seconds, const celsius_t hysteresis_degc);
  906. } tr_state_machine_t;
  907. static tr_state_machine_t tr_state_machine[NR_HEATER_RUNAWAY];
  908. #endif // HAS_THERMAL_PROTECTION
  909. };
  910. extern Temperature thermalManager;