Code to drive a CNC machine via a PC LPT port lookalike 25 pin 'D', experiment in 'PC/Mach3' replacement. Designed to compile and run on mbed LPC1768, Freescale KL25Z and Freescale KL46Z. Proved on LPC1768 and KL25Z, problem with serial port on KL46Z. Reads subset of 'G Codes' through usb/serial port and drives 3 stepper/servo drives for X, Y and Z, also similar Step/Dir outputs for spindle motor control. Emulates PC LPT, outputs 'charge pump', proved driving Seig KX3 CNC mill

Dependencies:   MODSERIAL mbed

command_interpreter.cpp

Committer:
JonFreeman
Date:
2014-03-14
Revision:
3:7aaf0072cc22
Parent:
2:b3c668ec43ac

File content as of revision 3:7aaf0072cc22:

#include "mbed.h"
#include "rtos.h"
#include "MODSERIAL.h"
#include "cnc.h"
#include <cctype>

extern  MODSERIAL pc;
extern  struct  Gparams    last_position;
extern  void    move_to_XYZ   (struct pirbufgrain & ins)   ;
extern  void    flags_report_cmd (struct singleGparam * a)   ;
extern  void    report_inputs   ()  ;

fl_typ  feed_rate = 1.0;  //  global scope, mm per minute. DEFAULTS to 1.0mm per min, very slow.
signed long    spindle_rpm = 0;  //  global scope




#if defined I2C_Enable
extern    I2CSlave slave;//(PTE0, PTE1); on KL25

int i2c_checksumchecker (char * buf, int len)    {
    int k, i = 0x01;
    for (k = 0; k < len; k++)
        i += buf[k];
    i &= 0x0ff;
    return  i;
}

int i2c_checksumchecker (char * buf)    {
    return  i2c_checksumchecker (buf, strlen(buf));
}

char * add_csum (char * buf, int len)    {           //  Adds checksum to end of binary string of known length
    int j;
    char    cs = 0;
    for (j = 0; j < len; j++) {
        cs += buf[j];
    }
    buf[len] = 0xff - cs;
    buf[len + 1] = 0;
    return  buf;
}

char * add_csum (char * buf)    {           //  Adds checksum to end of null terminated string
    return  add_csum    (buf, strlen(buf));
}

void    i2c_handler    (void const * name)
{
    const int i2buflen = 16;
    int err = 0;
    char    buf[i2buflen];
    char msg[20] = "Message 2snd\0";
    add_csum(msg);
    slave.address(0xc0);
    err = slave.write(msg, strlen(msg) + 1); // Includes null char    //  returns 0 on success, nz otherwise
    while   (true)  {
        int i = slave.receive();
        switch (i) {
            case I2CSlave::NoData:  //  Happens most of the time    NoData - the slave has not been addressed
                osThreadYield();  //  Using RTOS on this project
                break;
            case I2CSlave::ReadAddressed:   //   - the master has requested a read from this slave
                err = slave.write(msg, strlen(msg) + 1); // Includes null char    //  returns 0 on success, nz otherwise
                pc.printf("RdAddr'd ");
                break;
            case I2CSlave::WriteGeneral:    //   - the master is writing to all slave
                err = slave.read(buf, i2buflen);    //  returns 0 on success, nz otherwise
                pc.printf("i=%d, - the master is writing to all slave %s\r\n", i, buf);
                break;
            case I2CSlave::WriteAddressed:  //   - the master is writing to this slave
                err = slave.read(buf, i2buflen);    //  returns 0 on success, nz otherwise
                pc.printf("M wr-> [%s]", buf);
                for (int z = 0; z < strlen(buf); z++)
                    pc.printf("%2x, ", buf[z]);
                pc.printf("cs %2x\r\n", i2c_checksumchecker(buf));
                break;
            default:
                pc.printf("Unknown I2C code %d\r\n");
                break;
        }   //  end of switch (i) upon result of slave.receive()
        if  (err)   {
            pc.printf("Err %d with i = %d\r\n", err, i);
            err = 0;
        }
        memset  (buf, 0, i2buflen);    // Clear buffer
    }   //  end of while (true)
}       //  end of void    i2c_handler    (void const * name)

#endif


const int   goodcodes[] = {0,'a','b','c','i','j','l','r','x','y','z'};    //  possible G Code options
const int   const_numofcodes = sizeof(goodcodes) / sizeof(int);

int find_char_in_goodcodes  (int target)        //  Returns position of char in goodcodes[], 0 if not found.
{
    for (int i = 1; i < const_numofcodes; i++)
        if  (goodcodes[i] == target)
            return  i;
    return  0;
}

/*
void    get_codepositions   (struct singleGparam * a, struct Gparams & p)
    Only call from "void    g0g1cmdcore (struct singleGparam * a, double f_rate)"
Purpose:
    G code line may have any number of valid axes or parameters entered in any order or position.
    This function detects any X,Y,Z,A,I,J,R entries in 'p' if present and copies values into their
    respective positions within singleGparam 'a', setting the 'changed' flag for each to true if found,
    false if not found
struct  Gparams {  //  Where possibly messy G code line gets ordered and sorted into
    struct  singleGparam   x, y, z, i, j, r, a, b, c, d;   //  After sorting, know where to find any X, Y etc values !
}   ;
*/
void    get_codepositions   (struct singleGparam * source_array, struct Gparams & dest)
{
//const int   goodcodes[] = {0,'a','b','c','i','j','l','r','x','y','z'};    //  possible G Code options
//const int   const_numofcodes = sizeof(goodcodes) / sizeof(int);
//  source_array is the array filled by function 'void    command_line_interpreter    ()'.
//  It contains any parameters read from the command line :
//  source_array[i].c may contain 'x' or 'y' etc to tie this entry to one of the 'goodcodes' - or not
    int         codecnt[const_numofcodes +1];
    int         codepos[const_numofcodes +1];
    int j;
    for (j = 0; j < const_numofcodes; j++)
        codecnt[j] = codepos[j] = 0;        //  Zero all results
    for (int i = 1; i <= source_array[0].i; i++)  {       //  for number of parameters passed to us here
        for(j = 0; j < const_numofcodes; j++)  {  //  for a, for b, ... for x, then y, then z
            if  (source_array[i].c == goodcodes[j])   {
                codecnt[j]++;   //  Count of number of 'a's, 'b's ... 'x's, 'y's, 'z's.  All should be 0 or 1 but could be more
                codepos[j] = i; //  Identifies the a[?] containing last incidence of goodcodes[j]
            }
        }
    }
    dest.x.changed = dest.y.changed = dest.z.changed = dest.a.changed = false;
    dest.i.changed = dest.j.changed = dest.r.changed = false;
    dest.x.flt = last_position.x.flt;   //  copy previous coordinates in case not re-specified
    dest.y.flt = last_position.y.flt;
    dest.z.flt = last_position.z.flt;
    dest.a.flt = last_position.a.flt;
    dest.i.flt = last_position.i.flt;
    dest.j.flt = last_position.j.flt;
    dest.r.flt = last_position.r.flt;
    j = codepos[find_char_in_goodcodes('a')];
    if  (j)  {
        dest.a.changed = true;
        dest.a.flt = source_array[j].flt;
    }
    j = codepos[find_char_in_goodcodes('x')];
    if  (j)  {
        dest.x.changed = true;
        dest.x.flt = source_array[j].flt;
    }
    j = codepos[find_char_in_goodcodes('y')];
    if  (j)  {
        dest.y.changed = true;
        dest.y.flt = source_array[j].flt;
    }
    j = codepos[find_char_in_goodcodes('z')];
    if  (j)  {
        dest.z.changed = true;
        dest.z.flt = source_array[j].flt;
    }
    j = codepos[find_char_in_goodcodes('i')];
    if  (j)  {
        dest.i.changed = true;
        dest.i.flt = source_array[j].flt;
    }
    j = codepos[find_char_in_goodcodes('j')];
    if  (j)  {
        dest.j.changed = true;
        dest.j.flt = source_array[j].flt;
    }
    j = codepos[find_char_in_goodcodes('r')];
    if  (j)  {
        dest.r.changed = true;
        dest.r.flt = source_array[j].flt;
    }
}


void    g2g3cmdcore (struct singleGparam * source_array, int twoorthree) {
    struct  Gparams tmp;
    struct  pirbufgrain start_point, end_point, centre_point, next_point;
    int state = 0, arc_steps;
    fl_typ  rad_start, rad_end, start_angle, end_angle, next_angle, swept_angle, angle_step, arc_len, z_step;
    if  (twoorthree != 2 && twoorthree != 3)    {
        pc.printf("Err got %d when should be 2 or 3", twoorthree);
        return;
    }
    if  (twoorthree == 2)
        pc.printf("g2 Clockwise Arc\r\n");
    else
        pc.printf("g3 CounterClockwise Arc\r\n");
    get_codepositions   (source_array, tmp);  //  will overwrite with new where entered
    pc.printf("X %s\r\n", tmp.x.changed ? "T":"F");
    pc.printf("Y %s\r\n", tmp.y.changed ? "T":"F");
    pc.printf("Z %s\r\n", tmp.z.changed ? "T":"F");
    pc.printf("R %s\r\n", tmp.r.changed ? "T":"F");
    pc.printf("I %s\r\n", tmp.i.changed ? "T":"F");
    pc.printf("J %s\r\n", tmp.j.changed ? "T":"F");
    if  (!tmp.x.changed || !tmp.y.changed)                  state |= 0x10000;    //  Error, X or Y missing
    if  (tmp.r.changed && !tmp.i.changed && !tmp.j.changed) state |= 1;  //  Validated R mode got R not I not J
    if  (!tmp.r.changed && tmp.i.changed && tmp.j.changed)  state |= 2;  //  Validated IJ mode not R got I got J
    start_point.x   = last_position.x.flt;
    start_point.y   = last_position.y.flt;
    start_point.z   = last_position.z.flt;
    end_point.x     = tmp.x.flt;
    end_point.y     = tmp.y.flt;
    end_point.z     = tmp.z.flt;
    switch  (state) {
        case    1:  //  Radius format arc
            pc.printf("Valid Radius format arc TO DO - not yet implemeted\r\n");
        break;
        case    2:  //  Centre format arc ** OFFSETS ARE RELATIVE ** Abs coordinates not catered for
            pc.printf("Valid Centre format arc\r\n");
            centre_point.x = start_point.x + tmp.i.flt;
            centre_point.y = start_point.y + tmp.j.flt;
            rad_start   = hypot(start_point.x - centre_point.x, start_point.y - centre_point.y);
            rad_end     = hypot(end_point.x - centre_point.x, end_point.y - centre_point.y);
            pc.printf("Start  point X %.3f, Y %.3f\r\n", start_point.x, start_point.y);
            pc.printf("Centre point X %.3f, Y %.3f\r\n", centre_point.x, centre_point.y);
            pc.printf("End    point X %.3f, Y %.3f\r\n", end_point.x, end_point.y);
            pc.printf("Rad start %.3f, Rad end %.3f\r\n", rad_start, rad_end);
            if  (fabs(rad_start - rad_end) > 0.001)  {
//            if  ((rad_start - rad_end) > 0.001 || (rad_start - rad_end) < -0.001)  {
                state |= 0x20000;
                pc.printf("Radii mismatch error in g2g3\r\n");
            }
            start_angle =   atan2(start_point.y - centre_point.y, start_point.x - centre_point.x);
            end_angle   =   atan2(end_point.y - centre_point.y, end_point.x - centre_point.x);
            swept_angle = end_angle - start_angle;
            //swept_angle = 0.0;  //=IF((B$8=2);IF((H$24>-0.0001);(H$24-2*PI());(H$24))
                                //           ;IF((H$24>0.0001);(H$24);(H$24+2*PI())))
            if  (twoorthree == 2)   {
                if  (swept_angle > -epsilon)
                    swept_angle -= TWO_PI;
            }
            else    {   //  twoorthree is 3
                if  (!(swept_angle > epsilon))
                    swept_angle += TWO_PI;
            }
            arc_len  = fabs(rad_start * swept_angle);
            pc.printf("start_angle %.3f, end_angle %.3f, swept_angle %.3f, arc_len %.3f\r\n", start_angle, end_angle, swept_angle, arc_len);
            arc_steps = (int)(4.0 + fabs(1.7 * rad_end * swept_angle)); //  fiddle factors adjusted empirically !
            angle_step = swept_angle / arc_steps;
            next_angle = start_angle;
            z_step = (end_point.z - start_point.z) / arc_steps;
            next_point.z = start_point.z;
            pc.printf("Number of steps = %d, angle_step %.3f\r\n", arc_steps, angle_step);
            for (int i = 0; i < arc_steps; i++) {   //  cut 'arc_steps' straight lines
                next_angle += angle_step;
                next_point.x = centre_point.x + (rad_start * cos(next_angle));
                next_point.y = centre_point.y + (rad_start * sin(next_angle));
                next_point.z += z_step; 
                pc.printf("X %.3f, Y %.3f\r\n", next_point.x, next_point.y);
                Thread::wait(300);
                next_point.f_rate = feed_rate;
                move_to_XYZ (next_point);
            }
        break;  //  end of case    2:  //  Centre format arc ** OFFSETS ARE RELATIVE ** Abs coordinates not catered for
        default:    //  Input error detected
            pc.printf("Input error detected in g2g3, code %x\r\n", state);
        break;
    }   //  end of switch(state)
}

void    g0g1cmdcore (struct singleGparam * source_array, fl_typ f_rate)    //  Updates any / all of x, y, z NCOs
{   //  Only get here when some G0 or G1 input has been read.  G0 or G1 determined by f_rate
    struct  pirbufgrain ins;//, outs;
    struct  Gparams tmp;
    get_codepositions   (source_array, tmp);  //  will overwrite with new where entered
    if  (!tmp.x.changed && !tmp.y.changed && !tmp.z.changed)    {
        pc.printf("No change in X, Y or Z in G0/G1. Ignoring\r\n");
        return;
    }
    ins.x  = tmp.x.flt;
    ins.y  = tmp.y.flt;
    ins.z  = tmp.z.flt;
    ins.f_rate = f_rate;
    move_to_XYZ (ins);
}

void    g0cmd (struct singleGparam * a)    //  Updates any / all of x, y, z NCOs
{
    g0g1cmdcore (a, feed_rate_max);     //  Defined parameter in code
}

void    g1cmd (struct singleGparam * a)    //  Updates any / all of x, y, z NCOs
{
    g0g1cmdcore (a, feed_rate);     //  Settable feed_rate
}

void    g2cmd (struct singleGparam * a)   { //  Clockwise arc
    g2g3cmdcore (a, 2);
}

void    g3cmd (struct singleGparam * a)   { //  Counter clockwise arc
    g2g3cmdcore (a, 3);
}

void    fcmd (struct singleGparam * a)   {  //  Set Feed Rate command
    if  (a[1].flt < 0.0)    {
        pc.printf("feed rate %.1f ? Setting to 0\r\n", a[1].flt);
        a[1].flt = 0.0;
    }
    if  (a[1].flt > feed_rate_max)    {
        pc.printf   ("Error, can't set feed rate to %.1f, max is %.1f, ", a[1].flt, feed_rate_max);
        a[1].flt = feed_rate_max;
    }
    pc.printf   ("Setting feed_rate to %.1f\r\n", a[1].flt);
    feed_rate = a[1].flt;
}
extern  void    spindle_control (signed long ss)  ;
extern  bool    spindle_running ()  ;

void    M3cmd (struct singleGparam * a)   { spindle_control (spindle_rpm);  }
void    M5cmd (struct singleGparam * a)   { spindle_control (0);  }

void    scmd (struct singleGparam * a)   {
    if  (fabs(a[1].flt) > spindle_max)    {
        pc.printf   ("Errror setting spindle RPM, can't set to %.0f, ignoring request\r\n", a[1].flt);
        return;
    }
    pc.printf   ("Setting spindle RPM to %.0f Can set Pos or Neg for fwd/rev\r\n", a[1].flt);
    spindle_rpm = (signed long) a[1].flt;
    if  (spindle_running())
        spindle_control (spindle_rpm);
    pc.printf("Readback ss %d\r\n", spindle_rpm);
}

extern  void    target_cmd (struct singleGparam * a)   ;

void    stopcmd (struct singleGparam * a)   {pc.printf("Stop ! er, not working yet\r\n");}
//void    m1cmd (struct singleGparam * a)   {pc.printf("m1 Optional Programme Stop\r\n");}
//void    m3cmd (struct singleGparam * a)   {pc.printf("m3 Rotate Spindle Clockwise\r\n");}
//void    m4cmd (struct singleGparam * a)   {pc.printf("m4 Rotate Spindle Counter Clockwise\r\n");}
//void    m5cmd (struct singleGparam * a)   {pc.printf("m5 Stop Spindle\r\n");}
/*void    m30cmd (struct singleGparam * a)   {pc.printf("m30 Programme End and Rewind\r\n");}
void    m47cmd (struct singleGparam * a)   {pc.printf("m47 Repeat Prog from First Line\r\n");}
void    m48cmd (struct singleGparam * a)   {pc.printf("m48 Enable Speed and Feed Override\r\n");}
void    m49cmd (struct singleGparam * a)   {pc.printf("m49 Disable Speed and Feed Override\r\n");}
void    m98cmd (struct singleGparam * a)   {pc.printf("m98 Call Subroutine\r\n");}
void    m99cmd (struct singleGparam * a)   {pc.printf("m99 Return from Subroutine\r\n");}
void    g10cmd (struct singleGparam * a)   {pc.printf("g10 Coord System Origin Set\r\n");}
void    g17cmd (struct singleGparam * a)   {pc.printf("g17 XY Plane Select\r\n");}
void    g20cmd (struct singleGparam * a)   {pc.printf("g20 Inch\r\n");}
void    g21cmd (struct singleGparam * a)   {pc.printf("g21 mm\r\n");}

void    g40cmd (struct singleGparam * a)   {pc.printf("g40 Cutter Compensation Off\r\n");}
void    g50cmd (struct singleGparam * a)   {pc.printf("g50 Reset Scale Factors\r\n");}
void    g53cmd (struct singleGparam * a)   {pc.printf("g53 Move in Absolute Coordinates\r\n");}
void    g90cmd (struct singleGparam * a)   {pc.printf("g90 Absolute Distance Mode\r\n");}
*/
//void    g4cmd (struct singleGparam * a)   {pc.printf("g4 Dwell\r\n");}
//void    g91p1cmd (struct singleGparam * a)   {pc.printf("g91.1 \r\n");}

//void    report_inputs   ()  {
void    report_ins_cmd  (struct singleGparam * a)  {
   report_inputs();
}
extern  void    lissajous   (fl_typ)  ;

void    lisscmd (struct singleGparam * a)   {
    lissajous   (feed_rate);
}

void    menucmd (struct singleGparam * a);
struct kb_command  {
    const char * cmd_word;         //  points to text e.g. "menu"
    const char * explan;
    void (*f)(struct singleGparam *);   //  points to function
}  ;

struct  kb_command const * command_list_ptr = NULL;   //  Pointer switched between 'input_syntax_check' and 'command_execute'

struct  kb_command const  input_syntax_check  [] = {
    {"menu", "Lists available commands, same as ls", menucmd},
    {"ls", "Lists available commands, same as menu", menucmd}    
    }   ;

struct  kb_command const command_execute[] = {
    {"menu", "Lists available commands, same as ls", menucmd},
    {"ls", "Lists available commands, same as menu", menucmd},
    {"stop", "To Stop the Machine !", stopcmd},
    {"f ", "To set Feed Rate mm/min, e.g. f 25", fcmd},
    {"s ", "To set Spindle RPM, e.g. S 1250", scmd},
    {"m3", "Start Spindle at last 'S'", M3cmd},
    {"m5", "Stop Spindle", M5cmd},
    {"g0", "Rapid move", g0cmd},
    {"g1", "Linear Interpolation - move straight at current feed rate", g1cmd},
    {"g2", "Helical Interpolation CW (Arc, circle)", g2cmd},
    {"g3", "Helical Interpolation CCW (Arc, circle)", g3cmd},
    {"liss", "Run Lissajous pattern generator", lisscmd},
    {"flags", "Report System Flags", flags_report_cmd},
    {"inputs", "Report State of Input bits", report_ins_cmd},
    {"target", "Identify computer device", target_cmd},
};

int numof_menu_items;
void    menucmd (struct singleGparam * a)
{
    pc.printf("At menucmd function - listing commands:-\r\n");
    for(int i = 0; i < numof_menu_items; i++)
        pc.printf("[%s]\t\t%s\r\n", command_list_ptr[i].cmd_word, command_list_ptr[i].explan);
    pc.printf("End of List of Commands\r\n");
}


void    nudger  (int code)  {   //  Allows <Ctrl> chars to nudge machine axes
    //  Using <Ctrl>+ 'F', 'B' for Y, 'L', 'R' for X, 'U', 'D' for Z
    //                 6    2          12   18         21   4
    struct  pirbufgrain dest;
    dest.x = last_position.x.flt;
    dest.y = last_position.y.flt;
    dest.z = last_position.z.flt;
    dest.f_rate = feed_rate;
    switch  (code)  {
        case    6:  //  'F' move -Y
        dest.y -= 0.1;
        break;
        case    2:  //  'B' move +Y
        dest.y += 0.1;
        break;
        case    12: //  'L' move +X
        dest.x += 0.1;
        break;
        case    18: //  'R' move -X
        dest.x -= 0.1;
        break;
        case    21: //  'U' move +Z
        dest.z += 0.1;
        break;
        case    4:  //  'D' move -Z
        dest.z -= 0.1;
        default:
        break;
    }   //  end of switch
    move_to_XYZ (dest);
}

////class CLI {

/*
void    command_line_interpreter    ()
Purpose:

*/

void    command_line_interpreter    (void const * name)
{
const int MAX_PARAMS = 10, MAX_CMD_LEN = 120;
static  char    cmd_line[MAX_CMD_LEN + 4];
static  struct  singleGparam   params[MAX_PARAMS + 1];
static  int     cl_index = 0, lastalpha = 0;
static  fl_typ  fracmul;
    if  (true)  {
        command_list_ptr = command_execute;
        numof_menu_items = sizeof(command_execute) / sizeof(kb_command);
    }
    else    {
        command_list_ptr = input_syntax_check;
        numof_menu_items = sizeof(input_syntax_check) / sizeof(kb_command);
    }
    while   (true)  {
        while  (pc.readable()) {
            int     ch;
            if  (cl_index > MAX_CMD_LEN)  {   //  trap out stupidly long command lines
                pc.printf   ("Keyboard Error!! Killing stupidly long command line");
                cl_index = 0;
            }
            ch = tolower(pc.getc());
            if  (ch == '\r' || ch >= ' ' && ch <= 'z')
                pc.printf("%c", ch);
            else    {                   //  Using <Ctrl>+ 'F', 'B' for Y, 'L', 'R' for X, 'U', 'D' for Z
                cl_index = 0;           //                 6    2          12   18         21   4
                pc.printf("[%d]", ch);
                nudger  (ch);
            }
            if(ch != '\r')  //  was this the 'Enter' key?
                cmd_line[cl_index++] = ch;  //  added char to command being assembled
            else    {   //  key was CR, may or may not be command to lookup
                cmd_line[cl_index] = 0; //  null terminate command string
                if(cl_index)    {   //  If have got some chars to lookup
                    int i, wrdlen;
                    for (i = 0; i < numof_menu_items; i++)   {   //  Look for input match in command list
                        wrdlen = strlen(command_list_ptr[i].cmd_word);
                        if(strncmp(command_list_ptr[i].cmd_word, cmd_line, wrdlen) == 0)  {   //  If match found
                            bool negflag = false;
                            int state = 0, paramindex;
    //                            pc.printf("Found match for word [%s]\r\n", kbc[i].wrd);
                            for(paramindex = 0; paramindex < MAX_PARAMS; paramindex++) {
                                // Clear out whole set of old parameters ready for anything new on this line
                                params[paramindex].i = 0;   //  for integer parameters
                                params[paramindex].c = 0;   //  for last alpha char, helps tie 'X' to '-23.5' etc
                                params[paramindex].flt = 0.0; //  for floating point parameters
                                params[paramindex].ul = 0;
                                params[paramindex].changed = false;
                            }
                            paramindex = 0;
                            //  read any parameters from command line here
                            //  Using parameters[0] as count of parameters to follow
                            while   (wrdlen <= cl_index)  {
                                ch = cmd_line[wrdlen++];
                                if(isalpha(ch)) lastalpha = ch;
                                if(ch == '-')   negflag = true;
                                if(ch == '+')   negflag = false;
                                switch  (state) {
                                    case    0:  //  looking for start of a number string
                                        if(isdigit(ch)) {   //  found first digit of a number string
                                            paramindex++;
                                            if(paramindex > MAX_PARAMS)    {
                                                wrdlen = cl_index;  //  exit condition
                                                pc.printf("WARNING - too many parameters, ignoring extra\r\n");
                                            } else    {
                                                params[paramindex].i = ch - '0';
                                                params[paramindex].c = lastalpha;
                                                state = 1;  //  Found first digit char of number string
                                            }
                                        }
                                        break;
                                    case    1:  //  looking for end of a number string
                                        if(isdigit(ch)) {   //  accumulating integer from string
                                            params[paramindex].i *= 10;
                                            params[paramindex].i += ch - '0';
                                        } else    { //  found non-digit terminating number
                                            if  (ch == '.')  {
                                                state = 2;
                                                fracmul = 0.1;
                                                params[paramindex].flt = (fl_typ)params[paramindex].i;
                                            } else    {
                                                params[0].i++;    //  count of validated parameters
                                                state = 0;  //  Have read past last digit of number string
                                                if(negflag) {
                                                    params[paramindex].i = -params[paramindex].i;
                                                    negflag = false;
                                                }
                                                params[paramindex].flt = (fl_typ)params[paramindex].i;
                                            }
                                        }
                                        break;
                                    case    2:  //  looking for fractional part of double
                                        if(isdigit(ch)) {   //  accumulating fractional part from string
                                            params[paramindex].flt += (fl_typ)((ch - '0') * fracmul);
                                            fracmul /= 10.0;
                                        } else    { //  found non-digit terminating double precision number
                                            params[0].i++;    //  count of validated parameters
                                            state = 0;  //  Have read past last digit of number string
                                            if(negflag) {
                                                params[paramindex].i = -params[paramindex].i;
                                                params[paramindex].flt = -params[paramindex].flt;
                                                negflag = false;
                                            }
                                        }
                                        break;
                                    default:
                                        break;
                                }   //  end of switch state
                            }       //  end of while wrdlen < cl_index
    //                            pc.printf("Found match to [%s] with %d parameters\r\n", command_list_ptr[i].wrd, paramindex);
                            command_list_ptr[i].f(params);   //  execute command
                            i = numof_menu_items + 1;    //  to exit for loop
                        }
                    }       // End of for numof_menu_items
                    if(i == numof_menu_items)
                        pc.printf("No Match Found for CMD [%s]\r\n", cmd_line);
                }           //  End of If have got some chars to lookup
                pc.printf("\r\n>");
                cl_index = lastalpha = 0;
            }               // End of else key was CR, may or may not be command to lookup
        }                   //  End of while (pc.readable())
        osThreadYield();  //  Using RTOS on this project
    }
}

////} cli;