diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 239a91facf..f6c48eae95 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -735,7 +735,12 @@ //#define MPC_AUTOTUNE_MENU // Add MPC auto-tuning to the "Advanced Settings" menu. (~350 bytes of flash) #define MPC_MAX 255 // (0..255) Current to nozzle while MPC is active. - #define MPC_HEATER_POWER { 40.0f } // (W) Heat cartridge powers. + #define MPC_HEATER_POWER { 40.0f } // (W) Nominal heat cartridge powers. + //#define MPC_PTC // Hotend power changes with temperature (e.g., PTC heat cartridges). + #if ENABLED(MPC_PTC) + #define MPC_HEATER_ALPHA { 0.0028f } // Temperature coefficient of resistance of the heat cartridges. + #define MPC_HEATER_REFTEMP { 20 } // (°C) Reference temperature for MPC_HEATER_POWER and MPC_HEATER_ALPHA. + #endif #define MPC_INCLUDE_FAN // Model the fan speed? diff --git a/Marlin/src/gcode/temp/M306.cpp b/Marlin/src/gcode/temp/M306.cpp index 12e175420d..e0c9d935e3 100644 --- a/Marlin/src/gcode/temp/M306.cpp +++ b/Marlin/src/gcode/temp/M306.cpp @@ -64,9 +64,13 @@ void GcodeSuite::M306() { case 2: tuning_type = Temperature::MPCTuningType::FORCE_ASYMPTOTIC; break; default: tuning_type = Temperature::MPCTuningType::AUTO; break; } - LCD_MESSAGE(MSG_MPC_AUTOTUNE); - thermalManager.MPC_autotune(e, tuning_type); - ui.reset_status(); + if (TERN0(MPC_PTC, tuning_type == Temperature::MPCTuningType::FORCE_ASYMPTOTIC)) + SERIAL_ECHOLNPGM("Aymptotic tuning not avaiable for PTC hotends"); + else { + LCD_MESSAGE(MSG_MPC_AUTOTUNE); + thermalManager.MPC_autotune(e, tuning_type); + ui.reset_status(); + } return; } #endif @@ -74,6 +78,10 @@ void GcodeSuite::M306() { if (parser.seen("ACFPRH")) { MPC_t &mpc = thermalManager.temp_hotend[e].mpc; if (parser.seenval('P')) mpc.heater_power = parser.value_float(); + #if ENABLED(MPC_PTC) + if (parser.seenval('L')) mpc.heater_alpha = parser.value_float(); + if (parser.seenval('Q')) mpc.heater_reftemp = parser.value_float(); + #endif if (parser.seenval('C')) mpc.block_heat_capacity = parser.value_float(); if (parser.seenval('R')) mpc.sensor_responsiveness = parser.value_float(); if (parser.seenval('A')) mpc.ambient_xfer_coeff_fan0 = parser.value_float(); @@ -94,16 +102,20 @@ void GcodeSuite::M306_report(const bool forReplay/*=true*/) { HOTEND_LOOP() { report_echo_start(forReplay); MPC_t &mpc = thermalManager.temp_hotend[e].mpc; - SERIAL_ECHOPGM(" M306 E", e, + SERIAL_ECHOLNPGM(" M306 E", e, " P", p_float_t(mpc.heater_power, 2), + #if ENABLED(MPC_PTC) + " L", p_float_t(mpc.heater_alpha, 4), + " Q", p_float_t(mpc.heater_reftemp, 2), + #endif " C", p_float_t(mpc.block_heat_capacity, 2), " R", p_float_t(mpc.sensor_responsiveness, 4), - " A", p_float_t(mpc.ambient_xfer_coeff_fan0, 4) + " A", p_float_t(mpc.ambient_xfer_coeff_fan0, 4), + #if ENABLED(MPC_INCLUDE_FAN) + " F", p_float_t(mpc.fanCoefficient(), 4), + #endif + " H", p_float_t(mpc.filament_heat_capacity_permm, 4) ); - #if ENABLED(MPC_INCLUDE_FAN) - SERIAL_ECHOPGM(" F", p_float_t(mpc.fanCoefficient(), 4)); - #endif - SERIAL_ECHOLNPGM(" H", p_float_t(mpc.filament_heat_capacity_permm, 4)); } } diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index f3f4a97dff..60b328ea67 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -1026,6 +1026,7 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i #undef MPC_AUTOTUNE #undef MPC_EDIT_MENU #undef MPC_AUTOTUNE_MENU + #undef MPC_PTC #endif #if ENABLED(MPC_INCLUDE_FAN) diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp index fde92376fb..c8e5df92b3 100644 --- a/Marlin/src/module/settings.cpp +++ b/Marlin/src/module/settings.cpp @@ -3710,6 +3710,10 @@ void MarlinSettings::reset() { #if ENABLED(MPCTEMP) constexpr float _mpc_heater_power[] = MPC_HEATER_POWER; + #if ENABLED(MPC_PTC) + constexpr float _mpc_heater_alpha[] = MPC_HEATER_ALPHA; + constexpr float _mpc_heater_reftemp[] = MPC_HEATER_REFTEMP; + #endif constexpr float _mpc_block_heat_capacity[] = MPC_BLOCK_HEAT_CAPACITY; constexpr float _mpc_sensor_responsiveness[] = MPC_SENSOR_RESPONSIVENESS; constexpr float _mpc_ambient_xfer_coeff[] = MPC_AMBIENT_XFER_COEFF; @@ -3719,6 +3723,10 @@ void MarlinSettings::reset() { constexpr float _filament_heat_capacity_permm[] = FILAMENT_HEAT_CAPACITY_PERMM; static_assert(COUNT(_mpc_heater_power) == HOTENDS, "MPC_HEATER_POWER must have HOTENDS items."); + #if ENABLED(MPC_PTC) + static_assert(COUNT(_mpc_heater_alpha) == HOTENDS, "MPC_HEATER_ALPHA must have HOTENDS items."); + static_assert(COUNT(_mpc_heater_reftemp) == HOTENDS, "MPC_HEATER_REFTEMP must have HOTENDS items."); + #endif static_assert(COUNT(_mpc_block_heat_capacity) == HOTENDS, "MPC_BLOCK_HEAT_CAPACITY must have HOTENDS items."); static_assert(COUNT(_mpc_sensor_responsiveness) == HOTENDS, "MPC_SENSOR_RESPONSIVENESS must have HOTENDS items."); static_assert(COUNT(_mpc_ambient_xfer_coeff) == HOTENDS, "MPC_AMBIENT_XFER_COEFF must have HOTENDS items."); @@ -3730,6 +3738,10 @@ void MarlinSettings::reset() { HOTEND_LOOP() { MPC_t &mpc = thermalManager.temp_hotend[e].mpc; mpc.heater_power = _mpc_heater_power[e]; + #if ENABLED(MPC_PTC) + mpc.heater_alpha = _mpc_heater_alpha[e]; + mpc.heater_reftemp = _mpc_heater_reftemp[e]; + #endif mpc.block_heat_capacity = _mpc_block_heat_capacity[e]; mpc.sensor_responsiveness = _mpc_sensor_responsiveness[e]; mpc.ambient_xfer_coeff_fan0 = _mpc_ambient_xfer_coeff[e]; diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 04431ff299..9c3dee6cdd 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -1864,7 +1864,8 @@ void Temperature::mintemp_error(const heater_id_t heater_id OPTARG(ERR_INCLUDE_T } // Update the modeled temperatures - float blocktempdelta = hotend.soft_pwm_amount * mpc.heater_power * (MPC_dT / 127) / mpc.block_heat_capacity; + const float _heater_power = DIV_TERN(MPC_PTC, mpc.heater_power, 1.0f + mpc.heater_alpha * (hotend.modeled_block_temp - mpc.heater_reftemp)); + float blocktempdelta = hotend.soft_pwm_amount * _heater_power * (MPC_dT / 127) / mpc.block_heat_capacity; blocktempdelta += (hotend.modeled_ambient_temp - hotend.modeled_block_temp) * ambient_xfer_coeff * MPC_dT / mpc.block_heat_capacity; hotend.modeled_block_temp += blocktempdelta; @@ -1888,7 +1889,7 @@ void Temperature::mintemp_error(const heater_id_t heater_id OPTARG(ERR_INCLUDE_T power -= (hotend.modeled_ambient_temp - hotend.modeled_block_temp) * ambient_xfer_coeff; } - float pid_output = power * 254.0f / mpc.heater_power + 1.0f; // Ensure correct quantization into a range of 0 to 127 + float pid_output = power * 254.0f / _heater_power + 1.0f; // Ensure correct quantization into a range of 0 to 127 LIMIT(pid_output, 0, MPC_MAX); /* <-- add a slash to enable diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index c96d9aba74..33b7bfb226 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -386,6 +386,10 @@ typedef struct { float p, i, d, c, f; } raw_pidcf_t; static bool e_paused; // Pause E filament permm tracking static int32_t e_position; // For E tracking float heater_power; // M306 P + #if ENABLED(MPC_PTC) + float heater_alpha; // M306 L + float heater_reftemp; // M306 Q + #endif float block_heat_capacity; // M306 C float sensor_responsiveness; // M306 R float ambient_xfer_coeff_fan0; // M306 A