GP-GL on the Graphtec CC100
Introduction
GP-GL (Graphtec Plotter Graphics Language) is the command language of Graphtec plotters and cutters, and of the Silhouette machines built on them. The full language covers text, hatching, polar coordinates, status words and configurable terminators. The Graphtec CC100, sold as Craft ROBO, speaks a compact subset of it, extended with commands of its own for the knife, the media and registration marks.
This article describes that dialect as of firmware 2.30: how bytes reach the plotter, how commands and numbers are parsed, what units mean, what replies look like, and what every command does.
Notation used below:
[x]is optional,...is repetition.- ETX is byte 0x03, ESC is byte 0x1B.
- int is an integer parameter, coord a coordinate in user units, angle degrees with up to two decimals.
The machine in brief
- One command interpreter reads a single receive buffer, fed by USB and RS-232.
- Only the plotting part of GP-GL does anything: lines, curves, circles, line types, windows, factor, offset, rotation, clipping.
- Device commands (the F and TB families) control knife, media and registration marks.
- Text, hatching, polar and status-word commands are accepted and silently ignored.
- There is no HP-GL mode.
Transport
USB
The plotter enumerates as a printer-class device:
VID / PID 0x0B4D / 0x110A
USB version 1.0, self-powered
Interface class 7/1/2 (printer, bidirectional)
Endpoints bulk OUT 0x01, bulk IN 0x82, 64 bytes each
Device ID MANUFACTURER:Graphtec;MODEL:CC100-20;CLASS:PRINTER;
DESCRIPTION:Graphtec CC100-20;
Commands go out on bulk OUT, replies come back on bulk IN. Flow control is done by the device: when fewer than 100 bytes are free in its receive buffer it stops accepting data and NAKs the host, and it resumes once more than 110 bytes are free. A host should therefore use write timeouts rather than assume a write returns immediately.
RS-232
9600 baud, 8 data bits, no parity, 2 stop bits
There is no flow control and no overflow check on the serial path. A reply always goes back on the interface that delivered the most recent data byte.
Escape sequences
A few two- or three-byte escape sequences are handled in the receive interrupt itself, so they work even while a job is running. On USB, ESC and its argument must arrive in the same USB packet; send each sequence in a single write.
Sequence USB RS-232 Effect
ESC 00 n yes yes remote jog keys, see below
ESC 01 yes no debug trap; afterwards byte 0x01 is stored as data
ESC 02 yes yes USB: reply "1" if powered on, "0" if soft-off
RS-232: cancel the running job
ESC 03 yes no cancel the running job
ESC 04 yes no ignored ("initialize" in host drivers)
ESC 05 yes no reply "0" if idle and waiting for data, "1" if busy
Replies to ESC 02 and ESC 05 are a single digit followed by ETX.
The jog mask n is active-low: bit 0 clear means +X, bit 1 −X, bit 2 +Y, bit 3 −Y. While the plotter waits for data and n is non-zero, the head jogs in the selected directions; n = 0 ends jog mode.
Two traps: on USB, an unknown `ESC x` drops the ESC and stores x as data; on RS-232 the
escape state machine waits for 00 or 02 and swallows everything else until then.
Syntax
Commands
A command is one character, or two for the forms listed later. For a two-letter command the interpreter reads the first letter and peeks at the second; if the pair is not a known command, the single-letter command runs and the peeked character becomes the start of its parameters.
Between commands the interpreter skips, without complaint:
- digits and
+,-. - space and all control characters, including the terminator
- the filler byte 0x80 and
#
Any byte above 0x5F (lowercase letters, {|}~, DEL, 8-bit bytes) and any unknown command
letter is ignored too; it only sets an error bit nobody can read. A useful consequence:
the parameters of an unsupported command are just numbers and commas, so they are
skipped harmlessly. The flip side: a letter inside a parameter list starts the next
command.
Parameters and the terminator
Parameters follow the command directly, separated by commas. A parameter list ends at
the terminator, at the next command letter, at /, or at ESC, 0x1C or 0x80.
The terminator is always ETX; the = command that would change it does nothing. The
terminator is also optional and never consumed by a command, so this is valid input:
M100,100D200,200,300,200
Commands that take a list of points (D, E, Y, _, >) keep reading while another
number follows. Commands with fixed arity read exactly their parameters; extra numbers
are skipped. A missing parameter reads as 0 without consuming anything, so M,5 moves to
(0,5).
Numbers
number = { skip } [ "-" ] digits [ "." digits ]
- Junk before the digits is skipped; a
-anywhere in it makes the number negative. - There is no exponent, and no sign after the digits.
- The integer part saturates at 8 388 607 (8 388 608 when negative).
- Only two decimals are kept; further digits are read and dropped, not rounded.
- An int is rounded half away from zero: 2.5 gives 3, −2.5 gives −3.
- An angle is kept in hundredths of a degree.
One quirk deserves attention: a negative number whose integer part is zero loses its
sign. -0.4 reads as 0, -0.25 as +0.25, and -0.5 rounds to +1. Small negative
relative moves therefore go the wrong way. Hosts should round values between −1 and 0 to
an integer before sending them.
Units and coordinates
Internally the plotter works in 0.005 mm. Every coord is multiplied by a step factor
taken from the table {20, 10, 5, 2}, which makes one user unit 0.1, 0.05, 0.025 or
0.01 mm. The step size is a factory setting; the standard one is 0.05 mm, that is
20 units per millimetre, which is what all examples here assume. A 272 × 200 mm page is
FU5440,4000, and TB51,400 is a 20 mm mark. Fractions are honoured down to the
internal resolution.
In portrait orientation (the default) X runs along the media feed and Y across the carriage; landscape swaps the axes. The usable width is 200 mm (190 mm with a 5 mm offset in the alternative media mode), and the page length defaults to 1000 mm.
Every point passes through the same pipeline before it reaches the motors:
1. line type dashing per L and B, in user space
2. clip polygons defined by >, drawing alternates even-odd at crossings
3. factor &
4. rotation /
5. orientation axis swap and mirror
6. window clipping against \ and Z
7. device distance correction (FB), registration transform (TB),
cutter offset compensation (FC), motion planner
The offset ^ sits between the user and device coordinates: changing it does not move
the tool.
Motion
- One motor step is 0.05 mm (a half-step).
- The speed level l of
!gives 10·l mm/s, so 10 to 100 mm/s. - Acceleration is 3000 mm/s² from a start speed of 10 mm/s; at most a quarter of each segment is spent accelerating and a quarter braking.
- The cutter stops at corners sharper than 25° on segments longer than 4 mm; gentler turns are blended (120 mm/s at 1.6° down to 23 mm/s at 24°).
- A tool-down is a short solenoid kick (5 ms at full drive, then 60 ms reduced) before the cutting force is applied.
Replies
Every reply is one or more decimal fields separated by commas and terminated by a single ETX. Fields are right-aligned in a fixed width, mostly 5 or 6 characters:
- a positive number always has one space where its sign would be,
- a negative number has
-right before the digits, - zero is just
0, padded.
A point reply looks like this (ETX not shown):
100, -5
Parsing with a whitespace-tolerant scanner works fine.
Errors
The interpreter keeps an error byte (unknown command, number overflow) behind a mask set
by ". No command ever reports it: V, #, : and ; are not implemented. In
practice there is no readable error status. The nearest thing is @, which reports
whether the current position lies outside the hard limits.
Command reference
Drawing
D Dx₁,y₁[,x₂,y₂...] draw to each absolute point M Mx,y move to an absolute point (one point only) E Edx₁,dy₁[,dx₂,dy₂...] draw, each point relative to the previous one O Odx,dy move relative (one point only) H H home: undo chart feed, wait, resynchronise W Wx₀,y₀,r₁,r₂,θ₁,θ₂[,d] arc or spiral around (x₀,y₀) ] ]r₁,r₂,θ₁,θ₂[,d] arc relative to the current point WP WPa,x₁,y₁,x₂,y₂,x₃,y₃[,d] circle through three points ) )a,x₀,y₀,r₁,r₂,θ₁,θ₂,θ₃[,d] ellipse Y Ya,x₁,y₁,x₂,y₂... smooth curve through the points _ _a,dx₁,dy₁,... smooth curve, relative points BZ BZa,x₁,y₁,...,x₄,y₄[,d] cubic Bézier X Xp,q,r[,t₁,t₂] axis with tick marks
Details worth knowing:
W: the radius goes linearly from r₁ to r₂ while the angle goes from θ₁ to θ₂, so it also draws spirals. The start point is reached pen-up.dis the chord angle per segment; 0 means automatic (about 3150°/√r, at most 50°), a negative value means |d| segments. A radius under 10 internal units plots a dot.]: the centre is placed so that the current point lies on radius r₁ at angle θ₁, and the first segment is drawn.WPand)start with an integerabefore the coordinates. Leaving it out shifts every following number by one.Yand_:a≠ 0 closes the curve. If the first point is not the current point, it is reached pen-up.BZ: P₁ is reached pen-down fora= 1, pen-up fora= 0.dis the number of segments, 0 means automatic (4 to 40). The curve ends exactly at P₄.X:p= 0 draws a Y axis, 1 an X axis; 2 and 3 do the same without accumulated rounding.qis the interval,rthe interval count,t₁ / t₂the tick lengths (default 1 mm).
Line type
L Lp line type -9..9 L100 L100,n,a,b[,c] user-defined dash pattern B Bl pattern period (0 keeps the old one)
Type 0 is solid, 9 puts dots at the vertices only, and 1 to 8 are built-in patterns. Lengths
alternate pen-down and pen-up and are scaled from a period of 64 to the B pitch; a
length of 0 is a dot. Negative types shift the pattern by half its first dash.
1 1,63 5 50,7,0,7 2 8,56 6 50,5,4,5 3 32,32 7 50,5,0,4,0,5 4 50,14 8 41,5,4,5,4,5
L100 builds its own pattern from positive lengths: n = 1 gives [a, b], 2 gives
[a, b, c, b], 3 gives [a, b, c, b, c, b]. L100,1,40,80 gives
dashed fold lines.
Coordinate setup
\ \x,y window lower left Z Zx,y window upper right ^ ^x,y origin offset; the tool does not move & &p,q,r factor: x·p/r, y·q/r / /x,y,θ rotate by θ around (x,y); θ = 0 turns it off > >x₁,y₁,... add a clipping polygon; > alone clears them all SO SO set origin at the carriage (NP is an alias) FJ FJ re-reference the origin, clearing transforms FT FT move pen-up to (0,0)
The window only clips along the feed axis after Z has been sent. & is ignored unless
all three values are non-zero, |p/r| and |q/r| are below 9, and |r/p| and |r/q| are at
most 4096. Polygons from > accumulate; drawing inside them is even-odd.
Queries
G, C x,y,p position after motion ends; p = tool·10 + 1 if pen up ? x,y offset [ x,y window lower left U x,y window upper right @ n position outside limits: 1 x>max, 2 x<min, 4 y>max, 8 y<min FG V2.30 firmware version (followed by four spaces) FA a,b distance correction FL n media mode FV n media type (unset reads as 13) FQn v stored setting n, see below TB71 x,y registration calibration TB98 1 constant 1
C behaves like G; it does not wait for a button press. FQ reports: 0 speed, 1 force
level, 2 cutter offset, 3 overcut, 4 orientation, 5 whether registration marks were
found. Other values give no reply at all, so don't wait for one.
Tool and media settings
Commands that accept a pen number parse it and ignore it; every setting applies to all eight pen slots. Settings marked saved go to EEPROM, written only when they change.
! !l[,n] speed 1..10 (101..110 also accepted), saved * *a,f[,n] force level f = 1..3 (a is ignored), saved FX FXn[,m] cutting force 1..30, saved FC FCp,q[,n] cutter offset p = 0..45 in 0.025 mm, saved FD FDθ[,n] blade rotation threshold, default 30° FE FEp[,n] overcut on (1) or off (0), saved FF FFa,b[,n] overcut lengths 0..9 in 0.1 mm, default 2 FK FK[n] full-force touchdown after a blade swivel FN FN[n] orientation 0 portrait, 1 landscape, saved FM FMn media mode: 1 = 200 mm, 0 = 190 mm with 5 mm offset FO FO[len] page length, 0 means 1000 mm FW FWn media type 1..13 (anything else becomes 13), saved FB FBa,b distance correction -200..200 (±2 %) each, saved FY FYn track enhancing: 0 on, 1 off, saved FU FUlen,w track-enhancing roll over len F Fl chart feed length (stored only) TT TT soft reset and home
Some of these surprise:
- Force:
*levels 1, 2, 3 drive the solenoid at 164, 177 and 203 (out of 255);FXinterpolates through the same calibration (FX2 ≈*1, FX10 ≈*2, FX27 ≈*3). Sending*clearsFX. FCis the drag-knife offset, not overcut.p= 0 disables compensation.qis accepted in the range 1..3.FD: at a corner sharper than this angle, the knife lifts and its tip is swivelled around the axis in 15° steps.FYis inverted:FY0enables track enhancing.FUis not a cutting area. It rolls the media back and forth, and only when the force is at least 19, track enhancing is on, and the factory pass count is non-zero. Host software sends the page size here.Fstores the feed length but never moves the media.FWsilently maps values outside 1..13 to 13.
Registration marks
The optical sensor sits 15 mm from the knife. During a search all moves are shifted so coordinates refer to the point under the sensor. Each mark is an L made of two bars; each bar is found with one coarse and two fine scans, and its position is the midpoint of the two detected edges. From the marks the plotter builds an affine transform, rotation and optionally scale, applied to everything plotted afterwards.
TB0 transform off TB99 transform on (applied after a successful search) TB50[,n] orientation for registration, 0 portrait TB51,len mark length, 5..20 mm TB52[,n] mark type, 1 or 2 (default 2) TB53,w mark line width 0.3..1.0 mm (stored, unused) TB54[,x[,y]] mark offset TB55,n scale to the nominal mark distance (0.667..2.0) TB23[,x,y] manual search, tool placed at the first mark TB123[,x,y] automatic search TB70 calibration cut over a printed cross TB72,x,y sensor-to-knife correction, ±40 in 0.05 mm, saved
TB22 and TB122 are the search-type-1 variants of TB23 and TB123. A search
replies 0 when the marks were found and non-zero otherwise; after a failed automatic
search the page reverts to 1000 × 200 mm. A typical host sequence:
TB50,0 TB51,400 TB99 TB55,1 TB123,x,y -> " 0" then FQ5
Test and maintenance
FH test cut: a 10 mm square with a triangle inside, then 10.5 mm feed FI endless test plot; only a power cycle stops it FP FPGRFCC1 unlocks service mode, anything else is ignored
FP is not a firmware update command; firmware updates are not part of the protocol.
Accepted, but no-ops
- polar:
DP,MP,EP,OP,RP - text:
P,K,$,S,Q,R,I,A,LP,RC - marks and patterns:
N,SP,SA,(,(P - hatching
%, buzzerT, buffer sizeBS, pen selectJ - error mask
", skip-to-next-commandZPandFR - status and interface:
V,#,:,;,<,',=
Since = does nothing, the two terminator bytes after it are interpreted as commands.
Don't send it.
Service mode
After FPGRFCC1, lowercase letters become commands until the next reset. They exist for
the factory and change calibration values, so host software should leave them alone.
a0..a3 sensor and LED tests j, k, y internal counters b home sensor state l internal flag c motion parameter m, q endless burn-in loops d0..d3 user unit 0.1..0.01 mm n mark sensor threshold e arithmetic self test o plot every received byte f read a RAM byte p toggle panel LEDs g service parameters t0..t4 mark sensor tests h rebuild speed tables z set output port bits i feed 40 mm and back
g17 is a factory reset of the user settings.
Settings stored in EEPROM
Offset Content Set by After factory reset 0x04 configuration, step size service unchanged 0x1C, 0x1E distance correction FB 0, 0 0x24, 0x26 sensor-to-knife correction TB72 0, 0 0x34 speed ! 10 0x35 force level * 3 0x36 cutter offset FC 0 0x37 orientation FN 0 0x38 overcut FE 0 0x39 track-enhancing passes service unchanged 0x3A media type FW 1 0x3B cutting force FX 0 (unset) 0x3C track enhancing off FY 0
A complete job
A real job looks like this. It draws a box net of about 245 × 157 mm, with dashed fold lines and solid cut lines:
FU5440,4000FM1TB50,0FO5440&100,100,100,\0,0,Z5940,4200,L100,1,40,80,^0,100 M44.006,1143.797,D1045.895,1143.797M1045.895,1992.228,D44.006,1992.228 ... L0,M29.895,1143.797,D59.881,1073.242D1015.909,1073.242 ... &1,1,1,TB50,0FO0
There are no terminators at all and trailing commas everywhere, and of the three decimals only two count.
Compared with full GP-GL
- The terminator is always ETX;
=,V,#,:,;and all text, hatching and polar commands are no-ops. WPand)take an extra leading integer.Cdoes not wait for a button.*ignores its first parameter; its second is a force level 1..3.!accepts only levels 1..10.- Pen numbers are ignored everywhere.
Fdoes not move the media.- Numbers keep two decimals, and
-0.xloses its sign.
Grammar
The grammar below is in Go EBNF: [x] is optional, {x} repeats, `a … b` is a
range. It describes well-formed input; the plotter is more forgiving. Between
commands it skips digits, signs, commas, dots, #, 0x80, spaces and control
characters, a missing parameter reads as 0, extra parameters are skipped, and where a
letter starts both a one- and a two-letter command (D / DP, W / WP, F / FX), the
two-letter form wins when the next byte matches.
Stream = { RealTime | Command | ServiceCommand | Filler } .
/* Real-time sequences, handled on receipt and never seen by the interpreter.
On USB ESC and its argument must be in the same packet. */
RealTime = esc ( "\x00" Byte /* jog keys, active-low bit mask */
| "\x01" /* debug trap */
| "\x02" /* USB: online? "1"/"0"; RS-232: cancel */
| "\x03" /* cancel job */
| "\x04" /* ignored */
| "\x05" /* busy? "1"/"0" */
) .
Filler = Terminator | Sep | " " | "\r" | "\n" | "\t" | "#" | "\x80" .
Terminator = etx .
Sep = "," .
/* ---- numbers ---- */
Number = [ "-" | "+" ] ( Digits [ "." { Digit } ] | "." Digits ) .
Digits = Digit { Digit } .
Digit = "0" … "9" .
Coord = Number .
Int = Number .
Angle = Number .
Point = Coord Sep Coord .
Points = Point { Sep Point } .
Pen = Int . /* pen number, parsed and ignored */
OptSep = [ Sep ] .
/* ---- commands ---- */
Command = Drawing | LineType | Setup | Query | Tool | Registration
| Maintenance | NoOp .
Drawing = Draw | Move | DrawRel | MoveRel | Home | Circle | CircleRel
| Circle3 | Ellipse | Curve | CurveRel | Bezier | Axis .
Draw = "D" Points OptSep .
Move = "M" Point OptSep .
DrawRel = "E" Points OptSep .
MoveRel = "O" Point OptSep .
Home = "H" .
Circle = "W" Point Sep Coord Sep Coord Sep Angle Sep Angle [ Sep Angle ] OptSep .
CircleRel = "]" Coord Sep Coord Sep Angle Sep Angle [ Sep Angle ] OptSep .
Circle3 = "WP" Int Sep Point Sep Point Sep Point [ Sep Angle ] OptSep .
Ellipse = ")" Int Sep Point Sep Coord Sep Coord Sep Angle Sep Angle Sep Angle
[ Sep Angle ] OptSep .
Curve = "Y" Int Sep Points OptSep .
CurveRel = "_" Int Sep Points OptSep .
Bezier = "BZ" Int Sep Point Sep Point Sep Point Sep Point [ Sep Int ] OptSep .
Axis = "X" Int Sep Coord Sep Int [ Sep Coord Sep Coord ] OptSep .
LineType = UserLine | Line | LineScale .
Line = "L" Int OptSep . /* -9 … 9 */
UserLine = "L100" Sep Int Sep Coord Sep Coord [ Sep Coord ] OptSep .
LineScale = "B" Coord OptSep .
Setup = LowerLeft | UpperRight | Offset | Factor | Rotate | Clip
| SetOrigin | Reorigin | GoOrigin .
LowerLeft = "\\" Point OptSep .
UpperRight = "Z" Point OptSep .
Offset = "^" Point OptSep .
Factor = "&" Int Sep Int Sep Int OptSep .
Rotate = "/" Point Sep Angle OptSep .
Clip = ">" [ Points ] OptSep . /* empty clears */
SetOrigin = ( "SO" | "NP" ) [ Int ] .
Reorigin = "FJ" .
GoOrigin = "FT" .
/* Replies: decimal fields, right-aligned, separated by "," and ended by ETX. */
Query = "G" | "C" | "?" | "[" | "U" | "@" | "FG" | "FA" | "FL" | "FV"
| "FQ" Int | "TB71" | "TB98" .
Tool = Speed | Force | CutForce | CutterOffset | BladeRotation | Overcut
| OvercutLength | Touchdown | Orientation | MediaMode | PageLength
| MediaType | DistanceCorr | TrackEnhance | TrackRoll | ChartFeed
| Reset .
Speed = "!" Int [ Sep Pen ] OptSep . /* 1 … 10 */
Force = "*" Int Sep Int [ Sep Pen ] OptSep . /* first ignored, 1 … 3 */
CutForce = "FX" Int [ Sep Int ] OptSep . /* 1 … 30 */
CutterOffset = "FC" Int Sep Int [ Sep Pen ] OptSep . /* 0 … 45, 1 … 3 */
BladeRotation = "FD" Angle [ Sep Pen ] OptSep .
Overcut = "FE" Int [ Sep Pen ] OptSep . /* 0, 1 */
OvercutLength = "FF" Int Sep Int [ Sep Pen ] OptSep . /* 0 … 9 */
Touchdown = "FK" [ Int ] OptSep .
Orientation = "FN" [ Int ] OptSep . /* 0 portrait, 1 landscape */
MediaMode = "FM" Int OptSep .
PageLength = "FO" [ Coord ] OptSep .
MediaType = "FW" Int OptSep . /* 1 … 13 */
DistanceCorr = "FB" Int Sep Int OptSep . /* -200 … 200 */
TrackEnhance = "FY" Int OptSep . /* 0 on, 1 off */
TrackRoll = "FU" Point OptSep .
ChartFeed = ( "F" | "FS" ) Coord OptSep .
Reset = "TT" .
Registration = "TB" ( "0" | "99" | "70"
| "50" [ Sep Int ]
| "51" Sep Coord
| "52" [ Sep Int ]
| "53" Sep Coord
| "54" [ Sep Coord [ Sep Coord ] ]
| "55" Sep Int
| "72" Sep Int Sep Int
| ( "22" | "23" | "122" | "123" ) [ Sep Point ]
) OptSep .
Maintenance = "FH" | "FI" | Unlock .
Unlock = "FPGRFCC1" . /* enables ServiceCommand until reset */
/* Accepted, parameters discarded by the filler rule. */
NoOp = "DP" | "MP" | "EP" | "OP" | "RP" | "P" | "K" | "$" | "S" | "Q" | "R"
| "I" | "A" | "LP" | "RC" | "N" | "SP" | "SA" | "(" | "(P" | "%" | "T"
| "J" | "BS" | "\"" | "ZP" | "FR" | "V" | ":" | ";" | "<" | "'" | "=" .
/* Only after Unlock. */
ServiceCommand = "a" … "z" [ Int { Sep Int } ] OptSep .
/* ---- bytes ---- */
esc = "\x1b" .
etx = "\x03" .
Byte = "\x00" … "\xff" .