diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 25c6ffdb22..20e74127a7 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -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 diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index ccf9905866..de29640f47 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -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 // diff --git a/Marlin/src/feature/probe_temp_compensation.cpp b/Marlin/src/feature/probe_temp_compensation.cpp new file mode 100644 index 0000000000..5d266e1d3c --- /dev/null +++ b/Marlin/src/feature/probe_temp_compensation.cpp @@ -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 . + * + */ + +#include "../inc/MarlinConfigPre.h" + +#if ENABLED(PROBE_TEMP_COMPENSATION) + +#include "probe_temp_compensation.h" +#include + +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(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(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(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(val2) / 1000.0f; + meas_temp += res_temp; + val1 = val2; + val2 = data[idx]; + } + const float factor = (meas_temp - temp) / static_cast(res_temp); + return (static_cast(val2) - static_cast(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(data[i]); + sum_x += xi; + sum_x2 += sq(xi); + sum_xy += xi * yi; + sum_y += yi; + } + + const float denom = static_cast(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(calib_idx + 1) * sum_xy - sum_x * sum_y) / denom; + d = (sum_y - k * sum_x) / static_cast(calib_idx + 1); + + return true; +} + +#endif // PROBE_TEMP_COMPENSATION diff --git a/Marlin/src/feature/probe_temp_compensation.h b/Marlin/src/feature/probe_temp_compensation.h new file mode 100644 index 0000000000..64f8cc7a06 --- /dev/null +++ b/Marlin/src/feature/probe_temp_compensation.h @@ -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 . + * + */ +#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; diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp index cf9cdf58e6..53c48a62b6 100644 --- a/Marlin/src/gcode/bedlevel/abl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp @@ -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; diff --git a/Marlin/src/gcode/calibrate/G76_M871.cpp b/Marlin/src/gcode/calibrate/G76_M871.cpp new file mode 100644 index 0000000000..0e0899fa7a --- /dev/null +++ b/Marlin/src/gcode/calibrate/G76_M871.cpp @@ -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 . + * + */ + +/** + * 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 diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 860e8c3552..5630146a52 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -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 diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index 468f44cbea..0fbfbfc404 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -67,6 +67,7 @@ * G34 - Z Stepper automatic alignment using probe: I T A (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 diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h index 4ad08ffa82..47de43cc46 100644 --- a/Marlin/src/inc/Conditionals_post.h +++ b/Marlin/src/inc/Conditionals_post.h @@ -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 @@ -1014,19 +1032,20 @@ // ADC Temp Sensors (Thermistor or Thermocouple with amplifier ADC interface) #define HAS_ADC_TEST(P) (PIN_EXISTS(TEMP_##P) && TEMP_SENSOR_##P != 0 && DISABLED(HEATER_##P##_USES_MAX6675)) -#define HAS_TEMP_ADC_0 HAS_ADC_TEST(0) -#define HAS_TEMP_ADC_1 HAS_ADC_TEST(1) -#define HAS_TEMP_ADC_2 HAS_ADC_TEST(2) -#define HAS_TEMP_ADC_3 HAS_ADC_TEST(3) -#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_CHAMBER HAS_ADC_TEST(CHAMBER) - -#define HAS_TEMP_HOTEND (HOTENDS > 0 && (HAS_TEMP_ADC_0 || ENABLED(HEATER_0_USES_MAX6675))) -#define HAS_TEMP_BED HAS_TEMP_ADC_BED -#define HAS_TEMP_CHAMBER HAS_TEMP_ADC_CHAMBER -#define HAS_HEATED_CHAMBER (HAS_TEMP_CHAMBER && PIN_EXISTS(HEATER_CHAMBER)) +#define HAS_TEMP_ADC_0 HAS_ADC_TEST(0) +#define HAS_TEMP_ADC_1 HAS_ADC_TEST(1) +#define HAS_TEMP_ADC_2 HAS_ADC_TEST(2) +#define HAS_TEMP_ADC_3 HAS_ADC_TEST(3) +#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 ((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 #if ENABLED(JOYSTICK) #define HAS_JOY_ADC_X PIN_EXISTS(JOY_X) @@ -1036,22 +1055,19 @@ #endif // Heaters -#define HAS_HEATER_0 (PIN_EXISTS(HEATER_0)) -#define HAS_HEATER_1 (PIN_EXISTS(HEATER_1)) -#define HAS_HEATER_2 (PIN_EXISTS(HEATER_2)) -#define HAS_HEATER_3 (PIN_EXISTS(HEATER_3)) -#define HAS_HEATER_4 (PIN_EXISTS(HEATER_4)) -#define HAS_HEATER_5 (PIN_EXISTS(HEATER_5)) -#define HAS_HEATER_BED (PIN_EXISTS(HEATER_BED)) +#define HAS_HEATER_0 (PIN_EXISTS(HEATER_0)) +#define HAS_HEATER_1 (PIN_EXISTS(HEATER_1)) +#define HAS_HEATER_2 (PIN_EXISTS(HEATER_2)) +#define HAS_HEATER_3 (PIN_EXISTS(HEATER_3)) +#define HAS_HEATER_4 (PIN_EXISTS(HEATER_4)) +#define HAS_HEATER_5 (PIN_EXISTS(HEATER_5)) +#define HAS_HEATER_BED (PIN_EXISTS(HEATER_BED)) // 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 diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 48f92d9858..780dd81d84 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -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 diff --git a/Marlin/src/module/configuration_store.cpp b/Marlin/src/module/configuration_store.cpp index d38cbbe674..b0a8a88aeb 100644 --- a/Marlin/src/module/configuration_store.cpp +++ b/Marlin/src/module/configuration_store.cpp @@ -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 // diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index b110afb85a..b0b57873ce 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -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: - #if HOTENDS - return temp_hotend[heater_id].soft_pwm_amount; - #else - return 0; - #endif + return (0 + #if HOTENDS + + 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) diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index 4eff4e4f62..1e814294a8 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -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; } diff --git a/Marlin/src/module/thermistor/thermistors.h b/Marlin/src/module/thermistor/thermistors.h index 8dac7e2aee..55336c8da7 100644 --- a/Marlin/src/module/thermistor/thermistors.h +++ b/Marlin/src/module/thermistor/thermistors.h @@ -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." ); diff --git a/Marlin/src/pins/lpc1768/pins_BTT_SKR.h b/Marlin/src/pins/lpc1768/pins_BTT_SKR.h index 7906e59724..516913c2d4 100644 --- a/Marlin/src/pins/lpc1768/pins_BTT_SKR.h +++ b/Marlin/src/pins/lpc1768/pins_BTT_SKR.h @@ -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 diff --git a/buildroot/share/tests/megaatmega2560-tests b/buildroot/share/tests/megaatmega2560-tests index 20379c408e..1438523d90 100755 --- a/buildroot/share/tests/megaatmega2560-tests +++ b/buildroot/share/tests/megaatmega2560-tests @@ -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 ..." #