(cross-posted from the machsupport forum)
Hi Folks,
I'm new to gcode programming, having purchased my first CNC machine earlier this year (a Tormach PCNC1100). I have been a computer hardware engineer for many years, and have done a lot of low-level coding (assembly, microcode, RTL, FSM, etc).
When I started out my gcode programming learning curve, at first I was greatly disheartened to read that Mach3 does not support conditional code execution, since I had been planning extensive use of fixturing to get around the lack of a toolchanger on the Tormach, and conditionals make a lot of complex functionality easy to implement. Working around the lack of conditionals started to get annoying.
However, I realized that since subroutine label numbers can be the result of an expression evaluated at run-time, we can use expression math to effect conditional execution. As long as we can reduce our condition to a 1 or 0, we can multiply this condition flag by the intended subroutine label number to either execute the operation or else execute subroutine 0, which is coded to always return immediately and thus provides the "false" path to avoid execution of the other subroutine. The same method can be used to create an if-then-else clause if you use addition instead of multiplication.
For example, let's say my part discovery subroutine has finished probing my 16-part fixture, discovered which slots are populated and which are mpty, and now we've got a set of parameter flags that enable or disable code execution for that fixture slot. Let's say the part enable flag for the current part is stored in parameter #4. If the part is present, we want to first execute sub 21, and then call either sub 22 or sub 23 depending on parameter #5.
An example of the condition code execution technique I've been using:
Code:
#4 = 0 (this flag is actually set by the part probing subroutine, set it here for this example)
#5 = 1 (this flag chooses between execution of sub 22 or sub 23)
(here is an effective if-then conditional clause)
M98 P[ #4 * 21 ] ( call either "dummy" sub 0 or "real" subroutine 21 if the part is enabled )
M30
O0 (this the "false" subroutine which always returns immediately)
M99
O21
... (real code goes here)
(here is an effective if-then-else conditional clause)
M98 P[ #5 + 22 ] ( call sub 23 if flag #5 is set, or else call sub 22 if flag is not set)
M99
O22
... (real code goes here)
M99
O23
... (real code goes here)
M99
In order to use a wider assortment of conditions, we can use math to reduce an
arbitrary expression to a 0 or 1 value.
Say, for example, that we want to execute an additional facing subroutine
only if the probed part turns out to exceed our nominal raw stock allowance
of 1.25 inches. The actual height of the part as discovered by the probing
routine is stored in parameter #6.
Here is the gcode to effect this pseudo-code: if( #6 > 1.25 ) then { call sub P30 }.
Code:
#1 = [ 1.25 - #6 ] ( subtract part actual height from intended - if result is negative, then actual is > 1.25)
#2 = [ #1 + ABS[ #1 ]] ( adding the absolute value of a negative number to itself produces zero; a positive number yields non-zero)
#3 = [ 1 XOR #2 ] ( this serves two purposes: invert the logic sense of #2, and force it from zero/non-zero to zero/one )
M98 P[ #3 * 30 ]
This could also be written more compactly like so:
Code:
M98 P[ 30 * [ 1 XOR [ [1.25 - #6] + ABS [ 1.25 - #6 ] ] ] (same as above, but harder to read)
The same techniques can be used to code case statements, since a base label number plus an offset effectively becomes a jump table. You could also store a table of operation subroutine numbers to call for each of a number of different cases, and then use the switch expression as an index into the parameter table to find which subroutine number to call for that case value.
Here is the gcode to effect this pseudo-code: switch (#6); case(1): call sub P41; case(2): call sub P42; case(3): call sub P43;
Of course, I highly recommend being careful to make sure your expressions will only result in valid label numbers. In this case, we must make sure that #6 can only be 1,2 or 3. A default case statement could be constructed with more math and range checking, etc.
One thing to note is that Mach seems to choke on this kind of code when it comes to generating the toolpath display, so I get all kinds of bogus screen displays for the toolpath, but it then executes the actual machine operations just as I intended.
I've been using these techniques for a few weeks now with my 16-part fixture, which I can load with any number of parts and have the operations executed only for the parts that are loaded. With the data returned from my part probing routine, I can also setup exact fixture offsets for each discovered part, and optionally include a coordinate system rotation for that slot if the part is a little skewed by burrs or swarf, etc.
Perhaps the biggest benefit I've gotten from using conditional code execution has been when I setup a series of flags at the top of my part-program to enable or disable certain operations or part slots. For example, if I break a tool on the seventh part during the fourth operation (out of many ops) then I can fix the problem, and restart the code by disabling all operations except #4, and also disabling parts 1-6. Then operation #4 on parts 7-16 executes, and then I re-enable the remaining operations and all of the part slots, then run the program again to complete execution. This has saved me much heartache from trying to use the "run from here" function or else lots of cutting-and-pasting to execute code fragments in new files to complete a partial operation.