mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 05:27:50 +00:00
Cooling: add per-printer non-zero fan PWM floor and fix N=0 fan ramp override (#13715)
* Cooling: add per-printer non-zero fan PWM floor and fix N=0 ramp override * updated parameter naming
This commit is contained in:
@@ -719,6 +719,9 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||||||
// Second generate the adjusted G-code.
|
// Second generate the adjusted G-code.
|
||||||
std::string new_gcode;
|
std::string new_gcode;
|
||||||
new_gcode.reserve(gcode.size() * 2);
|
new_gcode.reserve(gcode.size() * 2);
|
||||||
|
// ORCA: per-printer minimum non-zero fan PWM. Applied at every part-cooling fan emission below so any
|
||||||
|
// non-zero command is raised to at least this percent (0 disables the clamp).
|
||||||
|
const unsigned int part_cooling_fan_min_pwm = static_cast<unsigned int>(std::max(0, m_config.part_cooling_fan_min_pwm.value));
|
||||||
bool overhang_fan_control= false;
|
bool overhang_fan_control= false;
|
||||||
int overhang_fan_speed = 0;
|
int overhang_fan_speed = 0;
|
||||||
bool internal_bridge_fan_control= false; // ORCA: Add support for separate internal bridge fan speed control
|
bool internal_bridge_fan_control= false; // ORCA: Add support for separate internal bridge fan speed control
|
||||||
@@ -727,7 +730,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||||||
int supp_interface_fan_speed = 0;
|
int supp_interface_fan_speed = 0;
|
||||||
bool ironing_fan_control= false; // ORCA: Add support for ironing fan speed control
|
bool ironing_fan_control= false; // ORCA: Add support for ironing fan speed control
|
||||||
int ironing_fan_speed = 0; // ORCA: Add support for ironing fan speed control
|
int ironing_fan_speed = 0; // ORCA: Add support for ironing fan speed control
|
||||||
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode,
|
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, part_cooling_fan_min_pwm,
|
||||||
&overhang_fan_control, &overhang_fan_speed,
|
&overhang_fan_control, &overhang_fan_speed,
|
||||||
&internal_bridge_fan_control, &internal_bridge_fan_speed,
|
&internal_bridge_fan_control, &internal_bridge_fan_speed,
|
||||||
&supp_interface_fan_control, &supp_interface_fan_speed,
|
&supp_interface_fan_control, &supp_interface_fan_speed,
|
||||||
@@ -743,11 +746,11 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||||||
int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer);
|
int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer);
|
||||||
supp_interface_fan_speed = EXTRUDER_CONFIG(support_material_interface_fan_speed);
|
supp_interface_fan_speed = EXTRUDER_CONFIG(support_material_interface_fan_speed);
|
||||||
|
|
||||||
if (close_fan_the_first_x_layers <= 0 && full_fan_speed_layer > 0) {
|
// ORCA: previously a silent override forced `close_fan_the_first_x_layers` from 0 up to 1 whenever a ramp
|
||||||
// When ramping up fan speed from close_fan_the_first_x_layers to full_fan_speed_layer, force close_fan_the_first_x_layers above zero,
|
// was configured (`full_fan_speed_layer > 0`), so the first printed layer always had the fan disabled.
|
||||||
// so there will be a zero fan speed at least at the 1st layer.
|
// That hid the user's literal "no cooling for the first 0 layers" setting and produced a non-zero starting
|
||||||
close_fan_the_first_x_layers = 1;
|
// factor on the ramp denominator. The override has been removed: with N=0 and M>0 the ramp now genuinely
|
||||||
}
|
// starts on layer 0 at a factor of 1/M and reaches 100% at layer M-1, matching the intent of the option.
|
||||||
if (int(layer_id) >= close_fan_the_first_x_layers) {
|
if (int(layer_id) >= close_fan_the_first_x_layers) {
|
||||||
float fan_max_speed = EXTRUDER_CONFIG(fan_max_speed);
|
float fan_max_speed = EXTRUDER_CONFIG(fan_max_speed);
|
||||||
float slow_down_layer_time = float(EXTRUDER_CONFIG(slow_down_layer_time));
|
float slow_down_layer_time = float(EXTRUDER_CONFIG(slow_down_layer_time));
|
||||||
@@ -806,7 +809,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||||||
m_fan_speed = fan_speed_new;
|
m_fan_speed = fan_speed_new;
|
||||||
m_current_fan_speed = fan_speed_new;
|
m_current_fan_speed = fan_speed_new;
|
||||||
if (immediately_apply)
|
if (immediately_apply)
|
||||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed);
|
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed, part_cooling_fan_min_pwm);
|
||||||
}
|
}
|
||||||
//BBS
|
//BBS
|
||||||
if (additional_fan_speed_new != m_additional_fan_speed) {
|
if (additional_fan_speed_new != m_additional_fan_speed) {
|
||||||
@@ -980,26 +983,26 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||||||
|
|
||||||
if (need_set_fan) {
|
if (need_set_fan) {
|
||||||
if (fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START]){
|
if (fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START]){
|
||||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, overhang_fan_speed);
|
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, overhang_fan_speed, part_cooling_fan_min_pwm);
|
||||||
m_current_fan_speed = overhang_fan_speed;
|
m_current_fan_speed = overhang_fan_speed;
|
||||||
} else if (fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]){ // ORCA: Add support for separate internal bridge fan speed control
|
} else if (fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]){ // ORCA: Add support for separate internal bridge fan speed control
|
||||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, internal_bridge_fan_speed);
|
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, internal_bridge_fan_speed, part_cooling_fan_min_pwm);
|
||||||
m_current_fan_speed = internal_bridge_fan_speed;
|
m_current_fan_speed = internal_bridge_fan_speed;
|
||||||
}
|
}
|
||||||
else if (fan_speed_change_requests[CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START]){
|
else if (fan_speed_change_requests[CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START]){
|
||||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, supp_interface_fan_speed);
|
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, supp_interface_fan_speed, part_cooling_fan_min_pwm);
|
||||||
m_current_fan_speed = supp_interface_fan_speed;
|
m_current_fan_speed = supp_interface_fan_speed;
|
||||||
}
|
}
|
||||||
else if (fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START]){
|
else if (fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START]){
|
||||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, ironing_fan_speed);
|
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, ironing_fan_speed, part_cooling_fan_min_pwm);
|
||||||
m_current_fan_speed = ironing_fan_speed;
|
m_current_fan_speed = ironing_fan_speed;
|
||||||
}
|
}
|
||||||
else if(fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] && m_current_fan_speed != -1){
|
else if(fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] && m_current_fan_speed != -1){
|
||||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed);
|
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed, part_cooling_fan_min_pwm);
|
||||||
fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] = false;
|
fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed);
|
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed, part_cooling_fan_min_pwm);
|
||||||
need_set_fan = false;
|
need_set_fan = false;
|
||||||
}
|
}
|
||||||
pos = line_end;
|
pos = line_end;
|
||||||
|
|||||||
@@ -230,7 +230,10 @@ void FanMover::_remove_slow_fan(int16_t min_speed, float past_sec) {
|
|||||||
|
|
||||||
std::string FanMover::_set_fan(int16_t speed) {
|
std::string FanMover::_set_fan(int16_t speed) {
|
||||||
//const Tool* tool = m_writer.get_tool(m_currrent_extruder < 20 ? m_currrent_extruder : 0);
|
//const Tool* tool = m_writer.get_tool(m_currrent_extruder < 20 ? m_currrent_extruder : 0);
|
||||||
return GCodeWriter::set_fan(m_writer.config.gcode_flavor.value, speed);
|
// ORCA: apply the per-printer non-zero fan PWM floor so reposted fan commands respect the clamp too.
|
||||||
|
const int floor_pct = m_writer.config.part_cooling_fan_min_pwm.value;
|
||||||
|
const unsigned int part_cooling_fan_min_pwm = floor_pct > 0 ? static_cast<unsigned int>(floor_pct) : 0u;
|
||||||
|
return GCodeWriter::set_fan(m_writer.config.gcode_flavor.value, speed, part_cooling_fan_min_pwm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1092,9 +1092,13 @@ std::string GCodeWriter::unlift()
|
|||||||
return gcode;
|
return gcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int speed)
|
std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int speed, unsigned int part_cooling_fan_min_pwm)
|
||||||
{
|
{
|
||||||
std::ostringstream gcode;
|
std::ostringstream gcode;
|
||||||
|
// ORCA: clamp non-zero fan commands up to the configured PWM floor so fans that can't spool at low duty
|
||||||
|
// cycles still start reliably. Zero (fan off) is preserved exactly so disable-fan commands are never altered.
|
||||||
|
if (speed > 0 && part_cooling_fan_min_pwm > 0 && speed < part_cooling_fan_min_pwm)
|
||||||
|
speed = part_cooling_fan_min_pwm;
|
||||||
if (speed == 0) {
|
if (speed == 0) {
|
||||||
switch (gcode_flavor) {
|
switch (gcode_flavor) {
|
||||||
case gcfTeacup:
|
case gcfTeacup:
|
||||||
@@ -1129,7 +1133,9 @@ std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int sp
|
|||||||
std::string GCodeWriter::set_fan(unsigned int speed) const
|
std::string GCodeWriter::set_fan(unsigned int speed) const
|
||||||
{
|
{
|
||||||
//BBS
|
//BBS
|
||||||
return GCodeWriter::set_fan(this->config.gcode_flavor, speed);
|
// ORCA: pick up the per-printer PWM floor from the active config.
|
||||||
|
return GCodeWriter::set_fan(this->config.gcode_flavor, speed,
|
||||||
|
static_cast<unsigned int>(std::max(0, this->config.part_cooling_fan_min_pwm.value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
//BBS: set additional fan speed for BBS machine only
|
//BBS: set additional fan speed for BBS machine only
|
||||||
|
|||||||
@@ -98,7 +98,9 @@ public:
|
|||||||
void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; }
|
void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; }
|
||||||
Vec2f get_xy_offset() { return Vec2f{m_x_offset, m_y_offset}; };
|
Vec2f get_xy_offset() { return Vec2f{m_x_offset, m_y_offset}; };
|
||||||
// To be called by the CoolingBuffer from another thread.
|
// To be called by the CoolingBuffer from another thread.
|
||||||
static std::string set_fan(const GCodeFlavor gcode_flavor, unsigned int speed);
|
// ORCA: `part_cooling_fan_min_pwm` (0-100, default 0) is a floor applied only when `speed` is non-zero, used to overcome
|
||||||
|
// PWM start-up thresholds on fans that won't spool below a certain duty cycle. A `speed` of 0 is always honoured.
|
||||||
|
static std::string set_fan(const GCodeFlavor gcode_flavor, unsigned int speed, unsigned int part_cooling_fan_min_pwm = 0);
|
||||||
// To be called by the main thread. It always emits the G-code, it does not remember the previous state.
|
// To be called by the main thread. It always emits the G-code, it does not remember the previous state.
|
||||||
// Keeping the state is left to the CoolingBuffer, which runs asynchronously on another thread.
|
// Keeping the state is left to the CoolingBuffer, which runs asynchronously on another thread.
|
||||||
std::string set_fan(unsigned int speed) const;
|
std::string set_fan(unsigned int speed) const;
|
||||||
|
|||||||
@@ -1326,7 +1326,7 @@ static std::vector<std::string> s_Preset_machine_limits_options {
|
|||||||
static std::vector<std::string> s_Preset_printer_options {
|
static std::vector<std::string> s_Preset_printer_options {
|
||||||
"printer_technology",
|
"printer_technology",
|
||||||
"printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor",
|
"printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor",
|
||||||
"fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs",
|
"fan_kickstart", "part_cooling_fan_min_pwm", "fan_speedup_time", "fan_speedup_overhangs",
|
||||||
"single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
|
"single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
|
||||||
"printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type",
|
"printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type",
|
||||||
"printable_height", "extruder_printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
|
"printable_height", "extruder_printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
"fan_cooling_layer_time",
|
"fan_cooling_layer_time",
|
||||||
"full_fan_speed_layer",
|
"full_fan_speed_layer",
|
||||||
"fan_kickstart",
|
"fan_kickstart",
|
||||||
|
"part_cooling_fan_min_pwm",
|
||||||
"fan_speedup_overhangs",
|
"fan_speedup_overhangs",
|
||||||
"fan_speedup_time",
|
"fan_speedup_time",
|
||||||
"filament_colour",
|
"filament_colour",
|
||||||
|
|||||||
@@ -3724,6 +3724,28 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloat(0));
|
def->set_default_value(new ConfigOptionFloat(0));
|
||||||
|
|
||||||
|
// ORCA: minimum non-zero part cooling fan speed.
|
||||||
|
def = this->add("part_cooling_fan_min_pwm", coInt);
|
||||||
|
def->label = L("Minimum non-zero part cooling fan speed");
|
||||||
|
def->tooltip = L("Some part-cooling fans cannot start spinning when commanded below a certain PWM duty cycle. "
|
||||||
|
"When set above 0, any non-zero part-cooling fan command will be raised to at least this percentage "
|
||||||
|
"so the fan reliably starts. A fan command of 0 (fan off) is always honoured exactly. "
|
||||||
|
"This clamp is applied after every other fan calculation (first-layer ramp, layer-time interpolation, "
|
||||||
|
"overhang/bridge/support-interface/ironing overrides), so scaling still operates within the range "
|
||||||
|
"[this value, 100%]."
|
||||||
|
"\nIf your firmware already disables the fan below a threshold (for example Klipper's "
|
||||||
|
"[fan] off_below: 0.10 shuts the fan off whenever the commanded duty cycle is below 10%), "
|
||||||
|
"this option and the firmware threshold should ideally be set to the same value. Matching them "
|
||||||
|
"(e.g. off_below: 0.10 in Klipper and 10% here) guarantees the slicer never emits a non-zero "
|
||||||
|
"value that the firmware would silently drop, and the fan never receives a value below the one "
|
||||||
|
"you know it can actually spool at."
|
||||||
|
"\nSet to 0 to deactivate.");
|
||||||
|
def->sidetext = L("%");
|
||||||
|
def->min = 0;
|
||||||
|
def->max = 100;
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionInt(0));
|
||||||
|
|
||||||
|
|
||||||
def = this->add("time_cost", coFloat);
|
def = this->add("time_cost", coFloat);
|
||||||
def->label = L("Time cost");
|
def->label = L("Time cost");
|
||||||
|
|||||||
@@ -1309,6 +1309,10 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||||||
((ConfigOptionFloat, fan_kickstart))
|
((ConfigOptionFloat, fan_kickstart))
|
||||||
((ConfigOptionBool, fan_speedup_overhangs))
|
((ConfigOptionBool, fan_speedup_overhangs))
|
||||||
((ConfigOptionFloat, fan_speedup_time))
|
((ConfigOptionFloat, fan_speedup_time))
|
||||||
|
// ORCA: minimum PWM (as a percent 0-100) emitted when the part-cooling fan is asked for a non-zero speed.
|
||||||
|
// Used to overcome the PWM start-up threshold on fans that cannot spool below a certain duty cycle.
|
||||||
|
// A value of 0 (the default) leaves behaviour unchanged. A fan command of 0 (off) is always honoured.
|
||||||
|
((ConfigOptionInt, part_cooling_fan_min_pwm))
|
||||||
((ConfigOptionFloats, filament_diameter))
|
((ConfigOptionFloats, filament_diameter))
|
||||||
((ConfigOptionBoolsNullable, filament_adaptive_volumetric_speed))
|
((ConfigOptionBoolsNullable, filament_adaptive_volumetric_speed))
|
||||||
((ConfigOptionStrings, volumetric_speed_coefficients))
|
((ConfigOptionStrings, volumetric_speed_coefficients))
|
||||||
|
|||||||
@@ -4500,6 +4500,8 @@ void TabPrinter::build_fff()
|
|||||||
line.append_option(optgroup->get_option("fan_speedup_overhangs"));
|
line.append_option(optgroup->get_option("fan_speedup_overhangs"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
optgroup->append_single_option_line("fan_kickstart", "printer_basic_information_cooling_fan#fan-kick-start-time");
|
optgroup->append_single_option_line("fan_kickstart", "printer_basic_information_cooling_fan#fan-kick-start-time");
|
||||||
|
// ORCA: PWM floor for fans that won't spool at low duty cycles.
|
||||||
|
optgroup->append_single_option_line("part_cooling_fan_min_pwm", "printer_basic_information_cooling_fan#minimum-non-zero-part-cooling-fan-speed");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Extruder Clearance"), "param_extruder_clearance");
|
optgroup = page->new_optgroup(L("Extruder Clearance"), "param_extruder_clearance");
|
||||||
optgroup->append_single_option_line("extruder_clearance_radius", "printer_basic_information_extruder_clearance#radius");
|
optgroup->append_single_option_line("extruder_clearance_radius", "printer_basic_information_extruder_clearance#radius");
|
||||||
|
|||||||
Reference in New Issue
Block a user