3-D AxiDraw Plotting Hack - lurkertech.com
lurkertech.com 3-D AxiDraw Plotting Hack

3-D AxiDraw Plotting Hack

What Is It?

Did you know that the AxiDraw plotter from Evil Mad Scientists can actually give you more fine control of the vertical (Z) direction than just "pen up" vs. "pen down"? And that, with some hackery, you can actually adjust the vertical height dynamically whilst simultaneously moving in the X and Y directions?

This allows you to create interesting variable-stroke-thickness artistic effects with a flexible brush pen:

Notice how the line thickness varies as we raise and lower the pen during strokes:

Like this hack and/or want to do it yourself?

PLEASE HELP by clicking "watch" on our EggBot Firmware Enhancement Request (more info below) to make this AxiDraw feature even more powerful for everyone.

What Could I Use This For?

We can envision many 3-D applications:

Any other cool ideas or have you done a hack with this technology? Let us know at axidraw@lurkertech.com.

How Does it Work?

The AxiDraw pen holder uses a TowerPro SG90 servo which, while nowhere near as accurate as the XY steppers, can reliably give you around 10-20 positions across its vertical range, which is definitely enough for interesting artistic effects.

The servo in the plotter only lifts up the pen (it does not push the pen down) so the weight of the pen must be sufficient to do whatever drawing/scribing you want but not too heavy to overload the servo.

We used a flexible brush pen with a built-in (replaceable) ink cartridge, available cheaply at art supply stores, similar to this one:

While making the drawings seen in the video above, I was worried that (given the hacks in the software required, as explained below) the pen would not move fast enough. But it turns out I actually had to program in massive delays in order to give the pen time for its brush to get replenished with ink flowing down into the bristles!

How Can I Do It?

You can do this too if you write a program (Python, Perl, whatever) to directly command the AxiDraw using its fairly-well-documented EBB command set.

But from the (unusually good) dox it is still not clear how to make the device move in XY and Z simultaneously. That is documented in the comments of our github feature request here:

EggBot Firmware Enhancement Request

As you can see, there is sort of a way to do it now, within limitations, but it is not enough to do really expressive curved moves in 3-D.

We're asking the very nice firmware hackers behind the EBB board (the board that drives AxiDraw and other products) for a much simpler, cleaner, nicer way to accurately control all 3 dimensions with perfect sync. They seem into it!

PLEASE HELP by clicking "watch" on our enhancement request page if this feature interests you.

That will help the very nice EmbeddedMan and oskay prioritize their feature requests and let them know how people are using their awesome control board.

Version note: the method on this page worked for us on our AxiDraw with firmware revision 2.5.1 (you can get the current firmware rev from the "V" EBB command and it's probably shown somewhere in high-level software like Inkscape that connects to AxiDraw too). We do not know if the same sequence of EBB commands works on other revs or not. We did notice that after 2.5.1, the EBB folks fixed a bug related to whether or not a certain command waits for another command to finish, so it is conceivable that the change might affect our 3-D hack, but we haven't tried any other firmware revs yet. We are very much looking forward to the official 3-D command proposed in our enhancement request, which will make everything clean and easy to support.

Sample Code

Here is some Perl code that outputs the AxiDraw commands to do the 3-D control. This code uses the documented behavior of the servo to properly generate a command that will move between the specified points in X, Y, and Z linearly in the specified amount of time.

This is a primitive hack: there is no path planning, bandwidth restriction, smoothing, or anything fancy like that. Use at your own risk!

This code assumes that there is a computer somewhere connected to the AxiDraw and running our simple AxiDraw Server, but you can simply plug in your own code to send commands directly to AxiDraw assuming it is hooked up to your local machine. To send commands to AxiDraw, if you are a Python fan, check out fogleman's cool "Unofficial Python library" at https://github.com/fogleman/axi.

This code draws the starburst-shaped pattern above.

Code is also available for download here.

# 3daxi.pl - Plot in X, Y, and Z with AxiDraw Plotter
# - Chris Pirazzi and Paul Haeberli
#
# For details, see https://lurkertech.com/3daxi
#
# Accesses AxiDraw using server described at https://lurkertech.com/axiserver
#
 
use utf8;
use strict;
use English qw( -no_match_vars );
use Carp qw(cluck confess);
use Data::Dumper;

use Math::Trig qw(pi);

use IO::Socket::INET;
 
# auto-flush on socket
$| = 1;
 
# create a connecting socket
my $socket = new IO::Socket::INET (
    PeerHost => 'mycomputer.local', # <-- hostname of server
    PeerPort => '12345',            # <-- port of server
    Proto => 'tcp',
);
die "cannot connect to the server $!\n" unless $socket;
print "connected to the server\n";
 
sub cmd
{
    my $s = shift;

    # data to send to a server
    my $size = $socket->send($s . "\n");
    print "sent [$s]\n";
 
    # receive a response of up to 4096 characters from server
    my $response = "";
    $socket->recv($response, 4096);
    chomp $response;
    print "received response: [$response]\n";
    print "\n";

    #exit if ('OK' ne $response);
}

# servo levels ("min"/"max")
# - SC,5,Z (up) or SC,4,Z (dn)
# - Z in [1,65535] (for Axidraw [7500,28000])
# - controls a pulse width in ns for a servo controller
# - units of 83ns of duty cycle (units of 1/12000th of a second)
#   - e.g. 16000 * 83ns = 1.33ms
# - wider pulse makes the pen go higher in z
# - pulses are sent out each 24ms
#
# servo rate SC,10,R
# - if R=0 then SC,5 takes effect immediately 
#   - new pulse width starts coming out immediately after cmd executes
#   - I guess this means hardware goes as fast as it can
# - if R != 0 then when you issue SC,5
#   - first the hardware outputs the old rate
#   - then every 24ms
#     - the system adds/subtracts R to to get towards the new rate
#     - until enough 24ms periods happened to get to new rate
#   - so R    = height unit change per 24ms
#   - so if we have desired speed S = height unit change per ms
#        R = S*24
#
my $pen_x;
my $pen_y;
my $pen_z;
sub pen_z_to
{
    my $dst_z = shift;
    my $ms = shift;

    my $dz = abs($dst_z - $pen_z);

    my $S = $dz / (0.0 + $ms);

    # choose servo rate $R that will make a $dz change happen in $ms ms
    #
    my $R = $S * 24.0;

    print "dz=$dz ms=$ms S=$S R=$R\n";
    
    $R = int($R);
    
    cmd("SC,10,$R"); # servo rate
    cmd("SC,4,$dst_z"); # pen down pos, default 12000, more higher
    # XXX third arg should be $ms according to dox: CONFUSED
    cmd("SP,1,0"); # pen down
    
    $pen_z = $dst_z;
}

sub pen_up
{
    cmd("SC,10,0"); # servo rate ("quick as posible")
    cmd("SP,0,200"); # pen up (200ms enough for "quick as possible")
}

#cmd("R"); # reset

# set pen up pos nice and high
cmd("SC,5,20000"); # pen up pos, default 16000, more higher

# quickly put pen in known down pos
cmd("SC,10,0"); # servo rate ("quick as possible")
$pen_z = 20000;
cmd("SC,4,$pen_z"); # pen down pos, default 12000, more higher
cmd("SP,1,200"); # pen down (200ms enough for "quick as possible")

# assume xy pos
$pen_x = 0;
$pen_y = 0;

sub move_to
{
    my $x = int(shift);
    my $y = int(shift);
    my $z = int(shift);
    my $ms = int(shift);

    print "===== move to x=$x y=$y z=$z ms=$ms\n";

    print "cur pos pen_x=$pen_x pen_y=$pen_y pen_z=$pen_z\n";

    my $dx = $x - $pen_x;
    my $dy = $y - $pen_y;
    
    print "dx=$dx dy=$dy\n";

    # returns immediately, does not delay next command, move takes $ms
    pen_z_to($z, $ms);

    # returns immediately, does not delay next command, move takes $ms
    cmd("XM,$ms,$dx,$dy"); # move xy

    # ... somehow magically next command delayed by $ms?! HOW???

    $pen_x = $x;
    $pen_y = $y;
}

# centerburst
# starting at 0,0,20000
#
my $orad = 6000;
my $slices = 18;

for(my $out_to_in = 0; $out_to_in < 1; $out_to_in++)
{
    for(my $i=0; $i < $slices; $i++)
    {
        my $phase = ($out_to_in ? 0 : 0.5);
        
        my $ang = 2*pi * (($i + $phase) / $slices);
        
        pen_up();
        
        if ($out_to_in) # thick out to thin in
        {
            move_to($orad + $orad * sin($ang),
                    $orad + $orad * cos($ang),
                    20000,
                    10000);  # let ink flow down
            
            move_to($orad + $orad * sin($ang),
                    $orad + $orad * cos($ang),
                    9000,
                    200);
            
            move_to($orad,
                    $orad,
                    16000,
                    1200);
        }
        else # thick in to thin out
        {
            move_to($orad,
                    $orad,
                    20000,
                5000);  # let ink flow down
            
            move_to($orad,
                    $orad,
                    9000,
                    200);
        
            move_to($orad + $orad * sin($ang),
                    $orad + $orad * cos($ang),
                    16000,
                    1200);
        }
    }
}
pen_up();
move_to(0, 0, 20000, 2000);

$socket->close();

Access AxiDraw Remotely with Our Server

If you like this project, you may like our simple Python-based AxiDraw Server, which lets several people share a plotter remotely over the network.

Thanks

Thanks to Paul Haeberli for use of his AxiDraw and check out his cool projects:

https://twitter.com/graficaobscura

Thanks to EBB board firmware authors EmbeddedMan and oskay for their awesome support.

Submit This SiteLike what you see?
Help spread the word on social media:
Support This SiteI work on this site in my off hours. Please help me to push aside my day job and work on it more by supporting the site in one of these ways:
donate now   Donate Now
Use your credit card or PayPal to donate in support of the site.
get anything from amazon.com
Use this link to Amazon—you pay the same, I get 4%.
get my thai dictionary app
Learn Thai with my Talking Thai-English-Thai Dictionary app: iOS, Android, Windows.
get my thai phrasebook app
Experience Thailand richly with my Talking Thai-English-Thai Phrasebook app.
get my chinese phrasebook app
Visit China easily with my Talking Chinese-English-Chinese Phrasebook app.
get thailand fever
I co-authored this bilingual cultural guide to Thai-Western romantic relationships.
CopyrightAll text and images copyright 1999-2017 Chris Pirazzi unless otherwise indicated.
Submit This Site

Like what you see?
Help spread the word on social media:
Support This Site

I work on this site in my off hours. Please help me to push aside my day job and work on it more by supporting the site in one of these ways:
donate now   Donate Now
Use your credit card or PayPal to donate in support of the site.
get anything from amazon.com
Use this link to Amazon—you pay the same, I get 4%.
get my thai dictionary app
Learn Thai with my Talking Thai-English-Thai Dictionary app: iOS, Android, Windows.
get my thai phrasebook app
Experience Thailand richly with my Talking Thai-English-Thai Phrasebook app.
get my chinese phrasebook app
Visit China easily with my Talking Chinese-English-Chinese Phrasebook app.
get thailand fever
I co-authored this bilingual cultural guide to Thai-Western romantic relationships.
Copyright

All text and images copyright 1999-2017 Chris Pirazzi unless otherwise indicated.