-
Notifications
You must be signed in to change notification settings - Fork 112
Setting Up Templates and Calculators
Photonic3D can take STL files and print them directly, without needing to process them in advance with Creation Workshop or some other slicing tool.
To do so requires some setup, and this page describes that process.
In a generalized sense, resin printing involves a chain of alternating exposure and lift upwards and upwards until the part is completed. So when you print, you need CWH to expose a layer, then do the lifting necessary for the printer, expose the next layer, lift, etc... until the part is done.
CWH knows how to slice and display individual slices to the projector, but it doesn't know how your printer wants to perform lifting. Most printers in the sub-$5000 range are controlled over a USB serial connection using G-code. However, different printers need different G-code commands to complete the lifting necessary. For instance, the Muve3D (and many other tilt tray based printers) has a lift sequence involving the tray tilting mechanism to release the part of the vat and a slight lift in the Z axis to bring the part into position for the next layer. This tilt sequence is controlled by the M650
(to configure the parameters) and M651
(to execute the tilt) commands. The orchestration of the motors to tilt and untilt is handled internal to the firmware.
Some printers have other types of lift and separation mechanisms, for example they may utilize passive peeling or be top-down. They may only use the standard G1
commands and have the software explicitly control the different motors on the printer, perhaps executing a G1 X10 Z0.1
to slide a tray and lift the build plate, followed by G1 X-10
or G1 X0
to bring the build plate back to printing position, depending on whether the firmware is set in relative (G91
) or absolute (G90
) mode.
If you have an M650/M651 controlled printer, then the default CWH Machine Template is already configured and you can skip ahead to the Slicing Calculators section.
If you have a printer with a custom G-code lift sequence, you will need to figure out the commands for your printer.
In this example, I'll be setting up a G-code template for a Kudo3D Titan 1, which has a lift sequence that looks like:
This sequence will need to perform:
- Lift the amount necessary to passively peel the part off the vat, and do so slowly
- Lower back into position for the next layer, and do so quickly
- Delay a few seconds to allow the resin to settle
Let's take a look at what the templates give us and see what we'll need to change.
The Non-CWS GCode Templates are freemarker templates that specify what G-code will be output at various points of the print, with macros that will be filled in with values during the actual print sequence. Thus, what you put into these fields will mostly be output as-is, but sections inside of ${...}
will be substituted with a named variable or computation as we'll see in the examples.
The G-code templates are:
- Header - G-code execute once at the beginning of the print
- Pre-slice -
- Z Lift Sequence - G-code executed to perform lifting after exposure
- Footer - G-code executed once at the end of the print
- Shutter - Optional G-code to operate a shutter to block light when not exposing
- Set Z Lift Speed - G-code executed before lifting to set the lift speed
- Set Z Lift Distance - G-code executed before lifting to set the lift distance
Within each of the templates, there are a number of macros and variables you can use:
-
${ZLiftDist}
- The amount you need to lift for this layer, based on the output of the Z Lift Distance function in Slicing Calculators. This is typically in millimeters. -
${ZLiftRate}
- The speed to perform the lift at, based on the output of the Lift Speed function in Slicing Calculators. You should be careful of the units that the function outputs. TheM650 S###
command interprets the S (speed) value as speed in mm/second. TheG1 F###
commands interprets the F (feedrate) value as speed in mm/minute. Being off by a factor of 60 will alternatively make your prints take a lifetime to complete or tear them apart. Make sure that the Lift Speed function's output range is correct when fed to the G-code command you are using. -
${ZLayerThickness}
- The Slice Height specified under Ink Configuration -
${ZDir}
- 1/-1 for up/down -
${(ZLayerThickness * ZDir)}
- A mathematical computation done between two or more variables. You can use more parenthesis and common math operators like +-/* to perform the computation you need.
The Test button next to each field will try to evaluate the template and display you an example of the output where all of variables are zero.
Based on this, what do we need for the example lift sequence for our printer? Let's break down each template step by step.
The default header is the following:
G21 ;Set units to be mm
G91 ;Relative Positioning
G28 ; Home Printer
M650 D${ZLiftDist} S${ZLiftRate} P0; CWH Template Preferences
M17 ;Enable motors
You'll want to look at those commands line by line and see if they are appropriate for your printer.
My example Titan 1 machine has no endstops and thus can't be homed with G28
, so we'll need to take that out. Instead, users need to use the G92 command to set software zero, and alternate G92 and G1 to nudge the printer down to the point where the build plate is flush against the vat. So we'll either want to put in a G90
to switch to absolute mode followed by G1 Z0 F300
to quickly move down to Z=0, or put in nothing and expect the user to go to the right level before printing.
M650 is the macro for integrated peeling. The Titan 1 doesn't have this macro, so we'll need to take it out.
Since I feel somewhat uncomfortable with non-user-initiated moves, I'll settle on the following G-code:
G21 ;Set units to be mm
G91 ;Relative Positioning
M17 ;Enable motors
The default Z lift sequence template is:
M651; Do CWH Template Peel Move
G1 Z${((LayerThickness) * ZDir)}
For the Titan 1, we're going to need to make some significant changes since we need to make a couple of different moves at different speeds. I need to think carefully here, because if I am printing with a 0.1mm layer height and start at Z=0 with a 10mm lift, I need to end up at Z=0.1, which is higher than where I started by the amount of the layer height.
I'll start conservative and take the layer height lift first and then do a slow lift and fast drop of the full lift distance:
G1 Z${LayerThickness} F${ZLiftRate} ; lift by layer height
G1 Z${ZLiftDist} F${ZLiftRate)} ; lift sequence distance and height
G1 Z-${ZLiftDist} F180 ; lower at fixed 180mm/m speed
G4 S2 ; Dwell to make CWH wait until sequence is done until proceeding, plus 2 seconds for settling
Then, using the math expressions that I'm allowed to use inside the macros, I can optimize this to the following:
G1 Z${ZLiftDist} F${ZLiftRate} ; lift sequence distance and height
G1 Z-${(ZLiftDist - LayerThickness)} F180 ; lower, minus layer thickness, at fixed 180mm/m speed
G4 S2 ; Dwell to make CWH wait until sequence is done until proceeding, plus 2 seconds for settling
Make particular note of the G4 S2
- this is very important. By default movement commands like G0 and G1 are buffered, meaning the firmware will acknowledge the processing of the command before the movement actually occurs. That means that CWH will keep on going and expose the next layer while the motors may still be in the middle of a lift sequence! The G4
command is blocking, and will not acknowledge the command until all buffered moves are processed, plus the delay specified by the P
or S
parameters. This ensures that CWH waits until all of the lift moves are done before proceeding to the next layer.
The default footer template is:
M18 ;Disable Motors
There's no reason to change this, no special end-of-print activity is necessary for most printers.
This is blank by default. If you printer has a shutter, then you'll need to add the commands to control the shutter. In some cases this might be an M280
command if the shutter is servo based, or your shutter may be connected to one of the axis stepper controls.
The default Set Z Lift Speed template is:
M650 D${ZLiftDist} S${ZLiftRate}
Which sets both the lift speed and distance prior to a lift for M650/651 based printers. It is possible to set speed separate from movement in a G1
based lift sequence by doing a G1 F${ZLiftRate}
here and then omitting the F parameter in the lift sequence.
However in the case of my Titan 1 example, I use different speeds and distances for lift and lower, so I use the F parameter inline. Thus, for my case, I will make this section blank.
The default Set Z Lift Distance template is:
M650 D${ZLiftDist} S${ZLiftRate}
which is the same as the Set Z Lift Speed template. For the same reason as above, I will also make this blank for my example.
Now comes the math. If we had build plates with extremely powerful adhesion, vats with zero adhesion, and resin that cured totally hard with just a little light exposure yet no light bleed or overcure, we could use easy static values for all of these. Unfortunately, we live in a physically and chemically imperfect world and need a variety of variable factors relating to layer thickeness, layer height, and slice surface area in order to get a decent print out. A discussion of all of these factors is beyond the scope of this document, so as I proceed through each calculator, I will simply state the features that my example printer needs (which you will need to figure out on your own for your printer) and then describe the process of creating a function that has the necessary numerical properties.
First and foremost, how do these functions work? In math class, you might recall that a function takes some parameters as input and then returns a value as an output. Yet in these calculator functions, there are no clear inputs or outputs, just a bunch of JavaScript code. Basically, the input parameters to our functions are all implicit variables, and then the value of the last line of the function is the output value.
The implicit variables that we can use are:
-
$CURSLICE
- layer number of the current slice -
$buildAreaMM
- surface area of the current slice -
$NumFirstLayers
- value set for Number Of First Layers under Ink Configuration -
$FirstLayerTime
- value set for Exposure Time For First Layers under Ink Configuration -
$LayerTime
- value set for Exposure Time under Ink Configuration
We can use any of these variables in the function we set for each function. Let's go through them one by one.
This is the function that will output the value to ${ZLiftDist}
that we use in GCode templates, and should represent the number of mm of Z lift we need. In my example Titan 1, all of the peeling is provided by lift, and the more surface area we have the more lift we need. Since CWH already provides the surface area, we just need to provide the function. From experience, I know that I want about 8mm lift for the initial layers and then at least 2mm of lift for the part. Also from experience, I know that when when the surface area is about 2880 mm2 I want 7mm of lift, and probably need more when the surface area is even bigger. Thus, I'll need a two part function that is 9mm for the initial layers, then a linear function that starts at 2 with a slope of 5/2880.
The default function is:
var value = 9.0;
if ($CURSLICE > $NumFirstLayers) {
value = 3.5555555555555420e+000 * Math.pow($buildAreaMM,0) + 4.3333333333334060e-003 * Math.pow($buildAreaMM,1) + 1.1111111111110492e-006 * Math.pow($buildAreaMM,2);
}
value
Which provides a framework for this by setting a value for the first layers, and then a degree 2 polynomial function. I don't really need the square term but do need some different coefficients and a different initial value, so I'll use:
var value = 8.0;
if ($CURSLICE > $NumFirstLayers) {
value = 2.0 + (5.0/2880) * Math.pow($buildAreaMM,1);
}
value
That value
at the end is important, since the evaluated value of the last line is the return value of the function.
If you click the Test button, the UI will graph the function to visualize its output:
The X axis of the graph is the value of $buildAreaMM
going from 0 to 1875. The Y axis is the amount of resulting lift. There are two lines, the darker line represents the output when $CURSLICE
== $NumFirstLayers
and the lighter line is the output when $CURSLICE
== $NumFirstLayers
+ 1 so that we can see the behavior when we're in the first layers and when we're in the rest of print.
These graphs show the behavior I'm looking for. During the first layers, the lift is a static 8mm. Then during the remainder of the print, we start at a minimum of 2mm lift. 2880mm2 is off the chart, but since the function is linear, I would expect that half that amount (1440) to be at 4.5mm, which looks about right on the chart.
In this case, the amount of lift I use is unbounded, but the size of my build area is obviously limited, so that will naturally bound the top end of the scale. In other cases, I may need both a minimum and a maximum and thus need to use a different function. We'll soon see an example of that.
The output of this function will be used for the ${ZLiftRate}
macro in the G-code templates. This number is a bit trickier because we really need to understand what we need here. The default value for this function is:
var value = 0.25;
if ($CURSLICE > $NumFirstLayers) {
value = 4.6666666666666705e+000 * Math.pow($buildAreaMM,0) + -7.0000000000000184e-003 * Math.pow($buildAreaMM,1) + 3.3333333333333490e-006 * Math.pow($buildAreaMM,2);
}
value
Which is also a degree 2 polynomial. However, it outputs a fairly small value because it is designed to feed M650
which takes speed as mm/s. Recall that for my example printer, I will be executing lift with G1
, whose F
parameter is in mm/min. In my experience, I need lift to be 15mm/min at the initial layers, and then vary between 15mm/min for large surface areas, up to 40mm/min for small surface areas. I don't want these functions to be unbounded, in that I can reach 40mm/min when my surface area is at 100mm2, not necessarily at zero. On the other end, I want to slow down to 15mm/m once I reach around 4000mm2 or so and not go any slower. Ideally, I'd like to be at around 25mm/min at around 2500-3000mm2. An S-curve fits the bill, whose mathematical formula is the generalized logistic function.
The logistic function has a lot of features and going into that mathematical depth is beyond the scope of this article, but you can research it on Wikipedia and Wolfram Mathworld if you are interested. I'm just going to have to skip to the solution of:
var value = 15.0;
if ($CURSLICE > $NumFirstLayers) {
value = 15.0 + 30.0 / (1.0 + Math.pow(2.718,-(2.0 - 0.001*$buildAreaMM)));
}
value
The Test button shows this curve from 0 to 1875 mm2:
Wolfram Alpha graphs this function to a larger range like:
The Titan 1 has a big build area and I needed to see the graph out to 10,000mm2, so I needed to plot it in Wolfram Alpha with different parameters before bringing it into CWH. The overall method of arriving at this is akin to doing a logistic regression of my experience. I believe what I'm looking for theoretically is a formula that keeps the force at any moment of separation constant with respect to varying surface area by changing the speed of lift.
CWH can slice and print, but does not allow you to otherwise manipulate the STL file. That means that you need to add supports and properly orient your part in another tool, like MeshMixer or Tinkercad.
This is the easy part. Now that everything is set up, go to the Printables tab and click Print.