PINDA v2 temperature sensor / compensation (#16293)
This commit is contained in:
		
				
					committed by
					
						 Scott Lahteine
						Scott Lahteine
					
				
			
			
				
	
			
			
			
						parent
						
							4108c5d01f
						
					
				
				
					commit
					a338dce83f
				
			| @@ -412,6 +412,7 @@ | ||||
| #define TEMP_SENSOR_4 0 | ||||
| #define TEMP_SENSOR_5 0 | ||||
| #define TEMP_SENSOR_BED 0 | ||||
| #define TEMP_SENSOR_PROBE 0 | ||||
| #define TEMP_SENSOR_CHAMBER 0 | ||||
|  | ||||
| // Dummy thermistor constant temperature readings, for use with 998 and 999 | ||||
|   | ||||
| @@ -1529,6 +1529,38 @@ | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Thermal Probe Compensation | ||||
|  * Probe measurements are adjusted to compensate for temperature distortion. | ||||
|  * Use G76 to calibrate this feature. Use M871 to set values manually. | ||||
|  * For a more detailed explanation of the process see G76_M871.cpp. | ||||
|  */ | ||||
| #if HAS_BED_PROBE && TEMP_SENSOR_PROBE && TEMP_SENSOR_BED | ||||
|   // Enable thermal first layer compensation using bed and probe temperatures | ||||
|   #define PROBE_TEMP_COMPENSATION | ||||
|  | ||||
|   // Add additional compensation depending on hotend temperature | ||||
|   // Note: this values cannot be calibrated and have to be set manually | ||||
|   #ifdef PROBE_TEMP_COMPENSATION | ||||
|     // Max temperature that can be reached by heated bed. | ||||
|     // This is required only for the calibration process. | ||||
|     #define PTC_MAX_BED_TEMP 110 | ||||
|  | ||||
|     // Park position to wait for probe cooldown | ||||
|     #define PTC_PARK_POS_X 0.0F | ||||
|     #define PTC_PARK_POS_Y 0.0F | ||||
|     #define PTC_PARK_POS_Z 100.0F | ||||
|  | ||||
|     // Probe position to probe and wait for probe to reach target temperature | ||||
|     #define PTC_PROBE_POS_X  90.0F | ||||
|     #define PTC_PROBE_POS_Y 100.0F | ||||
|  | ||||
|     // Enable additional compensation using hotend temperature | ||||
|     // Note: this values cannot be calibrated automatically but have to be set manually | ||||
|     //#define USE_TEMP_EXT_COMPENSATION | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| // @section extras | ||||
|  | ||||
| // | ||||
|   | ||||
							
								
								
									
										223
									
								
								Marlin/src/feature/probe_temp_compensation.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								Marlin/src/feature/probe_temp_compensation.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,223 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2019 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 <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "../inc/MarlinConfigPre.h" | ||||
|  | ||||
| #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|  | ||||
| #include "probe_temp_compensation.h" | ||||
| #include <math.h> | ||||
|  | ||||
| ProbeTempComp temp_comp; | ||||
|  | ||||
| int16_t ProbeTempComp::z_offsets_probe[ProbeTempComp::cali_info_init[TSI_PROBE].measurements],  // = {0} | ||||
|         ProbeTempComp::z_offsets_bed[ProbeTempComp::cali_info_init[TSI_BED].measurements];      // = {0} | ||||
|  | ||||
| #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|   int16_t ProbeTempComp::z_offsets_ext[ProbeTempComp::cali_info_init[TSI_EXT].measurements];    // = {0} | ||||
| #endif | ||||
|  | ||||
| int16_t *ProbeTempComp::sensor_z_offsets[TSI_COUNT] = { | ||||
|   ProbeTempComp::z_offsets_probe, ProbeTempComp::z_offsets_bed | ||||
|   #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|     , ProbeTempComp::z_offsets_ext | ||||
|   #endif | ||||
| }; | ||||
|  | ||||
| const temp_calib_t ProbeTempComp::cali_info[TSI_COUNT] = { | ||||
|   ProbeTempComp::cali_info_init[TSI_PROBE], ProbeTempComp::cali_info_init[TSI_BED] | ||||
|   #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|     , ProbeTempComp::cali_info_init[TSI_EXT] | ||||
|   #endif | ||||
| }; | ||||
|  | ||||
| uint8_t ProbeTempComp::calib_idx; // = 0 | ||||
| float ProbeTempComp::init_measurement; // = 0.0 | ||||
|  | ||||
| void ProbeTempComp::clear_offsets(const TempSensorID tsi) { | ||||
|   for (uint8_t i = 0; i < cali_info[tsi].measurements; ++i) | ||||
|     sensor_z_offsets[tsi][i] = 0; | ||||
|   calib_idx = 0; | ||||
| } | ||||
|  | ||||
| bool ProbeTempComp::set_offset(const TempSensorID tsi, const uint8_t idx, const int16_t offset) { | ||||
|   if (idx >= cali_info[tsi].measurements) return false; | ||||
|   sensor_z_offsets[tsi][idx] = offset; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void ProbeTempComp::print_offsets() { | ||||
|   for (uint8_t s = 0; s < TSI_COUNT; s++) { | ||||
|     float temp = cali_info[s].start_temp; | ||||
|     for (int16_t i = -1; i < cali_info[s].measurements; ++i) { | ||||
|       serialprintPGM(s == TSI_BED ? PSTR("Bed") : | ||||
|         #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|           s == TSI_EXT ? PSTR("Extruder") : | ||||
|         #endif | ||||
|         PSTR("Probe") | ||||
|       ); | ||||
|       SERIAL_ECHOLNPAIR( | ||||
|         " temp: ", temp, | ||||
|         "C; Offset: ", i < 0 ? 0.0f : sensor_z_offsets[s][i], " um" | ||||
|       ); | ||||
|       temp += cali_info[s].temp_res; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ProbeTempComp::prepare_new_calibration(const float &init_meas_z) { | ||||
|   calib_idx = 0; | ||||
|   init_measurement = init_meas_z; | ||||
| } | ||||
|  | ||||
| void ProbeTempComp::push_back_new_measurement(const TempSensorID tsi, const float &meas_z) { | ||||
|   switch (tsi) { | ||||
|     case TSI_PROBE: | ||||
|     case TSI_BED: | ||||
|     //case TSI_EXT: | ||||
|       if (calib_idx >= cali_info[tsi].measurements) return; | ||||
|       sensor_z_offsets[tsi][calib_idx++] = static_cast<int16_t>(meas_z * 1000.0f - init_measurement * 1000.0f); | ||||
|     default: break; | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool ProbeTempComp::finish_calibration(const TempSensorID tsi) { | ||||
|   if (tsi != TSI_PROBE && tsi != TSI_BED) return false; | ||||
|  | ||||
|   if (calib_idx < 3) { | ||||
|     SERIAL_ECHOLNPGM("!Insufficient measurements (min. 3)."); | ||||
|     clear_offsets(tsi); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   const uint8_t measurements = cali_info[tsi].measurements; | ||||
|   const float start_temp = cali_info[tsi].start_temp, | ||||
|                 res_temp = cali_info[tsi].temp_res; | ||||
|   int16_t * const data = sensor_z_offsets[tsi]; | ||||
|  | ||||
|   // Extrapolate | ||||
|   float k, d; | ||||
|   if (calib_idx < measurements) { | ||||
|     SERIAL_ECHOLNPAIR("Got ", calib_idx, " measurements. "); | ||||
|     if (linear_regression(tsi, k, d)) { | ||||
|       SERIAL_ECHOPGM("Applying linear extrapolation"); | ||||
|       calib_idx--; | ||||
|       for (; calib_idx < measurements; ++calib_idx) { | ||||
|         const float temp = start_temp + float(calib_idx) * res_temp; | ||||
|         data[calib_idx] = static_cast<int16_t>(k * temp + d); | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       // Simply use the last measured value for higher temperatures | ||||
|       SERIAL_ECHOPGM("Failed to extrapolate"); | ||||
|       const int16_t last_val = data[calib_idx]; | ||||
|       for (; calib_idx < measurements; ++calib_idx) | ||||
|         data[calib_idx] = last_val; | ||||
|     } | ||||
|     SERIAL_ECHOLNPGM(" for higher temperatures."); | ||||
|   } | ||||
|  | ||||
|   // Sanity check | ||||
|   for (calib_idx = 0; calib_idx < measurements; ++calib_idx) { | ||||
|     // Restrict the max. offset | ||||
|     if (abs(data[calib_idx]) > 2000) { | ||||
|       SERIAL_ECHOLNPGM("!Invalid Z-offset detected (0-2)."); | ||||
|       clear_offsets(tsi); | ||||
|       return false; | ||||
|     } | ||||
|     // Restrict the max. offset difference between two probings | ||||
|     if (calib_idx > 0 && abs(data[calib_idx - 1] - data[calib_idx]) > 800) { | ||||
|       SERIAL_ECHOLNPGM("!Invalid Z-offset between two probings detected (0-0.8)."); | ||||
|       clear_offsets(TSI_PROBE); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const float &temp, float &meas_z) { | ||||
|   if (WITHIN(temp, cali_info[tsi].start_temp, cali_info[tsi].end_temp)) | ||||
|     meas_z -= get_offset_for_temperature(tsi, temp); | ||||
| } | ||||
|  | ||||
| float ProbeTempComp::get_offset_for_temperature(const TempSensorID tsi, const float &temp) { | ||||
|  | ||||
|   const uint8_t measurements = cali_info[tsi].measurements; | ||||
|   const float start_temp = cali_info[tsi].start_temp, | ||||
|                 end_temp = cali_info[tsi].end_temp, | ||||
|                 res_temp = cali_info[tsi].temp_res; | ||||
|   const int16_t * const data = sensor_z_offsets[tsi]; | ||||
|  | ||||
|   if (temp <= start_temp) return 0.0f; | ||||
|   if (temp >= end_temp) return static_cast<float>(data[measurements - 1]) / 1000.0f; | ||||
|  | ||||
|   // Linear interpolation | ||||
|   int16_t val1 = 0, val2 = data[0]; | ||||
|   uint8_t idx = 0; | ||||
|   float meas_temp = start_temp + res_temp; | ||||
|   while (meas_temp < temp) { | ||||
|     if (++idx >= measurements) return static_cast<float>(val2) / 1000.0f; | ||||
|     meas_temp += res_temp; | ||||
|     val1 = val2; | ||||
|     val2 = data[idx]; | ||||
|   } | ||||
|   const float factor = (meas_temp - temp) / static_cast<float>(res_temp); | ||||
|   return (static_cast<float>(val2) - static_cast<float>(val2 - val1) * factor) / 1000.0f; | ||||
| } | ||||
|  | ||||
| bool ProbeTempComp::linear_regression(const TempSensorID tsi, float &k, float &d) { | ||||
|   if (tsi != TSI_PROBE && tsi != TSI_BED) return false; | ||||
|  | ||||
|   if (!WITHIN(calib_idx, 2, cali_info[tsi].measurements)) return false; | ||||
|  | ||||
|   const float start_temp = cali_info[tsi].start_temp, | ||||
|                 res_temp = cali_info[tsi].temp_res; | ||||
|   const int16_t * const data = sensor_z_offsets[tsi]; | ||||
|  | ||||
|   float sum_x = start_temp, | ||||
|         sum_x2 = sq(start_temp), | ||||
|         sum_xy = 0, sum_y = 0; | ||||
|  | ||||
|   for (uint8_t i = 0; i < calib_idx; ++i) { | ||||
|     const float xi = start_temp + (i + 1) * res_temp, | ||||
|                 yi = static_cast<float>(data[i]); | ||||
|     sum_x += xi; | ||||
|     sum_x2 += sq(xi); | ||||
|     sum_xy += xi * yi; | ||||
|     sum_y += yi; | ||||
|   } | ||||
|  | ||||
|   const float denom = static_cast<float>(calib_idx + 1) * sum_x2 - sq(sum_x); | ||||
|   if (fabs(denom) <= 10e-5) { | ||||
|     // Singularity - unable to solve | ||||
|     k = d = 0.0; | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   k = (static_cast<float>(calib_idx + 1) * sum_xy - sum_x * sum_y) / denom; | ||||
|   d = (sum_y - k * sum_x) / static_cast<float>(calib_idx + 1); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| #endif // PROBE_TEMP_COMPENSATION | ||||
							
								
								
									
										116
									
								
								Marlin/src/feature/probe_temp_compensation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								Marlin/src/feature/probe_temp_compensation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2019 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 <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../inc/MarlinConfig.h" | ||||
|  | ||||
| enum TempSensorID : uint8_t { | ||||
|   TSI_PROBE, | ||||
|   TSI_BED, | ||||
|   #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|     TSI_EXT, | ||||
|   #endif | ||||
|   TSI_COUNT | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|   uint8_t measurements; // Max. number of measurements to be stored (35 - 80°C) | ||||
|   float   temp_res,     // Resolution in °C between measurements | ||||
|           start_temp,   // Base measurement; z-offset == 0 | ||||
|           end_temp; | ||||
| } temp_calib_t; | ||||
|  | ||||
| /** | ||||
|  * Probe temperature compensation implementation. | ||||
|  * Z-probes like the P.I.N.D.A V2 allow for compensation of | ||||
|  * measurement errors/shifts due to changed temperature. | ||||
|  */ | ||||
| class ProbeTempComp { | ||||
|   public: | ||||
|  | ||||
|     static constexpr temp_calib_t cali_info_init[TSI_COUNT] = { | ||||
|         {  30, 10,  5,  30 + 10 *  5 },       // Probe | ||||
|         {  60, 10,  5,  60 + 10 *  5 },       // Bed | ||||
|       #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|         { 180,  5, 20, 180 +  5 * 20 }        // Extruder | ||||
|       #endif | ||||
|     }; | ||||
|     static const temp_calib_t cali_info[TSI_COUNT]; | ||||
|  | ||||
|     // Where to park nozzle to wait for probe cooldown | ||||
|     static constexpr xyz_pos_t park_point = { PTC_PARK_POS_X, PTC_PARK_POS_Y, PTC_PARK_POS_Z }; | ||||
|  | ||||
|     static constexpr int  max_bed_temp         = PTC_MAX_BED_TEMP,  // Max temperature to avoid heating errors | ||||
|  | ||||
|                           // XY coordinates of nozzle for probing the bed | ||||
|                           measure_point_x      = PTC_PROBE_POS_X,   // X-coordinate to probe | ||||
|                           measure_point_y      = PTC_PROBE_POS_Y,   // Y-coordinate to probe | ||||
|                           //measure_point_x    = 12.0f,             // X-coordinate to probe on MK52 magnetic heatbed | ||||
|                           //measure_point_y    =  7.3f,             // Y-coordinate to probe on MK52 magnetic heatbed | ||||
|  | ||||
|                           probe_calib_bed_temp = max_bed_temp,      // Bed temperature while calibrating probe | ||||
|                           bed_calib_probe_temp = 30;                // Probe temperature while calibrating bed | ||||
|  | ||||
|     static int16_t *sensor_z_offsets[TSI_COUNT], | ||||
|                    z_offsets_probe[cali_info_init[TSI_PROBE].measurements], // (µm) | ||||
|                    z_offsets_bed[cali_info_init[TSI_BED].measurements];     // (µm) | ||||
|  | ||||
|     #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|       static int16_t z_offsets_ext[cali_info_init[TSI_EXT].measurements];   // (µm) | ||||
|     #endif | ||||
|  | ||||
|     static inline void reset_index() { calib_idx = 0; }; | ||||
|     static inline uint8_t get_index() { return calib_idx; } | ||||
|     static void clear_offsets(const TempSensorID tsi); | ||||
|     static inline void clear_all_offsets() { | ||||
|       clear_offsets(TSI_BED); | ||||
|       clear_offsets(TSI_PROBE); | ||||
|       #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|         clear_offsets(TSI_EXT); | ||||
|       #endif | ||||
|     } | ||||
|     static bool set_offset(const TempSensorID tsi, const uint8_t idx, const int16_t offset); | ||||
|     static void print_offsets(); | ||||
|     static void prepare_new_calibration(const float &init_meas_z); | ||||
|     static void push_back_new_measurement(const TempSensorID tsi, const float &meas_z); | ||||
|     static bool finish_calibration(const TempSensorID tsi); | ||||
|     static void compensate_measurement(const TempSensorID tsi, const float &temp, float &meas_z); | ||||
|  | ||||
|   private: | ||||
|     static uint8_t calib_idx; | ||||
|  | ||||
|     /** | ||||
|      * Base value. Temperature compensation values will be deltas | ||||
|      * to this value, set at first probe. | ||||
|      */ | ||||
|     static float init_measurement; | ||||
|  | ||||
|     static float get_offset_for_temperature(const TempSensorID tsi, const float &temp); | ||||
|  | ||||
|     /** | ||||
|      * Fit a linear function in measured temperature offsets | ||||
|      * to allow generating values of higher temperatures. | ||||
|      */ | ||||
|     static bool linear_regression(const TempSensorID tsi, float &k, float &d); | ||||
| }; | ||||
|  | ||||
| extern ProbeTempComp temp_comp; | ||||
| @@ -36,6 +36,11 @@ | ||||
| #include "../../../module/probe.h" | ||||
| #include "../../queue.h" | ||||
|  | ||||
| #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|   #include "../../../feature/probe_temp_compensation.h" | ||||
|   #include "../../../module/temperature.h" | ||||
| #endif | ||||
|  | ||||
| #if HAS_DISPLAY | ||||
|   #include "../../../lcd/ultralcd.h" | ||||
| #endif | ||||
| @@ -714,6 +719,14 @@ G29_TYPE GcodeSuite::G29() { | ||||
|             break; // Breaks out of both loops | ||||
|           } | ||||
|  | ||||
|           #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|             temp_comp.compensate_measurement(TSI_BED, thermalManager.degBed(), measured_z); | ||||
|             temp_comp.compensate_measurement(TSI_PROBE, thermalManager.degProbe(), measured_z); | ||||
|             #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|               temp_comp.compensate_measurement(TSI_EXT, thermalManager.degHotend(), measured_z); | ||||
|             #endif | ||||
|           #endif | ||||
|  | ||||
|           #if ENABLED(AUTO_BED_LEVELING_LINEAR) | ||||
|  | ||||
|             mean += measured_z; | ||||
|   | ||||
							
								
								
									
										407
									
								
								Marlin/src/gcode/calibrate/G76_M871.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										407
									
								
								Marlin/src/gcode/calibrate/G76_M871.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,407 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2019 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 <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * G76_M871.cpp - Temperature calibration/compensation for z-probing | ||||
|  */ | ||||
|  | ||||
| #include "../../inc/MarlinConfig.h" | ||||
|  | ||||
| #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|  | ||||
| #include "../gcode.h" | ||||
| #include "../../module/motion.h" | ||||
| #include "../../module/planner.h" | ||||
| #include "../../module/probe.h" | ||||
| #include "../../feature/bedlevel/bedlevel.h" | ||||
| #include "../../module/temperature.h" | ||||
| #include "../../module/probe.h" | ||||
| #include "../../feature/probe_temp_compensation.h" | ||||
|  | ||||
| /** | ||||
|  * G76: calibrate probe and/or bed temperature offsets | ||||
|  *  Notes: | ||||
|  *  - When calibrating probe, bed temperature is held constant. | ||||
|  *    Compensation values are deltas to first probe measurement at probe temp. = 30°C. | ||||
|  *  - When calibrating bed, probe temperature is held constant. | ||||
|  *    Compensation values are deltas to first probe measurement at bed temp. = 60°C. | ||||
|  *  - The hotend will not be heated at any time. | ||||
|  *  - On my Prusa MK3S clone I put a piece of paper between the probe and the hotend | ||||
|  *    so the hotend fan would not cool my probe constantly. Alternativly you could just | ||||
|  *    make sure the fan is not running while running the calibration process. | ||||
|  * | ||||
|  *  Probe calibration: | ||||
|  *  - Moves probe to cooldown point. | ||||
|  *  - Heats up bed to 100°C. | ||||
|  *  - Moves probe to probing point (1mm above heatbed). | ||||
|  *  - Waits until probe reaches target temperature (30°C). | ||||
|  *  - Does a z-probing (=base value) and increases target temperature by 5°C. | ||||
|  *  - Waits until probe reaches increased target temperature. | ||||
|  *  - Does a z-probing (delta to base value will be a compensation value) and increases target temperature by 5°C. | ||||
|  *  - Repeats last two steps until max. temperature reached or timeout (i.e. probe does not heat up any further). | ||||
|  *  - Compensation values of higher temperatures will be extrapolated (using linear regression first). | ||||
|  *    While this is not exact by any means it is still better than simply using the last compensation value. | ||||
|  * | ||||
|  *  Bed calibration: | ||||
|  *  - Moves probe to cooldown point. | ||||
|  *  - Heats up bed to 60°C. | ||||
|  *  - Moves probe to probing point (1mm above heatbed). | ||||
|  *  - Waits until probe reaches target temperature (30°C). | ||||
|  *  - Does a z-probing (=base value) and increases bed temperature by 5°C. | ||||
|  *  - Moves probe to cooldown point. | ||||
|  *  - Waits until probe is below 30°C and bed has reached target temperature. | ||||
|  *  - Moves probe to probing point and waits until it reaches target temperature (30°C). | ||||
|  *  - Does a z-probing (delta to base value will be a compensation value) and increases bed temperature by 5°C. | ||||
|  *  - Repeats last four points until max. bed temperature reached (110°C) or timeout. | ||||
|  *  - Compensation values of higher temperatures will be extrapolated (using linear regression first). | ||||
|  *    While this is not exact by any means it is still better than simply using the last compensation value. | ||||
|  * | ||||
|  *  G76 [B | P] | ||||
|  *  - no flag - Both calibration procedures will be run. | ||||
|  *  - `B` - Run bed temperature calibration. | ||||
|  *  - `P` - Run probe temperature calibration. | ||||
|  */ | ||||
| void GcodeSuite::G76() { | ||||
|   // Check if heated bed is available and z-homing is done with probe | ||||
|   #if TEMP_SENSOR_BED == 0 || !(HOMING_Z_WITH_PROBE) | ||||
|     return; | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(BLTOUCH) | ||||
|     // Make sure any BLTouch error condition is cleared | ||||
|     bltouch_command(BLTOUCH_RESET, BLTOUCH_RESET_DELAY); | ||||
|     set_bltouch_deployed(false); | ||||
|   #endif | ||||
|  | ||||
|   bool do_bed_cal = parser.boolval('B'), | ||||
|        do_probe_cal = parser.boolval('P'); | ||||
|   if (!do_bed_cal && !do_probe_cal) | ||||
|     do_bed_cal = do_probe_cal = true; | ||||
|  | ||||
|   // Synchronize with planner | ||||
|   planner.synchronize(); | ||||
|  | ||||
|   // Report temperatures every second and handle heating timeouts | ||||
|   millis_t next_temp_report = millis() + 1000; | ||||
|  | ||||
|   if (do_bed_cal || do_probe_cal) { | ||||
|     // Ensure park position is reachable | ||||
|     if (!position_is_reachable(ProbeTempComp::park_point.x, ProbeTempComp::park_point.y) | ||||
|       || !(WITHIN(ProbeTempComp::park_point.z, Z_MIN_POS - 0.001f, Z_MAX_POS + 0.001f)) | ||||
|     ) { | ||||
|       SERIAL_ECHOLNPGM("!Park position unreachable - aborting."); | ||||
|       return; | ||||
|     } | ||||
|     // Ensure probe position is reachable | ||||
|     destination.set( | ||||
|       temp_comp.measure_point_x - probe_offset.x, | ||||
|       temp_comp.measure_point_y - probe_offset.y | ||||
|     ); | ||||
|     if (!position_is_reachable_by_probe(destination)) { | ||||
|       SERIAL_ECHOLNPGM("!Probe position unreachable - aborting."); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     G28(true); | ||||
|   } | ||||
|  | ||||
|   /****************************************** | ||||
|    * Calibrate bed temperature offsets | ||||
|    ******************************************/ | ||||
|  | ||||
|   if (do_bed_cal) { | ||||
|  | ||||
|     uint16_t target_bed = temp_comp.cali_info_init[TSI_BED].start_temp, | ||||
|              target_probe = temp_comp.bed_calib_probe_temp; | ||||
|  | ||||
|     SERIAL_ECHOLNPGM("Waiting for printer to cool down."); | ||||
|     while (thermalManager.degBed() > target_bed | ||||
|       || thermalManager.degProbe() > target_probe | ||||
|     ) { | ||||
|       idle( | ||||
|         #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|           true | ||||
|         #endif | ||||
|       ); | ||||
|       const millis_t ms = millis(); | ||||
|       if (ELAPSED(ms, next_temp_report)) { | ||||
|         thermalManager.print_heater_states(active_extruder); | ||||
|         next_temp_report = ms + 1000; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Disable leveling so it won't mess with us | ||||
|     #if HAS_LEVELING | ||||
|       set_bed_leveling_enabled(false); | ||||
|     #endif | ||||
|  | ||||
|     bool timeout = false; | ||||
|     while (true) { | ||||
|       thermalManager.setTargetBed(target_bed); | ||||
|  | ||||
|       SERIAL_ECHOLNPAIR("Target Bed: ", target_bed, "; Probe: ", target_probe); | ||||
|  | ||||
|       // Park nozzle | ||||
|       do_blocking_move_to(ProbeTempComp::park_point.x, ProbeTempComp::park_point.y, ProbeTempComp::park_point.z); | ||||
|  | ||||
|       // Wait for heatbed to reach target temp and probe to cool below target temp | ||||
|       SERIAL_ECHOLNPGM("Waiting for bed and probe to reach target temp."); | ||||
|       const millis_t probe_timeout_ms = millis() + 900UL * 1000UL; | ||||
|       while (fabs(thermalManager.degBed() - float(target_bed)) > 0.1 || thermalManager.degProbe() > target_probe) { | ||||
|         idle( | ||||
|           #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|             true | ||||
|           #endif | ||||
|         ); | ||||
|         const millis_t ms = millis(); | ||||
|         if (ELAPSED(ms, next_temp_report)) { | ||||
|           thermalManager.print_heater_states(active_extruder); | ||||
|           next_temp_report = ms + 1000; | ||||
|         } | ||||
|         if (ELAPSED(ms, probe_timeout_ms)) { | ||||
|           SERIAL_ECHOLNPGM("!Bed heating timeout."); | ||||
|           timeout = true; | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (timeout) break; | ||||
|  | ||||
|       // Move probe to probing point and wait for probe to reach target temp | ||||
|       destination.set(temp_comp.measure_point_x, temp_comp.measure_point_y, 0.5); | ||||
|       do_blocking_move_to(destination.x, destination.y, destination.z); | ||||
|       SERIAL_ECHOLNPGM("Waiting for probe heating."); | ||||
|       while (thermalManager.degProbe() < target_probe) { | ||||
|         idle( | ||||
|           #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|             true | ||||
|           #endif | ||||
|         ); | ||||
|         const millis_t ms = millis(); | ||||
|         if (ELAPSED(ms, next_temp_report)) { | ||||
|           thermalManager.print_heater_states(active_extruder); | ||||
|           next_temp_report = ms + 1000; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       // Raise nozzle before probing | ||||
|       destination.z = 5.0; | ||||
|       do_blocking_move_to_z(destination.z); | ||||
|  | ||||
|       // Do a single probe | ||||
|       remember_feedrate_scaling_off(); | ||||
|       const float measured_z = probe_at_point( | ||||
|         destination.x + probe_offset.x, | ||||
|         destination.y + probe_offset.y, | ||||
|         PROBE_PT_NONE | ||||
|       ); | ||||
|       restore_feedrate_and_scaling(); | ||||
|  | ||||
|       if (isnan(measured_z)) { | ||||
|         SERIAL_ECHOLNPGM("!Received NAN measurement - aborting."); | ||||
|         break; | ||||
|       } | ||||
|       else | ||||
|         SERIAL_ECHOLNPAIR_F("Measured: ", measured_z); | ||||
|  | ||||
|       if (target_bed == temp_comp.cali_info_init[TSI_BED].start_temp) | ||||
|         temp_comp.prepare_new_calibration(measured_z); | ||||
|       else | ||||
|         temp_comp.push_back_new_measurement(TSI_BED, measured_z); | ||||
|  | ||||
|       target_bed += temp_comp.cali_info_init[TSI_BED].temp_res; | ||||
|       if (target_bed > temp_comp.max_bed_temp) break; | ||||
|     } | ||||
|  | ||||
|     SERIAL_ECHOLNPAIR("Retrieved measurements: ", temp_comp.get_index()); | ||||
|     if (temp_comp.finish_calibration(TSI_BED)) | ||||
|       SERIAL_ECHOLNPGM("Successfully calibrated bed."); | ||||
|     else | ||||
|       SERIAL_ECHOLNPGM("!Failed to calibrated bed - reset calibration values."); | ||||
|  | ||||
|     // Cleanup | ||||
|     thermalManager.setTargetBed(0); | ||||
|     #if HAS_LEVELING | ||||
|       set_bed_leveling_enabled(true); | ||||
|     #endif | ||||
|   } // do_bed_cal | ||||
|  | ||||
|   /******************************************** | ||||
|    * Calibrate probe temperature offsets | ||||
|    ********************************************/ | ||||
|  | ||||
|   if (do_probe_cal) { | ||||
|  | ||||
|     // Park nozzle | ||||
|     do_blocking_move_to(ProbeTempComp::park_point.x, ProbeTempComp::park_point.y, ProbeTempComp::park_point.z); | ||||
|  | ||||
|     // Initialize temperatures | ||||
|     uint16_t target_bed = temp_comp.probe_calib_bed_temp, | ||||
|              target_probe = temp_comp.cali_info_init[TSI_BED].start_temp; | ||||
|     thermalManager.setTargetBed(target_bed); | ||||
|     SERIAL_ECHOLNPGM("Waiting for bed and probe temperature."); | ||||
|     while (fabs(thermalManager.degBed() - float(target_bed)) > 0.1f | ||||
|            || thermalManager.degProbe() > target_probe | ||||
|     ) { | ||||
|       idle( | ||||
|         #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|           true | ||||
|         #endif | ||||
|       ); | ||||
|       const millis_t ms = millis(); | ||||
|       if (ELAPSED(ms, next_temp_report)) { | ||||
|         thermalManager.print_heater_states(active_extruder); | ||||
|         next_temp_report = ms + 1000; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Disable leveling so it won't mess with us | ||||
|     #if HAS_LEVELING | ||||
|       set_bed_leveling_enabled(false); | ||||
|     #endif | ||||
|  | ||||
|     bool timeout = false; | ||||
|     while (true) { | ||||
|       // Move probe to probing point and wait for it to reach target temperature | ||||
|       destination.set(temp_comp.measure_point_x, temp_comp.measure_point_y, 0.5); | ||||
|       do_blocking_move_to(destination); | ||||
|  | ||||
|       SERIAL_ECHOLNPAIR( | ||||
|         "Bed temp: ", target_bed, | ||||
|         "; Probe temp: ", target_probe, | ||||
|         "  Waiting for probe heating." | ||||
|       ); | ||||
|  | ||||
|       const millis_t probe_timeout_ms = millis() + 900UL * 1000UL; | ||||
|       while (thermalManager.degProbe() < target_probe) { | ||||
|         idle( | ||||
|           #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|             true | ||||
|           #endif | ||||
|         ); | ||||
|         const millis_t ms = millis(); | ||||
|         if (ELAPSED(ms, next_temp_report)) { | ||||
|           thermalManager.print_heater_states(active_extruder); | ||||
|           next_temp_report = ms + 1000; | ||||
|         } | ||||
|         if (ELAPSED(ms, probe_timeout_ms)) { | ||||
|           SERIAL_ECHOLNPGM("!Probe heating aborted due to timeout."); | ||||
|           timeout = true; | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (timeout) break; | ||||
|  | ||||
|       // Raise nozzle before probing | ||||
|       destination.z = 5.0; | ||||
|       do_blocking_move_to_z(destination.z); | ||||
|  | ||||
|       // Do a single probe | ||||
|       remember_feedrate_scaling_off(); | ||||
|       const float measured_z = probe_at_point( | ||||
|         destination.x + probe_offset.x, | ||||
|         destination.y + probe_offset.y, | ||||
|         PROBE_PT_NONE | ||||
|       ); | ||||
|       restore_feedrate_and_scaling(); | ||||
|  | ||||
|       if (isnan(measured_z)) { | ||||
|         SERIAL_ECHOLNPGM("!Received NAN measurement - aborting."); | ||||
|         break; | ||||
|       } | ||||
|       else | ||||
|         SERIAL_ECHOLNPAIR_F("Measured: ", measured_z); | ||||
|  | ||||
|       if (target_probe == temp_comp.cali_info_init[TSI_BED].start_temp) | ||||
|         temp_comp.prepare_new_calibration(measured_z); | ||||
|       else | ||||
|         temp_comp.push_back_new_measurement(TSI_PROBE, measured_z); | ||||
|  | ||||
|       target_probe += temp_comp.cali_info_init[TSI_BED].temp_res; | ||||
|       if (target_probe > temp_comp.cali_info_init[TSI_BED].end_temp) break; | ||||
|     } | ||||
|  | ||||
|     SERIAL_ECHOLNPAIR("Retrieved measurements: ", temp_comp.get_index()); | ||||
|     if (temp_comp.finish_calibration(TSI_PROBE)) | ||||
|       SERIAL_ECHOLNPGM("Successfully calibrated probe."); | ||||
|     else | ||||
|       SERIAL_ECHOLNPGM("!Failed to calibrated probe."); | ||||
|  | ||||
|     // Cleanup | ||||
|     thermalManager.setTargetBed(0); | ||||
|     #if HAS_LEVELING | ||||
|       set_bed_leveling_enabled(true); | ||||
|     #endif | ||||
|  | ||||
|     SERIAL_ECHOLNPGM("Final compensation values:"); | ||||
|     temp_comp.print_offsets(); | ||||
|   } // do_probe_cal | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * M871: Report / reset temperature compensation offsets. | ||||
|  *       Note: This does not affect values in EEPROM until M500. | ||||
|  * | ||||
|  *   M871 [ R | B | P | E ] | ||||
|  * | ||||
|  *    No Parameters - Print current offset values. | ||||
|  * | ||||
|  * Select only one of these flags: | ||||
|  *    R - Reset all offsets to zero (i.e., disable compensation). | ||||
|  *    B - Manually set offset for bed | ||||
|  *    P - Manually set offset for probe | ||||
|  *    E - Manually set offset for extruder | ||||
|  * | ||||
|  * With B, P, or E: | ||||
|  *    I[index] - Index in the array | ||||
|  *    V[value] - Adjustment in µm | ||||
|  */ | ||||
| void GcodeSuite::M871() { | ||||
|  | ||||
|   if (parser.seen('R')) { | ||||
|     // Reset z-probe offsets to factory defaults | ||||
|     temp_comp.clear_all_offsets(); | ||||
|     SERIAL_ECHOLNPGM("Offsets reset to default."); | ||||
|   } | ||||
|   else if (parser.seen("BPE")) { | ||||
|     if (!parser.seenval('V')) return; | ||||
|     const int16_t val = parser.value_int(); | ||||
|     if (!parser.seenval('I')) return; | ||||
|     const int16_t idx = parser.value_int(); | ||||
|     const TempSensorID mod = (parser.seen('B') ? TSI_BED : | ||||
|                               #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|                                 parser.seen('E') ? TSI_EXT : | ||||
|                               #endif | ||||
|                               TSI_PROBE | ||||
|                               ); | ||||
|     if (idx > 0 && temp_comp.set_offset(mod, idx - 1, val)) | ||||
|       SERIAL_ECHOLNPAIR("Set value: ", val); | ||||
|     else | ||||
|       SERIAL_ECHOLNPGM("!Invalid index. Failed to set value (note: value at index 0 is constant)."); | ||||
|  | ||||
|   } | ||||
|   else // Print current Z-probe adjustments. Note: Values in EEPROM might differ. | ||||
|     temp_comp.print_offsets(); | ||||
| } | ||||
|  | ||||
| #endif // PROBE_TEMP_COMPENSATION | ||||
| @@ -323,6 +323,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { | ||||
|         case 59: G59(); break; | ||||
|       #endif | ||||
|  | ||||
|       #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|         case 76: G76(); break;                                    // G76: Calibrate first layer compensation values | ||||
|       #endif | ||||
|  | ||||
|       #if ENABLED(GCODE_MOTION_MODES) | ||||
|         case 80: G80(); break;                                    // G80: Reset the current motion mode | ||||
|       #endif | ||||
| @@ -753,6 +757,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { | ||||
|         M810_819(); break;                                        // M810-M819: Define/execute G-code macro | ||||
|       #endif | ||||
|  | ||||
|       #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|         case 871: M871(); break;                                  // M871: Print/reset/clear first layer temperature offset values | ||||
|       #endif | ||||
|  | ||||
|       #if ENABLED(LIN_ADVANCE) | ||||
|         case 900: M900(); break;                                  // M900: Set advance K factor. | ||||
|       #endif | ||||
|   | ||||
| @@ -67,6 +67,7 @@ | ||||
|  * G34  - Z Stepper automatic alignment using probe: I<iterations> T<accuracy> A<amplification> (Requires Z_STEPPER_AUTO_ALIGN) | ||||
|  * G38  - Probe in any direction using the Z_MIN_PROBE (Requires G38_PROBE_TARGET) | ||||
|  * G42  - Coordinated move to a mesh point (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BLINEAR, or AUTO_BED_LEVELING_UBL) | ||||
|  * G76  - Calibrate first layer temperature offsets. (Requires PROBE_TEMP_COMPENSATION) | ||||
|  * G80  - Cancel current motion mode (Requires GCODE_MOTION_MODES) | ||||
|  * G90  - Use Absolute Coordinates | ||||
|  * G91  - Use Relative Coordinates | ||||
| @@ -243,6 +244,7 @@ | ||||
|  * M867 - Enable/disable or toggle error correction for position encoder modules. | ||||
|  * M868 - Report or set position encoder module error correction threshold. | ||||
|  * M869 - Report position encoder module error. | ||||
|  * M871 - Print/reset/clear first layer temperature offset values. (Requires PROBE_TEMP_COMPENSATION) | ||||
|  * M876 - Handle Prompt Response. (Requires HOST_PROMPT_SUPPORT and not EMERGENCY_PARSER) | ||||
|  * M900 - Get or Set Linear Advance K-factor. (Requires LIN_ADVANCE) | ||||
|  * M906 - Set or get motor current in milliamps using axis codes X, Y, Z, E. Report values if no axis codes given. (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660 or L6470) | ||||
| @@ -464,6 +466,10 @@ private: | ||||
|     static void G59(); | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|     static void G76(); | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(GCODE_MOTION_MODES) | ||||
|     static void G80(); | ||||
|   #endif | ||||
| @@ -874,6 +880,10 @@ private: | ||||
|     FORCE_INLINE static void M869() { I2CPEM.M869(); } | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|     static void M871(); | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(LIN_ADVANCE) | ||||
|     static void M900(); | ||||
|   #endif | ||||
|   | ||||
| @@ -341,7 +341,7 @@ | ||||
|  * Temp Sensor defines | ||||
|  */ | ||||
|  | ||||
| #define ANY_TEMP_SENSOR_IS(n) (TEMP_SENSOR_0 == (n) || TEMP_SENSOR_1 == (n) || TEMP_SENSOR_2 == (n) || TEMP_SENSOR_3 == (n) || TEMP_SENSOR_4 == (n) || TEMP_SENSOR_5 == (n) || TEMP_SENSOR_BED == (n) || TEMP_SENSOR_CHAMBER == (n)) | ||||
| #define ANY_TEMP_SENSOR_IS(n) (TEMP_SENSOR_0 == (n) || TEMP_SENSOR_1 == (n) || TEMP_SENSOR_2 == (n) || TEMP_SENSOR_3 == (n) || TEMP_SENSOR_4 == (n) || TEMP_SENSOR_5 == (n) || TEMP_SENSOR_BED == (n) || TEMP_SENSOR_PROBE == (n) || TEMP_SENSOR_CHAMBER == (n)) | ||||
|  | ||||
| #define HAS_USER_THERMISTORS ANY_TEMP_SENSOR_IS(1000) | ||||
|  | ||||
| @@ -521,7 +521,25 @@ | ||||
|   #undef CHAMBER_MAXTEMP | ||||
| #endif | ||||
|  | ||||
| #define HOTEND_USES_THERMISTOR ANY(HEATER_0_USES_THERMISTOR, HEATER_1_USES_THERMISTOR, HEATER_2_USES_THERMISTOR, HEATER_3_USES_THERMISTOR, HEATER_4_USES_THERMISTOR) | ||||
| #if TEMP_SENSOR_PROBE == -4 | ||||
|   #define HEATER_PROBE_USES_AD8495 | ||||
| #elif TEMP_SENSOR_PROBE == -3 | ||||
|   #error "MAX31855 Thermocouples (-3) not supported for TEMP_SENSOR_PROBE." | ||||
| #elif TEMP_SENSOR_PROBE == -2 | ||||
|   #error "MAX6675 Thermocouples (-2) not supported for TEMP_SENSOR_PROBE." | ||||
| #elif TEMP_SENSOR_PROBE == -1 | ||||
|   #define HEATER_PROBE_USES_AD595 | ||||
| #elif TEMP_SENSOR_PROBE > 0 | ||||
|   #define THERMISTORPROBE TEMP_SENSOR_PROBE | ||||
|   #define PROBE_USES_THERMISTOR | ||||
|   #if TEMP_SENSOR_PROBE == 1000 | ||||
|     #define PROBE_USER_THERMISTOR | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| #define HOTEND_USES_THERMISTOR ANY( \ | ||||
|   HEATER_0_USES_THERMISTOR, HEATER_1_USES_THERMISTOR, HEATER_2_USES_THERMISTOR, \ | ||||
|   HEATER_3_USES_THERMISTOR, HEATER_4_USES_THERMISTOR, HEATER_5_USES_THERMISTOR) | ||||
|  | ||||
| /** | ||||
|  * Default hotend offsets, if not defined | ||||
| @@ -1021,12 +1039,13 @@ | ||||
| #define HAS_TEMP_ADC_4        HAS_ADC_TEST(4) | ||||
| #define HAS_TEMP_ADC_5        HAS_ADC_TEST(5) | ||||
| #define HAS_TEMP_ADC_BED      HAS_ADC_TEST(BED) | ||||
| #define HAS_TEMP_ADC_PROBE    HAS_ADC_TEST(PROBE) | ||||
| #define HAS_TEMP_ADC_CHAMBER  HAS_ADC_TEST(CHAMBER) | ||||
|  | ||||
| #define HAS_TEMP_HOTEND (HOTENDS > 0 && (HAS_TEMP_ADC_0 || ENABLED(HEATER_0_USES_MAX6675))) | ||||
| #define HAS_TEMP_HOTEND   ((HAS_TEMP_ADC_0 || ENABLED(HEATER_0_USES_MAX6675)) && HOTENDS) | ||||
| #define HAS_TEMP_BED        HAS_TEMP_ADC_BED | ||||
| #define HAS_TEMP_PROBE      HAS_TEMP_ADC_PROBE | ||||
| #define HAS_TEMP_CHAMBER    HAS_TEMP_ADC_CHAMBER | ||||
| #define HAS_HEATED_CHAMBER (HAS_TEMP_CHAMBER && PIN_EXISTS(HEATER_CHAMBER)) | ||||
|  | ||||
| #if ENABLED(JOYSTICK) | ||||
|   #define HAS_JOY_ADC_X  PIN_EXISTS(JOY_X) | ||||
| @@ -1047,11 +1066,8 @@ | ||||
| // Shorthand for common combinations | ||||
| #define HAS_HEATED_BED (HAS_TEMP_BED && HAS_HEATER_BED) | ||||
| #define BED_OR_CHAMBER (HAS_HEATED_BED || HAS_TEMP_CHAMBER) | ||||
| #define HAS_TEMP_SENSOR (HAS_TEMP_HOTEND || BED_OR_CHAMBER) | ||||
|  | ||||
| #if !HAS_TEMP_SENSOR | ||||
|   #undef AUTO_REPORT_TEMPERATURES | ||||
| #endif | ||||
| #define HAS_TEMP_SENSOR (HAS_TEMP_HOTEND || BED_OR_CHAMBER || HAS_TEMP_PROBE) | ||||
| #define HAS_HEATED_CHAMBER (HAS_TEMP_CHAMBER && PIN_EXISTS(HEATER_CHAMBER)) | ||||
|  | ||||
| // PID heating | ||||
| #if !HAS_HEATED_BED | ||||
| @@ -1081,6 +1097,10 @@ | ||||
|   #define AUTO_CHAMBER_IS_E (_FANOVERLAP(CHAMBER,0) || _FANOVERLAP(CHAMBER,1) || _FANOVERLAP(CHAMBER,2) || _FANOVERLAP(CHAMBER,3) || _FANOVERLAP(CHAMBER,4) || _FANOVERLAP(CHAMBER,5)) | ||||
| #endif | ||||
|  | ||||
| #if !HAS_TEMP_SENSOR | ||||
|   #undef AUTO_REPORT_TEMPERATURES | ||||
| #endif | ||||
|  | ||||
| #if !HAS_AUTO_CHAMBER_FAN || AUTO_CHAMBER_IS_E | ||||
|   #undef AUTO_POWER_CHAMBER_FAN | ||||
| #endif | ||||
|   | ||||
| @@ -1571,6 +1571,16 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS | ||||
|   #error "TEMP_SENSOR_5 shouldn't be set with only 1 HOTEND." | ||||
| #endif | ||||
|  | ||||
| #if TEMP_SENSOR_PROBE | ||||
|   #if !PIN_EXISTS(TEMP_PROBE) | ||||
|     #error "TEMP_SENSOR_PROBE requires TEMP_PROBE_PIN." | ||||
|   #elif !HAS_TEMP_ADC_PROBE | ||||
|     #error "TEMP_PROBE_PIN must be an ADC pin." | ||||
|   #elif !ENABLED(FIX_MOUNTED_PROBE) | ||||
|     #error "TEMP_SENSOR_PROBE shouldn't be set without FIX_MOUNTED_PROBE." | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) && TEMP_SENSOR_1 == 0 | ||||
|   #error "TEMP_SENSOR_1 is required with TEMP_SENSOR_1_AS_REDUNDANT." | ||||
| #endif | ||||
|   | ||||
| @@ -114,6 +114,10 @@ | ||||
|   #include "../feature/tmc_util.h" | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|   #include "../feature/probe_temp_compensation.h" | ||||
| #endif | ||||
|  | ||||
| #pragma pack(push, 1) // No padding between variables | ||||
|  | ||||
| typedef struct { uint16_t X, Y, Z, X2, Y2, Z2, Z3, E0, E1, E2, E3, E4, E5; } tmc_stepper_current_t; | ||||
| @@ -212,6 +216,18 @@ typedef struct SettingsDataStruct { | ||||
|   // | ||||
|   uint16_t servo_angles[EEPROM_NUM_SERVOS][2];          // M281 P L U | ||||
|  | ||||
|   // | ||||
|   // Temperature first layer compensation values | ||||
|   // | ||||
|   #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|     int16_t z_offsets_probe[COUNT(temp_comp.z_offsets_probe)], // M871 P I V | ||||
|             z_offsets_bed[COUNT(temp_comp.z_offsets_bed)]      // M871 B I V | ||||
|             #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|               , z_offsets_ext[COUNT(temp_comp.z_offsets_ext)]  // M871 E I V | ||||
|             #endif | ||||
|           ; | ||||
|   #endif | ||||
|  | ||||
|   // | ||||
|   // BLTOUCH | ||||
|   // | ||||
| @@ -699,6 +715,19 @@ void MarlinSettings::postprocess() { | ||||
|       EEPROM_WRITE(servo_angles); | ||||
|     } | ||||
|  | ||||
|     // | ||||
|     // Thermal first layer compensation values | ||||
|     // | ||||
|     #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|       EEPROM_WRITE(temp_comp.z_offsets_probe); | ||||
|       EEPROM_WRITE(temp_comp.z_offsets_bed); | ||||
|       #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|         EEPROM_WRITE(temp_comp.z_offsets_ext); | ||||
|       #endif | ||||
|     #else | ||||
|       // No placeholder data for this feature | ||||
|     #endif | ||||
|  | ||||
|     // | ||||
|     // BLTOUCH | ||||
|     // | ||||
| @@ -1514,6 +1543,20 @@ void MarlinSettings::postprocess() { | ||||
|         EEPROM_READ(servo_angles_arr); | ||||
|       } | ||||
|  | ||||
|       // | ||||
|       // Thermal first layer compensation values | ||||
|       // | ||||
|       #if ENABLED(PROBE_TEMP_COMPENSATION) | ||||
|         EEPROM_READ(temp_comp.z_offsets_probe); | ||||
|         EEPROM_READ(temp_comp.z_offsets_bed); | ||||
|         #if ENABLED(USE_TEMP_EXT_COMPENSATION) | ||||
|           EEPROM_READ(temp_comp.z_offsets_ext); | ||||
|         #endif | ||||
|         temp_comp.reset_index(); | ||||
|       #else | ||||
|         // No placeholder data for this feature | ||||
|       #endif | ||||
|  | ||||
|       // | ||||
|       // BLTOUCH | ||||
|       // | ||||
|   | ||||
| @@ -262,6 +262,10 @@ Temperature thermalManager; | ||||
|   #endif // HAS_HEATED_CHAMBER | ||||
| #endif // HAS_TEMP_CHAMBER | ||||
|  | ||||
| #if HAS_TEMP_PROBE | ||||
|   probe_info_t Temperature::temp_probe; // = { 0 } | ||||
| #endif | ||||
|  | ||||
| // Initialized by settings.load() | ||||
| #if ENABLED(PIDTEMP) | ||||
|   //hotend_pid_t Temperature::pid[HOTENDS]; | ||||
| @@ -654,11 +658,11 @@ int16_t Temperature::getHeaterPower(const heater_ind_t heater_id) { | ||||
|       case H_CHAMBER: return temp_chamber.soft_pwm_amount; | ||||
|     #endif | ||||
|     default: | ||||
|       return (0 | ||||
|         #if HOTENDS | ||||
|         return temp_hotend[heater_id].soft_pwm_amount; | ||||
|       #else | ||||
|         return 0; | ||||
|           + temp_hotend[heater_id].soft_pwm_amount | ||||
|         #endif | ||||
|       ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -1398,7 +1402,7 @@ void Temperature::manage_heater() { | ||||
|         SERIAL_ECHO((int)e); | ||||
|         SERIAL_ECHOLNPGM(MSG_INVALID_EXTRUDER_NUM); | ||||
|         kill(); | ||||
|         return 0.0; | ||||
|         return 0; | ||||
|       } | ||||
|  | ||||
|     switch (e) { | ||||
| @@ -1498,6 +1502,7 @@ void Temperature::manage_heater() { | ||||
|     #elif ENABLED(HEATER_BED_USES_AD8495) | ||||
|       return TEMP_AD8495(raw); | ||||
|     #else | ||||
|       UNUSED(raw); | ||||
|       return 0; | ||||
|     #endif | ||||
|   } | ||||
| @@ -1516,11 +1521,31 @@ void Temperature::manage_heater() { | ||||
|     #elif ENABLED(HEATER_CHAMBER_USES_AD8495) | ||||
|       return TEMP_AD8495(raw); | ||||
|     #else | ||||
|       UNUSED(raw); | ||||
|       return 0; | ||||
|     #endif | ||||
|   } | ||||
| #endif // HAS_TEMP_CHAMBER | ||||
|  | ||||
| #if HAS_TEMP_PROBE | ||||
|   // Derived from RepRap FiveD extruder::getTemperature() | ||||
|   // For probe temperature measurement. | ||||
|   float Temperature::analog_to_celsius_probe(const int raw) { | ||||
|     #if ENABLED(PROBE_USER_THERMISTOR) | ||||
|       return user_thermistor_to_deg_c(CTI_PROBE, raw); | ||||
|     #elif ENABLED(PROBE_USES_THERMISTOR) | ||||
|       SCAN_THERMISTOR_TABLE(PROBE_TEMPTABLE, PROBE_TEMPTABLE_LEN); | ||||
|     #elif ENABLED(PROBE_USES_AD595) | ||||
|       return TEMP_AD595(raw); | ||||
|     #elif ENABLED(PROBE_USES_AD8495) | ||||
|       return TEMP_AD8495(raw); | ||||
|     #else | ||||
|       UNUSED(raw); | ||||
|       return 0; | ||||
|     #endif | ||||
|   } | ||||
| #endif // HAS_TEMP_PROBE | ||||
|  | ||||
| /** | ||||
|  * Get the raw values into the actual temperatures. | ||||
|  * The raw values are created in interrupt context, | ||||
| @@ -1543,6 +1568,9 @@ void Temperature::updateTemperaturesFromRawValues() { | ||||
|   #if HAS_TEMP_CHAMBER | ||||
|     temp_chamber.celsius = analog_to_celsius_chamber(temp_chamber.raw); | ||||
|   #endif | ||||
|   #if HAS_TEMP_PROBE | ||||
|     temp_probe.celsius = analog_to_celsius_probe(temp_probe.raw); | ||||
|   #endif | ||||
|   #if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) | ||||
|     redundant_temperature = analog_to_celsius_hotend(redundant_temperature_raw, 1); | ||||
|   #endif | ||||
| @@ -1721,6 +1749,9 @@ void Temperature::init() { | ||||
|   #if HAS_TEMP_CHAMBER | ||||
|     HAL_ANALOG_SELECT(TEMP_CHAMBER_PIN); | ||||
|   #endif | ||||
|   #if HAS_TEMP_PROBE | ||||
|     HAL_ANALOG_SELECT(TEMP_PROBE_PIN); | ||||
|   #endif | ||||
|   #if ENABLED(FILAMENT_WIDTH_SENSOR) | ||||
|     HAL_ANALOG_SELECT(FILWIDTH_PIN); | ||||
|   #endif | ||||
| @@ -2215,6 +2246,10 @@ void Temperature::set_current_temp_raw() { | ||||
|     temp_chamber.update(); | ||||
|   #endif | ||||
|  | ||||
|   #if HAS_TEMP_PROBE | ||||
|     temp_probe.update(); | ||||
|   #endif | ||||
|  | ||||
|   #if HAS_JOY_ADC_X | ||||
|     joystick.x.update(); | ||||
|   #endif | ||||
| @@ -2253,6 +2288,10 @@ void Temperature::readings_ready() { | ||||
|     temp_chamber.reset(); | ||||
|   #endif | ||||
|  | ||||
|   #if HAS_TEMP_PROBE | ||||
|     temp_probe.reset(); | ||||
|   #endif | ||||
|  | ||||
|   #if HAS_JOY_ADC_X | ||||
|     joystick.x.reset(); | ||||
|   #endif | ||||
| @@ -2661,6 +2700,11 @@ void Temperature::tick() { | ||||
|       case MeasureTemp_CHAMBER: ACCUMULATE_ADC(temp_chamber); break; | ||||
|     #endif | ||||
|  | ||||
|     #if HAS_TEMP_PROBE | ||||
|       case PrepareTemp_PROBE: HAL_START_ADC(TEMP_PROBE_PIN); break; | ||||
|       case MeasureTemp_PROBE: ACCUMULATE_ADC(temp_probe); break; | ||||
|     #endif | ||||
|  | ||||
|     #if HAS_TEMP_ADC_1 | ||||
|       case PrepareTemp_1: HAL_START_ADC(TEMP_1_PIN); break; | ||||
|       case MeasureTemp_1: ACCUMULATE_ADC(temp_hotend[1]); break; | ||||
| @@ -2774,6 +2818,9 @@ void Temperature::tick() { | ||||
|       #if HAS_TEMP_CHAMBER | ||||
|         case H_CHAMBER: k = 'C'; break; | ||||
|       #endif | ||||
|       #if HAS_TEMP_PROBE | ||||
|         case H_PROBE: k = 'P'; break; | ||||
|       #endif | ||||
|       #if HAS_TEMP_HOTEND | ||||
|         default: k = 'T'; break; | ||||
|         #if HAS_HEATED_BED | ||||
| @@ -2842,6 +2889,14 @@ void Temperature::tick() { | ||||
|         , H_CHAMBER | ||||
|       ); | ||||
|     #endif // HAS_TEMP_CHAMBER | ||||
|     #if HAS_TEMP_PROBE | ||||
|       print_heater_state(degProbe(), 0 | ||||
|         #if ENABLED(SHOW_TEMP_ADC_VALUES) | ||||
|           , rawProbeTemp() | ||||
|         #endif | ||||
|         , H_PROBE | ||||
|       ); | ||||
|     #endif // HAS_TEMP_PROBE | ||||
|     #if HOTENDS > 1 | ||||
|       HOTEND_LOOP() print_heater_state(degHotend(e), degTargetHotend(e) | ||||
|         #if ENABLED(SHOW_TEMP_ADC_VALUES) | ||||
|   | ||||
| @@ -47,8 +47,8 @@ | ||||
|  | ||||
| // Identifiers for other heaters | ||||
| typedef enum : int8_t { | ||||
|   INDEX_NONE = -4, | ||||
|   H_REDUNDANT, H_CHAMBER, H_BED, | ||||
|   INDEX_NONE = -5, | ||||
|   H_PROBE, H_REDUNDANT, H_CHAMBER, H_BED, | ||||
|   H_E0, H_E1, H_E2, H_E3, H_E4, H_E5 | ||||
| } heater_ind_t; | ||||
|  | ||||
| @@ -114,6 +114,9 @@ enum ADCSensorState : char { | ||||
|   #if HAS_TEMP_CHAMBER | ||||
|     PrepareTemp_CHAMBER, MeasureTemp_CHAMBER, | ||||
|   #endif | ||||
|   #if HAS_TEMP_PROBE | ||||
|     PrepareTemp_PROBE, MeasureTemp_PROBE, | ||||
|   #endif | ||||
|   #if HAS_TEMP_ADC_1 | ||||
|     PrepareTemp_1, MeasureTemp_1, | ||||
|   #endif | ||||
| @@ -202,6 +205,9 @@ struct PIDHeaterInfo : public HeaterInfo { | ||||
|     typedef heater_info_t bed_info_t; | ||||
|   #endif | ||||
| #endif | ||||
| #if HAS_TEMP_PROBE | ||||
|   typedef temp_info_t probe_info_t; | ||||
| #endif | ||||
| #if HAS_HEATED_CHAMBER | ||||
|   typedef heater_info_t chamber_info_t; | ||||
| #elif HAS_TEMP_CHAMBER | ||||
| @@ -258,6 +264,9 @@ typedef struct { int16_t raw_min, raw_max, mintemp, maxtemp; } temp_range_t; | ||||
|     #if ENABLED(HEATER_BED_USER_THERMISTOR) | ||||
|       CTI_BED, | ||||
|     #endif | ||||
|     #if ENABLED(HEATER_PROBE_USER_THERMISTOR) | ||||
|       CTI_PROBE, | ||||
|     #endif | ||||
|     #if ENABLED(HEATER_CHAMBER_USER_THERMISTOR) | ||||
|       CTI_CHAMBER, | ||||
|     #endif | ||||
| @@ -289,11 +298,12 @@ class Temperature { | ||||
|       #endif | ||||
|       static hotend_info_t temp_hotend[HOTEND_TEMPS]; | ||||
|     #endif | ||||
|  | ||||
|     #if HAS_HEATED_BED | ||||
|       static bed_info_t temp_bed; | ||||
|     #endif | ||||
|  | ||||
|     #if HAS_TEMP_PROBE | ||||
|       static probe_info_t temp_probe; | ||||
|     #endif | ||||
|     #if HAS_TEMP_CHAMBER | ||||
|       static chamber_info_t temp_chamber; | ||||
|     #endif | ||||
| @@ -301,7 +311,6 @@ class Temperature { | ||||
|     #if ENABLED(AUTO_POWER_E_FANS) | ||||
|       static uint8_t autofan_speed[HOTENDS]; | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(AUTO_POWER_CHAMBER_FAN) | ||||
|       static uint8_t chamberfan_speed; | ||||
|     #endif | ||||
| @@ -467,6 +476,9 @@ class Temperature { | ||||
|     #if HAS_HEATED_BED | ||||
|       static float analog_to_celsius_bed(const int raw); | ||||
|     #endif | ||||
|     #if HAS_TEMP_PROBE | ||||
|       static float analog_to_celsius_probe(const int raw); | ||||
|     #endif | ||||
|     #if HAS_TEMP_CHAMBER | ||||
|       static float analog_to_celsius_chamber(const int raw); | ||||
|     #endif | ||||
| @@ -662,6 +674,19 @@ class Temperature { | ||||
|  | ||||
|     #endif // HAS_HEATED_BED | ||||
|  | ||||
|     #if HAS_TEMP_PROBE | ||||
|       #if ENABLED(SHOW_TEMP_ADC_VALUES) | ||||
|         FORCE_INLINE static int16_t rawProbeTemp()    { return temp_probe.raw; } | ||||
|       #endif | ||||
|       FORCE_INLINE static float degProbe()            { return temp_probe.celsius; } | ||||
|     #endif | ||||
|  | ||||
|     #if WATCH_PROBE | ||||
|       static void start_watching_probe(); | ||||
|     #else | ||||
|       static inline void start_watching_probe() {} | ||||
|     #endif | ||||
|  | ||||
|     #if HAS_TEMP_CHAMBER | ||||
|       #if ENABLED(SHOW_TEMP_ADC_VALUES) | ||||
|         FORCE_INLINE static int16_t rawChamberTemp()    { return temp_chamber.raw; } | ||||
|   | ||||
| @@ -39,7 +39,7 @@ | ||||
|  | ||||
| #define OV(N) int16_t((N) * (OVERSAMPLENR) * (THERMISTOR_TABLE_SCALE)) | ||||
|  | ||||
| #define ANY_THERMISTOR_IS(n) (THERMISTOR_HEATER_0 == n || THERMISTOR_HEATER_1 == n || THERMISTOR_HEATER_2 == n || THERMISTOR_HEATER_3 == n || THERMISTOR_HEATER_4 == n || THERMISTOR_HEATER_5 == n || THERMISTORBED == n || THERMISTORCHAMBER == n) | ||||
| #define ANY_THERMISTOR_IS(n) (THERMISTOR_HEATER_0 == n || THERMISTOR_HEATER_1 == n || THERMISTOR_HEATER_2 == n || THERMISTOR_HEATER_3 == n || THERMISTOR_HEATER_4 == n || THERMISTOR_HEATER_5 == n || THERMISTORBED == n || THERMISTORCHAMBER == n || THERMISTORPROBE == n) | ||||
|  | ||||
| // Pt1000 and Pt100 handling | ||||
| // | ||||
| @@ -249,13 +249,20 @@ | ||||
| #else | ||||
|   #define CHAMBER_TEMPTABLE_LEN 0 | ||||
| #endif | ||||
| #ifdef THERMISTORPROBE | ||||
|   #define PROBE_TEMPTABLE TT_NAME(THERMISTORPROBE) | ||||
|   #define PROBE_TEMPTABLE_LEN COUNT(PROBE_TEMPTABLE) | ||||
| #else | ||||
|   #define PROBE_TEMPTABLE_LEN 0 | ||||
| #endif | ||||
|  | ||||
| // The SCAN_THERMISTOR_TABLE macro needs alteration? | ||||
| static_assert( | ||||
|      HEATER_0_TEMPTABLE_LEN < 256 && HEATER_1_TEMPTABLE_LEN < 256 | ||||
|   && HEATER_2_TEMPTABLE_LEN < 256 && HEATER_3_TEMPTABLE_LEN < 256 | ||||
|   && HEATER_4_TEMPTABLE_LEN < 256 && HEATER_5_TEMPTABLE_LEN < 256 | ||||
|   &&      BED_TEMPTABLE_LEN < 256 &&  CHAMBER_TEMPTABLE_LEN < 256, | ||||
|   &&      BED_TEMPTABLE_LEN < 256 &&  CHAMBER_TEMPTABLE_LEN < 256 | ||||
|   &&    PROBE_TEMPTABLE_LEN < 256, | ||||
|   "Temperature conversion tables over 255 entries need special consideration." | ||||
| ); | ||||
|  | ||||
|   | ||||
| @@ -58,6 +58,9 @@ | ||||
| #ifndef TEMP_BED_PIN | ||||
|   #define TEMP_BED_PIN     P0_23_A0   // A0 (T0) - (67) - TEMP_BED_PIN | ||||
| #endif | ||||
| #if HOTENDS == 1 && TEMP_SENSOR_PROBE | ||||
|   #define TEMP_PROBE_PIN   P0_25_A2   // TEMP_1_PIN | ||||
| #endif | ||||
|  | ||||
| // | ||||
| // Heaters / Fans | ||||
|   | ||||
| @@ -23,6 +23,10 @@ opt_set EXTRUDERS 2 | ||||
| opt_set TEMP_SENSOR_0 -2 | ||||
| opt_set TEMP_SENSOR_1 1 | ||||
| opt_set TEMP_SENSOR_BED 2 | ||||
| opt_set TEMP_SENSOR_PROBE 1 | ||||
| opt_add TEMP_PROBE_PIN 12 | ||||
| opt_set TEMP_SENSOR_CHAMBER 3 | ||||
| opt_add HEATER_CHAMBER_PIN 45 | ||||
| opt_set GRID_MAX_POINTS_X 16 | ||||
| opt_set FANMUX0_PIN 53 | ||||
| opt_disable USE_WATCHDOG | ||||
| @@ -42,8 +46,6 @@ opt_enable REPRAP_DISCOUNT_SMART_CONTROLLER LCD_PROGRESS_BAR LCD_PROGRESS_BAR_TE | ||||
|            PSU_CONTROL AUTO_POWER_CONTROL POWER_LOSS_RECOVERY POWER_LOSS_PIN POWER_LOSS_STATE \ | ||||
|            SLOW_PWM_HEATERS THERMAL_PROTECTION_CHAMBER LIN_ADVANCE \ | ||||
|            HOST_ACTION_COMMANDS HOST_PROMPT_SUPPORT PINS_DEBUGGING MAX7219_DEBUG M114_DETAIL | ||||
| opt_set TEMP_SENSOR_CHAMBER 3 | ||||
| opt_set HEATER_CHAMBER_PIN 45 | ||||
| exec_test $1 $2 "RAMPS | EXTRUDERS 2 | CHAR LCD + SD | FIX Probe | ABL-Linear | Advanced Pause | PLR | LEDs ..." | ||||
|  | ||||
| # | ||||
|   | ||||
		Reference in New Issue
	
	Block a user