Add a G90 / G1 Z{min(max_layer_z + 5, printable_height)} F600 move at the
start of machine_end_gcode, before M98 print_end shuts the printer down and
parks it. Besides clearing the part, the explicit Z coordinate lets the Duet
file-info parser pick up the object height.
Minimum layer time follows the derived cooling rule: heat per layer
scales with bead cross-section (layer height x line width), so the
0.8-nozzle-tuned values are scaled by q(d)/q(0.8) using each nozzle's
standard preset geometry. Bases carry the 1.2 value, children @0.4-@1.0
their nozzle-specific value.
Caps now follow V = 34*d * m / S: the 34*d limit line calibrated on
Micro Swiss Volcano plated copper at 2.85 mm, material factor m from
the OrcaFilamentLibrary generic ratios (PLA/ABS/ASA 1.0, PETG 10/12,
PETG-CF 11.5/12, PA-CF 8/12, PLA Matte 11/12), safety factor S = 1.25
covering batch/moisture/wear/diameter tolerances.
Each material base now serves the 1.2 nozzle at its maximum cap;
children @0.4-@1.0 lower the cap per nozzle. Machine variant
default_filament_profile entries point at the matching preset.
Per the profile creation guide, vendor filament profiles now inherit
from Generic * @System instead of vendor-local fdm_filament_* copies.
Each profile carries only Meltingplot-specific deviations plus the
2.85 mm filament diameter; the seven local fdm_filament_* base files
are removed.
Deliberate pins where the library default would change tuned behavior:
- PLA/ABS/TitanX keep filament_max_volumetric_speed 20 (0.6 nozzle cap)
- ABS/PET-CF15 keep close_fan_the_first_x_layers 1
- STYX 12 sets required_nozzle_HRC 0 (unfilled PA12, density 1.02)
Update Maschine G-Code according to latest Bambu Studio Version X2D
filament_change gcode: 2026/07/01
X2D layer_change gcode: 2026/07/01
X2D start gcode: 2026/06/05
X2D timelapse gcode: 2026/06/03
# Description
<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
> * What issue does this PR address or fix?
> * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->
# 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)
## What this does
Ports the AMS filament drying control feature from BambuStudio. Most
work was done by Claude Code with deepseek-v4-pro. Thanks Bambu & CC &
DeepSeek :P
Allows users to start, monitor, and stop AMS-based filament drying
directly from the OrcaSlicer UI for N3F (AMS 2 Pro) and N3S (AMS HT) AMS
units.
Mostly from
https://github.com/bambulab/BambuStudio/commit/c8f70c6ca76e53775aa021a197fe9ec972db709e
Part of #12091
## Screenshots
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/24f579cb-c67c-4d6e-bf77-c31e018f2f70"
/>
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/74f628aa-6f5f-4150-b2e9-082e4ffc3527"
/>
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/d2f26412-a054-4085-9236-5074a030b001"
/>
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/7beef770-8fbc-4375-b244-e0de44b8db2f"
/>
## Changes
- **Data model:** Added drying status enums (`DryStatus`,
`DrySubStatus`, `CannotDryReason`, etc.), `DrySettings` struct,
`DevFilamentDryingPreset` struct to `DevAms`/`DevFilaSystem`
- **Promoted `DevAmsType`** to a global enum (`EXT_SPOOL=0, AMS=1,
AMS_LITE=2, N3F=3, N3S=4`), renamed `DUMMY` → `EXT_SPOOL`
- **JSON parsing:** Extended `DevFilaSystemParser` to parse drying
status fields from printer status messages
- **Commands:** Added `CtrlAmsStartDryingHour()` and
`CtrlAmsStopDrying()` sending `"ams_filament_drying"` JSON via MQTT
- **Backend utility:** New `DevUtilBackend` class with
`GetFilamentDryingPreset()` for reading filament drying config keys
- **UI dialog:** New `AMSDryControl` dialog with three pages
(status/control, guide, progress) matching BambuStudio behavior
- **Integration:** Wired AMS humidity indicator click to open the drying
dialog for N3F/N3S AMS types
- **Assets:** 12 new drying-related images from BambuStudio
- **Firmware parsing:** Added `is_support_remote_dry` flag parsed from
`fun2` bit 5
## Constraints
- N3F/N3S only — standard AMS and AMS Lite continue to use the existing
humidity popup
- Backward compatible — existing `command_ams_drying_stop()` preserved
<!--
> 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)
The eight presets added in 0545750c1b never got ids: the six iQ processes
had none, and K1 SE 0.8 / V-Core 4 0.8 carried ids copied from the presets
they were duplicated from — K1 SE 0.8 still shared K1C 0.8's id. Regenerated
with scripts/assign_vendor_setting_ids.py; none of the eight have shipped in
a release, so no existing id changes meaning.
The filament-group golden harness landed with H2C/A2L support (#14685). Its
"FilamentGroup golden regression" / stress_66 case fails intermittently on
Windows x64, on main and on unrelated PRs alike. The test depends on how fast the
runner is.
The k-medoids clustering these goldens exercise is an anytime search bounded by a
3 second wall clock. Every restart is seeded from its own index, so nothing about
it is random. What varies is how many restarts fit in the budget, and the best
cost is a minimum over completed restarts, so a slower runner is never better.
Grading a score produced that way measures the machine as much as the code.
Add a ClusteringBudget struct and let the tests set it. The defaults are the
current 3 seconds and 30 restarts, so slicing behavior is unchanged. A
non-positive timeout removes the wall clock and bounds the search by restart
count alone.
The goldens are then graded under a fixed budget of four restarts, where every
one of them reaches the BambuStudio reference within 3%, so the score becomes a
property of the code. This retires the machine-specific 125103 lock on stress_66.
The default wall-clock path keeps its own test, asserting the grouping is valid
and the search does not run away. It makes no score assertion, because under a
wall clock that number is not a property of the code.
The golden test also checks the run fits in ten times the default wall clock.
Slicing quality depends on how many restarts fit in the budget, so a search an
order of magnitude slower would degrade real groupings while a fixed-budget score
gate stayed green.
The 3% tolerance stays as the parity allowance against the goldens. It also
covers a small spread across standard libraries: the k-medoids search seeds each
restart with std::shuffle, whose algorithm the C++ standard leaves unspecified,
so libstdc++, libc++ and the MSVC STL permute the same seed differently, start
from different medoids, and settle on slightly different groupings, about 3e-4
apart and only on the goldens heavy enough to reach the k-medoids search.
# Description
Adds a --slice (-s) mode to the profile validator that slices a
two-colour cube through every shipped printer, expanding all custom
g-code (change_filament_gcode, machine start/end, etc.). This catches
invalid-placeholder / bad-flow / slicing errors that the static JSON
checks and unit tests can't see.
Included:
- Validator: new -s sweep mode; per-profile error attribution in the
log; resolves the synthetic 2nd-filament nozzle-mapping so multi-nozzle
BBL printers (incl. the Direct-Drive+Bowden X2D) validate cleanly.
- CI, two complementary paths:
- check_profiles.yml — runs the sweep on profile-only PRs (nightly
binary).
- build_all.yml — new parallel slice_check_linux job runs it on
engine/src PRs with the PR-built binary (build_all doesn't trigger on
resources/**, so no overlap). Runs off the build's artifact, so it
doesn't lengthen the build leg.
- Profile fixes surfaced by the sweep: Creality, FLSun, Ginger, Qidi,
RatRig, iQ.
- Engine: whitelist BBL firmware T-opcodes (T1001/T65279/T65535) in the
time estimator (log-only, no g-code change); dedupe a
per-filament/per-layer log flood in get_config_index.
# 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)
The Unit Tests job sparse-checks-out only .github/scripts/tests, so the
baked-in absolute PROFILES_DIR was missing at runtime; the shipped-profile
test then read a non-existent JSON and null-dereferenced in opt_string.
Check out resources/ in the unit-test job, and guard the test helper to
skip when the profile is absent and require the key before dereferencing.
Cap filament volumetric speed per nozzle (0.4: 14, 0.6: 20, 0.8: 26,
1.0: 30, 1.2: 40 mm3/s). Filaments whose limit exceeds a nozzle's
capability get a nozzle-scoped child profile that only lowers
filament_max_volumetric_speed; filament_id is inherited so all nozzle
variants remain the same material.
Also include layer height and nozzle diameter in the g-code filename.
The new "Prime-tower visits..." test from #14685 (H2C/A2L support) throws
"Coordinate outside allowed range" at random on CI, on both Windows arm64 and
Linux x86_64. It's an uninitialized read of WipeTowerData::height.
#10780 (H2D/H2S) added a second wipe tower path, generate_new(), that fills in
depth, bbx, brim_width and rib_offset but not height. The older generate() sets
height, and clear() never did, so on the generate_new path it stays garbage.
first_layer_wipe_tower_corners() passes height to get_wipe_tower_cone_base() as
R = tan(cone_angle/2) * height. The stray bytes are usually zero, so R is zero
and the slice is fine, which is why it passes most runs on every platform. When
they aren't zero the cone radius runs past ClipperLib's limit and the slice
throws. Nothing selects for it, so it just flakes around.
#14685's test is the first to exercise this path, so that's when it started
showing up.
Initializing height in clear() fixes it, same as the m_origin fix in #13712. The
BBL generate_new path has no stabilization cone, so height = 0 is right.
Adds the Meltingplot vendor bundle with 5 printer models:
- CHX 350 (880x422x943, RepRapFirmware/Duet)
- MBL 133/136/308/480 (RepRapFirmware/Duet)
Each model ships 0.4/0.6/0.8/1.0/1.2 nozzle variants. Layer height
presets span 40-60% of the nozzle diameter and deliberately avoid
heights that divide the Z screw lead evenly (CHX: 1204 ball screw,
4 mm lead; MBL: TR4x2 trapezoidal screw, 8 mm lead) so periodic
screw error does not repeat as banding.
All printers use 2.85 mm filament, so the bundle carries its own
filament library (PLA, StoneFil, ABS, TitanX, ASA, PETG, PA6 CF HT,
STYX 12, PP 9-2, PET-CF15, Extrudr PLA NX2 Matt, Igus iglidur J260)
with the 2.85 mm diameter pinned once in the vendor-local
fdm_filament_common base.
Machine limits mirror the printers' firmware configuration; machine
limit emission is disabled so the values only feed the time estimator.