From 5cd54f56a116524630141e977f56825340b89fba Mon Sep 17 00:00:00 2001 From: Philip Mallonee Date: Thu, 1 Aug 2024 21:36:03 -0500 Subject: [PATCH 1/6] Add logic for Peltier --- Marlin/Configuration.h | 71 +++++++++++++++++++++++----- Marlin/Configuration_adv.h | 10 ++-- Marlin/src/inc/Conditionals_post.h | 10 ++++ Marlin/src/module/temperature.cpp | 75 +++++++++++++++++++++--------- Marlin/src/module/temperature.h | 7 +++ 5 files changed, 135 insertions(+), 38 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index fc0f73014c19..ffdad109ef2e 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -61,7 +61,7 @@ // @section info // Author info of this build printed to the host during boot and M115 -#define STRING_CONFIG_H_AUTHOR "(none, default config)" // Who made the changes. +#define STRING_CONFIG_H_AUTHOR "BeerMKR Control by PBM" // Who made the changes. //#define CUSTOM_VERSION_FILE Version.h // Path from the root directory (no quotes) // @section machine @@ -571,7 +571,7 @@ #define TEMP_SENSOR_5 0 #define TEMP_SENSOR_6 0 #define TEMP_SENSOR_7 0 -#define TEMP_SENSOR_BED 1 +#define TEMP_SENSOR_BED 1000 #define TEMP_SENSOR_PROBE 0 #define TEMP_SENSOR_CHAMBER 0 #define TEMP_SENSOR_COOLER 0 @@ -599,14 +599,14 @@ #if HAS_E_TEMP_SENSOR #define TEMP_RESIDENCY_TIME 10 // (seconds) Time to wait for hotend to "settle" in M109 - #define TEMP_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer - #define TEMP_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target + #define TEMP_WINDOW 0 // (°C) Temperature proximity for the "temperature reached" timer + #define TEMP_HYSTERESIS 1 // (°C) Temperature proximity considered "close enough" to the target #endif #if TEMP_SENSOR_BED #define TEMP_BED_RESIDENCY_TIME 10 // (seconds) Time to wait for bed to "settle" in M190 - #define TEMP_BED_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer - #define TEMP_BED_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target + #define TEMP_BED_WINDOW 0 // (°C) Temperature proximity for the "temperature reached" timer + #define TEMP_BED_HYSTERESIS 1 // (°C) Temperature proximity considered "close enough" to the target #endif #if TEMP_SENSOR_CHAMBER @@ -641,7 +641,7 @@ #define HEATER_5_MINTEMP 5 #define HEATER_6_MINTEMP 5 #define HEATER_7_MINTEMP 5 -#define BED_MINTEMP 5 +#define BED_MINTEMP 1 #define CHAMBER_MINTEMP 5 // Above this temperature the heater will be switched off. @@ -655,7 +655,7 @@ #define HEATER_5_MAXTEMP 275 #define HEATER_6_MAXTEMP 275 #define HEATER_7_MAXTEMP 275 -#define BED_MAXTEMP 150 +#define BED_MAXTEMP 80 #define CHAMBER_MAXTEMP 60 /** @@ -681,7 +681,7 @@ * PIDTEMP : PID temperature control (~4.1K) * MPCTEMP : Predictive Model temperature control. (~1.8K without auto-tune) */ -#define PIDTEMP // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning +//#define PIDTEMP // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning //#define MPCTEMP // See https://marlinfw.org/docs/features/model_predictive_control.html #define PID_MAX 255 // Limit hotend current while PID is active (see PID_FUNCTIONAL_RANGE below); 255=full current @@ -769,6 +769,53 @@ */ #define MAX_BED_POWER 255 // limits duty cycle to bed; 255=full current + +/** + * Peltier Logic + * A Peltier chip is a device that can transfer heat from one side to the other + * proportional to the amount of current flowing through the device. It is reversable. + * The same device can both heat or cool a side depending on the direction of current flow. + * Because of existing eqipment made to handle relatively high current for the + * heated bed in 3D printing the "Heated Bed" is used for the power control point for the Peltier. + * + * When "cooling" in addition to rejecting the heat transferred from hot side to + * cool side the power dissapted by the Peltier unit (voltage x current) must also be rejected. + * Peltier Fan control needs to work in tandem with unit energization. Peltier_FAN_PIN (PWM) is required + * + * Peltier units are typically run in bang-bang mode. They don't do well with PWM + * unless special filter circuitry is installed. PWM not supported at this time. + * + * Peltier logic uses Heated bed gcode at this time (Peltier in place of heated bed) + * Another pin or pins must be used to control the direction of current to the peltier. + * Two configurations are possible: Relay and H-Bridge + * only relay is supported on this pass. (H bridge requires 4 MOS switches configured in H-Bridge) + * //todo: H bridge pin configurations + */ +#define HAS_PELTIER 1 +/** + * HAS_PELTIER is master switch for function + * HAS_PELTIER uses heated bed control pins for the Peltier Power + * + * + * PELTIER_RELAY_PIN is the control pin for relay switching + * In the initial application heat is less common than cool + * Heating: Relay Energized + * Cooling: Relay in "Normal" state + * (Power is determined by Heated Bed setting - 0 or 255 ) + * + * PELTIER_PIN_INVERT will invert the logic on write to the relay. + * PELTIER_PIN and PELTIER_PIN_Invert are set in ??? + */ +#ifdef HAS_PELTIER + #define PELTIER_PIN 47 + #define PELTIER_PIN_INVERT true + #define Peltier_FAN_PIN 4 +//todo: Define Peltier Control Pin +//todo: Define Peltier Pin Invert +//todo: Write Peltier control direction +//Turn off power on zero setting and manage lower than ambient numbers +//todo: oops. move this out of pidtemp section +#endif /** * PID Bed Heating * @@ -795,7 +842,7 @@ // FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles. #else - //#define BED_LIMIT_SWITCHING // Keep the bed temperature within BED_HYSTERESIS of the target + #define BED_LIMIT_SWITCHING // Keep the bed temperature within BED_HYSTERESIS of the target #endif // Add 'M190 R T' for more gradual M190 R bed cooling. @@ -893,7 +940,7 @@ */ #define THERMAL_PROTECTION_HOTENDS // Enable thermal protection for all extruders -#define THERMAL_PROTECTION_BED // Enable thermal protection for the heated bed +//define THERMAL_PROTECTION_BED // Enable thermal protection for the heated bed #define THERMAL_PROTECTION_CHAMBER // Enable thermal protection for the heated chamber #define THERMAL_PROTECTION_COOLER // Enable thermal protection for the laser cooling @@ -2399,7 +2446,7 @@ // // M149 Set temperature units support // -//#define TEMPERATURE_UNITS_SUPPORT +#define TEMPERATURE_UNITS_SUPPORT // @section temperature diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 7704009107eb..6f7af16a35a9 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -129,8 +129,8 @@ #if TEMP_SENSOR_BED == 1000 #define BED_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define BED_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define BED_BETA 3950 // Beta value + #define BED_RESISTANCE_25C_OHMS 10000 // Resistance at 25C + #define BED_BETA 3380 // Beta value #define BED_SH_C_COEFF 0 // Steinhart-Hart C coefficient #endif @@ -203,7 +203,7 @@ #if DISABLED(PIDTEMPBED) #define BED_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control #if ENABLED(BED_LIMIT_SWITCHING) - #define BED_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS + #define BED_HYSTERESIS 1 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS #endif #endif @@ -4489,12 +4489,12 @@ // // M42 - Set pin states // -//#define DIRECT_PIN_CONTROL +#define DIRECT_PIN_CONTROL // // M43 - display pin status, toggle pins, watch pins, watch endstops & toggle LED, test servo probe // -//#define PINS_DEBUGGING +#define PINS_DEBUGGING // Enable Tests that will run at startup and produce a report //#define MARLIN_TEST_BUILD diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h index e6f307877d0f..3c0e642d2d05 100644 --- a/Marlin/src/inc/Conditionals_post.h +++ b/Marlin/src/inc/Conditionals_post.h @@ -2871,6 +2871,12 @@ /** * Heated bed requires settings */ +/** + * If Heated bed is performed by a Peltier device then a direction (heat/cool) is needed + * Heat/Cool can be implemented by a relay (single pin) or H bridge (2 or 4 pin) + * H Bridge can also perform PWM although PWM is not recommended for Peltier devices + * + */ #if HAS_HEATED_BED #ifndef MIN_BED_POWER #define MIN_BED_POWER 0 @@ -2878,7 +2884,11 @@ #ifndef MAX_BED_POWER #define MAX_BED_POWER 255 #endif + #define WRITE_HEATER_BED(v) WRITE(HEATER_BED_PIN, (v) ^ ENABLED(HEATER_BED_INVERTING)) + #if ENABLED(HAS_PELTIER) + #define WRITE_PELTIER_DIR(v) WRITE(PELTIER_PIN, (v) ^ PELTIER_PIN_INVERT) + #endif #endif /** diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 1b8ebeea6964..f68cdd0cd2ef 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -1841,22 +1841,40 @@ void Temperature::mintemp_error(const heater_id_t heater_id OPTARG(ERR_INCLUDE_T #else // Check if temperature is within the correct band if (WITHIN(temp_bed.celsius, BED_MINTEMP, BED_MAXTEMP)) { - #if ENABLED(BED_LIMIT_SWITCHING) - - // Range-limited "bang-bang" bed heating - if (temp_bed.is_above_target(BED_HYSTERESIS)) - temp_bed.soft_pwm_amount = 0; - else if (temp_bed.is_below_target(BED_HYSTERESIS)) - temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1; - - #else // !PIDTEMPBED && !BED_LIMIT_SWITCHING - - // Simple (noisy) "bang-bang" bed heating + #if DISABLED(HAS_PELTIER) + #if ENABLED(BED_LIMIT_SWITCHING) + + // Range-limited "bang-bang" bed heating + if (temp_bed.is_above_target(BED_HYSTERESIS)) + temp_bed.soft_pwm_amount = 0; + //direction = cool (implicit off) + else if (temp_bed.is_below_target(BED_HYSTERESIS)) + temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1; + //direction = heat + #else // not bed limit switching temp_bed.soft_pwm_amount = temp_bed.is_below_target() ? MAX_BED_POWER >> 1 : 0; + #endif + #else //HAS_PELTIER + //Peltier logic here + // Simple (noisy) "bang-bang" bed heating + if (temp_bed.is_above_target(BED_HYSTERESIS) && !temp_bed.target==0){ + temp_bed.soft_pwm_amount = MAX_BED_POWER; + temp_bed.peltier_dir = false; + //direction = cool + } + else if (temp_bed.is_below_target(BED_HYSTERESIS)){ + temp_bed.soft_pwm_amount = MAX_BED_POWER ; + temp_bed.peltier_dir = true; + //direction = heat + } + else + temp_bed.soft_pwm_amount = 0; + //coast + #endif } - else { + else { //Not within correct band temp_bed.soft_pwm_amount = 0; WRITE_HEATER_BED(LOW); } @@ -2868,6 +2886,10 @@ void Temperature::init() { #endif #if HAS_HEATED_BED + #if ENABLED(HAS_PELTIER) + SET_OUTPUT(PELTIER_PIN); + OUT_WRITE(PELTIER_PIN, PELTIER_PIN_INVERT); + #endif #ifdef BOARD_OPENDRAIN_MOSFETS OUT_WRITE_OD(HEATER_BED_PIN, ENABLED(HEATER_BED_INVERTING)); #else @@ -3848,6 +3870,9 @@ void Temperature::isr() { #if HAS_HEATED_BED _PWM_MOD(BED, soft_pwm_bed, temp_bed); + #if ENABLED(HAS_PELTIER) + WRITE_PELTIER_DIR(temp_bed.peltier_dir); + #endif #endif #if HAS_HEATED_CHAMBER @@ -4314,15 +4339,16 @@ void Temperature::isr() { #if HAS_TEMP_SENSOR /** * Print a single heater state in the form: - * Bed: " B:nnn.nn /nnn.nn" - * Chamber: " C:nnn.nn /nnn.nn" - * Probe: " P:nnn.nn" - * Cooler: " L:nnn.nn /nnn.nn" - * Board: " M:nnn.nn" - * SoC: " S:nnn.nn" - * Redundant: " R:nnn.nn /nnn.nn" - * Extruder: " T0:nnn.nn /nnn.nn" - * With ADC: " T0:nnn.nn /nnn.nn (nnn.nn)" + * Bed: " B:nnn.nn /nnn.nn" + * Chamber: " C:nnn.nn /nnn.nn" + * Probe: " P:nnn.nn" + * Cooler: " L:nnn.nn /nnn.nn" + * Board: " M:nnn.nn" + * SoC: " S:nnn.nn" + * Redundant: " R:nnn.nn /nnn.nn" + * Extruder: " T0:nnn.nn /nnn.nn" + * With ADC: " T0:nnn.nn /nnn.nn (nnn.nn)" + * With Peltier: " P@:X" */ static void print_heater_state(const heater_id_t e, const_celsius_float_t c, const_celsius_float_t t OPTARG(SHOW_TEMP_ADC_VALUES, const float r) @@ -4415,6 +4441,13 @@ void Temperature::isr() { #if HAS_MULTI_HOTEND HOTEND_LOOP() s.append(F(" @"), e, ':', getHeaterPower((heater_id_t)e)); #endif + #if HAS_PELTIER + s.append(" P@:"); + if (temp_bed.peltier_dir) + s.append ("H"); + else + s.append ("C"); + #endif s.echo(); } diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index 15cfeac91118..49036650cb69 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -435,6 +435,10 @@ typedef struct HeaterInfo : public TempInfo { uint8_t soft_pwm_amount; bool is_below_target(const celsius_t offs=0) const { return (target - celsius > offs); } // celsius < target - offs bool is_above_target(const celsius_t offs=0) const { return (celsius - target > offs); } // celsius > target + offs + #if ENABLED(HAS_PELTIER) + //the abstract peltier_dir is defined as heat=true, cool=false. See PELTIER_PIN_INVERT for implementation status + bool peltier_dir = false; + #endif } heater_info_t; // A heater with PID stabilization @@ -607,6 +611,9 @@ class Temperature { #if HAS_HEATED_BED static bed_info_t temp_bed; #endif + #if ENABLED(HAS_PELTIER) + static bool peltier_dir; + #endif #if HAS_TEMP_PROBE static probe_info_t temp_probe; #endif From 7182371206dfabb2d2f0d0b322ee6b913b480914 Mon Sep 17 00:00:00 2001 From: Philip Mallonee Date: Tue, 6 Aug 2024 19:35:23 -0500 Subject: [PATCH 2/6] Tidy Peltier Macro use --- Marlin/Configuration_adv.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index f23c12b4ecd1..b7cdc85574e6 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -129,8 +129,8 @@ #if TEMP_SENSOR_BED == 1000 #define BED_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define BED_RESISTANCE_25C_OHMS 10000 // Resistance at 25C - #define BED_BETA 3380 // Beta value + #define BED_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define BED_BETA 3950 // Beta value #define BED_SH_C_COEFF 0 // Steinhart-Hart C coefficient #endif @@ -202,8 +202,8 @@ // #if DISABLED(PIDTEMPBED) #define BED_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control - #if ENABLED(BED_LIMIT_SWITCHING) - #define BED_HYSTERESIS 1 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS + #if ENABLED(BED_LIMIT_SWITCHING) || ENABLED(HAS_PELTIER) + #define BED_HYSTERESIS 0 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS #endif #endif @@ -4489,12 +4489,12 @@ // // M42 - Set pin states // -#define DIRECT_PIN_CONTROL +//#define DIRECT_PIN_CONTROL // // M43 - display pin status, toggle pins, watch pins, watch endstops & toggle LED, test servo probe // -#define PINS_DEBUGGING +//#define PINS_DEBUGGING // Enable Tests that will run at startup and produce a report //#define MARLIN_TEST_BUILD From 8e34edac17cfb21daa378a317f8bd2d7a1d15736 Mon Sep 17 00:00:00 2001 From: Philip Mallonee Date: Tue, 6 Aug 2024 19:37:11 -0500 Subject: [PATCH 3/6] Revert config files to original include Peltier changes --- Marlin/Configuration.h | 98 +++++++++++++++---------------- Marlin/src/module/temperature.cpp | 2 +- 2 files changed, 47 insertions(+), 53 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 7e3231eae042..81e74cd87ca4 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -61,7 +61,7 @@ // @section info // Author info of this build printed to the host during boot and M115 -#define STRING_CONFIG_H_AUTHOR "BeerMKR Control by PBM" // Who made the changes. +#define STRING_CONFIG_H_AUTHOR "(none, default config)" // Who made the changes. //#define CUSTOM_VERSION_FILE Version.h // Path from the root directory (no quotes) // @section machine @@ -571,7 +571,7 @@ #define TEMP_SENSOR_5 0 #define TEMP_SENSOR_6 0 #define TEMP_SENSOR_7 0 -#define TEMP_SENSOR_BED 1000 +#define TEMP_SENSOR_BED 1 #define TEMP_SENSOR_PROBE 0 #define TEMP_SENSOR_CHAMBER 0 #define TEMP_SENSOR_COOLER 0 @@ -599,14 +599,14 @@ #if HAS_E_TEMP_SENSOR #define TEMP_RESIDENCY_TIME 10 // (seconds) Time to wait for hotend to "settle" in M109 - #define TEMP_WINDOW 0 // (°C) Temperature proximity for the "temperature reached" timer - #define TEMP_HYSTERESIS 1 // (°C) Temperature proximity considered "close enough" to the target + #define TEMP_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer + #define TEMP_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target #endif #if TEMP_SENSOR_BED #define TEMP_BED_RESIDENCY_TIME 10 // (seconds) Time to wait for bed to "settle" in M190 - #define TEMP_BED_WINDOW 0 // (°C) Temperature proximity for the "temperature reached" timer - #define TEMP_BED_HYSTERESIS 1 // (°C) Temperature proximity considered "close enough" to the target + #define TEMP_BED_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer + #define TEMP_BED_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target #endif #if TEMP_SENSOR_CHAMBER @@ -641,7 +641,7 @@ #define HEATER_5_MINTEMP 5 #define HEATER_6_MINTEMP 5 #define HEATER_7_MINTEMP 5 -#define BED_MINTEMP 1 +#define BED_MINTEMP 5 #define CHAMBER_MINTEMP 5 // Above this temperature the heater will be switched off. @@ -655,7 +655,7 @@ #define HEATER_5_MAXTEMP 275 #define HEATER_6_MAXTEMP 275 #define HEATER_7_MAXTEMP 275 -#define BED_MAXTEMP 80 +#define BED_MAXTEMP 150 #define CHAMBER_MAXTEMP 60 /** @@ -681,7 +681,7 @@ * PIDTEMP : PID temperature control (~4.1K) * MPCTEMP : Predictive Model temperature control. (~1.8K without auto-tune) */ -//#define PIDTEMP // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning +#define PIDTEMP // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning //#define MPCTEMP // See https://marlinfw.org/docs/features/model_predictive_control.html #define PID_MAX 255 // Limit hotend current while PID is active (see PID_FUNCTIONAL_RANGE below); 255=full current @@ -773,6 +773,35 @@ #define MAX_BED_POWER 255 // limits duty cycle to bed; 255=full current +/** + * PID Bed Heating + * + * The PID frequency will be the same as the extruder PWM. + * If PID_dT is the default, and correct for the hardware/configuration, that means 7.689Hz, + * which is fine for driving a square wave into a resistive load and does not significantly + * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 250W + * heater. If your configuration is significantly different than this and you don't understand + * the issues involved, don't use bed PID until someone else verifies that your hardware works. + * + * With this option disabled, bang-bang will be used. BED_LIMIT_SWITCHING enables hysteresis. + */ +//#define PIDTEMPBED + +#if ENABLED(PIDTEMPBED) + //#define MIN_BED_POWER 0 + //#define PID_BED_DEBUG // Print Bed PID debug data to the serial port. + + // 120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+) + // from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10) + #define DEFAULT_bedKp 10.00 + #define DEFAULT_bedKi .023 + #define DEFAULT_bedKd 305.4 + + // FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles. +#else + //#define BED_LIMIT_SWITCHING // Keep the bed temperature within BED_HYSTERESIS of the target +#endif + /** * Peltier Logic * A Peltier chip is a device that can transfer heat from one side to the other @@ -783,7 +812,8 @@ * * When "cooling" in addition to rejecting the heat transferred from hot side to * cool side the power dissapted by the Peltier unit (voltage x current) must also be rejected. - * Peltier Fan control needs to work in tandem with unit energization. Peltier_FAN_PIN (PWM) is required + * Peltier Fan control needs to work in tandem with unit energization. Some form of fan is required with + * a Peltier heat exchanger that is no implemented here. * * Peltier units are typically run in bang-bang mode. They don't do well with PWM * unless special filter circuitry is installed. PWM not supported at this time. @@ -794,58 +824,22 @@ * only relay is supported on this pass. (H bridge requires 4 MOS switches configured in H-Bridge) * //todo: H bridge pin configurations */ -#define HAS_PELTIER 1 +//#define HAS_PELTIER 1 /** * HAS_PELTIER is master switch for function * HAS_PELTIER uses heated bed control pins for the Peltier Power * - * - * PELTIER_RELAY_PIN is the control pin for relay switching + * PELTIER_PIN is the control pin for relay switching * In the initial application heat is less common than cool * Heating: Relay Energized * Cooling: Relay in "Normal" state * (Power is determined by Heated Bed setting - 0 or 255 ) * * PELTIER_PIN_INVERT will invert the logic on write to the relay. - * PELTIER_PIN and PELTIER_PIN_Invert are set in ??? - */ -#ifdef HAS_PELTIER + */ +#if ENABLED(HAS_PELTIER) #define PELTIER_PIN 47 #define PELTIER_PIN_INVERT true - #define Peltier_FAN_PIN 4 -//todo: Define Peltier Control Pin -//todo: Define Peltier Pin Invert -//todo: Write Peltier control direction -//Turn off power on zero setting and manage lower than ambient numbers -//todo: oops. move this out of pidtemp section -#endif -/** - * PID Bed Heating - * - * The PID frequency will be the same as the extruder PWM. - * If PID_dT is the default, and correct for the hardware/configuration, that means 7.689Hz, - * which is fine for driving a square wave into a resistive load and does not significantly - * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 250W - * heater. If your configuration is significantly different than this and you don't understand - * the issues involved, don't use bed PID until someone else verifies that your hardware works. - * - * With this option disabled, bang-bang will be used. BED_LIMIT_SWITCHING enables hysteresis. - */ -//#define PIDTEMPBED - -#if ENABLED(PIDTEMPBED) - //#define MIN_BED_POWER 0 - //#define PID_BED_DEBUG // Print Bed PID debug data to the serial port. - - // 120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+) - // from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10) - #define DEFAULT_bedKp 10.00 - #define DEFAULT_bedKi .023 - #define DEFAULT_bedKd 305.4 - - // FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles. -#else - #define BED_LIMIT_SWITCHING // Keep the bed temperature within BED_HYSTERESIS of the target #endif // Add 'M190 R T' for more gradual M190 R bed cooling. @@ -943,7 +937,7 @@ */ #define THERMAL_PROTECTION_HOTENDS // Enable thermal protection for all extruders -//define THERMAL_PROTECTION_BED // Enable thermal protection for the heated bed +#define THERMAL_PROTECTION_BED // Enable thermal protection for the heated bed #define THERMAL_PROTECTION_CHAMBER // Enable thermal protection for the heated chamber #define THERMAL_PROTECTION_COOLER // Enable thermal protection for the laser cooling @@ -2453,7 +2447,7 @@ // // M149 Set temperature units support // -#define TEMPERATURE_UNITS_SUPPORT +//#define TEMPERATURE_UNITS_SUPPORT // @section temperature diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 8c705ed96be5..196732aa3918 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -4451,7 +4451,7 @@ void Temperature::isr() { #if HAS_MULTI_HOTEND HOTEND_LOOP() s.append(F(" @"), e, ':', getHeaterPower((heater_id_t)e)); #endif - #if HAS_PELTIER + #if ENABLED(HAS_PELTIER) s.append(" P@:"); if (temp_bed.peltier_dir) s.append ("H"); From de2e4ea4e601d3e3f9e01c2db7f2cf322f1b9831 Mon Sep 17 00:00:00 2001 From: Philip Mallonee Date: Tue, 6 Aug 2024 19:41:38 -0500 Subject: [PATCH 4/6] Tweak Hysteresis back to original --- Marlin/Configuration_adv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index b7cdc85574e6..cf5b06126310 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -203,7 +203,7 @@ #if DISABLED(PIDTEMPBED) #define BED_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control #if ENABLED(BED_LIMIT_SWITCHING) || ENABLED(HAS_PELTIER) - #define BED_HYSTERESIS 0 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS + #define BED_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS #endif #endif From 5a536af2a703b6c98286106b4f09b5dfcf160caf Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 12 Sep 2024 18:21:04 -0500 Subject: [PATCH 5/6] review cleanup --- Marlin/Configuration.h | 61 +++++------ Marlin/Configuration_adv.h | 2 +- Marlin/src/inc/Conditionals-5-post.h | 18 ++-- Marlin/src/inc/Warnings.cpp | 7 ++ Marlin/src/module/temperature.cpp | 155 ++++++++++++++------------- Marlin/src/module/temperature.h | 8 +- 6 files changed, 125 insertions(+), 126 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 50997a5868c4..fdd67ccc70c0 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -773,7 +773,6 @@ */ #define MAX_BED_POWER 255 // limits duty cycle to bed; 255=full current - /** * PID Bed Heating * @@ -804,43 +803,37 @@ #endif /** - * Peltier Logic - * A Peltier chip is a device that can transfer heat from one side to the other - * proportional to the amount of current flowing through the device. It is reversable. - * The same device can both heat or cool a side depending on the direction of current flow. - * Because of existing eqipment made to handle relatively high current for the - * heated bed in 3D printing the "Heated Bed" is used for the power control point for the Peltier. - * - * When "cooling" in addition to rejecting the heat transferred from hot side to - * cool side the power dissapted by the Peltier unit (voltage x current) must also be rejected. - * Peltier Fan control needs to work in tandem with unit energization. Some form of fan is required with - * a Peltier heat exchanger that is no implemented here. - * - * Peltier units are typically run in bang-bang mode. They don't do well with PWM - * unless special filter circuitry is installed. PWM not supported at this time. - * - * Peltier logic uses Heated bed gcode at this time (Peltier in place of heated bed) - * Another pin or pins must be used to control the direction of current to the peltier. + * Peltier Bed - Heating and Cooling + * + * A Peltier device transfers heat from one side to the other in proportion to the amount of + * current flowing through the device and the direction of current flow. So the same device + * can both heat and cool. + * + * When "cooling" in addition to rejecting the heat transferred from the hot side to the cold + * side, the dissipated power (voltage * current) must also be rejected. Be sure to set up a + * fan that can be powered in sync with the Peltier unit. + * + * This feature is only set up to run in bang-bang mode because Peltiers don't handle PWM + * well without filter circuitry. + * + * Since existing 3D printers are made to handle relatively high current for the heated bed, + * we can use the heated bed power pins to control the Peltier power using the same G-codes + * as the heated bed (M140, M190, etc.). + * + * A second GPIO pin is required to control current direction. * Two configurations are possible: Relay and H-Bridge * only relay is supported on this pass. (H bridge requires 4 MOS switches configured in H-Bridge) * //todo: H bridge pin configurations + * + * Cooling applications are more common than heating, so the pin states are commonly: + * LOW = Heating = Relay Energized + * HIGH = Cooling = Relay in "Normal" state + * (Power is handled by the bang-bang control loop: 0 or 255) */ -//#define HAS_PELTIER 1 -/** - * HAS_PELTIER is master switch for function - * HAS_PELTIER uses heated bed control pins for the Peltier Power - * - * PELTIER_PIN is the control pin for relay switching - * In the initial application heat is less common than cool - * Heating: Relay Energized - * Cooling: Relay in "Normal" state - * (Power is determined by Heated Bed setting - 0 or 255 ) - * - * PELTIER_PIN_INVERT will invert the logic on write to the relay. - */ -#if ENABLED(HAS_PELTIER) - #define PELTIER_PIN 47 - #define PELTIER_PIN_INVERT true +//#define PELTIER_BED +#if ENABLED(PELTIER_BED) + #define PELTIER_DIR_PIN -1 // Relay control pin for Peltier + #define PELTIER_DIR_HEAT_STATE LOW // The relay pin state that causes the Peltier to heat #endif // Add 'M190 R T' for more gradual M190 R bed cooling. diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 2056a4841c33..f3a5dbcb9c38 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -203,7 +203,7 @@ // #if DISABLED(PIDTEMPBED) #define BED_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control - #if ENABLED(BED_LIMIT_SWITCHING) || ENABLED(HAS_PELTIER) + #if ANY(BED_LIMIT_SWITCHING, PELTIER_BED) #define BED_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS #endif #endif diff --git a/Marlin/src/inc/Conditionals-5-post.h b/Marlin/src/inc/Conditionals-5-post.h index 69fc09233d12..0cbe0176e136 100644 --- a/Marlin/src/inc/Conditionals-5-post.h +++ b/Marlin/src/inc/Conditionals-5-post.h @@ -3039,13 +3039,7 @@ #endif /** - * Heated bed requires settings - */ -/** - * If Heated bed is performed by a Peltier device then a direction (heat/cool) is needed - * Heat/Cool can be implemented by a relay (single pin) or H bridge (2 or 4 pin) - * H Bridge can also perform PWM although PWM is not recommended for Peltier devices - * + * Heated Bed required settings */ #if HAS_HEATED_BED #ifndef MIN_BED_POWER @@ -3054,10 +3048,14 @@ #ifndef MAX_BED_POWER #define MAX_BED_POWER 255 #endif - #define WRITE_HEATER_BED(v) WRITE(HEATER_BED_PIN, (v) ^ ENABLED(HEATER_BED_INVERTING)) - #if ENABLED(HAS_PELTIER) - #define WRITE_PELTIER_DIR(v) WRITE(PELTIER_PIN, (v) ^ PELTIER_PIN_INVERT) + #if ENABLED(PELTIER_BED) + /** + * A "Heated Bed" Peltier device needs a direction (heat/cool) to be + * implemented by a relay (single pin) or H-bridge (2 or 4 pin). + * H-Bridge can also perform PWM. (Not recommended for Peltier devices). + */ + #define WRITE_PELTIER_DIR(v) WRITE(PELTIER_DIR_PIN, (v) ? PELTIER_DIR_HEAT_STATE : !PELTIER_DIR_HEAT_STATE) #endif #endif diff --git a/Marlin/src/inc/Warnings.cpp b/Marlin/src/inc/Warnings.cpp index f10408dd9c44..0ccc9d6acf68 100644 --- a/Marlin/src/inc/Warnings.cpp +++ b/Marlin/src/inc/Warnings.cpp @@ -921,3 +921,10 @@ #if defined(ARDUINO_ARCH_HC32) && F_CPU == 200000000 #warning "HC32 clock is assumed to be 200MHz. If this isn't the case for your board please submit a report so we can add support." #endif + +/** + * Peltier with PIDTEMPBED + */ +#if ALL(PELTIER_BED, PIDTEMPBED) + #warning "PELTIER_BED with PIDTEMPBED requires extra circuitry. Use with caution." +#endif diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index e221ac7cb87b..000f9bccb417 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -1861,7 +1861,7 @@ void Temperature::mintemp_error(const heater_id_t heater_id OPTARG(ERR_INCLUDE_T static bool last_pause_state; #endif - do { + do { // 'break' out of this block #if DISABLED(PIDTEMPBED) if (PENDING(ms, next_bed_check_ms) @@ -1887,56 +1887,63 @@ void Temperature::mintemp_error(const heater_id_t heater_id OPTARG(ERR_INCLUDE_T constexpr bool bed_timed_out = false; #endif - if (!bed_timed_out) { - if (is_bed_preheating()) { - temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1; + if (bed_timed_out) break; + + if (is_bed_preheating()) { + temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1; + break; + } + + #if ENABLED(PIDTEMPBED) + + // + // PID Bed Heating + // + temp_bed.soft_pwm_amount = WITHIN(temp_bed.celsius, BED_MINTEMP, BED_MAXTEMP) ? (int)get_pid_output_bed() >> 1 : 0; + + #else // !PIDTEMPBED + + // + // Range-limited "bang-bang" bed heating + // + + // Bed Off if the current bed temperature is outside the allowed range + if (!WITHIN(temp_bed.celsius, BED_MINTEMP, BED_MAXTEMP)) { + temp_bed.soft_pwm_amount = 0; + WRITE_HEATER_BED(LOW); + break; } - else { - #if ENABLED(PIDTEMPBED) - temp_bed.soft_pwm_amount = WITHIN(temp_bed.celsius, BED_MINTEMP, BED_MAXTEMP) ? (int)get_pid_output_bed() >> 1 : 0; - #else - // Check if temperature is within the correct band - if (WITHIN(temp_bed.celsius, BED_MINTEMP, BED_MAXTEMP)) { - #if DISABLED(HAS_PELTIER) - #if ENABLED(BED_LIMIT_SWITCHING) - - // Range-limited "bang-bang" bed heating - if (temp_bed.is_above_target(BED_HYSTERESIS)) - temp_bed.soft_pwm_amount = 0; - //direction = cool (implicit off) - else if (temp_bed.is_below_target(BED_HYSTERESIS)) - temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1; - //direction = heat - #else // not bed limit switching - temp_bed.soft_pwm_amount = temp_bed.is_below_target() ? MAX_BED_POWER >> 1 : 0; - #endif - #else //HAS_PELTIER - - //Peltier logic here - // Simple (noisy) "bang-bang" bed heating - if (temp_bed.is_above_target(BED_HYSTERESIS) && !temp_bed.target==0){ - temp_bed.soft_pwm_amount = MAX_BED_POWER; - temp_bed.peltier_dir = false; - //direction = cool - } - else if (temp_bed.is_below_target(BED_HYSTERESIS)){ - temp_bed.soft_pwm_amount = MAX_BED_POWER ; - temp_bed.peltier_dir = true; - //direction = heat - } - else - temp_bed.soft_pwm_amount = 0; - //coast - - #endif - } - else { //Not within correct band + + #if ENABLED(PELTIER_BED) + /** + * Peltier bang-bang maintains max bed power but changes + * current direction to switch between heating/cooling. + */ + if (temp_bed.target && temp_bed.is_above_target(BED_HYSTERESIS)) { // Fast Cooling + temp_bed.soft_pwm_amount = MAX_BED_POWER; + temp_bed.peltier_dir_heating = false; + } + else if (temp_bed.is_below_target(BED_HYSTERESIS)) { // Heating + temp_bed.soft_pwm_amount = MAX_BED_POWER; + temp_bed.peltier_dir_heating = true; + } + else + temp_bed.soft_pwm_amount = 0; // Off (ambient cooling) + + #else // !PELTIER_BED + + #if ENABLED(BED_LIMIT_SWITCHING) + if (temp_bed.is_above_target(BED_HYSTERESIS)) // Cooling (implicit off) temp_bed.soft_pwm_amount = 0; - WRITE_HEATER_BED(LOW); - } + else if (temp_bed.is_below_target(BED_HYSTERESIS)) // Heating + temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1; + #else // Not bed limit switching + temp_bed.soft_pwm_amount = temp_bed.is_below_target() ? MAX_BED_POWER >> 1 : 0; #endif - } - } + + #endif // !PELTIER_BED + + #endif // !PIDTEMPBED } while (false); } @@ -2942,9 +2949,9 @@ void Temperature::init() { #endif #if HAS_HEATED_BED - #if ENABLED(HAS_PELTIER) - SET_OUTPUT(PELTIER_PIN); - OUT_WRITE(PELTIER_PIN, PELTIER_PIN_INVERT); + #if ENABLED(PELTIER_BED) + SET_OUTPUT(PELTIER_DIR_PIN); + OUT_WRITE(PELTIER_DIR_PIN, !PELTIER_DIR_HEAT_STATE); #endif #ifdef BOARD_OPENDRAIN_MOSFETS OUT_WRITE_OD(HEATER_BED_PIN, ENABLED(HEATER_BED_INVERTING)); @@ -3926,8 +3933,8 @@ void Temperature::isr() { #if HAS_HEATED_BED _PWM_MOD(BED, soft_pwm_bed, temp_bed); - #if ENABLED(HAS_PELTIER) - WRITE_PELTIER_DIR(temp_bed.peltier_dir); + #if ENABLED(PELTIER_BED) + WRITE_PELTIER_DIR(temp_bed.peltier_dir_heating); #endif #endif @@ -4395,16 +4402,15 @@ void Temperature::isr() { #if HAS_TEMP_SENSOR /** * Print a single heater state in the form: + * Extruder: " T0:nnn.nn /nnn.nn" * Bed: " B:nnn.nn /nnn.nn" * Chamber: " C:nnn.nn /nnn.nn" - * Probe: " P:nnn.nn" * Cooler: " L:nnn.nn /nnn.nn" + * Probe: " P:nnn.nn" * Board: " M:nnn.nn" * SoC: " S:nnn.nn" * Redundant: " R:nnn.nn /nnn.nn" - * Extruder: " T0:nnn.nn /nnn.nn" * With ADC: " T0:nnn.nn /nnn.nn (nnn.nn)" - * With Peltier: " P@:X" */ static void print_heater_state(const heater_id_t e, const_celsius_float_t c, const_celsius_float_t t OPTARG(SHOW_TEMP_ADC_VALUES, const float r) @@ -4422,12 +4428,12 @@ void Temperature::isr() { #if HAS_TEMP_CHAMBER case H_CHAMBER: k = 'C'; break; #endif - #if HAS_TEMP_PROBE - case H_PROBE: k = 'P'; show_t = false; break; - #endif #if HAS_TEMP_COOLER case H_COOLER: k = 'L'; break; #endif + #if HAS_TEMP_PROBE + case H_PROBE: k = 'P'; show_t = false; break; + #endif #if HAS_TEMP_BOARD case H_BOARD: k = 'M'; show_t = false; break; #endif @@ -4454,6 +4460,17 @@ void Temperature::isr() { delay(2); } + /** + * Print all heater states followed by power data on a single line. + * See print_heater_state for heater output strings. + * Power output strings are in the format: + * Extruder: " @:nnn" + * Bed: " B@:nnn" + * Peltier: " P@:H/C" + * Chamber: " C@:nnn" + * Cooler: " L@:nnn" + * Hotends: " @0:nnn @1:nnn ..." + */ void Temperature::print_heater_states(const int8_t target_extruder OPTARG(HAS_TEMP_REDUNDANT, const bool include_r/*=false*/) ) { @@ -4485,25 +4502,13 @@ void Temperature::isr() { HOTEND_LOOP() print_heater_state((heater_id_t)e, degHotend(e), degTargetHotend(e) OPTARG(SHOW_TEMP_ADC_VALUES, rawHotendTemp(e))); #endif SString<100> s(F(" @:"), getHeaterPower((heater_id_t)target_extruder)); - #if HAS_HEATED_BED - s.append(" B@:", getHeaterPower(H_BED)); - #endif - #if HAS_HEATED_CHAMBER - s.append(" C@:", getHeaterPower(H_CHAMBER)); - #endif - #if HAS_COOLER - s.append(" C@:", getHeaterPower(H_COOLER)); - #endif + TERN_(HAS_HEATED_BED, s.append(F(" B@:"), getHeaterPower(H_BED))); + TERN_(PELTIER_BED, s.append(F(" P@:"), temp_bed.peltier_dir_heating ? 'H' : 'C')); + TERN_(HAS_HEATED_CHAMBER, s.append(F(" C@:"), getHeaterPower(H_CHAMBER))); + TERN_(HAS_COOLER, s.append(F(" L@:"), getHeaterPower(H_COOLER))); #if HAS_MULTI_HOTEND HOTEND_LOOP() s.append(F(" @"), e, ':', getHeaterPower((heater_id_t)e)); #endif - #if ENABLED(HAS_PELTIER) - s.append(" P@:"); - if (temp_bed.peltier_dir) - s.append ("H"); - else - s.append ("C"); - #endif s.echo(); } diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index 5cf231d000db..67276e4d8a23 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -437,9 +437,8 @@ typedef struct HeaterInfo : public TempInfo { uint8_t soft_pwm_amount; bool is_below_target(const celsius_t offs=0) const { return (target - celsius > offs); } // celsius < target - offs bool is_above_target(const celsius_t offs=0) const { return (celsius - target > offs); } // celsius > target + offs - #if ENABLED(HAS_PELTIER) - //the abstract peltier_dir is defined as heat=true, cool=false. See PELTIER_PIN_INVERT for implementation status - bool peltier_dir = false; + #if ENABLED(PELTIER_BED) + bool peltier_dir_heating; // = false #endif } heater_info_t; @@ -613,9 +612,6 @@ class Temperature { #if HAS_HEATED_BED static bed_info_t temp_bed; #endif - #if ENABLED(HAS_PELTIER) - static bool peltier_dir; - #endif #if HAS_TEMP_PROBE static probe_info_t temp_probe; #endif From c9657d99826b2b0002ee5c3a21f07bf0b19c0b5f Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 12 Sep 2024 19:35:23 -0500 Subject: [PATCH 6/6] tweak --- Marlin/Configuration.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index fdd67ccc70c0..b9ed44dffac0 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -822,13 +822,13 @@ * * A second GPIO pin is required to control current direction. * Two configurations are possible: Relay and H-Bridge - * only relay is supported on this pass. (H bridge requires 4 MOS switches configured in H-Bridge) - * //todo: H bridge pin configurations * + * (At this time only relay is supported. H-bridge requires 4 MOS switches configured in H-Bridge.) + * + * Power is handled by the bang-bang control loop: 0 or 255. * Cooling applications are more common than heating, so the pin states are commonly: - * LOW = Heating = Relay Energized - * HIGH = Cooling = Relay in "Normal" state - * (Power is handled by the bang-bang control loop: 0 or 255) + * LOW = Heating = Relay Energized + * HIGH = Cooling = Relay in "Normal" state */ //#define PELTIER_BED #if ENABLED(PELTIER_BED)