Yet another iteration of nothing
Random header image... Refresh for more!

Category — C Programming

Brief History of G-Code, Numerical Control, and G-Code Complilers

G-Code (more precisely G and M Code, more formally, RS-274D or other names) is a programming code that dates back circa 1950, and was originally developed to control mechanical machining operations using paper tape.

It shows.

The last ratified standard was in 1980, and the last open source variant in 2000 (RS274NGC_3.pdf – 2000). Most manufacturers extend it in their own directions, making incompatible changes to others doing the same thing. The standardized code has no ability to do any of the commonly done flow control (loops)or logic operations (if, then, else, and, nor). Outrageous, says someone coming from a computing background.

When one examines in detail the actual history, intended operations and uses of these machines, a different picture emerges.

Simple Machines have existed for thousands of years, with compound or complex machines for hundreds, and each generally performed simple, repeated tasks. For example: windmills were used to grind grains to flour continuously, the Archimedes screw was used to lift water. More complex machines, such as the lathe, exist to rotate items, removing material from their diameters. In most cases, the machining progression is a straightforward, beginning-to-end process. 

To illustrate, here is a sample process to produce a wooden candlestick from a raw work-piece on a lathe. It can be broken down into single, discrete steps:

  • load the work-piece into the lathe
  • start the lathe (bow, pedal, water, steam, electricity or other motive power)
  • turn the work-piece cylindrical with a tool
  • carve away material to form the general intended shape
  • carve decorative features if desired
  • drill a hole for the candle
  • stop the lathe
  • unload the machined work-piece

In the above example, there is very little need for any advanced flow control or logic. The workflow is a straightforward progression of isolated steps performed in a procedural manner.

Historically, someone would perform all those steps by hand, and doing so quickly with minimal mistakes became a production bottleneck when industries switched from cottage level craftsmanship to mass production. After all, a Master woodworker can only turn out so many finished pieces in a day. Adding more people and more lathes would speed up production, but costs also rise, since more Master level woodworkers are needed, as is more machines. It is an oversimplification, but it can be said the focus changed to more production at less cost. 

As technology progressed, what used to take a Master level worker with a handheld gouge and pedal powered rotary motion to produce could now be manufactured in a single cycle by an unskilled person placing a work-piece blank into a lathe equipped with multiple cutting knives on a bar and triggering the entire series of operations with a single motion, drastically decreasing machining time. Factories in the 20th century had rows of such machines, each doing a single set of operations on the work-piece, and once completing its process, transferred the work-piece to the next machine to do the following operations. In the generally more complex metalworking world, ways were found of reproducing the actions of the master worker via mechanical means (cams, levers and gears shaped to replicate the same actions). Now that same Master level worker could oversee gangs of lesser skilled personnel overseeing the mechanically automated machines.

Manufacturing continued to move from skilled worker to assembly line worker to automation.

This was acceptable for industry for a time, but setting up each machine mechanically to do only one job and then changing the job performed required expensive and time consuming changes to the mechanisms. A logical extension of the technique was to make a single machine capable of performing the actions of several other machines. Enter the world of Numerical Control (NC); the precursor to Computer Numerical Control.

Machines were constructed that could have their work patterns altered by a punched paper tape, which was fed into a reader, which in turn controlled the machine operation. As technology progressed, the paper tape was replaced with magnetic media, but the basic operations required to produced the same candlestick really did not change; the same operations had to occur, in the same order; only the timeframe for the production of the product had shifted from hours to seconds. The production of G-Code reflected that process; the produced code had little need of more than the ability to loop operations and utilize subroutines. Typical operations were:

  • re-iterate a cutting/drilling/boring/milling procedure until final depth was reached
  • utilize a preexisting movements (subroutine) when a particular point in the main program was reached (move the machine to a specific spot, trigger the drilling subroutine, move to another spot, trigger the drilling routine again)

Very procedural programming.

Enter the 21st century, with vertical machining centers capable of self-feeding in work stock, performing lathe and mill operations simultaneously while interfacing to external automation systems, and now operational complexity has outstripped the original purposes.

Machine control manufactures extended the standard G-Code to include storage variables, more so-called canned cycles, goto, if-then-else flow control, AND,OR,NOR logic, and even so-called conversational programming (select operation, select size, select depth, select direction, speeds and feeds, push go; the machine generates the G-Code), all to the goal of increasing production, decreasing cycle time, improving cost-effectiveness.

But what if you have none of those features built in to the controller? Enter G-Code Compilers.

General purpose computer programming languages have long since needed flow control and conditional operations, and it is entirely possible to interface the two. An example from my own research is using C language to handle the flow and conditionals, and have it produce executable G-Code for each case. Here is an example from the web; someone made a compiler that converts logical arguments in C to LinuxCNC compatible G-Code:

CGCC Gcode CompilerFeatures and Examples

// Constants
const float X_Holes = 10;
const float Y_Holes = 10;
// Loop
for (float y = 0; y < Y_Holes; y++) {
for (float x = 0; x < X_Holes; x++) {
if (x != y) {
G00 Z1
G00 X[x] Y[y]
G01 Z0 F1
G00 Z1
}
}}
The above code is as follows:
– Want to drill a grid of 100 holes in a 10 x 10 inch grid
– the code is repeated 100 times with different X and Y coordinates.
– The code has to skip a line of holes down the diagonal where X equals Y

The person states that LinuxCNC CAN produce this workflow through the added-on O-codes (extension of the original RS-274D standard) but it is hard to produce good programs quickly. The result is a program that takes C language syntax incorporating G-Code and produces LinuxCNC compatible Gcode.

Others have similar needs:

GCME – G-Code Meta Compiler

January 5, 2016   Comments Off on Brief History of G-Code, Numerical Control, and G-Code Complilers

KFLOP C Programming Dissection – My Init.c Pendant Code

#include “KMotionDef.h”

// Example Init program that includes “smooth” MPG motion example
// which makes use of the exponential motion command.

// Which Pins were somewhat arbitrary; 11 I/O were needed, so JP4 and JP6 were used. The remainder were wired to level shifting boards and remain unused.
#define SELECTX 24 // KFLOP JP4, Pin 15 (I/O 24):
#define SELECTY 17 // KFLOP JP4, Pin 06 (I/O 17):
#define SELECTZ 19 // KFLOP JP4, Pin 10 (I/O 19):
#define SELECT4 21 // KFLOP JP4, Pin 12 (I/O 21):

#define FACTOR1 23 // KFLOP JP4, Pin 14 (I/O 23):
#define FACTOR10 25 // KFLOP JP4, Pin 16 (I/O 25):
#define FACTOR100 26 // KFLOP JP6, Pin 05 (I/O 26):

#define TAU 0.02 // smoothness factor (Low Pass Time constant seconds)
#define FINAL_TIME 1.0 // Set final dest after this amount of time with no change

#define QA 16 // KFLOP JP4, Pin 5 (I/O 16): define to which IO bits the AB signals are connected; I’ve assumed A+ and B+
#define QB 18 // KFLOP JP4, Pin 7 (I/O 18):
//Note: Dynomotion seems to just use single ended, the pendant is dual/differental; as such, two inputs are not needed, could reduce total to nine from eleven.
//Note: Current assignment of QA and QB results in backwards operation. Swapped lower down in code. Need to verify hardwire assignment correct.

int main()
{
double T0, LastX=0, LastY=0, LastZ=0, Tau;
int result;
int BitA,Change1=0,Change2=0, DiffX2;
int PosNoWrap, NewPos, Pos=0, wraps;
int InMotion=FALSE,Axis,LastAxis=-1;
double LastChangeTime=0,Target,Factor=0;

// Add a small amount of Coordinated Motion Path smoothing if desired
// Tau = 0.001; // seconds for Low Pass Filter Time Constant
// KLP = exp(-TIMEBASE/Tau);
KLP=0; // force to 0 to disable
// printf(“Tau=%f KLP=%f\n”,Tau,KLP);

// Main program starts here; includes MPGSmooth subroutines
for (;;) //Main program, cycle forever
{
// Pendant reading code
// convert quadrature to 2 bit binary
//BitA = ReadBit(QA);
//PosNoWrap = (ReadBit(QB) ^ BitA) | (BitA<<1);
BitA = ReadBit(QB);
PosNoWrap = (ReadBit(QA) ^ BitA) | (BitA<<1);

// Diff between expected position based on average of two prev deltas
// and position with no wraps. (Keep as X2 to avoid division by 2)
DiffX2 = 2*(Pos-PosNoWrap) + (Change2+Change1);

// Calc quadrature wraparounds to bring Diff nearest zero
// offset by 128 wraps to avoid requiring floor()
wraps = ((DiffX2+1028)>>3)-128;

// factor in the quadrature wraparounds
NewPos = PosNoWrap + (wraps<<2);

Change2 = Change1;
Change1 = NewPos – Pos;
Pos = NewPos;

// Determine which Axis is selected; Logic tree. If none are selected, disable pendant MPG control, as the unit is in the OFF position
// Pendant purchased did not have a ENABLE switch, no free conductors to install one; this will work fine to function as an erstaz one.

if (ReadBit(SELECTX)) // is x selected?
Axis=0; //X axis in my setup
else if (ReadBit(SELECTY)) // is y selected?
Axis=1; //Y Axis in my setup
else if (ReadBit(SELECTZ)) // is z selected?
Axis=2; //Z axis in my setup
else if (ReadBit(SELECT4)) // is 4th axis selected?
Axis=3; //A axis in my setup
else
Change1 = 0; // Disable Pendant, since OFF is selected (none of the above resolve to TRUE)
Factor = 0; // Force the change factor off (13 Nov 2015)

// Determine which multiplier is selected; unlike above, must be one of the three positions by hardware design
// Factor numbers by design, how much to move per MPG pulse

if (ReadBit(FACTOR1)) // is X1 selected?
// Factor = 1.5748031496062992125984251968504;
Factor = 1;
else if (ReadBit(FACTOR10)) // is X10 selected?
Factor = 10;
//Factor = 15.748031496062992125984251968504;
else if (ReadBit(FACTOR100)) // is X100 selected?
Factor =15;
//Factor =157.48031496062992125984251968504 ;
}

return 0;
}

// Debounce a bit
// return 1 one time when first debounced high
// return 0 one time when first debounced low
// return -1 otherwise
#define DBTIME 300
int Debounce(int n, int *cnt, int *last, int *lastsolid)
{
int v = -1;

if (n == *last) // same as last time?
{
if (*cnt == DBTIME-1)
{
if (n != *lastsolid)
{
v = *lastsolid = n; // return debounced value
}
}
if (*cnt < DBTIME) (*cnt)++;
}
else
{
*cnt = 0; // reset count
}
*last = n;
return v;
}

November 17, 2015   Comments Off on KFLOP C Programming Dissection – My Init.c Pendant Code

KFLOP C Programming Dissection – My Init.c E-Stop Code

#include “KMotionDef.h”

int elast=0,elastsolid=-1,ecount=0; // for debouncing estop pushbutton
int Debounce(int n, int *cnt, int *last, int *lastsolid);
int DoPC(int cmd);

#define ESTOP 168 // set to the external estop input bit
#define TMP 10 // which spare persist to use to transfer data
#include “C:\KMotion433\C Programs\KflopToKMotionCNCFunctions.c”

int main()
{
double T0, LastX=0, LastY=0, LastZ=0, Tau;
int result;
int BitA,Change1=0,Change2=0, DiffX2;
int PosNoWrap, NewPos, Pos=0, wraps;
int InMotion=FALSE,Axis,LastAxis=-1;
double LastChangeTime=0,Target,Factor=0;

// Main program starts here; includes ESTOP, MPGSmooth subroutines
for (;;) //Main program, cycle forever
{
WaitNextTimeSlice();

// Handle ESTOP interrupts
result = Debounce(ReadBit(ESTOP),&ecount,&elast,&elastsolid);
if (result == 0)
{
DoPC(PC_COMM_ESTOP);
printf(“Local ESTOP Active!\n”);
MsgBox(“Local ESTOP ACTIVE!”,MB_OK|MB_ICONEXCLAMATION);
}
}

// Debounce a bit
// return 1 one time when first debounced high
// return 0 one time when first debounced low
// return -1 otherwise
#define DBTIME 300
int Debounce(int n, int *cnt, int *last, int *lastsolid)
{
int v = -1;

if (n == *last) // same as last time?
{
if (*cnt == DBTIME-1)
{
if (n != *lastsolid)
{
v = *lastsolid = n; // return debounced value
}
}
if (*cnt < DBTIME) (*cnt)++;
}
else
{
*cnt = 0; // reset count
}
*last = n;
return v;
}

November 17, 2015   Comments Off on KFLOP C Programming Dissection – My Init.c E-Stop Code