Optimizing My Pen Plotter Workflow with Custom Zsh Scripts (Sovol SO-1 + vpype + juicy-gcode)
If you’ve ever spent more time fighting with G-code settings than actually watching a pen move across paper, you know the "plotting headache." Recently, I’ve been refining the pipeline for my Sovol SO-1. The goal was simple: move from a raw SVG to a finished plot with as little friction as possible. By leveraging AI to help script the "boring stuff," I’ve landed on a workflow that handles optimization, file management, and multi-color layers with single-line commands.
The Tech Stack
- Hardware/Management: OctoPrint by Gina Häußge (The essential interface for wireless management).
- Firmware: Marlin Firmware (The engine powering the Sovol SO-1's movement).
- Optimization: vpype by Antoine Beyeler (The "Swiss Army knife" of plotter tools).
- Conversion: juicy-gcode by Egon Elbre (A high-performance SVG-to-G-code converter).
- Automation: Custom Zsh scripts.
Update: juicy-gcode & G-code Flavor
I recently upgraded to the latest version of juicy-gcode, which required updating my G-code flavor configuration to ensure compatibility with the Sovol SO-1's servo-based pen lift.
Below is the sovol_config.yaml file I use. This file lives in the configs directory within the main Plotter folder (~/Plotter/configs/sovol_config.yaml). It defines the startup sequence, the shutdown routine, and the specific servo commands (M280) for the pen-up and pen-down movements.
sovol_config.yaml
begin: | M280P0S30 G90 G21 G28 X Y G1 F1000 M201 X1000 Y1000 Z1000 E1000 M204 P1000.00 R1000.00 T1000.00 M205 S0.00 T0.00 B20000 X5.00 Y5.00 Z0.40 E.500 end: | G4 P50 M280P0S30 G1 X0 Y0 toolon: | M280P0S29 G4 P25 M280P0S28 G4 P25 M280P0S27 G4 P25 M280P0S26 G4 P25 M280P0S25 G4 P25 M280P0S24 G4 P25 M280P0S23 G4 P25 M280P0S22 G4 P25 M280P0S21 G4 P25 M280P0S20 G4 P50 tooloff: | G4 P100 M280P0S30 G4 P100
1. The Power of Project Structure
Organization is half the battle. To keep things clean, everything is contained within a root Plotter folder in the home directory. This folder contains two primary sub-directories: configs for machine-specific settings and projects for the actual artwork.
~/Plotter/
├── configs/
│ └── sovol_config.yaml
└── projects/
└── [project_name]/
├── code/ # Python/HTML generators
├── svg/ # Source/Generated SVGs
├── gcode/ # Machine-ready files
├── temp/ # Optimized intermediate SVGs
├── export/ # Previews and combined files
└── notes.md # Pen order and settings
To automate this, I use a script called newplot. It scaffolds the folders and a notes.md template in one go.
newplot
#!/bin/zsh
name="$1"
if [[ -z "$name" ]]; then
echo "Usage: newplot project_name"
exit 1
fi
base="$HOME/Plotter/projects/$name"
if [[ -d "$base" ]]; then
echo "Project already exists"
exit 1
fi
mkdir -p "$base"/{code,svg,gcode,temp,export}
cat <<EOF > "$base/notes.md"
# Project: $name
## Pen Order
1.
2.
3.
## Notes
EOF
echo "Created project: $base"
Example Usage:
newplot pulley
2. The "One-Command" Conversion
The heart of this workflow is turning an SVG into G-code that doesn't waste time. I use vpype to perform critical tasks like linemerge, linesimplify, and linesort (which minimizes "pen-up" travel time).
I wrapped this logic into plotsvg. Now, converting a file is a single command (assuming you are in the project's svg folder):
plotsvg drawing.svg
plotsvg
#!/bin/zsh
input="$1"
project_root=$(cd "$(dirname "$input")/.." && pwd)
base=$(basename "$input" .svg)
temp_svg="$project_root/temp/${base}_opt.svg"
out="$project_root/gcode/${base}.gcode"
vpype read "$input" \
linemerge \
linesimplify \
reloop \
linesort \
write "$temp_svg"
juicy-gcode "$temp_svg" \
-f "$HOME/Plotter/configs/sovol_config.yaml" \
-o "$out"
echo "Created: $out"
Example Usage:
plotsvg pulley.svg
3. Physical Alignment: The Corner Brackets
A crucial part of my setup isn't a digital tool, but a physical alignment technique. To ensure the paper is exactly where the plotter expects it to be, I use a dedicated corner brackets SVG.
The Process:
- Prep the Bed: I apply 3M tape directly onto the mirror (the plotter's bed).
- Mark the Bounds: I run the "corner brackets" G-code first. This draws small "L" markers directly onto the tape.
- Align the Paper: I use these drawn brackets as a perfect physical guide to place my paper.
- Secure: Once the paper is aligned with the marks on the tape, I tape it in place and start the actual plot.
This ensures the artwork is perfectly squared and positioned every single time, especially useful for multi-color jobs where I'm swapping pens and restarting files.
4. Multi-Color Workflow
For multi-color plots, I export a separate SVG for each pen (e.g., sky_blue.svg, grass_green.svg). Using the plotmulti script, I can batch-process an entire project in seconds.
plotmulti
#!/bin/zsh
for input in "$@"; do
project_root=$(cd "$(dirname "$input")/.." && pwd)
base=$(basename "$input" .svg)
temp_svg="$project_root/temp/${base}_opt.svg"
out="$project_root/gcode/${base}.gcode"
vpype read "$input" \
linemerge \
linesimplify \
reloop \
linesort \
write "$temp_svg"
juicy-gcode "$temp_svg" \
-f "$HOME/Plotter/configs/sovol_config.yaml" \
-o "$out"
echo "Created: $out"
done
Execution: plotmulti black.svg red.svg blue.svg
Load the first pen → run the first file → swap pen → run next.
Key Lessons Learned
- Structure Matters: A consistent place for
tempfiles andnotesprevents the "Which version was this?" confusion. - Physical Registration is King: Using the 3M tape/mirror trick for paper alignment is a 30-second insurance policy against a ruined piece of paper.
- Optimization is Essential: Running
vpypereduces wear and tear on the plotter's motors by eliminating unnecessary, jerky movements. - Automate the Repetitive: One-line commands let me focus on the art rather than the syntax of shell scripts or YAML configurations.
Happy Plotting!

Comments
Post a Comment