Ages ago I bought an EleksMaker plotter. The basic idea is to use a couple of stepper motors to move a pen across paper, and a servo to lift it. That’s enough to draw things!
I think it’s fair to say the EleksMaker is at least heavily inspired by the AxiDraw1 from Evil Mad Scientist Labs2, though it’s much cheaper and less polished. Perhaps inevitably I think the documentation has rotted since the device was released: these are my brief notes on how to get it all working.
These days, I think most people building the Eleksmaker tend to install a laser rather than a pen, which can be rather confusing. For example the software goes to some length to avoid firing the laser continuously at the same spot because it might cause a fire.
Hardware
The machine came as a kit which was fairly easy to build. Two static stepper motors drive a belt, which is looped in a clever way to move the pen arbitrarily in a plane. In CNC circles it’s refered to as a CoreXY3 design.
Vertical motion is controlled by a servo. There’s not a direct linkage between the servo arm and the pen: rather the servo arm can lift the pen away from the paper, but when the servo arm swings out of the way gravity pulls the pen down. In practice I found this didn’t work very well, but adding a spring to pull the pen down solved the problem.
It is tempting to replace this part of the mechanism. However, it’s not entirely trivial: it is hard to align the machine such that it moves the head parallel to the paper, so you either need some sort of springiness in the pen holder, or use a pen which can tolerate the variation. This is essentially the bed-levelling problem well-known to the owners of 3D-printers, but tolerances here are much less precise.
When connecting a servo with the usual brown-orange-yellow colours, the brown goes closest to the micro-USB port.
Firmware
The EleksMaker is controlled by an Arduino Nano running grbl4. Both firmware and hardware are essentially obsolete but are still available. Oddly most of the grbl code lives in a library: the grblUpload.ino file which you compile to make the firmware has but a single include:
#include <grbl.h>
Configuring grbl
By default grbl assumes that there are independent X- and Y-axis motors. To support the CoreXY geometry it’s enough edit config.h and
#define COREXY
Firmware size
When compiled my grbl firmware was 31,320 bytes long, which is too big for the default Arduino Nano settings (30,720 bytes). Annoyingly, the problem isn’t with the bootloader, but rather with the fuses that set how much flash is available for user (i.e. non-bootloader) code. This issue5 comes up repeatedly on the Arduino forum
So the first step in uploading firmware to a new board is to fix the fuses: the easiest way to do this is to burn a new bootloader pretending that the Nano is a Uno. The Uno has the same processor but sane fuse and bootloader settings. Happily you only have to entertain this deception once.
Servo control
Originally in grbl the servo output was used to control the speed of a spindle on a CNC machine. As mentioned above, these days people typically put a laser, rather than a pen, on the plotter, and use the servo output to modulate its intensity. In both cases, the servo output is a PWM signal, which is good for RC servo control, but the parameters need to be changed.
To control a RC servo6 we need a frequency of 50Hz with a pulse length of 1–2ms, which we can achieve by changing this definition (which occurs twice) in cpu_map.h:
#define SPINDLE_TCCRB_INIT_MASK ((1<<CS22)|(1<<CS21) | (1<<CS20))
// 1/1024 prescaler ~60Hz for servo
Once patched thus, on-times of 19–31 move the servo to sensible places.
grbl implements safety interlocks which couple the servo output to the motion. This makes sense when driving a spindle or laser, but can be confusing when you’re just moving a pen up and down.
grbl settings
grbl has a number of configuration parameters. These are the settings I’m using:
>>> $I
[VER:1.1h.20190825:]
[OPT:VC,15,128]
ok
>>> $$
$0 = 10 (Step pulse time, microseconds)
$1 = 25 (Step idle delay, milliseconds)
$2 = 0 (Step pulse invert, mask)
$3 = 1 (Step direction invert, mask)
$4 = 0 (Invert step enable pin, boolean)
$5 = 0 (Invert limit pins, boolean)
$6 = 0 (Invert probe pin, boolean)
$10 = 1 (Status report options, mask)
$11 = 0.010 (Junction deviation, millimeters)
$12 = 0.002 (Arc tolerance, millimeters)
$13 = 0 (Report in inches, boolean)
$20 = 0 (Soft limits enable, boolean)
$21 = 0 (Hard limits enable, boolean)
$22 = 0 (Homing cycle enable, boolean)
$23 = 0 (Homing direction invert, mask)
$24 = 25.000 (Homing locate feed rate, mm/min)
$25 = 500.000 (Homing search seek rate, mm/min)
$26 = 250 (Homing switch debounce delay, milliseconds)
$27 = 1.000 (Homing switch pull-off distance, millimeters)
$30 = 255 (Maximum spindle speed, RPM)
$31 = 0 (Minimum spindle speed, RPM)
$32 = 1 (Laser-mode enable, boolean)
$100 = 100.000 (X-axis travel resolution, step/mm)
$101 = 100.000 (Y-axis travel resolution, step/mm)
$102 = 100.000 (Z-axis travel resolution, step/mm)
$110 = 5000.000 (X-axis maximum rate, mm/min)
$111 = 5000.000 (Y-axis maximum rate, mm/min)
$112 = 5000.000 (Z-axis maximum rate, mm/min)
$120 = 200.000 (X-axis acceleration, mm/sec^2)
$121 = 200.000 (Y-axis acceleration, mm/sec^2)
$122 = 200.000 (Z-axis acceleration, mm/sec^2)
$130 = 200.000 (X-axis maximum travel, millimeters)
$131 = 200.000 (Y-axis maximum travel, millimeters)
$132 = 200.000 (Z-axis maximum travel, millimeters)
ok
>>> $G
[GC:G0 G54 G17 G21 G90 G94 M5 M9 T0 F0 S0]
ok
Some of these set scale sizes. For example $30 and $31 control the mapping between the spindle speed set in G-code and the value poked into the PWM peripheral.
G-code
To control the plotter we send it G-code7. G-code covers all sorts of machines, but we only need a small subset here.
Preamble
G28 ; XXXXX
G53 ; XXXXX
M03 S17 ; lift the pen
G1 X0 Y0 F1000 ; move to the origin and set default speed
Epilogue
M03 S17 ; lift the pen
G28 ; XXXXX
Movement
This moves us to (123,45) measured in mm at the default speed.
G1 X123 Y45
Pen control
To move the pen up:
M03 S17 ; lift the pen
and to move it down:
M03 S31 ; drop the pen
Set coordinate origin
Sometimes it’s helpful to move the coordinate origin. Usually I want to tell the machine to use the current position as the origin in future.
G92 X0 Y0 ; Set (0,0) to current location
Useful links
Universal G-code Sender
To control the machine the Universal G-code Sender8 was very useful.
Other Eleksmaker articles
Jan Delgado wrote some notes9 for an A3 laser engraver.
A Danish FabLab wrote some notes10 about a pen plotter. I found them useful but not all their comments matched my experience (e.g. there wasn’t an A1 command in my firmware).
Generating G-code
There are numerous tools to convert SVG to G-code. Here are a few I found useful:
Dlacko wrote juicy-gcode11 in Haskell.
There are lots of python tools on PyPI12. I found several helpful for code snippets, but didn’t have complete success with any of them: that was probably due to my incompetence though.
Plugins for Inkscape can generate G-code. Johny Mattsson has the best fork13.
References
- 1. https://axidraw.com
- 2. https://www.evilmadscientist.com
- 3. https://en.wikipedia.org/wiki/CoreXY
- 4. https://github.com/gnea/grbl/wiki
- 5. https://github.com/arduino/ArduinoCore-avr/issues/308
- 6. https://en.wikipedia.org/wiki/Servo_%28radio_control%29
- 7. https://en.wikipedia.org/wiki/G-code
- 8. http://winder.github.io/ugs_website/
- 9. https://github.com/jandelgado/eleksmaker_a3
- 10. https://fablab.ruc.dk/using-a-grbl-powered-drawing-machine-e-g-eleksmaker/
- 11. https://github.com/domoszlai/juicy-gcode
- 12. https://pypi.org/search/?q=gcode
- 13. https://github.com/jmattsson/eleksmaker-inkscape-extension/blob/master/README.md