Contents

Multi-Material Printing Hack for Prusa XL

In the same spirit as an earlier post, here is a quick hack I used to get my dual-headed Prusa XL printer to print with three materials at once.

While the technique worked, it may not be the best method, so proceed with caution.

The problem

I am trying to print a part that requires two different materials at the same time: ABS and TPU.

The shaded blue area is meant to be TPU/FLEX material. The shaded yellow area is meant to be ABS material.

The shaded blue area is meant to be TPU/FLEX material. The shaded yellow area is meant to be ABS material.

The first complication comes from the “unfriendly” shape; the part is spherical and has no ideal orientation for printing without any supports.

Grey is TPU, Green is ABS, and the Blue/Grey mix is the support material composed of interleaved PLA and TPU layers.

Grey is TPU, Green is ABS, and the Blue/Grey mix is the support material composed of interleaved PLA and TPU layers.

To make matters slightly more complicated, TPU is one of those filaments that likes to stick to pretty much everything; I have few good options for support material. The least sticky material is PLA.

So now I have a single job that requires three materials: ABS, TPU, and PLA…. but still only two print heads to work with.

What to do?

Usually when faced with the “more materials than heads” problem, the solution is to use the M600 gCode command to have the printer pause, eject the current material and wait for the user to load the new material.

This technique is not new; it’s widely supported and works perfectly if the materials are similar enough that the extrusion settings can be shared between them.

For my part, the tool head will have to transition from PLA to ABS which means virtually every instruction in the gCode will have to change as well.

The loophole

Fortunately, the geometry for this part has a “gap” between the initial section where supports are needed and the internal section where the ABS material is needed.

This is the 'per-layer' view from the slicer with a 'bottom-up' perspective. As before, Grey is TPU, Green is ABS, and the thin blue circle represents the topmost layer of the support material.

This is the 'per-layer' view from the slicer with a 'bottom-up' perspective. As before, Grey is TPU, Green is ABS, and the thin blue circle represents the topmost layer of the support material.

Notice that the last support layer (blue circle) is still a few layers away from the first ABS layer (the green section).

Essentially, I do need three materials, but only two at a time. What a coincidence that I have a printer with two heads!

The solution hack

Warning

This is a hack and may not work for all printers or all slicers. Think of this more as a “proof of concept” than a reliable technique. You can likely adapt this technique to other printers but you will need to understand the gCode generated by your slicer and the firmware running on your printer.

Ideally, this serves as a demonstration and inspires the development of a more robust solution; conceivably, the prusa slicer/firmware could be modified to support this use case directly with not much effort.

The solution is to lie to the slicer and tell it that I have three tool heads. This way, I can use correct profiles for each material and the slicer will generate the correct gCode for each material.

All I have to do is post-process the gCode to pause for a manual material swap at the correct time and then replace all references to the third tool head with the second tool head after the swap.

Simple, right?

(yes, but not as simple as I thought)

This particular part has a little over 400K lines of gCode so I’m not going to do this by hand! Here’s a quick and dirty Python script that rewrote the gCode for me:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.13"
# dependencies = [
#   "structlog",
# ]
# ///
##
# See: https://github.com/prusa3d/Prusa-Firmware-Buddy/blob/master/lib/Marlin/Marlin/src/gcode/control/T.cpp
# See: https://help.prusa3d.com/article/buddy-firmware-specific-g-code-commands_633112
# See: https://old.reddit.com/r/prusa3d/comments/15djlgu/change_filament_settings_mid_print_not_just_colour/ju2c37f/
import re

import structlog


log = structlog.get_logger(__name__)


def replace_t2_with_t1_after_m600(input_file, output_file):
    """
    Modifies a GCode file based on the following rules:
    1. Before the `M600` line, comment out any lines mentioning `T2` by adding `; HACK ;`.
    2. After the `M600` line, replace all instances of `T2` with `T1` if surrounded by whitespace.
    3. Lines beginning with `;` are always ignored.

    Args:
    - input_file: Path to the input GCode file.
    - output_file: Path to the output GCode file.
    """
    m600_found = False
    line_number = 0
    log.info("Starting...")
    with open(input_file, "r") as infile, open(output_file, "w") as outfile:
        for line in infile:
            # For debugging / status
            line_number += 1

            # Skip lines starting with a semicolon
            if line.strip().startswith(";"):
                outfile.write(line)
                continue

            # For all intents and purposes, tool head 3 (T2) does not exist.
            # AT the time of the "color swap", The second tool head stops being a PLA and starts being an ABS tool.
            # This means that we need to comment out all mentions of T2 before the swap and
            #   after the swap, re-write everything.
            # In all cases, we should never refer to T2.
            ##
            if not m600_found:
                # Check for M600 line
                if re.match(r"^M600$", line.strip()):
                    m600_found = True
                    log.info("'M600' found!", line_number=line_number)
                    outfile.write(line)
                    continue

                # Comment out lines mentioning T2 before M600
                if re.search(r"\bT2\b", line):
                    line = f"; HACK ;{line}"
                    log.debug(
                        "Removing mention of Tool Head 3 (t2)", line_number=line_number
                    )
                outfile.write(line)
                continue

            # Replace 'T2' with 'T1' only if surrounded by whitespace after M600
            modified_line = re.sub(r"\bT2\b", "T1", line)
            outfile.write(modified_line)

input_file = "your_gcode_file_here.gcode"
output_file = f"MOD.{input_file}"
replace_t2_with_t1_after_m600(input_file, output_file)

log.info("Done!")

I ran that script, loaded the modified gCode into the printer, and…was not able to fool the printer.

Prusa XL refusing to start the print because it knows I'm trying to cheat.

Prusa XL refusing to start the print because it knows I'm trying to cheat.

The printer was - somehow - aware that the gCode wanted three heads with three distinct materials and refused to start the print until I added a third head.

As it turns out, that python script isn’t quite enough to fool the printer. I’ll spare you from the many iterative attempts to get this working and say that the following additional optimizations/deletions were necessary:

Manual fix 1: Remove all routines for the third tool head

Somewhere early on in the gCode is a section that looks like this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
;
; purge third tool
;
G1 F24000
P0 S1 L2 D0; park the tool
M109 T2 S255
T2 S1 L0 D0; pick the tool
G92 E0 ; reset extruder position

M104.1 T0 P13 Q13 S230
G0 X210 Y-7 Z10 F24000 ; move close to the sheet's edge
G0 E30 X220 Z0.2 F170 ; purge while moving towards the sheet
G0 X250 E9 F800 ; continue purging and wipe the nozzle
G0 X253 Z0.05 F8000 ; wipe, move close to the bed
G0 X256 Z0.2 F8000 ; wipe, move quickly away from the bed
G1 E-1.05 F2400 ; retract
 ; update slicer internal retract variable
G92 E0 ; reset extruder position

M104 S100 T2

This can be removed from the gCode as there is no physical 3rd head to purge or park, heat up, probe…etc.

If the python script naïvely replaced all instances of T2 with T1, this section would be left in the gCode and would waste time running the second print head through the same routine twice.

Manual fix 2: Remove all metadata

This one took quite a few trial/error attempts to figure out. At the tail end of the unmodified gCode there are some “shutdown” commands and then a ton of metadata:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<... many many lines omitted ...>
; turn off extruder heaters
 M104 T0 S0 
 M104 T1 S0 
 M104 T2 S0 

<...>

; objects_info = {"objects":<...>}
; filament used [mm] = 24022.62, 2012.80, 3975.01
; filament used [cm3] = 57.78, 4.84, 9.56
<...>

; prusaslicer_config = begin
<...>
; filament_type = FLEX;PLA;ABS
<...>
; filament_colour = #408080;#FF8000;#FFF2EC
<...>
; prusaslicer_config = end

As best as I can tell, there’s a few hundred lines that encode some basic metadata about the part(s) and the filament(s) used / print times and the slicer specific configuration information. If you look closely, you can see that a fair number of properties have three values: one for each tool head:

1
2
3
<...>
; filament_type = FLEX;PLA;ABS
<...>

I spent a few iterations trying to identify what each property did and if it was safe to drop the third value but wasn’t making a ton of progress. Eventually I just decided to drop all of the metadata… and that worked!

Initially I didn’t think that the printer firmware would be reading ANY of the commented out sections of the file but I guess this makes sense for metadata and anything else that’s not a gCode instruction.

As best as I can tell, nothing contained in the metadata sections changes how the actual print is executed. All of the values are for the slicer UI and - in some cases - for the printer firmware to sanity check the gCode before starting the print.

I could be wrong about this but I don’t have the time/patience/resources to properly test this. The gCode works well enough for my needs without the metadata so I’m content to move on to the next steps on this project.

If I am wrong about what the metadata sections do and it turns out that they do help refine/improve how the printer firmware processes instructions…please do let me know!

TL;DR

If you have a need to print with more materials than you have tool heads, you might be able to trick the slicer/printer into re-configuring a print head mid-print.

To do this, get the slicer to generate valid and correct gCode for each material/tool head combination as if you had an appropriately configured printer. After the gCode has been generated, map every “virtual” too head to a real tool head and remove any additional references to the virtual tool heads.