</>CoderDIY
← Back to list
Article

Build a Mini CNC Pen Plotter with Arduino

Author: CoderDIY··Đọc bằng tiếng Việt
🖊️

A “real” CNC machine or laser cutter is expensive and needs dedicated space, but a mini CNC pen plotter — swapping the cutting head for a pen — fits on a desk with very cheap parts. It’s a great foundation project for understanding how a CNC machine moves precisely along X-Y coordinates using G-code commands, before investing in an actual CNC mill or laser cutter.

How it works

  1. The Arduino runs GRBL firmware (open-source, purpose-built for CNC control), receiving G-code commands over Serial from a computer — each G-code line describes a motion (e.g. “move to coordinate X=50, Y=30” or “raise/lower the pen”).
  2. GRBL translates each move command into a sequence of pulses driving the two stepper motors (X and Y axes), computing speed and acceleration so the drawn line moves smoothly instead of jerkily.
  3. The pen sits on a simple lift mechanism (usually a small servo) — lowered while drawing a stroke, raised while moving between disconnected strokes.
  4. On the computer, software like Inkscape (with a G-code export plugin) or image-to-vector tools convert an image or handwriting into a G-code file, then a control program (Universal G-code Sender or similar) streams each command line by line.
<rect x="10" y="20" width="140" height="60" rx="10" class="box" />
<text x="80" y="45" text-anchor="middle" class="label">Vector file</text>
<text x="80" y="62" text-anchor="middle" class="label">(Inkscape → G-code)</text>

<rect x="170" y="20" width="140" height="60" rx="10" class="box" />
<text x="240" y="45" text-anchor="middle" class="label">Stream over Serial</text>
<text x="240" y="62" text-anchor="middle" class="label">(UGS)</text>

<rect x="330" y="20" width="140" height="60" rx="10" class="box" />
<text x="400" y="45" text-anchor="middle" class="label">GRBL on Arduino</text>
<text x="400" y="62" text-anchor="middle" class="label">computes motor pulses</text>

<rect x="490" y="20" width="140" height="60" rx="10" class="box" />
<text x="560" y="45" text-anchor="middle" class="label">2 stepper motors</text>
<text x="560" y="62" text-anchor="middle" class="label">+ pen-lift servo</text>

<line x1="150" y1="50" x2="170" y2="50" class="arrow" />
<line x1="310" y1="50" x2="330" y2="50" class="arrow" />
<line x1="470" y1="50" x2="490" y2="50" class="arrow" />

<text x="320" y="130" text-anchor="middle" class="sublabel">GRBL replies "ok" after each command so UGS sends the next line</text>
Data flow from the design file to physical motion: vectorize, export G-code, stream each command over Serial, GRBL drives the motors.

Parts list

  • An Arduino Uno (flashed with GRBL firmware, well-supported by common CNC shields)
  • A CNC Shield v3 (or an equivalent hand-wired setup) carrying 2-3 A4988 stepper drivers
  • 2 NEMA17 stepper motors for the X and Y axes
  • A small servo (SG90) for raising/lowering the pen
  • Mechanical frame: linear rails, GT2 timing belt, or a pre-made DIY CNC plotter kit (widely available from electronics part suppliers)
  • A 12V power supply for the CNC Shield/motor drivers
<rect x="30" y="90" width="150" height="90" rx="10" class="box" />
<text x="105" y="140" text-anchor="middle" class="label">Arduino Uno</text>

<rect x="240" y="90" width="150" height="90" rx="10" class="box" />
<text x="315" y="130" text-anchor="middle" class="label">CNC Shield</text>
<text x="315" y="148" text-anchor="middle" class="label">+ A4988 drivers</text>

<rect x="450" y="30" width="150" height="70" rx="10" class="box" />
<text x="525" y="70" text-anchor="middle" class="label">X/Y stepper motors</text>

<rect x="450" y="150" width="150" height="70" rx="10" class="box" />
<text x="525" y="190" text-anchor="middle" class="label">Pen-lift servo</text>

<line x1="180" y1="135" x2="240" y2="135" class="wire" />
<text x="210" y="125" text-anchor="middle" class="sublabel">shield plugs in directly</text>

<line x1="390" y1="115" x2="450" y2="65" class="wire" />
<text x="420" y="95" text-anchor="middle" class="sublabel">2x coil pairs</text>

<line x1="390" y1="155" x2="450" y2="185" class="wire" />
<text x="420" y="175" text-anchor="middle" class="sublabel">PWM</text>
The CNC Shield plugs directly onto the Arduino Uno, driving the X/Y stepper motors through A4988 drivers; the pen-lift servo connects to a spare PWM pin.

Sample code

Flash GRBL firmware onto the Arduino through the Arduino IDE (just download the GRBL source, open the example, and upload — no need to write firmware from scratch). Once flashed, you can stream G-code directly via the Serial Monitor or a simple Python script to test it:

import serial
import time

PORT = "COM3"  # change to your port
BAUD = 115200

gcode_lines = [
    "G21",           # units in mm
    "G90",           # absolute positioning
    "G0 X10 Y10",    # rapid move to the starting point, no drawing
    "M3 S0",         # lower the pen (servo, depends on GRBL config)
    "G1 X50 Y10 F1000", # draw a straight line to X=50,Y=10 at 1000mm/min
    "G1 X50 Y50",
    "G1 X10 Y50",
    "G1 X10 Y10",
    "M5",            # raise the pen
]

ser = serial.Serial(PORT, BAUD, timeout=2)
time.sleep(2)  # wait for GRBL to boot

for line in gcode_lines:
    ser.write((line + "\n").encode())
    while True:
        response = ser.readline().decode().strip()
        if response == "ok":
            break
        elif response:
            print("GRBL:", response)

print("Finished drawing the square!")
ser.close()

Common pitfalls

  • Not calibrating $100/$101 (steps/mm) in GRBL produces drawings at the wrong scale compared to the original design — measure the actual distance moved against the commanded distance to recompute these settings for your specific mechanics.
  • A loose GT2 timing belt causes step loss on sudden direction changes, making the drawing drift out of alignment over time — keep the belt tensioned just enough, not so tight it overloads the motor, and not so loose it slips.
  • The pen-lift servo reacting out of sync with movement commands leaves stray marks while traveling to the start of a new stroke — add a small delay (G4 P0.2 — pause 0.2 seconds) after raise/lower commands before continuing to move.
  • Streaming the entire G-code file at once without waiting for “ok” overflows GRBL’s Serial buffer, causing the plotter to drop commands or stall mid-drawing — always send one line at a time and wait for “ok,” as shown in the sample code.

Extensions

Swap the pen for a low-power laser diode module to lightly engrave wood/leather (mind eye safety and fire risk), upgrade to three axes to turn it into a low-power mini mill, or check out the self-balancing robot with PID control post for a deeper look at precise real-time motor control loops.

Tags:#arduino#cnc#robotics

Comments