← back

Rotary 3D Printing

Using an Open 5X Voron V0 and Meta Gcode to print spirals and more!

Written by: Tony Lock Posted on: 2024-04-18

Rotary 3d printing

Adding one or more rotary axes to a traditional 3d printer opens up a wide range of opportunities for part design that are not available in traditional 2.5D printing. One of those is printing on a cylindrical or cylinder like object. This is a different way of thinking about constructing objects, for example here is a “propeller” like structure printed on a twisted 6 faced base:

A 6 "bladed" propeller like structure printed on an Open5X converted Prusa Mk3 printer by Freddie Hong

The Open5X project has dramatically reduced the cost of MultiAxis printing, providing a modification path for machines like the Prusa Mk3, the E3D and Jubilee Toolchangers and the Voron V0. More recent work at Loughborough University has further reduced the barrier to entry with a simple 4 axis modification to low cost machines.

As further experiment in multi axis printing, we have been printing on printed cylinders, using toolpaths generated using RepRapFirmware Meta G-code. Meta G-code, designed and implemented by Duet3D is a simple but powerful extension of G-code that allows for programming constructs such as variables, loops and conditional statements. These small but powerful gcode programs can define quite complex behaviour for the machine, or in this case the toolpath for an object, without a slicer or other toolpath generator.

Here is a spiral printed on a printed cylinder using just a few lines of gcode: A green thick spiral being printed as a thick single wall with 12 turns on a black 3d printed cylinder.

The gcode for the printed cylinder is:

var mandrelR=5;
var mH=50;
var lH=0.5
var mStartZ=var.lH
var mRots={var.mH/var.lH}

G1 Y-60 F7000
G1 B0 F2000
G1 Z{var.mStartZ+5} X{var.mandrelR} Y-55 F5000

G1 X{var.mandrelR} Y0 Z1 F5000
G1 Z{var.mStartZ}
G92 C0
G1 C{var.mRots*360} Z{var.mH} E{var.mRots*12} F9
G92 C0
G1 Y-57 F7000

If you don’t want to use the variables to generalise printing cylinders, you could just send:

G1 C36000 Z50 E1200 F9

What is happening is a spiral is being printed from the start Z height, which is set to the “layer height” of 0.5, up to Z 50, with 100 rotations, to print a 50mm high cylinder with the diameter of the X offset, in this case 5mm. This diameter is actually 5mm + the extusion width which in this case is ~0.8mm. The gcode for the spiral printed on the cylinder is:

var pitch=3;
var startX=12
var rots=12
var endX=var.startX+var.pitch*var.rots
var startZ=5.8
var zStep=0.4
var zPasses=50
var eRot=11 
var fr=8

G1 Y-60 F7000
G1 B90 F2000
G1 Z{var.startZ+5} X{var.startX} Y-55 F5000
G1 X{var.startX} Y0 F7000
while iterations<var.zPasses
    set var.startX={var.startX+0.2}
    set var.endX={var.endX+0.2}
    var currentZ=var.startZ+var.zStep*iterations
    var scale=(1+(var.currentZ-var.startZ)/(var.startZ))
    G1 Z{var.currentZ} F5000
    G92 C0 E0
    var dir = (mod(iterations,2)=0)?-1:1
    var currentEndX=((var.dir>0)?var.startX:var.endX)
    G1 C{-360*var.rots*var.dir} X{var.currentEndX}  E{var.scale*var.eRot*var.rots} F{var.fr/var.scale}
    ;echo "C move: "^{-360*var.rots*var.dir}^" xmove: "^var.currentEndX ^" rots: "^var.rots 
    G92 C0 E0

G1 Y-55 F5000

Note the ”eRotCurrent” variable scales the amount of extrusion per layer so that the extrusion/mm is constant as the diameter of the spiral increases. Also in every iteration the spiral is shifted slightly forward to get a slight conical shape.

Adding some conditional statements to the gcode allows us to lean the spiral path forward to start with, then backward while its printing. Getting a “Z” shaped cross section to the spiral. A black spiral that shows a forward lean for the first half, then a backward lean for the second half The “while” block from the above gcode is expanded to:

while iterations<var.zPasses
    if iterations/var.zPasses < 0.5 
        set var.startX={var.startX+0.3}
        set var.endX={var.endX+0.3}
    elif iterations/var.zPasses < 0.6 
        set var.startX={var.startX+0.05}
        et var.endX={var.endX+0.05}
        set var.startX={var.startX-0.3}
        set var.endX={var.endX-0.3}
    var currentZ=var.startZ+var.zStep*iterations
    var eRotCurrent=(1+(var.currentZ-var.startZ)/(var.startZ))*var.eRot
    G1 X{var.startX} Y0 F5000
    G1 Z{var.currentZ} F5000
    G92 C0 E0
    G1 C{-360*var.rots} X{var.endX} E{var.eRotCurrent*var.rots} F7

In the meta gcode examples thus far there has been a move back to the start on each “layer” however there is no reason to do this, it is easy to add a check that switches the direction of the X movement and rotation at each end of the spiral:

var dir=(mod(iterations,2)=0)?1:-1 
var currentX=(mod(iterations,2)=0)?var.endX:var.startX 
G1 C{-360*var.rots*var.dir} X{var.currentX} E{var.eRotCurrent*var.rots} F7

So now the move switches direction of rotation, and changes the X move destination, based on if the iterations of the while loop are even or odd. The “ternary operator: a?b:c” used is a quick shortcut for an “if ‘a’ is true do ‘b’, if its false do ‘c’”

If we just reverse the direction of X movement and not the direction of rotation then we can get two overlapping spirals, one right handed, one left handed. In that case it is better to only increment the Z every second move to get good layer adhesion.

Of course we are not limited to printing spirals. If we only print part way round the cylinder before reversing the direction of rotation, while moving up and down in X we get a wavy pattern line this:

Purple translucent filament printing in a wavy pattern on a black wavy pattern, on a black cylinder. a second wavy pattern is shown below

The spiral is from +45 to -45 degrees in C, so there is space to print a number of them around the cylinder. For this example there are three sets of spirals spaced 120 degrees apart.

The loop for this is three nested while blocks:

var waveAngle=45
var pitch=6
var steps=5
while iterations <3
    G1 C-120 F4000
    G92 C0
    G1 C{var.waveAngle} X{var.startX} Y0 F7000
    G1 E2 F300 ;unretract
    while iterations<var.zPasses
        var currentZ=var.startZ+var.zStep*iterations
        var scale=(1+(var.currentZ-var.startZ)/(var.startZ))
        G1 Z{var.currentZ} F5000
        var dir = (mod(iterations,2)=0)?-1:1 ;evens go forward, odds go backwards
        while iterations<var.steps
            var stepDir = ((mod(iterations,2)=0)?1:-1)
            G1 C{-var.waveAngle*var.dir*var.stepDir} X{-var.pitch*var.dir}  E{var.scale*var.eRot} F{var.fr/var.scale}
        ;echo "C move: "^{-360*var.rots*var.dir}^" xmove: "^var.currentEndX ^" rots: "^var.rots
    G9 E-2 F300
G1 Y-55 F5000

In all these examples, the B axis has been held constant, if we start to rotate the B axis then the tool path can get very complex. Here is a very simple example of a ball being printed by rotating B while moving X and spinning C:

A thin walled purple ball part way through being printed with the build plate tilted relative to the nozzle

At this point we have probably reached whats easy to do in meta gcode for now. It is possible to adapt these examples to use G2/G3 arcs rather than straight lines, but to go further a more powerful tool pathing software, like FullControlGcode is needed.

We hope this example helps you see some of the possibilities of multi axis 3d printing. The Open5X project has already got 5 printer options to choose from, and the simple mod from Loughborough University is very accessible to build. We look forward to seeing your builds and experiments on our forum.

← back