CNC From the Absolute Basics

by Jon Freeman

Part 5 - Engineering in Miniature - Jan 2014

Quick Links to - Sep13 Oct13 Nov13 Dec13 Jan14 Feb14 Mar14 Apr14 May14 JunJul14

Good programme – let's make it better !

In the previous article, a G code CNC programme was successfully designed to mill out a large hole using a small milling cutter. It was developed using the simple process of thinking through and writing down a workable sequence of cutting actions – a 'pseudo-code' list – and then converting this almost line-by-line into workable 'in-line' G code. The hole diameter and depth and the tool size were decided at the outset, and these specific dimensions were embedded throughout the programme wherever needed. While this was a perfectly good way to illustrate a method of programme development, the programme will need further effort applied should it need to be reused to make a hole of other dimensions or using a tool of another size. The number of spiral turns used to reach the depth of cut was fixed at four, this will not be suitable for all, or even many, uses, nor will the hard-coded depth of 8.0mm. These and other issues will be addressed once the other two big weapons are explained – parameters and subroutines.


As has been seen, in any in-line programme it is highly likely the same numbers will crop up in many places throughout the code. These numbers might be the coordinates of a hole centre, the bottom left corner of the work piece, a tool size and so on. Should you need to alter any one of these values, it soon becomes difficult if the whole code needs to be re-examined searching for all places where the number crops up. This is made more difficult when, for example, the corner radius just happens to be the same figure as the tool size. It is all too easy to miss a number that should be changed, or to change one that shouldn't be. Parameters to the rescue. Mach3 G code allows the use of computer memory to store values in parameters, thousands of unique values may be stored. Parameters may be thought of as numbered pigeon-holes, each of which may be used to contain one numeric value. This enables the programmer to allocate each of our job specific numbers – the hole diameter, the hole depth, the tool size and so on – to its own unique parameter. Each of these job specific numbers may then be specified once only in the programme, preferably grouped together near the top of the programme listing in a 'User Interface' section. The rest of the programme is then written to refer to parameters in the User Interface, rather than having these numbers embedded throughout the code. In this way, the tool size, the corner radius and whatever may be found easily and changed with confidence knowing the rest of the programme will always pick up this new value. The hash '#' character is used to identify a parameter, the '#' followed by a number being the pigeon-hole number. For example the line:

 #123 = -8.15 ; Hole bottom

assigns a value of minus 8.5 to the parameter numbered 123, and the comment reminds us what we are using the parameter #123 pigeon-hole memory to contain. Parameters may be used in the programme wherever a number might be used, for example

 G1 Z#123 ; Drill to depth

instructs the system to move the Z axis at the current feed rate to the coordinate contained in the parameter numbered 123. This further highlights the value of comment, the reader would most likely be at a loss here without. Mach3 provides space to store over ten thousand parameters, some of which are used by Mach3 itself, however parameters numbered 1 to 1999 are free and safe to use. Should this be insufficient for an application, the Mach3 documentation details which ranges of numbers to avoid.

Parameters are also useful for containing results of arithmetic, trig functions, temporary values and more. There are a few minor irritations, some things tried might seem sensible but get rejected with a cryptic error message, for example

 G1 X #100 ; This works!

works and moves the X axis at the current feed rate to the controlled point X coordinate contained within parameter #100, as might be expected. However

 G1 X - #100 ; This does not work!

looks like a perfectly reasonable request to move X to a mirror-image position of that above, useful when, say, drilling holes symmetrically either side of something. A minor irritation, some simple arithmetic (explained in a following section) is needed to make this work:

 G1 X[0.0 - #100] ; This works!

and of course

G1 X #-100 ; This can never work!

is total nonsense, as parameters are numbered from +1 upwards.

As a tease for now, the reader is asked to consider what is meant by the perfectly legal (and potentially enormously useful):

 ##123 ; What's this ?


A G code programme begins at the first line of the file, instructions are executed in the order from the start until a M2 or M30 'Programme End' instruction, or the end of file is encountered. The one exception to this is when a M98 subroutine call code is encountered. In response to the M98 subroutine call, Mach3 leaves, or jumps out of, the programme, and continues by executing instructions found from the start of the specified subroutine location. Execution of the subroutine continues, working sequentially down the subroutine listing in the same way as any other part of the programme listing, until a M99 'Return from subroutine' instruction is encountered. This causes the programme to resume from the line following the one containing the invoking M98 subroutine call.

Subroutines can only be used in programmes that end properly using a M2 or M30 'Programme end' code (no programme should ever be written without using a proper 'Programme end' code, as this would be just bad, unsafe practice). All subroutines are written in the programme listing in lines below the 'Programme End' M2 or M30 codes so that subroutine code is not entered accidentally, and all subroutines must end with a M99 'Return' instruction, resuming execution from the instruction following the invoking subroutine call.

Subroutine calls may optionally include a 'Repeat a Number of Times' value. This provides the only 'loop' mechanism available to us. Loops are useful for repeating groups of codes a number of times – for example cutting a number of spiral turns, drilling a row of holes etc. Subroutines may call other subroutines, this is known as 'nesting', and can be useful in producing very compact code.

Extending the logic of using subroutines one stage further, if the entire 'hole mill' function was to be encapsulated in a subroutine, this may then be 'called' any number of times from a larger programme to cut any number of holes of the same or different sizes in a number of positions. This new encapsulating subroutine is of course free to call, or be called by, other subroutines – nesting.

Bringing it all together - Comments, Parameters, Subroutines

A start may now be made on making the hole mill programme into something really versatile and useful, easy to understand, alter and reuse. Considering what values might need to be easily alterable, in general these will fall into one of three groups: numbers about the job, numbers about the tool or tools, and numbers about the method. These are all candidates for grouping together and assigning to parameters. As a general rule, it is as well to include as much as may ever need to be known about the job, only that which is necessary about the tool, and as a result of good design very little, and only that which is essential about the method. When wishing to use a programme again in future, the machinist needs to know the programme and machine will do the job, cut the hole or whatever – the “what it does”, without having to be distracted too much by the finer detail of the “how it does it” – life is too short.

Numbers about the job – for this hole cutting programme, need the hole size, hole depth, and hole position X and Y coordinates. To keep this as universally useful as possible, eliminating assumptions where possible, the hole depth could be better expressed using two values, the Z coordinates for the top and the bottom of the hole. Additionally, including a safe tool height parameter allows the machinist to adjust easily to clear the work-piece and any fixtures.

Numbers about the tool – need the tool size, tool feed rate, and tool speed RPM. Nothing else required in this programme but some more complex programmes might allow some means of tool type choice here, for example a programme may require an end mill for roughing and a ball nose tool for finishing.

Numbers about the method – need the number of spiral turns in this case.

An example of putting this together and assigning numbers to system parameters is shown here : -

 ; Assign users numbers to 'parameters' here
 ; Numbers about the job – 'Hole mill'
 #101 = 10.0 ; Safe tool height
 #102 = 2.0 ; Hole Z top
 #103 = -8.0 ; Hole Z bottom
 #104 = 25.4 ; Hole Diameter mm
 #105 = 0.0 ; Hole centre X
 #106 = 0.0 ; Hole centre Y
 ; Numbers about the tool
 #107 = 6.35 ; Tool Diameter mm
 #108 = 50 ; Tool Feed rate mm/min
 #109 = 650 ; Tool rotation RPM
 ; Numbers about the method
 #110 = 4 ; Turns of the spiral
 ; User Interface End

As a first step in re-writing the hole mill programme in a versatile, reusable style, the information in Fig 401 may be typed into a new copy of the template programme. This has gathered information in a manner that's as helpful as we can make it to the machinist, not necessarily to the machine!

Allowing the machinist to decide how many spiral turns provides an interesting challenge. In the first 'in-line' style programme using a fixed four turns, this is implemented using four similar G code lines repeated in the main programme listing. It would be poor practice indeed if this could only be changed by cutting and pasting similar lines into or out of a programme file. Fortunately Mach3 G code allows the subroutine to be 'called' any number of times for situations just like this. This is the only 'loop' method available. In the 'in-line' style version, the depth value is changed between lines in the programme file. Obviously this will not work in a loop, and a mechanism will need to be provided within the loop to set a new depth for each iteration of the loop. This is not difficult, the cut depth per turn needs to be calculated once in the programme and then added to the 'Z' value each time around the loop. Time for a gentle introduction to arithmetic in Mach3 G code.

Arithmetic – What computers do best, so you don't need to

No need for pages of figures and crossings-out, no need for calculators, slide rules or four figure log tables, why bother with any of that tedious, error-prone nonsense when you have a computer? It's a smart move to make your machining aspirations all the easier to realise by letting the computer do all the calculating that you don't want to, and in a loop it can do it over and over again !

In the 'hole mill' programme, no more than simple addition, subtraction, multiplication and division is needed. Mach3 G code arithmetic should hold few surprises, the four basic arithmetic operators used are '+' add, '-' subtract, '*' multiply and '/' divide. One difference is in the choice of brackets used in expressions. Whereas the rest of the world uses round brackets ( ) or parentheses, as part of the ancient legacy of G code, these are not available for this purpose and '[ ]' square brackets are used instead. Thus what the rest of the world would write as (A+B) becomes [A+B] in G code. It also seems to need brackets in places where they might more commonly be deemed optional, so if in doubt put them in. This should help make sense of:

 G1 X [0.0 - #100] ;

With this knowledge of Mach3 G code arithmetic, pseudo-coding a new 'hole mill' programme using a subroutine to cut spiral turns becomes straightforward, one possible pseudo-code interpretation is shown next : -

 ; Programme code goes below here
 ; In programme 'Hole mill'
 ; Some arithmetic in preparation
 ; Cut depth = [Hole Z bottom – Hole Z top]
 ; Z change = [Cut depth / Turns of the spiral]
 ; New Z = Hole Z top
 ; Move to start, set speed and feed
 ; Call 'Spiral Turn sub', 'Turns of the spiral' times
 ; Z change = 0.0
 ; Call 'Spiral Turn sub' once to finish with circle
 ; Tool up and clear
 ; Stop and Programme End

 ; G Code Subroutines below here
 ; Spiral Turn Subroutine
 ; New Z = [New Z + Z change]
 ; Cut single spiral turn to depth New_Z
 ; Return from subroutine

 ; Programme File End

This identifies some new parameters to contain and use values derived from arithmetic results. These might reasonably be named 'Cut depth', 'Z change' and 'New Z', and need to be assigned to numbered parameter pigeon-holes. Looking for other numeric tidy-ups, new internal parameters may be used with advantage to take values to the 'G2' line in the subroutine – the only line in the whole programme that does any cutting! In the original code there were hard-coded figures of 9.5 and -9.5 used, the exact value equating to difference between hole and tool radii. This is a prime candidate for parameterisation, and choosing to make this value negative at the outset (i.e. using [Tool Radius – Hole Radius]) simplifies its use later as it can be used straight as 'I#nnn' rather than 'I[0.0 - #nnn]'. This could be named 'G2I'. In the other place where this value was seen, the 'X' offset from centre, the hole is now not assumed to be centred at X coordinate 0.0 but the value put into #105 in the user interface. This new 'X' value may also be parameterised and named, maybe, 'G2X', and will be [#105 – G2I], the 'minus' here as G2I is negative already.

There are no particular rules regarding which parameter numbers should be used for what, but having used parameter numbers 101 and up for all values a machinist may wish to alter, use of numbers 201 and up will help identify these internal variables to the reader as belonging to a different group. We could, quite arbitrarily, assign #201 to 'Cut depth', #202 to 'Z change', #203 to 'New Z', #204 to 'G2I', and #205 to 'G2X'.

Implementing the G Code Subroutine

The syntax of the subroutine calling mechanism is at first sight a little quaint and obscure. This stems from the ancient roots of G code and might take a little getting used to, however there's nothing too difficult about it. The start of a subroutine is marked in the programme code file by means of a letter 'O' as the first character of a line. This is followed by up to five digits, for example, a valid programme file fragment for a particular subroutine might read:

 O 12345 ; Start of subroutine
 ; Instructions within subroutine
 M99	; Return from subroutine

One possible cause of confusion, the string of up to five digits following the letter 'O' have no numeric value, serving merely as a unique label to identify this subroutine from any others. It would have been so much nicer if we were allowed a text string here instead; O “Cut_one_clockwise_spiral_turn”, for example, to allow subroutine names to bear some relation to their use – however. Subroutines may appear in any order within the file – an 'O 100' subroutine may appear in the listing before or after any 'O 200' subroutine. When writing code for subroutines, a good 'defensive programming' habit is to write the 'M99 ; Return' line as the first thing you do. That way you won't have to find out the hard way what might go wrong if you leave it out !

To call a subroutine the 'M98' code is used with letter 'P' followed by the subroutine identifying digits label: M98 P 12345 ; Calls sub 'O 12345' once To call a subroutine more than once, the letter 'L' for loop is used followed by the number of times the subroutine 'loop' should be repeated. If the 'L' term is omitted the subroutine is executed once.

A Complete Hole Mill Programme

Bringing all this work together gives a versatile, easy to use and reuse programme. A complete listing of a working programme is included below. Most of the top half is recognisable from the template file used as the starting point. It begins with the 'preamble' codes used to put the machine into a known, safe state. The rest of it is the filled-out 'User Interface' section complete with the parameter assignments as shown above. For those with a regular need to make large holes in sheet, for example, all the things that may ever need to be changed are contained in this part of the file. The 'G21' in the preamble might need changing to 'G20' for those using inches, but to stress a point, effort is put into the programme design to make its use and reuse easier.

All of the work is done in the second half of the programme, listed in Fig 404. The main programme has only five lines that do anything, the comments in the code should explain sufficiently. The rest is all done in the two subroutines. With the development explained above, and with the comment included in the code, no further explanations should be needed !

With the following code listing typed in (or cut and pasted) using a 'Notepad'-like text editor, saved and loaded into Mach3, it will be possible for Mach3 to simulate cutting a hole in the same way as the 'in-line' coded version developed previously. The biggest difference for the machinist is in how easy this new version is to use – and reuse.

Just for fun, adding a few new lines near the top of the main programme demonstrates how easy it now is to reuse the hole mill subroutine to cut another hole using different dimensions - the second code fragemnt shows how. The rotated Mach3 tool path of Fig 406 shows two holes, the hole depth, size, position and number of spirals used has been changed between the first and second hole. This ease of reuse leads on to a slightly more useful and ambitious idea.

Fig 406 - Double Hole mill tool path

 ; Programme File Start
 ; Preamble - puts machine into known, safe state
 G17 ; Select XY plane
 G21 ; Units are mm, use G20 for inches
 G40 ; Cancel cutter radius compensation
 G49 ; Cancel tool length offset
 G61 ; Exact stop
 G50 ; Reset all scale factors to 1.0
 G90 ; Absolute distance mode
 G94 ; Feed mm per minute - mm selected by G21
 ; Machine now set into known, safe state
 ; User Interface Start
 ; Programme Title : 'Hole Mill' - Example
 ; Date : Apr 2013
 ; Author : Jon Freeman
 ; Why was this programme written?
 ; ... Demonstrate prog design using params+subs
 ; What does the programme do?
 ; ... Cuts large holes using small end mill
 ; Any other useful information?
 ; ...
 ; Assign users numbers to 'parameters' here
 ; Numbers about the job 'Hole mill'
 #101 = 10.0 ; Safe tool height, clear of all
 #102 = 2.0 ; Hole Z top
 #103 = -8.0 ; Hole Z bottom
 #104 = 25.4 ; Hole Diameter mm
 #105 = 0.0 ; Hole centre X
 #106 = 0.0 ; Hole centre Y
 ; Numbers about the tool
 #107 = 6.35 ; Tool Diameter mm
 #108 = 50 ; Tool Feed rate mm/min
 #109 = 650 ; Tool rotation RPM
 ; Numbers about the method
 #110 = 4 ; Turns of the spiral
 ; User Interface End

 ; Programme code goes below here
 ; 'Hole mill' parameters set above - See Fig 401
 S#109 F#108 M3 ; Set speed, feed, start spindle
 G0 Z#101 ; Ensure tool up at safe height
 M98 P 2000 ; Call the 'hole miller' subroutine
 M5 ; STOP Spindle
 M30 ; END PROGRAMME and Rewind
 ; G Code Subroutines below here
 O 2000 ; 'Hole mill' subroutine
 ; Some arithmetic in preparation
 ; Cut depth = [Hole Z bottom – Hole Z top]
 #201 = [#103 - #102] ; Cut depth
 ; Z change = [Cut depth / Turns of the spiral]
 #202 = [#201 / #110] ; Z change
 ; New Z = Hole Z top
 #203 = #102 ; Copy to not destroy original
 ; G2I = [[Tool Diameter – Hole Diameter] / 2.0]
 #204 = [[#107 - #104] / 2.0] ; G2I
 ; G2X = Hole centre X - G2I
 #205 = [#105 - #204] ; G2X
 G0 X#205 Y#106 ; Move tool to above start position
 G0 Z#102 ; Tool down to almost touch job
 ; Call 'Spiral Turn Sub', 'Turns of the spiral' times
 M98 P 12345 L #110 ; Call sub 'Turns of spiral' times
 #202 = 0.0 ; Set depth change to zero
 M98 P 12345 ; Single call cuts final circle
 G0 Z#101 ; Tool up and away
 M99 ;Return from 'Hole mill' subroutine
 O 12345 ; Spiral Turn Subroutine
 ; New_Z = [New_Z + Z_Change]
 #203 = [#203 + #202] ; Depth 'New_Z' updated
 ; Cut single spiral turn to depth 'New_Z'
 G2 X#205 Y#106 Z#203 I#204 J0.0 ; Cut one spiral turn
 M99 ; Return from 'Spiral Turn' subroutine
 ; G Code Programme File End

An example - modify above code to machine two holes : -

 ; Programme code goes below here
 ; 'Hole mill' parameters set above - See Fig 401
 S#109 F#108 M3 ; Set speed, feed, start spindle
 G0 Z#101 ; Ensure tool up at safe height
 M98 P 2000 ; Call the 'Hole mill' subroutine
 ; Some Fun – Set-up for and cut another hole
 #103 = -11.9 ; New hole depth
 #104 = 18.3 ; New hole size
 #105 = 22.75 ; New hole X position
 #110 = [#110 * 2.0] ; Double num of spirals
 M98 P 2000 ; Mill Another Hole
 ; End of New Fun Stuff
 M5 ; STOP Spindle
 M30 ; END PROGRAMME and Rewind

To be continued

Continued in Part Six