• A GCode sender for up to two GRBLs to control a CNC and a tool changer.

  • Controlling a 2nd Arduino with GRBL for tool change or 4th (5th, 6th) axis control (GRBL-Plotter is waiting for 'IDLE' on one GRBL before controlling the other). Commands for 2nd GRBL will be introduced via special formatted remarks in GCode e.g.: (^2 G90 X2) to move 2nd GRBL to position X2.

1. G-Code Editor

The used component Fast Colored TextBox to display the GCode, supports a lot of hotkeys for text manipulation:

  • Left, Right, Up, Down, Home, End, PageUp, PageDown - moves caret
  • Shift+(Left, Right, Up, Down, Home, End, PageUp, PageDown) - moves caret with selection
  • Ctrl+F, Ctrl+H - shows Find and Replace dialogs
  • F3 - find next
  • Ctrl+G - shows GoTo dialog
  • Ctrl+(C, V, X) - standard clipboard operations
  • Ctrl+A - selects all text
  • Ctrl+Z, Alt+Backspace, Ctrl+R - Undo/Redo opertions
  • Tab, Shift+Tab - increase/decrease left indent of selected range
  • Ctrl+Home, Ctrl+End - go to first/last char of the text
  • Shift+Ctrl+Home, Shift+Ctrl+End - go to first/last char of the text with selection
  • Ctrl+Left, Ctrl+Right - go word left/right
  • Shift+Ctrl+Left, Shift+Ctrl+Right - go word left/right with selection
  • Ctrl+-, Shift+Ctrl+- - backward/forward navigation
  • Ctrl+U, Shift+Ctrl+U - converts selected text to upper/lower case
  • Ctrl+Shift+C - inserts/removes comment prefix in selected lines
  • Ins - switches between Insert Mode and Overwrite Mode
  • Ctrl+Backspace, Ctrl+Del - remove word left/right
  • Alt+Mouse, Alt+Shift+(Up, Down, Right, Left) - enables column selection mode
  • Alt+Up, Alt+Down - moves selected lines up/down
  • Shift+Del - removes current line
  • Ctrl+B, Ctrl+Shift-B, Ctrl+N, Ctrl+Shift+N - add, removes and navigates to bookmark
  • Esc - closes all opened tooltips, menus and hints
  • Ctrl+Wheel - zooming
  • Ctrl+M, Ctrl+E - start/stop macro recording, executing of macro
  • Alt+F [char] - finds nearest [char]
  • Ctrl+(Up, Down) - scrolls Up/Down
  • Ctrl+(NumpadPlus, NumpadMinus, 0) - zoom in, zoom out, no zoom
  • Ctrl+I - forced AutoIndentChars of current line

2. Joystick control

The machine can be controlled via

  • mouse at the user interface inside the virtual joystick control

  • external game pad
    • The control range of the analoge joysticks are divided into 6 segments to get same functionality as the virtual joystick has. Using the same settings, described below.
  • control via keyboard is supported since version 1.2.2

The idea is to get a continuous movement as long as the joystick is triggered, and to stop directly. With grbl 0.9 it wasn't easy to implement. My solution was to send every 0.5 seconds a command for a short movement - a distance which could be reached almost within 0.5 seconds. To avoid an extra edit box for the feed rate I designed the control above, which offers 5 different speeds for each direction.

2.1 Setup

For each feed rate option (Pos 1 to 5, from inner to outer border of control) the distance (step width) and feed rate (speed) must be set. To avoid a halting movement, an 'overlap' is recommended: the next movement command must be sent before the previous movement is finished. To do so, set a desired step with an calculate the needed feed rate: the feed rate must be almost step-with*60/0.5 mm/min. E.g. a step-with of 10 results to a feed rate of 1200, to avoid halting I choose 1000 for the default setup.

Note: the 'Calc. best speed' buttons are disabled

Note: the maximum travel speed is limited by the grbl Settings $110 to $112.

An optional 4th axis (other grbl branch) uses same settings as the Z axis.

2.2 Game pad

Check button number of your game pad just by pressing a button, in the setup tab, the corresponding field will light in green. To each button you can assign

  • a file (with path) containing GCode to process
  • one ore more lines with GCodes, lines are seperated by ';'
  • special commands as described on the tab for homing, start streaming or feed rate override

Don't forget to enable the game pad at the upper left check box.

Note: real control is enabled after closing the setup window

3. Hotkeys / Keyboard control

Most GUI functions can now be controlled via hotkeys. The list below shows the default assignment with all possible functions.

The file hotkeys.xml must be edited manually, but content and a sand-box to try key-codes are available in [Setup-HotKeys].

Click into the joystick control or press the Num-Lock button to activate the keyboard control - background color switches to green. As long as the focus is on one of the joystick controls, the machine can be controlled via the keyboard (background is green):

3.1 Implemented controls - default

  • XY Directions (wind rose) S, SE, E, NE, N, NW, W, SW via 8,9,6,3,2,1,4,7
  • also S, E, N, W via arrow keys
  • Z Directions via + and -

Change green marker in control to select step size and speed:

  • XY++ via HOME
  • XY– via END
  • Z++ via Page Up
  • Z– via Page Down

3.2 Content of hotkeys.xml

I hope the key-words for “action” are meaningful enough and default assignments are user-friendly.

<?xml version="1.0"?>
<hotkeys name="GRBL-Plotter default">

<!-- Jog commands will only be sent if any virtual joystick control is focused -->
  <bind keydata="Left" 		action="JogAxisXDec" />
  <bind keydata="NumPad4" 	action="JogAxisXDec" />
  <bind keydata="Right" 	action="JogAxisXInc" />
  <bind keydata="NumPad6" 	action="JogAxisXInc" />
  <bind keydata="Down" 		action="JogAxisYDec" />
  <bind keydata="NumPad2" 	action="JogAxisYDec" />
  <bind keydata="Up" 		action="JogAxisYInc" />
  <bind keydata="NumPad8" 	action="JogAxisYInc" />
  <bind keydata="NumPad1" 	action="JogAxisXDecYDec" />
  <bind keydata="NumPad7" 	action="JogAxisXDecYInc" />
  <bind keydata="NumPad9" 	action="JogAxisXIncYInc" />
  <bind keydata="NumPad3" 	action="JogAxisXIncYDec" />
  <bind keydata="Add" 		action="JogAxisZDec" />
  <bind keydata="Substract"	action="JogAxisZInc" />
  <bind keydata="NumPad0" 	action="JogAxisADec" />
  <bind keydata="Decimal" 	action="JogAxisAInc" />
  <bind keydata="Insert" 	action="JogAxisADec" />
  <bind keydata="Delete" 	action="JogAxisAInc" />
  <bind keydata="NumPad5" 	action="JogAxisStop" />

  <bind keydata="Home" 		action="JogSpeedXYInc" />
  <bind keydata="End" 		action="JogSpeedXYDec" />
  <bind keydata="PageUp" 	action="JogSpeedZInc" />
  <bind keydata="PageDown"	action="JogSpeedZDec" />
<!-- Buttons on GUI-->  
  <bind keydata="A, Control, Alt"	action="StreamStartPause" />
  <bind keydata="Space, Control, Alt"	action="StreamStop" />
  <bind keydata="S, Control, Alt"	action="StreamCheck" />

  <bind keydata="F10"	        action="ToggleSpindle" />
  <bind keydata="F11"	        action="ToggleCoolant" />
  <bind keydata="F12"	        action="ToggleToolInSpindle" />

  <bind keydata="D1, Control"	action="OverrideFeedDec10" />
  <bind keydata="D2, Control"	action="OverrideFeedDec1" />
  <bind keydata="D3, Control"	action="OverrideFeedSet100" />
  <bind keydata="D4, Control"	action="OverrideFeedInc1" />
  <bind keydata="D5, Control"	action="OverrideFeedInc10" />

  <bind keydata="D1, Control, Alt"	action="OverrideSpindleDec10" />
  <bind keydata="D2, Control, Alt"	action="OverrideSpindleDec1" />
  <bind keydata="D3, Control, Alt"	action="OverrideSpindleSet100" />
  <bind keydata="D4, Control, Alt"	action="OverrideSpindleInc1" />
  <bind keydata="D5, Control, Alt"	action="OverrideSpindleInc10" />
<!-- Set coordinates to zero -->    
  <bind keydata="Q, Control, Alt"	action="OffsetX" />
  <bind keydata="W, Control, Alt"	action="OffsetY" />
  <bind keydata="E, Control, Alt"	action="OffsetZ" />
  <bind keydata="R, Control, Alt"	action="OffsetA" />
  <bind keydata="T, Control, Alt"	action="OffsetXY" />
<!-- Keyboard DE -->
  <bind keydata="Z, Control, Alt"	action="OffsetXYZ" />
<!-- Keyboard US -->
  <bind keydata="Y, Control, Alt"	action="OffsetXYZ" />

<!-- Move to zero -->    
  <bind keydata="Q, Control"	action="MoveZeroX" />
  <bind keydata="W, Control"	action="MoveZeroY" />
  <bind keydata="E, Control"	action="MoveZeroZ" />
  <bind keydata="R, Control"	action="MoveZeroA" />
  <bind keydata="T, Control"	action="MoveZeroXY" />

<!-- grbl specific -->
  <bind keydata="H, Control, Alt"	action="grblHome" />
  <bind keydata="F, Control, Alt"	action="grblFeedHold" />
  <bind keydata="R, Control, Alt"	action="grblReset" />
  <bind keydata="G, Control, Alt"	action="grblResume" />
  <bind keydata="X, Control, Alt"	action="grblKillAlarm" />

<!-- Custom Button commands will only be sent if buttons are enabled -->  
  <bind keydata="F1" action="CustomButton1" />
  <bind keydata="F2" action="CustomButton2" />
  <bind keydata="F3" action="CustomButton3" />
  <bind keydata="F4" action="CustomButton4" />
  <bind keydata="F5" action="CustomButton5" />
  <bind keydata="F6" action="CustomButton6" />
  <bind keydata="F7" action="CustomButton7" />
  <bind keydata="F8" action="CustomButton8" />

4. DIY control

  • Open connection window via menu [Machine control - DIY control] or enable “Open serial port on prog. startup” on Setup - Control.
  • Connect your DIY control pad / pendant via serial connection.
  • Send grbl conform commands to your machine (regular G-Code and grbl related commands), terminated by 'CR', 'LF' or 'CR LF'.
    • commands will be blocked if streaming is in progress.
  • Send grbl specific real time commands - will be forwarded directly.
    • commands will be blocked if streaming is in progress. ← perhaps should be passed through?
  • No need to send '?', GRBL-Plotter is requesting the status frequntly and forwards only if report content changes.
  • Messages send by GRBL-Plotter can also be checked without serial connection:

5. Surface scan

5.1 How to get the height information of a work piece.

  • Install a probe at your machine which triggers the Probe pin (A5 at arduino) when touching the work piece surface. A NO (normally open) switch, connected to ground and A5, is what we need. (Or when using a NC (normally closed) switch, change $6=1).

Note: if the probe is triggered, the Z axis needs some time and distance to decelerate, which could cause mechnical stress to your probe-switch (depending on mounting).

  • Z position by probe trigger -13.654:
  • Z position after stop -13.794:
  • Be sure the max. speed settings $110, $111, $112 are not too high - causing step loss
  • A homing of the machine is recommended
  • Find the needed travel range downwards for the Z axis. Manual probing can be started via the command “G38.3 Z-5 F200” (probe toward workpiece, stop on contact), where Z-5 a max. travel depth of 5 units (mm) defines with a speed of 200. If the probe is not triggered until -5, a depth of -5 will be assumed.
  • With the manual triggered probing you can check the difference between Z position where the probe was triggered (check output in COM window) and the final stop position (check position in main window).
  • Setup and run the surface scan to get a height map
  • Apply height map to GCode to do auto leveling
  • Or export height map for further use as STL or X3D (soon)

5.2 The Probe

Some examples of DIY probes:
1 with micro-switch, 2, 3 using a needle, 4, 5 made of brass

5.3 Setup of the surface scan

  • Open the surface scan window via [Machine control - Surface scan].
  • Define the area where to scan the height profile via lower-left and upper-right edge, or via lower-left edge and X- and Y-dimension.
  • Set “Grid size X and Y” the distance from one to the next measurement point in units (mm)
  • Set “Max depth” the needed depth for probing before abort a specific probing
  • Set “Save height” above the work piece. Note: as less travel way the Z axis has, as faster the surface scan is.
  • Finally set “Probing speed Z” the speed of the downwards movement. The upwards speed is defined by the $112 setup, because “G0” is used.
  • Be sure the probe is above the work piece and Z coordinate is set to zero
  • Start the surface scan via “Generate Height Map”

5.4 During the surface scan

  • The main window shows the scan grid in yellow
  • The depth will be measured from the lower-left to the upper-right point, line by line, raster-point by raster-point.
  • Because the final depth-range is not known during the surface scan the result picture looks strange at the beginning.
  • If the probe-switch is still triggered when the next probing started, an error occurs and the scan will be aborted.

Alarm 4 “Probe fail. The probe is not in the expected initial state before starting probe cycle, where G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered.” Error 9 “G-code locked out during alarm or jog state” - because there are still commands in the queue.

5.5 After the surface scan

  • After finishing the surface scan, the picture will be updated with the correct color-range and interpolated values (steps will be smoothed)
  • The offset to the max. measured Z value can be reomved from all measure points via “set max. value = 0”
  • The generated height map can be applied to an already loaded GCode. Be sure height map is greater than GCode object!
    • All circles (G2, G3) will be converted to tiny line-segments All G1 movements will be converted to tiny line-segments The interpolated Z-value from the height map will be applied to each line-segment

Note: any line-segment outside the height map area will be set to Zmax of the height map (lowest depth).

5.6 Getting height map data

5.7 Export of height map as X3D model

It took almost 9 hours to probe 32761 points in 0.1 mm resolution for this coin. Z axis was stretched by factor 100 before export to X3D.

5.8 Z-Value from DIY-Control interface

Setup your external hardware to send the Z-Value in the format “(PRB:Z-5.12)”, where -5.12 the actual Z-Value is.

Your hardware should send the value frequently or on trigger when receiving “<IDLE…”

In the surface-scan-window set “Probe-Max. depth” to zero. This triggers GRBL-Plotter to process received Z.values instead of sending G38 to grbl. Change Save height also to zero to speed up scan.

6. Auto leveling

Just a video up to now…

7. Rotary axis

In this example the Y axis will be substituted by a rotary axis. A toilet paper roll with a diameter of 45mm will be machined.


  • secure the GRBL Settings for the Y-axis ($101=40; $121=500) 40 steps/mm (belt drive)
  • switch off electronis (to protect motor drivers)
  • rebuild mechanics
  • switch on electronis
  • set GRBL Settings for the rotary axis ($101=4.4444; $121=100) 4.444 steps/degree (1600 steps/turn)
  • Attention: the Y axis scaling is now in degree
  • set the diameter of work piece
  • Adaption of the gcode by scaling the Y axis (attention G2 / G3 commands must be converted into lines prior)

Via '$$' the actual settings can be read out, they can be set in the setup-tab for automatic transfer.

Automatisches Setzen der Motorparameter, abhängig von der Auswahl “Achsenersetzung”

  • Original GCode (max. Y is 35.25mm)
  • Set of work piece diameter (45mm → circumference = 141mm)
  • scaling of the Y-axis to the circumference of the work piece
  • The gcode was scaled to 90 (now degree) on the Y axis (this correlates to 35.25mm on the circumference)

8. Subroutines

GRBL-Ploter supports now M98 and M99 commands.

Further information can be found here: m97-m98-m99-sub-programs-or-sub-routines.html


when using auto leveling the subroutines will be processed to apply Z values.

Example squares

(Test subroutine)
G0 X10 Y10 Z2 F2000
M98 P1234       (call sub once)
G0 X30 Y10 Z2
M98 P1234 L2    (call sub twice)
G0 X50 Y10 Z2
M98 P1234 L3    (call sub 3 times)

( Test relative moves)
O1234   (start of sub)
G1 Z-4
G0 Z4
X1 Y1
M99     (end of sub)

Example scale

(Draw a scale on the )
(circumference of a cylinder)
(using a rotary axis)
G00 Z2
G00 X0 Y0
M98 P0090 L4
M98 P0030 L12
M98 P0005 L72

(90° graduation)
G01 Z-4
G00 Z4
X-10 Y90

(30° graduation)
G01 Z-4
G00 Z4
X-7.5 Y-30

(5° graduation)
G01 Z-4
G00 Z4
X-5 Y5

9. Camera setup

The camera needs to be mounted close to the tool - adjustable. Either connected to the Z axis (like on my plotter), or independent from the Z axis.

For the next steps it is recommended to machine a horizontal marker for camera rotation and a small marker at position 0;0 to calculate the offset - using the tool.

Setup of the camera

9.1 Selecting the video source

9.2 Setup of the camera rotation

Just change the rotation angle until the picture is exactly horizontally aligned. The horizontal marker-line should be parallel to the cross hairs.

If the camera is mounted to the Z axis, it must be aligned in that way, that the center of the picture doesn't move if the Z position is changed. This can be done is this way:

  • a) move Z axis to lowest point, move marker to center of cross hairs (XY position)
  • b) move Z axis to highest point, tilt camera on it's fixture until marker is in center of cross hairs Repeat steps a) and b) until desired accuracy is reached.
  • Furthermore a 'Homing' must be performed, because the scaling will be saved depending on the machine coordinates. The work piece must be marked with markers of known size to be able to scale the grafics on it. In my example I used circles with r=5mm and r=15mm.

9.3 Adjustment of the Z depending scaling

  1. Move camera to owest point and click “Teach Lower Position”, now click inside the picture to mark the distance from center given in the field “Set teach radius bottom” (in example circle with r=5)
  2. Move camera to highest point and click “Teach Upper Position”, now click inside the picture to mark the distance from center given in the field “Set teach radius top” (in example circle r=15)

Now the scaling will be calculated depending on the machine Z position (not world position).

If the camera is not mounted on the Z axis, don't change height when teaching lower and upper scaling radius (both radii must be same) on same picture position.

9.4 Adjustment of the camera offset

Now a separate coordinate system (G59) will be used to switch between tool and camera.

  • Switch to 'Tool coord'.
  • Set a marker with the tool on workpiece and zero the coordinate system (zero X and Y axis)
  • Move marker below cross hairs and click on “Teach Offset - Teach camera offset”
  • Switch to 'Cam coord'.
  • Now the XY position can be switched between camera and tool position using the buttons 'Tool coord.' and 'Cam coord'.

10. PCB drilling

Setup the GCode for drilling an already etched PCB in offset, orientation and scale. The Camera must be setup already to go on with the following steps.

The following pictures are showing a printout, not a real etched PCB.


Inside the GCode, two teachpoints (fiducials) needs to be selected (they should be as far away from each other as possible) and assigned in the camera view. With this assignment the code will be rotated and scaled to match the camera view.

For an automatic recognition of teachpoints they must be round or rectangular - without any conductor track.

10.1 Load the drill file (e.g. RS485_smd1.drd)

Load the file, e.g. by drag and drop

Remove offset, mark 1st teachpoint by clicking into 2D-view

10.2. Open Camera window and adjust GCode

Switch to 'Cam coord.', enable shape recognition if needed, 'Select filter set' to load other parameter set if recognition result is not ok.

Use mouse wheel to zoom into picture, press 'Space' on key board to hide 2D view

Press right mouse button, select 'Set actual pos. to marker pos.' to apply offset to cam coordinate system.

Select 2nd teachpoint in GUI 2D-view

Move to 2nd teachpoint in camera view and center the mark. Press right mouse button, select 'Compensate angle and scale' to rotate and scale GCode to match 2nd teachpoint.

Now the system is ready to start drilling

ATTENTION! Before 'Start' set back the coordinate system to G54 ('Tool coord.')

11. Image import

The main problem when importing images is the color assignment: each image color must be assigned a limited number of tool (pen) colors, so called color quantization. Some image filters from AForge were implemented with realtime update to improove the color quantization. To cover wavy edges in horizontal drawing mode, a surrounding outline can be drawn. Note: when “outline” is enabled, the image processing works internally with up to 5x resolution for a smooth outline, which slows down the real-time update.

Internally the color processing works with three images:

  • 'original' image from loading, stays untouched - Keep the button “Original” pressed to check image.
  • 'adjusted' image shows the result after applying all filters to the original image.
  • 'result' image simulates the plotter output, white background shows areas where no tool will be applied - Keep the button “Preview” pressed to check result.

Open Image via file dialog, drag & drop or copy & paste.

Example picture was copied & pasted from google picture search result “keith haring flying devil”.

After loading an image, the first tab shows some information:

* amount of colors (original image)
  * Note: JPG images usually have many more colors than obviously visible, due to compression artifacts
* Number of pens (= amount of tools/colors in tool table)
* Size of original and result image


Use predefined settings to convert image:

  • Comic - few colors
    • preset brightness, contrast, gamma
    • reduce amount of colors to amount of tool-colors using AForge 'color quantiziser',which exceeds the setted occurence in %
    • set exception color to white (otherwise white background would be replaced by brightest color in tool-table - e.g. 'apricot')
    • remove isolated pixels
    • finally use color replacing 'Distance in RGB space'
  • Graphic - many colors
    • apply AForge 'Posterize' filter
    • finally use color replacing 'Distance in color hue, saturation and brightness' (HSV color range)
  • Image - dark background
    • set saturation
    • apply AForge 'channel filtering' to set dark areas to black
    • set exception color to black
    • uncheck “Draw outline” to save processing time
  • Contour
    • set grayscale mode
    • apply AForge 'Edge filter'
    • set adjusted image to new original image
    • invert image
    • set max. colors to two

Or do manual settings on the corresponding tabs

Tab 2

Set the dimension and resolution (= pen width) of the result image.

Tab 3

Apply some filters.
Tab 4

Set final color quantization and deselect colors (= tools) if not needed

Tab 5

Finally check gcode parameters for conversion.

Note: using “Draw outline” causes image processing with up to 5x higher resolution to get smooth outline path. This will slow down the realtime update.


(first image, 'Comic' preset and deselected colors yellow and red, Output width = 100, resolution = 1): 


   horizontal with outline outline only


12. Drag tool compensation

To take account of the behavior of a brush, not to follow the gcode path exactly:

A compensation is needed:

  • The yellow path shows the orginal graphics
  • The red lines shows the needed extension of the path to compensate the drag of the brush
  • The green lines shows the final path - drag tool compensated
  • Depending on the swivel angle between two lines, the path will be shorten or an arc will be drawn.
  • Show pagesource