PINDA v2 temperature sensor / compensation (#16293)

2.0.x
tompe-proj 5 years ago committed by Scott Lahteine
parent 4108c5d01f
commit a338dce83f

@ -412,6 +412,7 @@
#define TEMP_SENSOR_4 0 #define TEMP_SENSOR_4 0
#define TEMP_SENSOR_5 0 #define TEMP_SENSOR_5 0
#define TEMP_SENSOR_BED 0 #define TEMP_SENSOR_BED 0
#define TEMP_SENSOR_PROBE 0
#define TEMP_SENSOR_CHAMBER 0 #define TEMP_SENSOR_CHAMBER 0
// Dummy thermistor constant temperature readings, for use with 998 and 999 // Dummy thermistor constant temperature readings, for use with 998 and 999

@ -1529,6 +1529,38 @@
#endif #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 // @section extras
// //

@ -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

@ -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 "../../../module/probe.h"
#include "../../queue.h" #include "../../queue.h"
#if ENABLED(PROBE_TEMP_COMPENSATION)
#include "../../../feature/probe_temp_compensation.h"
#include "../../../module/temperature.h"
#endif
#if HAS_DISPLAY #if HAS_DISPLAY
#include "../../../lcd/ultralcd.h" #include "../../../lcd/ultralcd.h"
#endif #endif
@ -714,6 +719,14 @@ G29_TYPE GcodeSuite::G29() {
break; // Breaks out of both loops 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) #if ENABLED(AUTO_BED_LEVELING_LINEAR)
mean += measured_z; mean += measured_z;

@ -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; case 59: G59(); break;
#endif #endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
case 76: G76(); break; // G76: Calibrate first layer compensation values
#endif
#if ENABLED(GCODE_MOTION_MODES) #if ENABLED(GCODE_MOTION_MODES)
case 80: G80(); break; // G80: Reset the current motion mode case 80: G80(); break; // G80: Reset the current motion mode
#endif #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 M810_819(); break; // M810-M819: Define/execute G-code macro
#endif #endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
case 871: M871(); break; // M871: Print/reset/clear first layer temperature offset values
#endif
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
case 900: M900(); break; // M900: Set advance K factor. case 900: M900(); break; // M900: Set advance K factor.
#endif #endif

@ -67,6 +67,7 @@
* G34 - Z Stepper automatic alignment using probe: I<iterations> T<accuracy> A<amplification> (Requires Z_STEPPER_AUTO_ALIGN) * 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) * 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) * 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) * G80 - Cancel current motion mode (Requires GCODE_MOTION_MODES)
* G90 - Use Absolute Coordinates * G90 - Use Absolute Coordinates
* G91 - Use Relative Coordinates * G91 - Use Relative Coordinates
@ -243,6 +244,7 @@
* M867 - Enable/disable or toggle error correction for position encoder modules. * M867 - Enable/disable or toggle error correction for position encoder modules.
* M868 - Report or set position encoder module error correction threshold. * M868 - Report or set position encoder module error correction threshold.
* M869 - Report position encoder module error. * 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) * M876 - Handle Prompt Response. (Requires HOST_PROMPT_SUPPORT and not EMERGENCY_PARSER)
* M900 - Get or Set Linear Advance K-factor. (Requires LIN_ADVANCE) * 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) * 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(); static void G59();
#endif #endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
static void G76();
#endif
#if ENABLED(GCODE_MOTION_MODES) #if ENABLED(GCODE_MOTION_MODES)
static void G80(); static void G80();
#endif #endif
@ -874,6 +880,10 @@ private:
FORCE_INLINE static void M869() { I2CPEM.M869(); } FORCE_INLINE static void M869() { I2CPEM.M869(); }
#endif #endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
static void M871();
#endif
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
static void M900(); static void M900();
#endif #endif

@ -341,7 +341,7 @@
* Temp Sensor defines * 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) #define HAS_USER_THERMISTORS ANY_TEMP_SENSOR_IS(1000)
@ -521,7 +521,25 @@
#undef CHAMBER_MAXTEMP #undef CHAMBER_MAXTEMP
#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) #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 * Default hotend offsets, if not defined
@ -1014,19 +1032,20 @@
// ADC Temp Sensors (Thermistor or Thermocouple with amplifier ADC interface) // 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_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_0 HAS_ADC_TEST(0)
#define HAS_TEMP_ADC_1 HAS_ADC_TEST(1) #define HAS_TEMP_ADC_1 HAS_ADC_TEST(1)
#define HAS_TEMP_ADC_2 HAS_ADC_TEST(2) #define HAS_TEMP_ADC_2 HAS_ADC_TEST(2)
#define HAS_TEMP_ADC_3 HAS_ADC_TEST(3) #define HAS_TEMP_ADC_3 HAS_ADC_TEST(3)
#define HAS_TEMP_ADC_4 HAS_ADC_TEST(4) #define HAS_TEMP_ADC_4 HAS_ADC_TEST(4)
#define HAS_TEMP_ADC_5 HAS_ADC_TEST(5) #define HAS_TEMP_ADC_5 HAS_ADC_TEST(5)
#define HAS_TEMP_ADC_BED HAS_ADC_TEST(BED) #define HAS_TEMP_ADC_BED HAS_ADC_TEST(BED)
#define HAS_TEMP_ADC_CHAMBER HAS_ADC_TEST(CHAMBER) #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_BED HAS_TEMP_ADC_BED #define HAS_TEMP_HOTEND ((HAS_TEMP_ADC_0 || ENABLED(HEATER_0_USES_MAX6675)) && HOTENDS)
#define HAS_TEMP_CHAMBER HAS_TEMP_ADC_CHAMBER #define HAS_TEMP_BED HAS_TEMP_ADC_BED
#define HAS_HEATED_CHAMBER (HAS_TEMP_CHAMBER && PIN_EXISTS(HEATER_CHAMBER)) #define HAS_TEMP_PROBE HAS_TEMP_ADC_PROBE
#define HAS_TEMP_CHAMBER HAS_TEMP_ADC_CHAMBER
#if ENABLED(JOYSTICK) #if ENABLED(JOYSTICK)
#define HAS_JOY_ADC_X PIN_EXISTS(JOY_X) #define HAS_JOY_ADC_X PIN_EXISTS(JOY_X)
@ -1036,22 +1055,19 @@
#endif #endif
// Heaters // Heaters
#define HAS_HEATER_0 (PIN_EXISTS(HEATER_0)) #define HAS_HEATER_0 (PIN_EXISTS(HEATER_0))
#define HAS_HEATER_1 (PIN_EXISTS(HEATER_1)) #define HAS_HEATER_1 (PIN_EXISTS(HEATER_1))
#define HAS_HEATER_2 (PIN_EXISTS(HEATER_2)) #define HAS_HEATER_2 (PIN_EXISTS(HEATER_2))
#define HAS_HEATER_3 (PIN_EXISTS(HEATER_3)) #define HAS_HEATER_3 (PIN_EXISTS(HEATER_3))
#define HAS_HEATER_4 (PIN_EXISTS(HEATER_4)) #define HAS_HEATER_4 (PIN_EXISTS(HEATER_4))
#define HAS_HEATER_5 (PIN_EXISTS(HEATER_5)) #define HAS_HEATER_5 (PIN_EXISTS(HEATER_5))
#define HAS_HEATER_BED (PIN_EXISTS(HEATER_BED)) #define HAS_HEATER_BED (PIN_EXISTS(HEATER_BED))
// Shorthand for common combinations // Shorthand for common combinations
#define HAS_HEATED_BED (HAS_TEMP_BED && HAS_HEATER_BED) #define HAS_HEATED_BED (HAS_TEMP_BED && HAS_HEATER_BED)
#define BED_OR_CHAMBER (HAS_HEATED_BED || HAS_TEMP_CHAMBER) #define BED_OR_CHAMBER (HAS_HEATED_BED || HAS_TEMP_CHAMBER)
#define HAS_TEMP_SENSOR (HAS_TEMP_HOTEND || BED_OR_CHAMBER) #define HAS_TEMP_SENSOR (HAS_TEMP_HOTEND || BED_OR_CHAMBER || HAS_TEMP_PROBE)
#define HAS_HEATED_CHAMBER (HAS_TEMP_CHAMBER && PIN_EXISTS(HEATER_CHAMBER))
#if !HAS_TEMP_SENSOR
#undef AUTO_REPORT_TEMPERATURES
#endif
// PID heating // PID heating
#if !HAS_HEATED_BED #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)) #define AUTO_CHAMBER_IS_E (_FANOVERLAP(CHAMBER,0) || _FANOVERLAP(CHAMBER,1) || _FANOVERLAP(CHAMBER,2) || _FANOVERLAP(CHAMBER,3) || _FANOVERLAP(CHAMBER,4) || _FANOVERLAP(CHAMBER,5))
#endif #endif
#if !HAS_TEMP_SENSOR
#undef AUTO_REPORT_TEMPERATURES
#endif
#if !HAS_AUTO_CHAMBER_FAN || AUTO_CHAMBER_IS_E #if !HAS_AUTO_CHAMBER_FAN || AUTO_CHAMBER_IS_E
#undef AUTO_POWER_CHAMBER_FAN #undef AUTO_POWER_CHAMBER_FAN
#endif #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." #error "TEMP_SENSOR_5 shouldn't be set with only 1 HOTEND."
#endif #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 #if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) && TEMP_SENSOR_1 == 0
#error "TEMP_SENSOR_1 is required with TEMP_SENSOR_1_AS_REDUNDANT." #error "TEMP_SENSOR_1 is required with TEMP_SENSOR_1_AS_REDUNDANT."
#endif #endif

@ -114,6 +114,10 @@
#include "../feature/tmc_util.h" #include "../feature/tmc_util.h"
#endif #endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
#include "../feature/probe_temp_compensation.h"
#endif
#pragma pack(push, 1) // No padding between variables #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; 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 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 // BLTOUCH
// //
@ -699,6 +715,19 @@ void MarlinSettings::postprocess() {
EEPROM_WRITE(servo_angles); 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 // BLTOUCH
// //
@ -1514,6 +1543,20 @@ void MarlinSettings::postprocess() {
EEPROM_READ(servo_angles_arr); 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 // BLTOUCH
// //

@ -262,6 +262,10 @@ Temperature thermalManager;
#endif // HAS_HEATED_CHAMBER #endif // HAS_HEATED_CHAMBER
#endif // HAS_TEMP_CHAMBER #endif // HAS_TEMP_CHAMBER
#if HAS_TEMP_PROBE
probe_info_t Temperature::temp_probe; // = { 0 }
#endif
// Initialized by settings.load() // Initialized by settings.load()
#if ENABLED(PIDTEMP) #if ENABLED(PIDTEMP)
//hotend_pid_t Temperature::pid[HOTENDS]; //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; case H_CHAMBER: return temp_chamber.soft_pwm_amount;
#endif #endif
default: default:
#if HOTENDS return (0
return temp_hotend[heater_id].soft_pwm_amount; #if HOTENDS
#else + temp_hotend[heater_id].soft_pwm_amount
return 0; #endif
#endif );
} }
} }
@ -1398,7 +1402,7 @@ void Temperature::manage_heater() {
SERIAL_ECHO((int)e); SERIAL_ECHO((int)e);
SERIAL_ECHOLNPGM(MSG_INVALID_EXTRUDER_NUM); SERIAL_ECHOLNPGM(MSG_INVALID_EXTRUDER_NUM);
kill(); kill();
return 0.0; return 0;
} }
switch (e) { switch (e) {
@ -1498,6 +1502,7 @@ void Temperature::manage_heater() {
#elif ENABLED(HEATER_BED_USES_AD8495) #elif ENABLED(HEATER_BED_USES_AD8495)
return TEMP_AD8495(raw); return TEMP_AD8495(raw);
#else #else
UNUSED(raw);
return 0; return 0;
#endif #endif
} }
@ -1516,11 +1521,31 @@ void Temperature::manage_heater() {
#elif ENABLED(HEATER_CHAMBER_USES_AD8495) #elif ENABLED(HEATER_CHAMBER_USES_AD8495)
return TEMP_AD8495(raw); return TEMP_AD8495(raw);
#else #else
UNUSED(raw);
return 0; return 0;
#endif #endif
} }
#endif // HAS_TEMP_CHAMBER #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. * Get the raw values into the actual temperatures.
* The raw values are created in interrupt context, * The raw values are created in interrupt context,
@ -1543,6 +1568,9 @@ void Temperature::updateTemperaturesFromRawValues() {
#if HAS_TEMP_CHAMBER #if HAS_TEMP_CHAMBER
temp_chamber.celsius = analog_to_celsius_chamber(temp_chamber.raw); temp_chamber.celsius = analog_to_celsius_chamber(temp_chamber.raw);
#endif #endif
#if HAS_TEMP_PROBE
temp_probe.celsius = analog_to_celsius_probe(temp_probe.raw);
#endif
#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) #if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)
redundant_temperature = analog_to_celsius_hotend(redundant_temperature_raw, 1); redundant_temperature = analog_to_celsius_hotend(redundant_temperature_raw, 1);
#endif #endif
@ -1721,6 +1749,9 @@ void Temperature::init() {
#if HAS_TEMP_CHAMBER #if HAS_TEMP_CHAMBER
HAL_ANALOG_SELECT(TEMP_CHAMBER_PIN); HAL_ANALOG_SELECT(TEMP_CHAMBER_PIN);
#endif #endif
#if HAS_TEMP_PROBE
HAL_ANALOG_SELECT(TEMP_PROBE_PIN);
#endif
#if ENABLED(FILAMENT_WIDTH_SENSOR) #if ENABLED(FILAMENT_WIDTH_SENSOR)
HAL_ANALOG_SELECT(FILWIDTH_PIN); HAL_ANALOG_SELECT(FILWIDTH_PIN);
#endif #endif
@ -2215,6 +2246,10 @@ void Temperature::set_current_temp_raw() {
temp_chamber.update(); temp_chamber.update();
#endif #endif
#if HAS_TEMP_PROBE
temp_probe.update();
#endif
#if HAS_JOY_ADC_X #if HAS_JOY_ADC_X
joystick.x.update(); joystick.x.update();
#endif #endif
@ -2253,6 +2288,10 @@ void Temperature::readings_ready() {
temp_chamber.reset(); temp_chamber.reset();
#endif #endif
#if HAS_TEMP_PROBE
temp_probe.reset();
#endif
#if HAS_JOY_ADC_X #if HAS_JOY_ADC_X
joystick.x.reset(); joystick.x.reset();
#endif #endif
@ -2661,6 +2700,11 @@ void Temperature::tick() {
case MeasureTemp_CHAMBER: ACCUMULATE_ADC(temp_chamber); break; case MeasureTemp_CHAMBER: ACCUMULATE_ADC(temp_chamber); break;
#endif #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 #if HAS_TEMP_ADC_1
case PrepareTemp_1: HAL_START_ADC(TEMP_1_PIN); break; case PrepareTemp_1: HAL_START_ADC(TEMP_1_PIN); break;
case MeasureTemp_1: ACCUMULATE_ADC(temp_hotend[1]); break; case MeasureTemp_1: ACCUMULATE_ADC(temp_hotend[1]); break;
@ -2774,6 +2818,9 @@ void Temperature::tick() {
#if HAS_TEMP_CHAMBER #if HAS_TEMP_CHAMBER
case H_CHAMBER: k = 'C'; break; case H_CHAMBER: k = 'C'; break;
#endif #endif
#if HAS_TEMP_PROBE
case H_PROBE: k = 'P'; break;
#endif
#if HAS_TEMP_HOTEND #if HAS_TEMP_HOTEND
default: k = 'T'; break; default: k = 'T'; break;
#if HAS_HEATED_BED #if HAS_HEATED_BED
@ -2842,6 +2889,14 @@ void Temperature::tick() {
, H_CHAMBER , H_CHAMBER
); );
#endif // HAS_TEMP_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 #if HOTENDS > 1
HOTEND_LOOP() print_heater_state(degHotend(e), degTargetHotend(e) HOTEND_LOOP() print_heater_state(degHotend(e), degTargetHotend(e)
#if ENABLED(SHOW_TEMP_ADC_VALUES) #if ENABLED(SHOW_TEMP_ADC_VALUES)

@ -47,8 +47,8 @@
// Identifiers for other heaters // Identifiers for other heaters
typedef enum : int8_t { typedef enum : int8_t {
INDEX_NONE = -4, INDEX_NONE = -5,
H_REDUNDANT, H_CHAMBER, H_BED, H_PROBE, H_REDUNDANT, H_CHAMBER, H_BED,
H_E0, H_E1, H_E2, H_E3, H_E4, H_E5 H_E0, H_E1, H_E2, H_E3, H_E4, H_E5
} heater_ind_t; } heater_ind_t;
@ -114,6 +114,9 @@ enum ADCSensorState : char {
#if HAS_TEMP_CHAMBER #if HAS_TEMP_CHAMBER
PrepareTemp_CHAMBER, MeasureTemp_CHAMBER, PrepareTemp_CHAMBER, MeasureTemp_CHAMBER,
#endif #endif
#if HAS_TEMP_PROBE
PrepareTemp_PROBE, MeasureTemp_PROBE,
#endif
#if HAS_TEMP_ADC_1 #if HAS_TEMP_ADC_1
PrepareTemp_1, MeasureTemp_1, PrepareTemp_1, MeasureTemp_1,
#endif #endif
@ -202,6 +205,9 @@ struct PIDHeaterInfo : public HeaterInfo {
typedef heater_info_t bed_info_t; typedef heater_info_t bed_info_t;
#endif #endif
#endif #endif
#if HAS_TEMP_PROBE
typedef temp_info_t probe_info_t;
#endif
#if HAS_HEATED_CHAMBER #if HAS_HEATED_CHAMBER
typedef heater_info_t chamber_info_t; typedef heater_info_t chamber_info_t;
#elif HAS_TEMP_CHAMBER #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) #if ENABLED(HEATER_BED_USER_THERMISTOR)
CTI_BED, CTI_BED,
#endif #endif
#if ENABLED(HEATER_PROBE_USER_THERMISTOR)
CTI_PROBE,
#endif
#if ENABLED(HEATER_CHAMBER_USER_THERMISTOR) #if ENABLED(HEATER_CHAMBER_USER_THERMISTOR)
CTI_CHAMBER, CTI_CHAMBER,
#endif #endif
@ -289,11 +298,12 @@ class Temperature {
#endif #endif
static hotend_info_t temp_hotend[HOTEND_TEMPS]; static hotend_info_t temp_hotend[HOTEND_TEMPS];
#endif #endif
#if HAS_HEATED_BED #if HAS_HEATED_BED
static bed_info_t temp_bed; static bed_info_t temp_bed;
#endif #endif
#if HAS_TEMP_PROBE
static probe_info_t temp_probe;
#endif
#if HAS_TEMP_CHAMBER #if HAS_TEMP_CHAMBER
static chamber_info_t temp_chamber; static chamber_info_t temp_chamber;
#endif #endif
@ -301,7 +311,6 @@ class Temperature {
#if ENABLED(AUTO_POWER_E_FANS) #if ENABLED(AUTO_POWER_E_FANS)
static uint8_t autofan_speed[HOTENDS]; static uint8_t autofan_speed[HOTENDS];
#endif #endif
#if ENABLED(AUTO_POWER_CHAMBER_FAN) #if ENABLED(AUTO_POWER_CHAMBER_FAN)
static uint8_t chamberfan_speed; static uint8_t chamberfan_speed;
#endif #endif
@ -467,6 +476,9 @@ class Temperature {
#if HAS_HEATED_BED #if HAS_HEATED_BED
static float analog_to_celsius_bed(const int raw); static float analog_to_celsius_bed(const int raw);
#endif #endif
#if HAS_TEMP_PROBE
static float analog_to_celsius_probe(const int raw);
#endif
#if HAS_TEMP_CHAMBER #if HAS_TEMP_CHAMBER
static float analog_to_celsius_chamber(const int raw); static float analog_to_celsius_chamber(const int raw);
#endif #endif
@ -662,6 +674,19 @@ class Temperature {
#endif // HAS_HEATED_BED #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 HAS_TEMP_CHAMBER
#if ENABLED(SHOW_TEMP_ADC_VALUES) #if ENABLED(SHOW_TEMP_ADC_VALUES)
FORCE_INLINE static int16_t rawChamberTemp() { return temp_chamber.raw; } 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 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 // Pt1000 and Pt100 handling
// //
@ -249,13 +249,20 @@
#else #else
#define CHAMBER_TEMPTABLE_LEN 0 #define CHAMBER_TEMPTABLE_LEN 0
#endif #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? // The SCAN_THERMISTOR_TABLE macro needs alteration?
static_assert( static_assert(
HEATER_0_TEMPTABLE_LEN < 256 && HEATER_1_TEMPTABLE_LEN < 256 HEATER_0_TEMPTABLE_LEN < 256 && HEATER_1_TEMPTABLE_LEN < 256
&& HEATER_2_TEMPTABLE_LEN < 256 && HEATER_3_TEMPTABLE_LEN < 256 && HEATER_2_TEMPTABLE_LEN < 256 && HEATER_3_TEMPTABLE_LEN < 256
&& HEATER_4_TEMPTABLE_LEN < 256 && HEATER_5_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." "Temperature conversion tables over 255 entries need special consideration."
); );

@ -58,6 +58,9 @@
#ifndef TEMP_BED_PIN #ifndef TEMP_BED_PIN
#define TEMP_BED_PIN P0_23_A0 // A0 (T0) - (67) - TEMP_BED_PIN #define TEMP_BED_PIN P0_23_A0 // A0 (T0) - (67) - TEMP_BED_PIN
#endif #endif
#if HOTENDS == 1 && TEMP_SENSOR_PROBE
#define TEMP_PROBE_PIN P0_25_A2 // TEMP_1_PIN
#endif
// //
// Heaters / Fans // Heaters / Fans

@ -23,6 +23,10 @@ opt_set EXTRUDERS 2
opt_set TEMP_SENSOR_0 -2 opt_set TEMP_SENSOR_0 -2
opt_set TEMP_SENSOR_1 1 opt_set TEMP_SENSOR_1 1
opt_set TEMP_SENSOR_BED 2 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 GRID_MAX_POINTS_X 16
opt_set FANMUX0_PIN 53 opt_set FANMUX0_PIN 53
opt_disable USE_WATCHDOG 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 \ PSU_CONTROL AUTO_POWER_CONTROL POWER_LOSS_RECOVERY POWER_LOSS_PIN POWER_LOSS_STATE \
SLOW_PWM_HEATERS THERMAL_PROTECTION_CHAMBER LIN_ADVANCE \ SLOW_PWM_HEATERS THERMAL_PROTECTION_CHAMBER LIN_ADVANCE \
HOST_ACTION_COMMANDS HOST_PROMPT_SUPPORT PINS_DEBUGGING MAX7219_DEBUG M114_DETAIL 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 ..." exec_test $1 $2 "RAMPS | EXTRUDERS 2 | CHAR LCD + SD | FIX Probe | ABL-Linear | Advanced Pause | PLR | LEDs ..."
# #

Loading…
Cancel
Save