Batch processing with Slic3r Prusa Edition

Todo

Batch processing with Slic3r Prusa Edition scripts.

Note

These notes apply to the Prusa i3 Mk3 printer. If you are using a different printer, please verify the hardware details are same. These pages may be a bit rough as I revise them and add new material. Please check back regularly for updates.

.ini scripts

Listing 4 Slic3r Prusa Edition printer definition .ini file
 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
bed_shape = 0x0,250x0,250x210,0x210
bridge_acceleration = 1000
end_gcode = ; Last updated 20181007\nG4 ; wait\nG92 E0 ; prepare to retract\nG1 E-0.8 F2100; retract to avoid ooze\nM221 S100 ; reset extruder factor to 100%\nM900 K0 ; reset linear acceleration\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+60, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 ; present bed\nM84 ; disable motors\nM300 S100 P10 ; chirp
gcode_flavor = marlin
extruder_clearance_height = 20
extruder_clearance_radius = 20
extruder_colour = #FFFF00
extruder_offset = 0x0
extrusion_axis = E
machine_max_acceleration_e = 5000,5000
machine_max_acceleration_extruding = 1250,1250
machine_max_acceleration_retracting = 1250,1250
machine_max_acceleration_x = 1500,960
machine_max_acceleration_y = 1500,960
machine_max_acceleration_z = 1000,1000
machine_max_feedrate_e = 120,120
machine_max_feedrate_x = 200,100
machine_max_feedrate_y = 200,100
machine_max_feedrate_z = 12,12
machine_max_jerk_e = 1.5,1.5
machine_max_jerk_x = 8,8
machine_max_jerk_y = 8,8
machine_max_jerk_z = 0.4,0.4
machine_min_extruding_rate = 0,0
machine_min_travel_rate = 0,0
max_volumetric_speed = 11.5
max_print_height = 210
output_filename_format = [input_filename_base].gcode
printer_model = MK3
;print_center = 125,105
remaining_times = 1
silent_mode = 1
start_gcode = ; Last updated 20181018\nM115 U3.3.1 ; tell printer latest fw version\n; Set initial warmup temps\nM104 S140 ; set extruder no-ooze temp\nM140 S{max(first_layer_bed_temperature[0],60)}  ; set bed PINDA warmup temp\n; Nozzle warmup routine\nM300 S40 P10 ; chirp\nG28 W ; home all without mesh bed level\nG0 X0 Y200 Z100.0 F10200 ; raise nozzle and present bed for cleaning\nM109 ; wait for extruder no-ooze warmup temp before mesh bed leveling, cool hot PINDA\n; PINDA warmup routine\nM300 S40 P10 ; chirp\nG0 X125 Y105 Z0.10 F10200; PINDA warming position\nM860 S35 ; wait for PINDA temp to stabilize\nM300 S40 P10 ; chirp\nG28 W ; home all without mesh bed level\nM140 S[first_layer_bed_temperature] ; set bed final temp\nG80 ; mesh bed leveling\n; Final warmup routine\nM104 S[first_layer_temperature] ; set extruder final temp\nM109 S[first_layer_temperature] ; wait for extruder final temp\nM190 S[first_layer_bed_temperature] ; wait for bed final temp\n; Prime line routine\nM300 S25 P10 ; chirp\nM83  ; extruder relative mode\nM900 K0; Disable LA for prime line\nG92 E0.0 ; reset extrusion distance\nG1 Y-3.0 F1000.0 ; go outside print area\nG1 E2 F1000 ; de-retract and push ooze\nG1 X20.0 E6  F1000.0 ; fat 20mm intro line @ 0.30\nG1 X60.0 E3.2  F1000.0 ; thin +40mm intro line @ 0.08\nG1 X100.0 E6  F1000.0 ; fat +40mm intro line @ 0.15\nG1 E-0.8 F2100; retract to avoid stringing\nG1 X99.0 E0 F1000.0 ; -1mm intro line @ 0.00\nG1 X110.0 E0 F1000.0 ; +10mm intro line @ 0.00\nG1 E0.5 F1500; de-retract\nG92 E0.0 ; reset extrusion distance\n; Final print adjustments\nM92 E282.54 ; adjust extrusion ratio\n;M221 S{if layer_height==0.05}100{else}95{endif}
threads = 4
use_relative_e_distances = 1
use_volumetric_e = 0
variable_layer_height = 1
Listing 5 Slic3r Prusa Edition nozzle definition .ini file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
brim_width = 0
external_perimeter_extrusion_width = 0
extrusion_width = 0
first_layer_extrusion_width = 0
infill_extrusion_width = 0
max_layer_height = 0.32
min_layer_height = 0.1
nozzle_diameter = 0.4
perimeter_extrusion_width = 0
perimeters = 3
solid_infill_extrusion_width = 0
support_material_extrusion_width = 0
top_infill_extrusion_width = 0
Listing 6 Slic3r Prusa Edition filament definition .ini file
 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
; filament characteristics
filament_type = PLA
filament_diameter = 1.715
extrusion_multiplier = 0.98
filament_max_volumetric_speed = 11.5
z_offset = 0
start_filament_gcode = "M900 K30"

; temperatures
bed_temperature = 55
temperature = 200
first_layer_bed_temperature = 60
first_layer_temperature = 205

; cooling
max_fan_speed = 100
min_fan_speed = 100
disable_fan_first_layers = 1
fan_always_on = 1
fan_below_layer_time = 100
bridge_fan_speed = 100
cooling = 1

; retraction
retract_before_travel = 1
retract_layer_change = 1
retract_length = 0.8
retract_lift = 0.6
retract_lift_above = 0
retract_lift_below = 209
retract_restart_extra = 0
retract_speed = 35
deretract_speed = 25

; bridges
dont_support_bridges = 1
bridge_flow_ratio = 1
bridge_speed = 30
Listing 7 Slic3r Prusa Edition print job definition .ini file
 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
76
77
78
79
80
81
82
83
84
notes = Normal
;quality settings
perimeters = 3
external_perimeters_first = 0
avoid_crossing_perimeters = 1
external_fill_pattern = rectilinear
extra_perimeters = 1
infill_every_layers = 2
only_retract_when_crossing_perimeters = 1
ooze_prevention = 0
overhangs = 1
seam_position = aligned
spiral_vase = 0
variable_layer_height = 1
xy_size_compensation = 0
gcode_comments = 1

; layer height
layer_height = 0.2
first_layer_height = 0.2
bottom_solid_layers = 5
top_solid_layers = 5

; speeds
perimeter_speed = 45
external_perimeter_speed = 35
small_perimeter_speed = 25
first_layer_speed = 20
gap_fill_speed = 40
infill_speed = 200
solid_infill_speed = 200
min_print_speed = 15
top_solid_infill_speed = 50
travel_speed = 180

; acceleration
default_acceleration = 1000
first_layer_acceleration = 1000
infill_acceleration = 1250
perimeter_acceleration = 800

; print characteristics
fill_angle = 45
fill_density = 20%
fill_pattern = grid
infill_first = 0
infill_only_where_needed = 0
infill_overlap = 25%
solid_infill_below_area = 0
solid_infill_every_layers = 0
solid_infill_extruder = 1
solid_infill_extrusion_width = 0

interface_shells = 0

; skirt, brim & raft
raft_layers = 0
skirt_distance = 2
skirt_height = 1
skirts = 1
min_skirt_length = 4

; support
support_material = 0
support_material_angle = 0
support_material_auto = 1
support_material_buildplate_only = 0
support_material_contact_distance = 0.25
support_material_enforce_layers = 0
support_material_extruder = 0
support_material_extrusion_width = 0
support_material_interface_contact_loops = 1
support_material_interface_extruder = 0
support_material_interface_layers = 1
support_material_interface_spacing = 0.2
support_material_interface_speed = 100%
support_material_pattern = rectilinear
support_material_spacing = 2
support_material_speed = 50
support_material_synchronize_layers = 0
support_material_threshold = 45
support_material_with_sheath = 0
support_material_xy_spacing = 60%

Build scripts

Listing 8 Slic3r Prusa Edition build script
 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
76
#!/bin/sh
# Normal mode prints
DIR_BASENAME="./output"
find "./models/normal" -iname "*.stl" | while read MODEL
do 
    echo "${MODEL}"
    for NOZZLE in nozzles/*.ini; do 
        DIR_NOZZLE="$(basename -s .ini $NOZZLE)"
        for FILAMENT in filaments/XT.ini; do 
            DIR_FILAMENT="$(basename -s .ini $FILAMENT)"
            for SETTINGS in settings/settings_020_layers_normal.ini ; do 
                DIR_PATH="${DIR_BASENAME}/${DIR_FILAMENT}/${DIR_NOZZLE}"
                mkdir -p "${DIR_PATH}"
                run_slic3r \
                --load printers/printer_mk3.ini \
                --load $NOZZLE \
                --load $FILAMENT \
                --load $SETTINGS \
                --post-process scripts/slic3r_massage.py \
                --output "${DIR_PATH}" "${MODEL}"

            done
        done
    done
done

# Vase mode prints
find "./models/vase" -iname "*.stl" | while read MODEL
do 
    echo "${MODEL}"
    for NOZZLE in nozzles/*.ini; do 
        DIR_NOZZLE="$(basename -s .ini $NOZZLE)"
        for FILAMENT in filaments/XT.ini; do 
            DIR_FILAMENT="$(basename -s .ini $FILAMENT)"
            for SETTINGS in settings/settings_020_layers_vase.ini ; do 
                DIR_PATH="${DIR_BASENAME}/${DIR_FILAMENT}/${DIR_NOZZLE}"
                echo "Output file is $DIR_BASENAME/${DIR_FILAMENT}/${DIR_NOZZLE}/${DIR_MODEL}"
                mkdir -p "${DIR_PATH}"
                run_slic3r \
                --load printers/printer_mk3.ini \
                --load $NOZZLE \
                --load $FILAMENT \
                --load $SETTINGS \
                --post-process scripts/slic3r_massage.py \
                --output "${DIR_PATH}" "$MODEL"

            done
        done
    done
done

# Concentric fill prints
find "./models/concentric" -iname "*.stl" | while read MODEL
do 
    echo "${MODEL}"
    for NOZZLE in nozzles/*.ini; do 
        DIR_NOZZLE="$(basename -s .ini $NOZZLE)"
        for FILAMENT in filaments/XT.ini; do 
            DIR_FILAMENT="$(basename -s .ini $FILAMENT)"
            for SETTINGS in settings/settings_020_layers_normal.ini ; do 
                DIR_PATH="${DIR_BASENAME}/${DIR_FILAMENT}/${DIR_NOZZLE}"
                echo "Output file is $DIR_BASENAME/${DIR_FILAMENT}/${DIR_NOZZLE}/${DIR_MODEL}"
                mkdir -p "${DIR_PATH}"
                run_slic3r \
                --load printers/printer_mk3.ini \
                --load $NOZZLE \
                --load $FILAMENT \
                --load $SETTINGS \
                --post-process scripts/slic3r_massage.py \
                --output "${DIR_PATH}" "$MODEL" \
                --external-fill-pattern concentric

            done
        done
    done
done
Listing 9 Slic3r Prusa Edition run script
1
2
3
#!/bin/sh
/Applications/Slic3r.app/Contents/MacOS/Slic3r --no-gui --print-center 125,105 $*

Listing 10 Slic3r Prusa Edition postprocessing script
  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
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/python
import sys
import re
import os

parmEstimatedHours = ''
parmEstimatedMinutes = ''
parmEstimatedSeconds = ''
parmNotes = ''

sourceFile=sys.argv[1]

# Read the ENTIRE g-code file into memory
with open(sourceFile, "r") as f:
    lines = f.readlines()

    coordMinX = 9999.9
    coordMaxX =0.0
    coordMinY = 9999.9
    coordMaxY = 0.0
    currLine = 1
    parsing = False

for line in lines:
    currLine += 1
    parts = line.split(';', 1)
    if len(parts) > 0:
        # Parse command
        command = parts[0].strip()
        if len(parts) > 1:
            # Parse comments
            comment = parts[1].strip()

        # Track extruder movement ranges
        if not re.search(";", comment):
            # Limit track to print movements, avoid start and end gcode blocks
            if re.search("move to first skirt point", comment):
                #if not parsing:
                #    print("Start extruder movement parsing at line %d with %s" % (currLine, comment))
                parsing = True
            if re.search("PURGING FINISHED", comment):
                #print("Stop extruder movement parsing at line %d with %s" % (currLine, comment))
                parsing = False
        if parsing:
            stringMatch = re.search ('^G[01].*X([0-9.]+)', command)
            if stringMatch:
                val = float(stringMatch.group(1))
                # print("%s: X is %d" % (currLine, val))
                if(val < coordMinX):
                    coordMinX = val
                if(val > coordMaxX):
                    coordMaxX = val
            stringMatch = re.search ('^G[01].*Y([0-9.]+)', command)
            if stringMatch:
                val = float(stringMatch.group(1))
                if(val < coordMinY):
                    coordMinY = val
                if(val > coordMaxY):
                    coordMaxY = val

        # Include files
        includeFile = re.search('#INCLUDE\s+(.*)', comment)
        #if includeFile:
        #    outputFile.write(' -- Would include '+includeFile.group(1)+' stuff here')

        # Parse estimated print time
        stringMatch = re.search ('^notes = (.*)', comment)
        if stringMatch:
            parmNotes = stringMatch.group(1).strip()
        stringMatch = re.search('estimated printing time \(normal mode\) =.* ([0-9]+)h.*', comment)
        if stringMatch:
            parmEstimatedHours = stringMatch.group(1)+'h'
        stringMatch = re.search('estimated printing time \(normal mode\) =.* ([0-9]+)m.*', comment)
        if stringMatch:
            parmEstimatedMinutes = stringMatch.group(1)+'m'
        stringMatch = re.search('estimated printing time \(normal mode\) =.* ([0-9]+)s.*', comment)
        if stringMatch:
            parmEstimatedSeconds = stringMatch.group(1)+'s'
        # Parse print job parameters
        stringMatch = re.search('filament_type = (.*)', comment)
        if stringMatch:
            parmFilamentType = stringMatch.group(1)
        stringMatch = re.search('nozzle_diameter = (.*)', comment)
        if stringMatch:
            parmNozzleDiameter = stringMatch.group(1)
        stringMatch = re.search('layer_height = (.*)', comment)
        if stringMatch:
            parmLayerHeight = stringMatch.group(1)
        # Parse temperatures
        stringMatch = re.search('bed_temperature = (.*)', comment)
        if stringMatch:
            parmBedTemperature = stringMatch.group(1)
        stringMatch = re.search('first_layer_bed_temperature = (.*)', comment)
        if stringMatch:
            parm1stLayerBedTemperature = stringMatch.group(1)
        stringMatch = re.search('temperature = (.*)', comment)
        if stringMatch:
            parmTemperature = stringMatch.group(1)
        stringMatch = re.search('first_layer_temperature = (.*)', comment)
        if stringMatch:
            parm1stLayerTemperature = stringMatch.group(1)

destFile = re.sub('\.gcode$','',sourceFile)
if parmNotes:
    destFile = destFile + ' ('+parmNotes+')'
#destFile = destFile + ' S3r'
if parmFilamentType:
    destFile = destFile + ' '+parmFilamentType
if parmLayerHeight:
    destFile = destFile + ' '+parmLayerHeight
if parmNozzleDiameter:
    destFile = destFile + 'X'+parmNozzleDiameter
if parm1stLayerTemperature:
    destFile = destFile + ' '+parm1stLayerTemperature
if parmTemperature:
    destFile = destFile + '-'+parmTemperature
if parm1stLayerBedTemperature:
    destFile = destFile + ' '+parm1stLayerBedTemperature
if parmBedTemperature:
    destFile = destFile + '-'+parmBedTemperature
if parmEstimatedHours or parmEstimatedMinutes or parmEstimatedSeconds:
    destFile = destFile + ' '
    if parmEstimatedHours:
        destFile = destFile + parmEstimatedHours
    if parmEstimatedMinutes:
        destFile = destFile + parmEstimatedMinutes
    if parmEstimatedSeconds:
        destFile = destFile + parmEstimatedSeconds
destFile = destFile + '.gcode'
#print('Writing to %s' % re.sub('.*/','',destFile))

with open(destFile, "w") as of:
    of.write('; Minimum X = '+ str(coordMinX)+'\n')
    of.write('; Maximum X = '+ str(coordMaxX)+'\n')
    of.write('; Minimum Y = '+ str(coordMinY)+'\n')
    of.write('; Maximum Y = '+ str(coordMaxY)+'\n')
    for lIndex in xrange(len(lines)):
        oline = lines[lIndex]
        #if ("G0" in line or "G1" in line) and ("E" in line) :
        #    line = re.sub("E","B",line)
        of.write(oline)

of.close()
f.close()

os.remove(sourceFile)
Batch build directory structure

Fig. 17 Batch build directory structure

Batch output directory structure

Fig. 18 Batch output directory structure

Contact and feedback

You can find me on the Prusa forums or Reddit where I lurk in many of the 3D printing-related subreddits.

Last updated on Nov 03, 2018