Introduction to Conditions and Looping
This installment of our Mini-Tutorial on G-Code Macros is all about three important constructs in G-Code Macro Programming:
- GOTO: The ability to jump around arbitrarily in G-Code.
- IF: The ability to make decisions based on Conditions.
- WHILE: The ability for G-Code to perform repetitive tasks.
So far, we've learned how to parameterize g-code with variables and expressions and how to package it into building blocks using subprogram and macro calls, but these three simple concepts of GOTO, IF, and WHILE, will boost the power of g-code even further.
GOTO: Jumping Ahead in G-Code
Sometimes we don't want to just keep executing g-code one block after the other from top to bottom. It can be helpful if the g-code jumps around. We've seen some of this jumping in the form of subprogram and macro calls, but those are specialized kinds of jumps that assume you'll want to pick up right after the jump after the subprogram or macro finishes.
GOTO is pretty simple to use, it's just GOTO followed by the sequence number ("N" number) you'd like to jump to. For example:
N100 GOTO 120
N110 M2 (Program Stop)
N120 (GOTO jumps directly here)
That program snippet uses a GOTO whose address is "120" to jump over N110 and go directly to N120. The G-Code has no choice in the matter, it will always follow the GOTO, and for that reason, GOTO is sometimes referred to as an "Unconditional Branch". If all we added to the G-Code language was the GOTO, it would not be all that useful. Perhaps it would be handy for temporarily jumping over some code. It's certainly easier to stick one GOTO in to jump over code that to go through line by line and add a Block Skip ("/") character, but that's about it.
The real power of GOTO comes when we're able to let the G-Code make decisions about when to use the GOTO. For that, we use the "IF" statement.
IF: Conditional Branching
While GOTO represents an "unconditional branch" in G-Code execution, IF allows for "conditional branching". Imagine being able to ask a question, and perform the GOTO only if the answer to the question is "Yes". In essence, that's what the IF does:
IF [condition is true] GOTO x
The entity enclosed in the square brackets is called a "conditional expression", and they don't really look like "condition is true", that's just an English translation of their general form.
Instead, Conditional Expression consist of a comparison of some kind:
IF [#100 EQ 0] GOTO 110
That "[#100 EQ 0]" is a conditional expression that checks whether the value of #100 is zero. If it is, the G-Code will perform a GOTO taking it to N110. If its value isn't zero, the G-Code falls through to the next line, which we see is an alternate GOTO that takes us to N200. That's how conditional branching with IF and GOTO work together. Note that the line following the IF statement could have been anything, it doesn't have to be a GOTO.
But what if we wanted to do something other than "GOTO" if #100 is equal to zero? Perhaps we simply want to change the value of #100 so it isn't zero, avoiding a potential divide by zero problem down the line. When we want to do something other than GOTO, we use an "IF..THEN" statement:
IF [#100 EQ 0] THEN #100 = 1 (Avoid dividing by zero!)
#110 = #105 / #100
We can put any macro expression after the THEN, but no g or m-codes. If you want g or m-codes, use the GOTO's to arrange to go to lines containing those codes.
Let's talk about the rules for Conditional Expressions. Not just any expression can be a conditional expression. Here are the rules:
- Conditional expressions must be enclosed in square brackets ("[" and "]").
- A Conditional Expression consists of two operands and a comparison operator.
- An operand may be a # variable or a number.
- Comparison Operators:
NE: Not equal
GT: Greater than
GE: Greater than or equal
LT: Less than
LE: Less than or equal
- Comparisons may be further grouped with square brackets and AND, OR, or XOR. For example:
[[0 LE #100] AND [#100 LE 1000]] (True if the value of #100 is 0 to 1000)
Those are the basics. Be sure to consult your CNC controller documentation for more details. G-Wizard Editor will do a quick check on those rules for you and flag expressions that need to be conditional but that are not.
WHILE: Automating Repetition
WHILE statements make it easy to automate repetition:
WHILE [conditional expression is true] DOm (m = 1, 2, or 3)
(one or more lines of g-code to repeat)
ENDm (m = the same value as the DO's m)
WHILE uses a Conditional Expression just like IF, and it is subject to the same rules. In WHILE, the Conditional Expression will case the g-code blocks between the DO and END to be executed over and over while checking the Conditional Expression. So long as it is true, the WHILE loop will execute again. Once it turns false, execution skips the blocks between DO and END and goes directly to the block following the END.
Let's suppose you wanted to perform a particular g-code operation 5 times. You might write the WHILE loop to do that as follows:
#100 = 1
WHILE [#100 LE 5] DO1
(Some G-Code Blocks Go Here to Be Repeated Each Loop)
#100 = #100 + 1 (Increase #100 by 1 each iteration of the loop)
That's all there is to it. We use g-code variable #100 as a counter. It starts out as 1, and we add 1 to it each time around the loop. So long as it is less than or equal to 5, we keep doing the loop. When it gets to be 6, it is greater than 5, so the Conditional Expression becomes false and we immediately skip running the loop and go to whatever g-code follows the END for the loop.
DO and END Addresses
You probably noticed the single digits after DO and END in the example. These are required and are typically 1, 2, or 3. Your controller may vary. Their purpose is to match up the different DO's and END's when WHILE loops are nested:
WHILE [conditional] DO1
WHILE [conditional] DO2
WHILE [conditional] DO3
Remember to keep the correct number so the correct END goes with its DO!
The G-Wizard CNC Editor Can Help You Debug Macros and Identify Infinite Loops
I got an inquiry from a G-Wizard G-Code Editor user wanting to know if I could help diagnose a problem he was having. It seems he was trying to figure out why a program that had been output by his BobCAD CAM software was getting an error message out of Mach3. I his post wasn't set up quite right for the Mach3 g-code dialect at first, but his description of the problem was intriguing and sounded deeper than that, so I volunteered to try to help. He had run the code through the Predator Editor/Simulator that comes with BobCAD, but it saw no errors. Mach3 on the other hand was seemingly never finishing the backplot and complaining cryptically that "Return called with no sub in effect":
Mach3 executing g-code that infinite loops...
I hadn't had the pleasure of seeing this behavior from Mach, so I thought I'd learn something knew by helping diagnose the problem. I also suspected I could make GWE better in the process, and no sooner did I load this machinist's program than GWE promptly locked up and started acting hung. Excellent--that which does not kill us, makes us stronger!
It didn't take long looking at the program in Notepad to see what was happening. Looks like BobCAD wants to create a subprogram for each machining operation and then call them one after the other at the top. It can make reading the code less than obvious, but it is not that uncommon a practice. Unfortunately, for some reason, the software forgot to put a program stop after it was done calling the subprograms for each operation. This caused execution to just fall through the bottom of the main program and start executing the very first subprogram all over again. The subprogram wasn't expecting this, since it was designed to have been directly called with an M98 (if you're not following these g-codes, don't worry, I will explain them in our G-Code tutorial). When the subprogram gets done, it uses an M99 to return to the block right after the M98. Therein lies the problem. If it was never called by M98, there is no return location.
Most g-code dialects, when faces with an M99 and no M98 having called will simply start executing the very first line of g-code at the top of the program again. It's not the worst choice they could've made, but I would've preferred an error (or alarm as the CNC world likes to call them) be issued. Instead, we get a situation where this particular program was going round and round doing everything over and over again.
Now that Mach3 message makes a bit more sense. We did indeed issue a "Return" (M99) without a "Sub" (M98) in effect, and so we are indeed "looping". In fact, we're engaged in what the software world calls an "Infinite Loop". Absent the operator stopping the program, it will never stop of its own accord. The fix is easy--just add an M02 in the right place to stop before falling through to the subprogram.
Here is some sample code that demonstrates the problem:
An infinite loop in g-code...
This code just draws a little 1" square, then it calls the subprogram at line N600. Execution would jump down to the sub and run until it hits the return "M99" at line N1100. Right below the sub call I have commented out the line that should've been there to stop further execution--see line N800. We needed an M02 right there to stop the program. Since there is nothing there but a comment, the g-code falls on through N800 and starts executing the subprogram below. DOH!
How about a little help finding these things, G-Wizard?
Well why not help, it isn't that hard. Two capabilities were added to the GW CNC Editor--the ability to detect and automatically stop infinite loops, and a simulator enhancement that makes it easy to step through and see what all those M97/98/99, G65, and GOTO (if you run Fanuc Macros) commands are doing.
First, the infinite loop detection. Since we can't get a loop unless the code, um loops, GWE keeps track of every block that causes execution to deviate from just going to the next block below. It keeps a count of how many times the deviation is made. In an infinite loop, one or more of those deviations (let's just call them "Goto's" because that's what they are) will happen an infinite number of times. Now I don't know about you, but I'm not getting any younger, and I don't want to wait that long to see if I have an infinite loop. Therefore, GWE has a "goto limit" defined, and if any particular goto exceeds that limit, it issues a warning message and stops the program. Here is the message for the sample above:
The infinite loop warning tells exactly where the trouble is occuring and what the suspicious behavior is...
As you can see, the warning tells exactly where the trouble is (line 13), and what the suspicious behavior is--line 13 issued a goto 5 times, which is the default goto limit. Now does that guarantee an infinite loop? No, it doesn't. You could easily have written code that calls the same routine more than 5 times intentionally. However, we have to pick a limit somewhere to work with, and that's one that won't let the loop get too carried away before we intervene and have a look at it. If we are running a program where we expect to run more than 5 times, we can use the Simulator Options menu to change the limit:
In addition to the popup, we add an appropriate Hint to help diagnose what's going on and draw attention to the line in question. You can see line 13 is lit with the red "ERROR" indicator. Here is the Hint for that line:
Hint for a possible infinite loop...
BTW, can you see how that hint makes it a whole lot easier to understand what all is going on there than just starting at "N1100 M99"? Hints are designed to refresh your memory, teach you what the g-codes mean, and provide information that is otherwise hard to come by for deeper understanding. Isn't that just what we need from a CNC Simulator?
Okay, that makes it pretty easy to tell what's going on when there is an infinite loop. But, understanding what's going on when a program is jumping and looping all over the place is still not easy. As I was stepping through this BobCAD-generated code, I realized we need a way to navigate that focuses on the goto's. Perhaps you've seen or even tried out our fancy "5 Step" function. It does so much more than the normal "Step" or "Single Blocking" we see in most simulators. If you right click the "5 Step" toolbar button (or you can use the Simulator menu), you'll get the Simulator Options--refer to the screenshot above. "5 Step" gives us the ability to:
- Step by any number of blocks, the default being "5", hence the name.
- Step to the next rapid move. This is likely a retract that lets you get past a bunch of feedrate moves you're not interested in.
- Step the the next Dwell (G04). This is a sneaky one. Dwells typically don't affect a program much. So you can stick one in and use it as a bookmark. Use "5 Step" to jump to your next "bookmark".
- Step to next toolchange (M06). Always a popular time saver.
- Step to the next subprogram op or GOTO. This is our new addition.
Stepping to a goto makes navigating through these subprograms fast and easy. If you're going to be working on a g-code program with a lot of goto-activity, I'd recommend you get a flavor for the high level workings by using the "Step to the next GOTO" option first. That'll let you breeze through, see who is calling who, and understand the high level structure of the g-code. You'll also see the effects of each subprogram unfolding on the backplot. Take a note or two on what you want to revisit and dig deeper into. Drop some G04 Dwells, switch to stepping to dwells, and you'll be able to focus on those areas.
If this all sounds interesting, useful, or even fun, please join our G-Wizard Editor Trial. The editor is free for a 30-day trial and useful for all sorts of things.
1. Create an infinite loop in a subprogram so you can see how GW Editor detects them.
Try the Free Trial Version of G-Wizard CNC Program Editor...
No credit card required--just your name and email.
Next Article: Accessing Modal Data