Code for 'Smart Regulator' featured in 'Model Engineer', November 2020 on. Contains all work to August 2020 including all code described. Top level algorithm development is quite spares, leaving some work for you! Any questions - jon@jons-workshop.com

Dependencies:   mbed BufferedSerial Servo2 PCT2075 I2CEeprom FastPWM

cli.cpp

Committer:
JonFreeman
Date:
2020-12-05
Revision:
5:6ca3e7ffc553
Parent:
3:43cb067ecd00

File content as of revision 5:6ca3e7ffc553:

/*
Command Line Interpreter code module.
Purpose -
    Provides easy interface to pc terminal programme for use during programme development, debug etc.
    Also usable as comms subsystem in finished code for accepting commands, reporting data etc.
*/
#include "mbed.h"
#include "field.h"
#include "Alternator.h"
#include "BufferedSerial.h"
#include <cctype>
using namespace std;

ee_settings_2020 user_settings     ;

extern  BufferedSerial pc;
extern  void    maketable   ()  ;
extern  void    query_system    (struct parameters & a)    ;
extern  uint32_t    ReadEngineRPM  ()   ;
extern  double  Read_Link_Volts   ()  ;
extern  double  Read_Field_Volts   ()  ;
extern  double  Read_Ammeter        ()  ;
extern  void    charge_pump_override    (parameters & a) ;   //  0 disables, !0 enables charge pump
extern  void    set_v_out_opamp    (parameters & a) ;   //  0 to 1.0 sets opamp output in range 0 to 5v, charge pump permitting

//bool    ee_settings_2020::wr   (char c, uint32_t i)  {           //  Write one setup char value to private buffer 'settings'
/*
void    slope_cmd   (struct parameters & a)   {
    Provides a quick way of filling lookup table.
    Sets percent at 3000 RPM with first parameter 0 to 100
*/
void    slope_cmd   (struct parameters & a)   {     //  Requires two params. First %@ 3000 rpm, second slope MAX +/-20 % per krpm above
    const   int startat = 1800; //  rpm to start at
    const   int rpm_per = 200; //  rpm per lookup table step
    const   int threshold = startat / rpm_per;
    signed char    at_power_beyond, slope;
    pc.printf   ("Slope - set pct = %d @ 3krpm, slope %d pct above\r\n", (int32_t)a.dbl[0], (int32_t)a.dbl[1]);
    if  (a.numof_dbls != 2) 
        pc.printf   ("Need 2 params in slope, got %d, ", a.numof_dbls);
    else    {
        pc.printf   ("Got slope params %.1f, %.1f\r\n", a.dbl[0], a.dbl[1]);
        if  (a.dbl[0] > 100.0)  a.dbl[0] = 100.0;
        if  (a.dbl[0] < 0.0)    a.dbl[0] = 0.0;
        if  (a.dbl[1] > +20.0)  a.dbl[1] = +20.0;
        if  (a.dbl[1] < -20.0)  a.dbl[1] = -20.0;
        at_power_beyond    = (signed char)a.dbl[0];
        slope   = (signed char)a.dbl[1];
        pc.printf   ("Setting slope ");
        for (int i = 0; i < threshold; i++) {   //  Zero all very low speed settings
            user_settings.wr    (0, i);
        }
        for (int i = threshold; i < 21; i++)    {
            user_settings.wr    (at_power_beyond, i);
            pc.printf   ("%d, ", at_power_beyond);
            at_power_beyond += slope;
            if  (at_power_beyond < 0)      at_power_beyond = 0;
            if  (at_power_beyond > 100)    at_power_beyond = 100;
        }
        pc.printf   ("\r\nDone\r\n");
        user_settings.save  ();
        maketable   ();
    }
}

void    table_tweak_cmd   (struct parameters & a)   {     //  Requires two params. First '20', '22' etc representing hundreds RPM. Second 0 to 99 percent
    char    txt[100];
    uint32_t    d[3];
    txt[0] = 0;
    if  (a.numof_dbls != 2) 
        sprintf   (txt, "Need 2 params, got %d, ", a.numof_dbls);
    else    {
        d[2] = (uint32_t)a.dbl[0];
        d[0] = d[2] / 2;
        d[1] = (uint32_t)a.dbl[1];
        if  (d[0] > 20 || d[1] > 100 || d[2] != d[0] * 2)
            sprintf (txt + strlen(txt), "Param out of range %d, %d, ", d[2], d[1]);
        else    {
            pc.printf   ("Off to reset table %d RPM, %d percent\r\n", d[2] * 100, d[1]);
            user_settings.wr    ((char)d[1], d[0]);
            user_settings.save  ();
            maketable   ();
        }
    }
    if  (txt[0])
        pc.printf   ("Errors in table_tweak_cmd - %s\r\n", txt);
    else
        pc.printf   ("Good in table_tweak_cmd, RPM=%d, percentage=%d\r\n", d[0] * 500, d[1]);
}


//extern  VEXT_Data   Field;
extern  FieldControl   Field;

extern  int32_t set_engine_RPM_lit  (uint32_t   RPMrequest) ;   //  Returns speed error pos or neg
extern  int32_t set_engine_RPM_pct  (uint32_t   RPMrequest) ;   //  Returns speed error pos or neg
void    ss_cmd   (struct parameters & a)   {     //  Set engine Speed 0 - 8000 RPM
    uint32_t    v = (uint32_t) a.dbl[0];
    pc.printf   ("Setting engine RPM to %d, measured RPM returned = %d\r\n", v, set_engine_RPM_lit  (v));
}

void    sp_cmd   (struct parameters & a)   {     //  Set engine Speed 0 - 8000 RPM
    uint32_t    v = (uint32_t) a.dbl[0];
    pc.printf   ("Setting engine RPM percent to %d, measured RPM returned = %d\r\n", v, set_engine_RPM_pct  (v));
}

void    rfcmd   (struct parameters & a)   {     //  
//    pc.printf   ("Field.measured_period = %u", (uint32_t)Field.get_measured_period());
//    pc.printf   (", Field.measured_pw_us = %u, duty_cycle = %.3f\r\n", (uint32_t)Field.measured_pw_us, Field.duty_cycle());
    pc.printf   ("Field duty cycle measured = %.3f\r\n", Field.get_duty_ratio());
}

void    vcmd    (struct parameters & a)   {
    pc.printf   ("link volts %.2f, field volts %.2f\r\n", Read_Link_Volts(), Read_Field_Volts());
}

void    acmd    (struct parameters & a)   {
    pc.printf   ("amps %.2f\r\n", Read_Ammeter());
}


extern  void    set_pwm (double)   ;    //  Range 0.0 to 1.0
void    fls_cmd (struct parameters & a)   {
    pc.printf   ("Setting field.limiter to %d percent\r\n", (int)a.dbl[0]);
    set_pwm (a.dbl[0] / 100.0);
}

void    set_defaults_cmd (struct parameters & a)   {
    struct  sldandt * p = NULL;
    bool    flag = true;
    int i = 0;
    while   (flag)  {
        p = user_settings.inform(i);    //  Returns NULL when i goes out of range
        if  (p == NULL)
            flag = false;
        else    {
            pc.printf   ("min %d, max %d, default %d, text %s\r\n", p->min, p->max, p->de_fault, p->txt);
            user_settings.wr    (p->de_fault, i);
            i++;
        }
    }
    user_settings.save  ();
}

void    servodir_cmd (struct parameters & a)   {
    char    ch = (char)a.dbl[0];
    if  (a.numof_dbls != 1 || ch > 1) {
        pc.printf   ("Wrong servodir set\r\n");
        return  ;
    }
    if  (user_settings.rd(SERVO_DIR) != ch) {
        pc.printf   ("Setting servo dir %.1f  \r\n", a.dbl[0]);
        user_settings.wr(ch, SERVO_DIR);
        user_settings.save();
    }
}

char * modes_txt[] =    {
    "0\tSafe nothing mode for cli cmd testing",
    "1\tPot to Servo direct, field OFF",
    "2\tVariable voltage",
    "3\tFixed voltage",
    "4\tEngine Revs Control",
    "5\tSet Engine to Driver's Pot",
    "6\tControl Engine by Current Load",
    "7\tAuto Test",
}   ;

int numof_op_modes = sizeof(modes_txt) / sizeof(char *);

char *  get_mode_text   (uint32_t mode)  {
    if  (mode > numof_op_modes)   {
        pc.printf   ("mode OOR in get_mode_text, %d\r\n", mode);
        mode = numof_op_modes - 1;
    }
    return  modes_txt[mode];
}

void    mode20_cmd (struct parameters & a)   ;
void    mode_cmd (struct parameters & a)   {
    if  (a.numof_dbls == 1 && (uint32_t) a.dbl[0] <= numof_op_modes)  {
        a.dbl[1] = a.dbl[0];
        a.dbl[0] = OP_MODE; //23.0;
        a.numof_dbls = 2;
        mode20_cmd  (a);
        return;
    }
    pc.printf   ("Current mode is %d  \r\nTo set operating mode, use mode n :- where \r\n", user_settings.rd(OP_MODE));
    for (int i = 0; i < numof_op_modes; i++)
        pc.printf   ("%s\r\n", get_mode_text(i));
}

void    mode20_cmd (struct parameters & a)   {
    struct  sldandt * p = NULL;     //  Pointer to struct containing max, min and default values, and brief text descriptor for a command
    int i = 0;
    bool    flag = true;
    bool    save_settings = false;
    int32_t    cmd_num = (int32_t) a.dbl[0], first_p = (int32_t) a.dbl[1];
    pc.printf   ("At user setting, numofparams = %d, dbl[0]=%.2f, dbl[1]=%.2f\r\n", a.numof_dbls, a.dbl[0], a.dbl[1]);
    if  (a.numof_dbls < 2)   {              //  Need at least command number followed by at least one parameter
        pc.printf   ("Listing Setup\r\nTo alter, enter us param number, new value\r\n");
        while (flag)    {
            p = user_settings.inform(i);    //  Returns false when i goes out of range
            if  (p == NULL)
                flag = false;
            else    {
                pc.printf   ("%d\tval %d, min %d, max %d, default %d, text %s\r\n", i, user_settings.rd(i), p->min, p->max, p->de_fault, p->txt);
                i++;
            }
        }
        return  ;
    }       //  When too few parameters, output list. Done.
    p = user_settings.inform(cmd_num);  //  Set  pointer to min, max, default and text info
    if  (p == NULL) {
        pc.printf   ("Invalid command number %d in user setting entry\r\n", cmd_num);
        return  ;
    }
    if  (first_p < p->min || first_p > p->max)  {
        pc.printf   ("%s\r\nParameter min %d, max %d, you entered %d. Not setting\r\n", p->txt, p->min, p->max, first_p);
        return  ;
    }
    pc.printf   ("Hoping to set [%s] to %d\r\n", p->txt, first_p);
    switch  (cmd_num)    {
        case    OP_MODE:    //  
        case    WARM_UP_DELAY:
        case    WARMUP_SERVO_POS:
        case    SPEED_CTRL_P:
        case    SERVO_DIR:
            i = user_settings.rd(cmd_num);
            if  (i == first_p)
                pc.printf   ("No need, [%s] already set to %d\r\n", p->txt, i);
            else    {
                user_settings.wr((char)first_p, cmd_num);
                save_settings = true;
                pc.printf   ("Setting [%s] to %d\r\n", p->txt, first_p);
            }
            break;
        default:
            pc.printf   ("No code for [%s]\r\n", p->txt);
            break;
    }
    if  (save_settings)
        user_settings.save();
}

extern  void    auto_test_initiate  (int bulb_count)  ;
void    auto_test_kickoff_cmd (struct parameters & a)   {
    auto_test_initiate  ((int)a.dbl[0]);
}

void    test_Fsfs_cmd (struct parameters & a)   {
    uint32_t    rpm = (uint32_t)a.dbl[0];
    pc.printf   ("Field.set_for_speed %d returned %d\r\n", rpm, Field.set_for_speed(rpm));
}

void    null_cmd (struct parameters & a)   {
    pc.printf   ("At null_cmd, parameters : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
}

void    menucmd (struct parameters & a);

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

struct  kb_command const command_list[] = {
    {"?", "Lists available commands, same as ls", menucmd},
    {"cp", "Charge pump disable (0), enable (!0)", charge_pump_override},
    {"vo", "Set out volts to ESC 0 - 100 pct", set_v_out_opamp},
    {"ft", "Test Field.set_for_speed fn", test_Fsfs_cmd},
    {"at", "Initiate Auto Test sequence", auto_test_kickoff_cmd},
    {"svod", "Set servo sense 0 or 1", servodir_cmd},
    {"sd", "Set User Settings Defaults", set_defaults_cmd},
    {"us", "Set User Settings", mode20_cmd},
    {"tt", "Table Tweak 0 - 100", table_tweak_cmd},
    {"ss", "Set Speed 0 - 8000 RPM", ss_cmd},
    {"sp", "Set Speed 0 - 100 percent", sp_cmd},
//    {"tl", "Throttle logger tester, enter param 0.0 - 1.0", throt_log_cmd},
    {"rf", "Check rise and fall on VEXT", rfcmd},
    {"v", "Read Battery volts", vcmd},
    {"i", "Read Ammeter", acmd},
    {"fl", "Field limiter set 0 to 99 percent", fls_cmd},
    {"mode", "Set operating mode - as us23", mode_cmd},
    {"slope", "Field limiter set pct 0 to 99 @3k, slope pct per k above", slope_cmd},
    {"q", "Query system - toggle message stream on/off", query_system},
    {"nu", "do nothing", null_cmd},
};

const int numof_menu_items = sizeof(command_list) / sizeof(kb_command);
void    menucmd (struct parameters & a)
{
    pc.printf("\r\nIntelligent Alternator Controller - Jon Freeman 2020\r\nAt 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[i].cmd_word, command_list[i].explan);
    pc.printf("End of List of Commands\r\n");
}

void    command_line_interpreter    ()
{

    const int MAX_CMD_LEN = 120;
    static  char    cmd_line[MAX_CMD_LEN + 4];
    static  int     cl_index = 0;
    int ch;
    char * pEnd;
    static struct  parameters  param_block  ;
    while  (pc.readable()) {
        ch = tolower(pc.getc());
       // pc.printf("%c", ch);
        if  (cl_index > MAX_CMD_LEN)  {   //  trap out stupidly long command lines
            pc.printf   ("Error!! Stupidly long cmd line\r\n");
            cl_index = 0;
        }
        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); //  was used on cnc to nudge axes a tad
        }
        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[i].cmd_word);
                    if(strncmp(command_list[i].cmd_word, cmd_line, wrdlen) == 0 && !isalpha(cmd_line[wrdlen]))  {   //  If match found
                        for (int k = 0; k < MAX_PARAMS; k++)    {
                            param_block.dbl[k] = 0.0;
                        }
                        param_block.position_in_list = i;
                        //param_block.last_time = clock    ();
                        param_block.numof_dbls = 0;
                        pEnd = cmd_line + wrdlen;
                        while   (*pEnd)  {          //  Assemble all numerics as doubles
                            param_block.dbl[param_block.numof_dbls++] = strtod    (pEnd, &pEnd);
                            while   (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd)  {   
                                pEnd++;
                            }
                        }
                        pc.printf   ("\r\n");
//                        for (int k = 0; k < param_block.numof_dbls; k++)
//                            pc.printf   ("Read %.3f\r\n", param_block.dbl[k]);
//                        param_block.times[i] = clock();
                        command_list[i].f(param_block);   //  execute command
                        i = numof_menu_items + 1;    //  to exit for loop
                    }   //  end of match found
                }       // 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 = 0;
        }               // End of else key was CR, may or may not be command to lookup
    }                   //  End of while (pc.readable())

}