Make the wipe tower's planner flush and dwell work on Klipper (#15133)

# Description

On Klipper the wipe tower's motion-queue synchronization silently did
nothing. Klipper acts on commands the moment it parses them, and its
`G4` reads only `P` in milliseconds — it ignores `S` — so the `G4 S0`
the tower used to flush the queue before a temperature change never
synchronized anything, and the cooling delay after a filament's cooling
moves passed instantly instead of waiting. The tower now emits `M400`
for the flush and `G4 P<ms>` for the dwell when the flavor is Klipper.

Only `gcode_flavor = klipper` is affected; G-code for every other flavor
is byte-identical, so no shipped profile or existing project file
changes.

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
This commit is contained in:
SoftFever
2026-08-05 18:21:05 +08:00
committed by GitHub
5 changed files with 152 additions and 7 deletions

View File

@@ -617,6 +617,18 @@ Polygon generate_rectange_polygon(const Vec2f &wt_box_min ,const Vec2f & wt_box_
return res;
}
const char* flush_planner_queue_command(GCodeFlavor flavor)
{
return flavor == gcfKlipper ? "M400\n" : "G4 S0\n";
}
std::string wait_command(GCodeFlavor flavor, float seconds)
{
if (flavor == gcfKlipper)
return "G4 P" + std::to_string(std::lround(seconds * 1000.f)) + "\n";
return "G4 S" + Slic3r::float_to_string_decimal_point(seconds, 3) + "\n";
}
class WipeTowerWriter
{
public:
@@ -1145,7 +1157,7 @@ public:
{
if (time==0.f)
return *this;
m_gcode += "G4 S" + Slic3r::float_to_string_decimal_point(time, 3) + "\n";
m_gcode += wait_command(m_gcode_flavor, time);
return *this;
}
@@ -1190,7 +1202,7 @@ public:
WipeTowerWriter& flush_planner_queue()
{
m_gcode += "G4 S0\n";
m_gcode += flush_planner_queue_command(m_gcode_flavor);
return *this;
}
@@ -1333,6 +1345,8 @@ public:
{
std::string buffer;
if (wait_for_moves)
// Not flush_planner_queue_command(): this BBL precool path wants M400, which every
// flavor it reaches understands, not the zero dwell the other flavors flush with.
buffer += "M400\n";
buffer += "M104";
if (target_extruder != -1)

View File

@@ -26,6 +26,12 @@ enum GCodeFlavor : unsigned char;
Polylines construct_gap_for_skip_points(
const Polygon& polygon, const std::vector<Vec2f>& skip_points, float wt_width, float gap_length, Polygon& insert_skip_polygon);
// Klipper acts on commands the instant it parses them, and its G4 reads only P (milliseconds),
// so the zero-second and seconds-valued dwells every other flavor uses neither synchronize nor
// pause there. Both defined in WipeTower.cpp, shared by WipeTower and WipeTower2.
const char* flush_planner_queue_command(GCodeFlavor flavor); // finish queued moves, e.g. around M104/M109
std::string wait_command(GCodeFlavor flavor, float seconds); // pause for `seconds`
class WipeTower
{
public:

View File

@@ -386,7 +386,8 @@ public:
}
WipeTowerWriter2& switch_filament_monitoring(bool enable) {
m_gcode += std::string("G4 S0\n") + "M591 " + (enable ? "R" : "S0") + "\n";
flush_planner_queue();
m_gcode += enable ? "M591 R\n" : "M591 S0\n";
return *this;
}
@@ -625,7 +626,7 @@ public:
// Set extruder temperature, don't wait by default.
WipeTowerWriter2& set_extruder_temp(int temperature, bool wait = false)
{
m_gcode += "G4 S0\n"; // to flush planner queue
flush_planner_queue();
m_gcode += "M" + std::to_string(wait ? 109 : 104) + " S" + std::to_string(temperature) + "\n";
return *this;
}
@@ -635,7 +636,7 @@ public:
{
if (time==0.f)
return *this;
m_gcode += "G4 S" + Slic3r::float_to_string_decimal_point(time, 3) + "\n";
m_gcode += wait_command(m_gcode_flavor, time);
return *this;
}
@@ -677,8 +678,8 @@ public:
}
WipeTowerWriter2& flush_planner_queue()
{
m_gcode += "G4 S0\n";
{
m_gcode += flush_planner_queue_command(m_gcode_flavor);
return *this;
}