Dual Brushless Motor ESC, 10-62V, up to 50A per motor. Motors ganged or independent, multiple control input methods, cycle-by-cycle current limit, speed mode and torque mode control. Motors tiny to kW. Speed limit and other parameters easily set in firmware. As used in 'The Brushless Brutalist' locomotive - www.jons-workshop.com. See also Model Engineer magazine June-October 2019.

Dependencies:   mbed BufferedSerial Servo PCT2075 FastPWM

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers cli_BLS_nortos.cpp Source File

cli_BLS_nortos.cpp

00001 /**
00002 *   Code in this file : -
00003 *
00004 *   STM3_ESC board uses two serial ports. 
00005 *       One (use pc.printf etc) provides 9600 baud comms to a pc or other terminal. Essential for test and setup, not used in everyday use.
00006 *
00007 *       com2 provides 19200 baud comms via opto-isolators to Touch Screen Controller (see "Brute_TS_Controller_2018_11"). Opto isolation allows
00008 *       for several boards to parallel up on this port, each STM3_ESC board having a unique single byte ID char in range '0' to '9'.
00009 *       This enables Touch Screen controller to address ESC boards individually e.g. for requesting speed, RPM etc
00010 *       while also allowing broadcast of commands not requiring responses
00011 *
00012 *   Code implements a Command Line Interpreter (see class   cli_2019)
00013 *   Two instantiations of class   cli_2019 are used, 'pcc' for pc comms and 'tsc' for touch screen comms
00014 *   These read incoming commands and execute code functions accordingly. These functions are all of type
00015 *       void    func    (struct parameters &)  ;
00016 *   This allows any command line parameters to pass to 'func'
00017 */
00018 //  Brushless_STM3_Ctrl_2018_11
00019 #include "mbed.h"
00020 #include "BufferedSerial.h"
00021 #include "STM3_ESC.h"
00022 #include "brushless_motor.h"
00023 
00024 #include <cctype>
00025 using namespace std;
00026 
00027 extern  eeprom_settings     user_settings     ;
00028 extern  error_handling_Jan_2019     ESC_Error    ;         //  Provides array usable to store error codes.
00029 extern  int     WatchDog;           //  from main
00030 extern  bool    WatchDogEnable;     //  from main
00031 //extern  bool    read_temperature    (float & t) ;   //  from main March 2020
00032 
00033 extern  brushless_motor MotorA, MotorB;     //  Controlling two motors together or individually
00034 extern  const char *    get_version    ()  ;    //  Need this as extern const char can not be made to work. This returns & const_version_string
00035 
00036 extern  BufferedSerial com2, pc;            //  The two com ports used. There is also an unused com port, com3 setup @ 1200 baud
00037 extern  void    setVI_both   (double v, double i)  ;     //  Set motor voltage limit and current limit
00038 extern  double  Read_DriverPot  ();
00039 extern  double  Read_BatteryVolts   ();
00040 extern  void    mode_set_motors_both   (int mode)  ;   //  called from cli to set fw, re, rb, hb
00041 
00042 //  All void    func    (struct parameters &)  ; functions addressed by command line interpreter are together below here
00043 
00044 /**
00045 void    ver_cmd (struct parameters & a)
00046     Responds YES, causes action NO
00047     PC or TS able to read software / firmware / hardware version string
00048 */
00049 void    ver_cmd (struct parameters & a)
00050 {
00051     if  (a.source == SOURCE_PC) {
00052         pc.printf   ("Version [%s]\r\n", get_version());
00053     }
00054     else    {
00055         if  (a.source == SOURCE_TS)
00056             if (a.respond)  {     //  Only respond if this board addressed
00057                 a.com->printf   ("%s\r", get_version());
00058             }
00059         else
00060             pc.printf   ("Crap source %d in ver_cmd\r\n", a.source);
00061     }
00062 }
00063 
00064 /**
00065 void    pot_cmd (struct parameters & a)
00066     Responds YES, causes action NO
00067     pc reads DriverPot. No sense in TS reading as STM3_ESC uses either/or TS, DriverPot
00068 */
00069 void    pot_cmd (struct parameters & a)
00070 {   if  (a.source == SOURCE_PC)
00071         pc.printf   ("Driver's pot %.3f\r\n", Read_DriverPot  ());
00072     else
00073         pc.printf   ("Wrong use of pot_cmd\r\n");
00074 }
00075 
00076 /**
00077 *   Do nothing command, but does report board ID code '0' to '9'
00078 */
00079 void    null_cmd (struct parameters & a)
00080 {
00081     if  (a.respond) 
00082         a.com->printf   ("Board ID %c\r\n", user_settings.rd(BOARD_ID));
00083 }
00084 
00085 
00086 /**
00087 *   void    rdi_cmd (struct parameters & a)  //  read motor currents (uint32_t) and BatteryVolts (double)
00088 
00089 void    rdi_cmd (struct parameters & a)  //  read motor currents (uint32_t) and BatteryVolts (double)
00090 {   //  Voltage reading true volts, currents only useful as relative values
00091     if  (a.respond) 
00092 //        a.com->printf ("rdi%d %d %.1f\r%s", MotorA.I.ave, MotorB.I.ave, Read_BatteryVolts  (), a.source == SOURCE_PC ? "\n" : "");
00093         a.com->printf ("rdi%.1f %.1f %.1f\r%s", MotorA.Idbl, MotorB.Idbl, Read_BatteryVolts  (), a.source == SOURCE_PC ? "\n" : "");
00094           //  Format good to be unpicked by cli in touch screen controller
00095 }
00096 */
00097 /**
00098 *   void    rvi_cmd (struct parameters & a)  //  read last normalised motor voltage and current values sent to pwms
00099 *
00100 */
00101 //void    rvi_cmd (struct parameters & a)  //  read last normalised values sent to pwms
00102 //{
00103 //    if  (a.respond) 
00104 //        a.com->printf ("rvi%.2f %.2f %.2f %.2f\r%s", MotorA.last_V, MotorA.last_I, MotorB.last_V, MotorB.last_I, a.source == SOURCE_PC ? "\n" : "");
00105 //}
00106 
00107 /**
00108 *   void    fw_cmd (struct parameters & a)  //  Forward command
00109 *   Broadcast to all STM3_ESC boards, required to ACT but NOT respond
00110 */
00111 void    fw_cmd (struct parameters & a)  //  Forward command
00112 {
00113 //    MotorA.set_mode (MOTOR_FORWARD);
00114 //    MotorB.set_mode (MOTOR_FORWARD);
00115     mode_set_motors_both   (MOTOR_FORWARD);
00116     if  (a.source == SOURCE_PC)
00117         pc.printf   ("fw\r\n");     //  Show response to action if command from pc terminal
00118 }
00119 
00120 /**
00121 *   void    re_cmd (struct parameters & a)  //  Reverse command
00122 *   Broadcast to all STM3_ESC boards, required to ACT but NOT respond
00123 */
00124 void    re_cmd (struct parameters & a)  //  Reverse command
00125 {
00126     mode_set_motors_both   (MOTOR_REVERSE);
00127     if  (a.source == SOURCE_PC)
00128         pc.printf   ("re\r\n");
00129 }
00130 
00131 /**
00132 *   void    rb_cmd (struct parameters & a)      //  Regen brake command
00133 *   Broadcast to all STM3_ESC boards, required to ACT but NOT respond
00134 */
00135 void    rb_cmd (struct parameters & a)      //  Regen brake command
00136 {
00137     double  tmp = a.dbl[0] / 100.0;
00138     MotorA.brake    (tmp);
00139     MotorB.brake    (tmp);  //  Corrected May 2020 - previously MotorA twice
00140     if  (a.source == SOURCE_PC)
00141         pc.printf   ("rb %.2f\r\n", tmp);
00142 }
00143 
00144 /**
00145 *   void    hb_cmd (struct parameters & a)      //  Hand brake command
00146 *   Broadcast to all STM3_ESC boards, required to ACT but NOT respond
00147 *
00148 *   NOTE    Jan 2019 Hand brake not implemented
00149 *   May 2020 - Implemented, but not very good
00150 */
00151 void    hb_cmd (struct parameters & a)      //  Hand brake command
00152 {
00153     double  tmp;
00154     if  (a.numof_dbls != 0)  tmp = a.dbl[0] / 100.0;    //  a.numof_dbls is int32_t
00155     else                    tmp = 0.33;
00156     if  (a.respond) {
00157 //        a.com->printf   ("numof params = %d\r\n", a.numof_dbls);
00158         a.com->printf   ("Hand Brake : Force 0 to 99 %.0f\r\n", tmp * 100.0);
00159     }
00160     mode_set_motors_both   (MOTOR_HANDBRAKE);
00161     if  (tmp < 0.0)    tmp = 0.0;
00162     if  (tmp > 1.0)    tmp = 1.0;
00163     setVI_both (tmp / 5.0, 0.99);
00164 }
00165 
00166 
00167 
00168 
00169 /**void    user_settings_cmd (struct parameters & a)       //  With no params, reads eeprom contents. With params sets eeprom contents
00170 *   user_settings_cmd called only from pc comms. No sense calling from Touch Screen Controller
00171 *
00172 *   Called without parameters - Lists to pc terminal current settings
00173 *
00174 */
00175 void    user_settings_cmd (struct parameters & a)       //  With no params, reads eeprom contents. With params sets eeprom contents
00176 {
00177     user_settings.edit   (a.dbl, a.numof_dbls);
00178 }
00179 
00180 extern  struct  optpar option_list[] ;  //= {
00181 
00182 void    brake_eff_set_cmd  (struct parameters & a)  {     //  set brake effectiveness from TS May 2020
00183     char    be = (char) a.dbl[0];
00184     pc.printf   ("BRAKE_EFFECTIVENESS min = %d, max = %d\r\n", option_list[BRAKE_EFFECTIVENESS].min, option_list[BRAKE_EFFECTIVENESS].max);
00185     if  (be > option_list[BRAKE_EFFECTIVENESS].max)   be = option_list[BRAKE_EFFECTIVENESS].max;
00186     if  (be < option_list[BRAKE_EFFECTIVENESS].min)   be = option_list[BRAKE_EFFECTIVENESS].min;
00187     user_settings.wr(be, BRAKE_EFFECTIVENESS);
00188     user_settings.save   ();
00189     pc.printf   ("Set brake effectiveness to %d pct\r\n", be);
00190 }
00191 
00192 void    ssl_cmd  (struct parameters & a)  {     //  set speed limit NEW and untested July 2019. Stored as speed * 10 to get 1dec place
00193     char    sp = (char) (a.dbl[0] * 10.0);
00194     if  (sp > option_list[TOP_SPEED].max)   sp = option_list[TOP_SPEED].max;
00195     if  (sp < option_list[TOP_SPEED].min)   sp = option_list[TOP_SPEED].min;
00196     user_settings.wr(sp, TOP_SPEED);
00197     user_settings.save   ();
00198     pc.printf   ("Set speed limit to %.1f mph\r\n", a.dbl[0]);
00199 }
00200 
00201 /**
00202 *   void    temperature_cmd  (struct parameters & a)  {
00203 *   Few boards have temperature sensor fitted. Now only supports LM75B i2c sensor
00204 */
00205 /*void    read_temperature_cmd  (struct parameters & a)  {
00206     float   t = -99.25;
00207     if  (a.respond) {
00208         a.com->printf ("tem%c ", user_settings.rd(BOARD_ID));
00209         if  (read_temperature(t))
00210             a.com->printf   ("Temperature = %7.3f\r\n", t);
00211         else
00212             a.com->printf   ("Temp sensor not fitted\r\n");
00213     }
00214 }*/
00215 
00216 /**
00217 *void    rpm_cmd (struct parameters & a) //  to report e.g. RPM 1000 1000 ; speed for both motors
00218 */
00219 void    rpm_cmd (struct parameters & a) //  to report e.g. RPM 1000 1000 ; speed for both motors
00220 {
00221     if  (a.respond) 
00222 //        a.com->printf  ("rpm%c %d %d\r%s", user_settings.rd(BOARD_ID), MotorA.RPM, MotorB.RPM, a.source == SOURCE_PC ? "\n" : "");
00223         a.com->printf  ("rpm%c %.0f %.0f\r%s", user_settings.rd(BOARD_ID), MotorA.dRPM, MotorB.dRPM, a.source == SOURCE_PC ? "\n" : "");
00224 }
00225 
00226 /**
00227 *void    mph_cmd (struct parameters & a) //  to report miles per hour
00228 */
00229 void    mph_cmd (struct parameters & a) //  to report miles per hour to 1 decimal place - now 2dp May 2020
00230 {
00231     int j = 0;
00232     double  speedmph = 0.0;
00233     if  (MotorA.exists  ()) {
00234         j |= 1;
00235         speedmph = MotorA.dMPH;
00236     }
00237     if  (MotorB.exists  ()) {
00238         j |= 2;
00239         speedmph += MotorB.dMPH;
00240     }
00241     if  (j == 3)
00242         speedmph /= 2.0;
00243     if  (a.respond) 
00244 //  May 17th 2020 modified line below - removed superfluous cast, upped to 2 decimal places
00245 //        a.com->printf ("mph%c %.2f\r%s", user_settings.rd(BOARD_ID), speedmph, a.source == SOURCE_PC ? "\n" : "");
00246         a.com->printf ("?s%c %.2f\r%s", user_settings.rd(BOARD_ID), speedmph, a.source == SOURCE_PC ? "\n" : "");
00247 }
00248 
00249 /**
00250 *void    sysV_report (struct parameters & a) //  to report system voltage
00251 *   Reports system link voltage to one decimal place
00252 */
00253 void    sysV_report (struct parameters & a) //  
00254 {
00255     if  (a.respond) 
00256         a.com->printf ("?v%c %.1f\r%s", user_settings.rd(BOARD_ID), Read_BatteryVolts(), a.source == SOURCE_PC ? "\n" : "");
00257 }
00258 
00259 /**
00260 *void    sysI_report (struct parameters & a) //  to report motor currents
00261 *   Reports doubles for each motor current amps to 2 decimal places
00262 */
00263 void    sysI_report (struct parameters & a) //  
00264 {
00265     if  (a.respond)
00266         a.com->printf ("?i%c %.2f %.2f\r%s", user_settings.rd(BOARD_ID), MotorA.Idbl, MotorB.Idbl, a.source == SOURCE_PC ? "\n" : "");
00267 }
00268 
00269 /*
00270 
00271 New December 2019 - possible for implementation
00272     Having implemented radio control inputs and driver pot, there is some sense in moving slider handler from touch screen controller
00273     into STM3_ESC code.
00274         New instructions to be accepted from touch screen : -
00275     {"w", "touch screen new slider touch", slider_touch_cmd},
00276     {"x", "updated slider RANGE 0 to 99", slider_touch_position_cmd},   //  max two digits
00277     {"y", "touch_screen slider finger lifted clear", slider_untouch_cmd},
00278 */
00279 bool    finger_on_slider = false;
00280 /**void    slider_touch_cmd (struct parameters & a)
00281 *
00282 *   Message from touch screen controller, slider touched
00283 */
00284 void    slider_touch_cmd (struct parameters & a)    {
00285     finger_on_slider = true;
00286 }
00287 
00288 /**void    slider_untouch_cmd (struct parameters & a)
00289 *
00290 *   Message from touch screen controller, finger taken off slider
00291 */
00292 void    slider_untouch_cmd (struct parameters & a)    {
00293     finger_on_slider = false;
00294 }
00295 
00296 /**void    slider_touch_position_cmd (struct parameters & a)
00297 *
00298 *   Message from touch screen controller, latest slider touch position
00299 */
00300 void    slider_touch_position_cmd (struct parameters & a)   {
00301 }
00302 
00303 //  End of  New December 2019 
00304 
00305 
00306 /**void    vi_cmd (struct parameters & a)
00307 *
00308 *   For setting motor voltage and current limits from pc terminal for test   
00309 */
00310 void    vi_cmd (struct parameters & a)
00311 {
00312     setVI_both   (a.dbl[0] / 100.0, a.dbl[1] / 100.0);
00313 //    pc.printf   ("setVI_both from %s\r\n", a.source == SOURCE_PC ? "PC" : "Touch Screen");
00314 }
00315 
00316 /**
00317 *void    v_cmd (struct parameters & a)
00318 *    Set motor voltage limit from either source without checking for addressed board
00319 */
00320 void    v_cmd (struct parameters & a)
00321 {
00322     MotorA.set_V_limit  (a.dbl[0] / 100.0);
00323     MotorB.set_V_limit  (a.dbl[0] / 100.0);
00324 }
00325 
00326 /**
00327 *void    i_cmd (struct parameters & a)
00328 *    Set motor current limit from either source without checking for addressed board
00329 */
00330 void    i_cmd (struct parameters & a)
00331 {
00332     MotorA.set_I_limit  (a.dbl[0] / 100.0);
00333     MotorB.set_I_limit  (a.dbl[0] / 100.0);
00334 }
00335 
00336 /**
00337 *void    kd_cmd (struct parameters & a)  //  kick and enable the watch dog
00338 *
00339 *   Brute_TS_Controller or other external controller to issue regular 'kd\r' to come here.
00340 *   WatchDog disabled by default, enabled on first call to here
00341 *   This is where WatchDog timer is reset and reloaded.
00342 *   Timeout may be detected and handled in 8Hz loop in main programme loop
00343 */
00344 void    kd_cmd (struct parameters & a)  //  kick the watchdog. Reached from TS or pc.
00345 {
00346     WatchDog = WATCHDOG_RELOAD + (user_settings.rd(BOARD_ID) & 0x0f);   //  Reload watchdog timeout. Counted down @ 8Hz
00347     WatchDogEnable = true;                          //  Receipt of this command sufficient to enable watchdog
00348 }
00349 
00350 void    wd_report (struct parameters & a)     //  Reachable always from pc. Only addressed board responds to TS
00351 {
00352     pc.printf   ("WatchDog %d\r\n", WatchDog);
00353 }
00354 /**
00355 *void    who_cmd (struct parameters & a)     //  Reachable always from pc. Only addressed board responds to TS
00356 *
00357 *   When using STM3_ESC boards together with 'Brute Touch Screen Controller', controller needs to identify number and identity
00358 *   of all connected STM3_ESC boards. To do this the Touch Screen issues '0who', '1who' ... '9who' allowing time between each
00359 *   for an identified STM3_ESC to respond with 'who7' (if it was number 7) without causing contention of paralleled serial opto isolated bus
00360 */
00361 void    who_cmd (struct parameters & a)     //  Reachable always from pc. Only addressed board responds to TS
00362 {
00363     if  (a.source == SOURCE_PC || user_settings.rd(BOARD_ID) == a.target_unit)
00364         a.com->printf ("who%c\r%s", user_settings.rd(BOARD_ID), a.source == SOURCE_PC ? "\n" : "");
00365 }
00366 
00367 extern  void    rcins_report ()  ;
00368 void    qrc_cmd (struct parameters & a)     //  report RC1 and RC2 input condition and activity
00369 {
00370     rcins_report    ();
00371 }
00372 
00373 /**
00374 *void    rcin_pccmd (struct parameters & a)
00375 *
00376 *   For test, reports to pc terminal info about radio control input channels
00377 */
00378 //void    rcin_pccmd (struct parameters & a)
00379 //{
00380 //    rcin_report ();
00381 //}
00382 /*
00383 void    scmd (struct parameters & a)    //  filter coefficient fiddler
00384 {
00385     switch  ((int)a.dbl[0]) {
00386         case    1:
00387             MotorA.sdbl[1] = MotorB.sdbl[1] = a.dbl[1];
00388             break;
00389         case    2:
00390             MotorA.sdbl[2] = MotorB.sdbl[2] = a.dbl[1];
00391             break;
00392         case    3:
00393             MotorA.sdbl[3] = MotorB.sdbl[3] = a.dbl[1];
00394             break;
00395         case    4:
00396             MotorA.sdbl[4] = MotorB.sdbl[4] = a.dbl[1];
00397             break;
00398         default:
00399             pc.printf   ("Wrong use of scmd %f\r\n", a.dbl[0]);
00400     }
00401     pc.printf   ("Filter Coefficient Fiddler - used in brushless_motor::speed_monitor_and_control   ()");
00402     pc.printf   ("Filter Coeffs 1 to 4\r\n");
00403     pc.printf   ("1 %.3f\tPscale 0.01-0.5\r\n",     MotorA.sdbl[1]);
00404     pc.printf   ("2 %.3f\tP_gain 1.0-1000.0\r\n",   MotorA.sdbl[2]);
00405     pc.printf   ("3 %.3f\tDscale 0.01-0.5\r\n",     MotorA.sdbl[3]);
00406     pc.printf   ("4 %.3f\tD_gain 1.0-1000.0\r\n",   MotorA.sdbl[4]);
00407 //    pc.printf   ("5 Set target_speed\r\n");
00408 }
00409 */
00410     struct kb_command  {    //  Commands tabulated as list of these structures as seen below
00411     const char * cmd_word;              //  points to text e.g. "menu"
00412     const char * explan;                //  very brief explanation or clue as to purpose of function
00413     void (*f)(struct parameters &);     //  points to function
00414 }  ;                                    //  Positioned in code here as knowledge needed by following menucmd
00415 
00416 /**
00417 *   void    menucmd (struct parameters & a)
00418 *
00419 *   List available terminal commands to pc terminal. No sense in touch screen using this
00420 */
00421 void    menucmd (struct parameters & a)
00422 {
00423     if  (a.respond) {
00424         a.com->printf("\r\n\nDual BLDC ESC type STM3 2018-20, Ver %s\r\nAt menucmd function - listing commands, source %s:-\r\n", get_version(), a.source == SOURCE_PC ? "PC" : "TS");
00425         for(int i = 0; i < a.numof_menu_items; i++)
00426             a.com->printf("[%s]\t\t%s\r\n", a.command_list[i].cmd_word, a.command_list[i].explan);
00427         a.com->printf("End of List of Commands\r\n");
00428     }
00429 }
00430 
00431 /********************** END OF COMMAND LINE INTERPRETER COMMANDS *************************************/
00432 
00433 
00434 /**
00435 *   struct  kb_command const loco_command_list[] = {
00436 *   List of commands accepted from external controller through opto isolated com port 19200, 8,n,1
00437 */
00438 struct  kb_command const loco_command_list[] = {    //  For comms between STM3_ESC and 'Brute Touch Screen Controller'
00439     //  ***** Broadcast commands for all STM3_ESC boards to execute. Boards NOT to send serial response *****
00440     {"fw", "forward", fw_cmd},
00441     {"re", "reverse", re_cmd},
00442     {"rb", "regen brake 0 to 99 %", rb_cmd},    //  max two digits
00443     {"hb", "hand brake", hb_cmd},
00444     {"v", "set motors V percent RANGE 0 to 99", v_cmd},
00445     {"i", "set motors I percent RANGE 0 to 99", i_cmd},
00446     {"vi", "set motors V and I percent RANGE 0 to 99", vi_cmd},
00447     {"w", "touch screen new slider touch", slider_touch_cmd},
00448     {"x", "updated slider RANGE 0 to 99", slider_touch_position_cmd},
00449     {"y", "touch_screen slider finger lifted clear", slider_untouch_cmd},
00450     {"kd", "kick the dog, reloads WatchDog", kd_cmd},
00451     {"ssl", "set speed limit e.g. 10.7", ssl_cmd},              //  NEW July 2019
00452     {"sbe", "set brake effectiveness 5 to 90 percent", brake_eff_set_cmd},              //  NEW May 2020
00453     //  ***** Endof Broadcast commands for all STM3_ESC boards to execute. Boards NOT to send serial response *****
00454 
00455     //  ***** Following are rx'd requests for serial response from addressed STM3_ESC only
00456     {"?v", "Report system bus voltage", sysV_report},
00457     {"?i", "Report motor both currents", sysI_report},
00458     {"who", "search for connected units, e.g. 3who returs 'who3' if found", who_cmd},
00459 //    {"tem", "report temperature", read_temperature_cmd},
00460     {"mph", "read loco speed miles per hour", mph_cmd},
00461     {"?s", "read loco speed miles per hour", mph_cmd},       //  Shorter-hand added 17th May 2020
00462 //    {"ssl", "set speed limit e.g. 10.7", ssl_cmd},              //  NEW July 2019
00463 //    {"sbe", "set brake effectiveness 5 to 90 percent", brake_eff_set_cmd},              //  NEW May 2020
00464 //    {"rvi", "read most recent values sent to pwms", rvi_cmd},
00465 //    {"rdi", "read motor currents and power voltage", rdi_cmd},
00466     //  ***** Endof
00467 }   ;
00468 
00469 
00470 /**
00471 struct  kb_command const loco_command_list[] = {
00472 List of commands accepted from external pc through non-opto isolated com port 9600, 8,n,1
00473 */
00474 struct  kb_command const pc_command_list[] = {
00475     {"ls", "Lists available commands", menucmd},
00476     {"?", "Lists available commands, same as ls", menucmd},
00477     {"pot", "read drivers control pot", pot_cmd},
00478     {"fw", "forward", fw_cmd},
00479     {"re", "reverse", re_cmd},
00480     {"rb", "regen brake 0 to 99 %", rb_cmd},
00481     {"hb", "hand brake", hb_cmd},
00482     {"v", "set motors V percent RANGE 0 to 100", v_cmd},
00483     {"i", "set motors I percent RANGE 0 to 100", i_cmd},
00484     {"vi", "set motors V and I percent RANGE 0 to 100", vi_cmd},
00485     {"?v", "Report system bus voltage", sysV_report},
00486     {"?i", "Report motor both currents", sysI_report},
00487     {"?w", "show WatchDog timer contents", wd_report},
00488     {"kd", "kick the dog, reloads WatchDog", kd_cmd},
00489     {"who", "search for connected units, e.g. 3who returs 'who3' if found", who_cmd},
00490     {"us", "read or set user settings in eeprom", user_settings_cmd},                                 //  Big change Jan 2019
00491     {"ssl", "set speed limit e.g. 10.7", ssl_cmd},              //  NEW July 2019 ONLY HERE FOR TEST, normal use is from Touch Screen only.
00492     {"sbe", "set brake effectiveness 5 to 90 percent", brake_eff_set_cmd},              //  NEW May 2020
00493 //    {"erase", "set eeprom contents to all 0xff", erase_cmd},
00494 //    {"tem", "report temperature", read_temperature_cmd},                                     //  Reports -50 when sensor not fitted
00495     {"ver", "Version", ver_cmd},
00496     {"rpm", "read motor pair speeds", rpm_cmd},
00497 //    {"mph", "read loco speed miles per hour", mph_cmd},
00498     {"?s", "read loco speed miles per hour", mph_cmd},
00499     {"?rc", "report RC1 and RC2 input condition and activity", qrc_cmd},
00500 //    {"rvi", "read most recent values sent to pwms", rvi_cmd},
00501 //    {"rdi", "read motor currents and power voltage", rdi_cmd},
00502 //    {"bc", "bogie constants - wheel dia, motor pinion, wheel gear", bogie_constants_report_cmd},
00503 //    {"gbb", "get bogie bytes from eeprom and report", gbb_cmd},    //  OBSOLETE, replaced by 'gbb'
00504     {"nu", "do nothing", null_cmd},
00505 }   ;
00506 
00507 //    cli_2019    (BufferedSerial * comport, kb_command const * list, int list_len, int source)  {
00508 /**
00509 *   cli_2019    pcc (&pc,   pc_command_list,    sizeof(pc_command_list) / sizeof(kb_command), SOURCE_PC)  ;
00510 *   cli_2019    tsc (&com2, loco_command_list,  sizeof(loco_command_list) / sizeof(kb_command), SOURCE_TS)  ;
00511 *
00512 *   Instantiate two Command Line Interpreters, one for pc terminal and one for touch screen controller
00513 */
00514 cli_2019    pcc (&pc,   pc_command_list,    sizeof(pc_command_list) / sizeof(kb_command), SOURCE_PC)  ;
00515 cli_2019    tsc (&com2, loco_command_list,  sizeof(loco_command_list) / sizeof(kb_command), SOURCE_TS)  ;
00516 
00517 /*
00518 New - March 2018
00519 Using opto isolated serial port, paralleled up using same pair to multiple STM3_ESC boards running this code.
00520 New feature - commands have optional prefix digit 0-9 indicating which unit message is addressed to.
00521 Commands without prefix digit - broadcast to all units, all to obey but none to respond.
00522 Only units recognising its address from prefix digit may respond. This avoids bus contention.
00523 But for BROADCAST commands, '0' may respond on behalf of the group
00524 */
00525 
00526 /**
00527 *   void    cli_2019::test ()  {
00528 *
00529 *   Daft check that class instantiation worked
00530 */
00531 void    cli_2019::flush ()  {
00532 //    char ch;
00533     while   (a.com->readable())
00534         a.com->getc();
00535     //pc.printf   ("At cli2019::test, source %d len %d,\r\n", a.source, a.numof_menu_items);
00536 }
00537 
00538 /**
00539 *   void    cli_2019::core ()  {    
00540 *
00541 *   Command Line Interpreter.
00542 *   This to be called every few milli secs from main programme loop.
00543 *   Reads any rx'd chars into command line buffer, returns when serial buffer empty.
00544 *   If last char rx'd war '\r' end of text delimiter, apt command_list is searched for a matched command in command line
00545 *   If matched command found, apt function is executed.
00546 *   Parameters available to functions from 'parameters' struct.
00547 */
00548 void    cli_2019::core ()  {    
00549     int ch, IAm = user_settings.rd(BOARD_ID);
00550     char * pEnd;//, * cmd_line_ptr;
00551     while  (a.com->readable()) {
00552         ch = a.com->getc();
00553         if  (clindex > MAX_CMD_LEN)  {   //  trap out stupidly long command lines
00554             ESC_Error.set   (FAULT_COM_LINE_LEN, 1);                      //  Set FAULT_EEPROM bit 0 if 24LC64 problem
00555             a.com->printf   ("Error!! Stupidly long cmd line\r\n");
00556             clindex = 0;
00557         }
00558         if(ch != '\r')  {   //  was this the 'Enter' key?
00559             if  (ch != '\n')       //  Ignore line feeds
00560                 cmdline[clindex++] = ch;  //  added char to command being assembled
00561         }
00562         else    {   //  key was CR, may or may not be command to lookup
00563             a.target_unit = BROADCAST;    //  Set to BROADCAST default once found command line '\r'
00564             cmdline_ptr = cmdline;
00565             cmdline[clindex] = 0; //  null terminate command string
00566             if(clindex)    {   //  If have got some chars to lookup
00567                 int i, wrdlen;
00568                 if  (isdigit(cmdline[0]))  {   //  Look for command with prefix digit
00569                     cmdline_ptr++;     //  point past identified digit prefix
00570                     a.target_unit = cmdline[0];  //  '0' to '9'
00571                     //com->printf ("Got prefix %c\r\n", cmd_line[0]);
00572                 }
00573                 for (i = 0; i < a.numof_menu_items; i++)   {   //  Look for input match in command list
00574                     wrdlen = strlen(commandlist[i].cmd_word);
00575                     if(strncmp(commandlist[i].cmd_word, cmdline_ptr, wrdlen) == 0 && !isalpha(cmdline_ptr[wrdlen]))  {   //  If match found
00576                         for (int k = 0; k < MAX_CLI_PARAMS; k++)    {
00577                             a.dbl[k] = 0.0;
00578                         }
00579                         a.position_in_list = i;
00580                         a.numof_dbls = 0;
00581 //                                pEnd = cmdline + clindex; // does indeed point to null terminator
00582                         pEnd = cmdline_ptr + wrdlen;
00583 //                        while   (*pEnd)  {          //  Assemble all numerics as doubles
00584                         while   (*pEnd && a.numof_dbls < MAX_CLI_PARAMS)  {          //  Assemble all numerics as doubles
00585                             a.dbl[a.numof_dbls++] = strtod    (pEnd, &pEnd);
00586                             while   (*pEnd && (pEnd < cmdline + clindex) && *pEnd && !isdigit(*pEnd) && ('.' != *pEnd) && ('-' != *pEnd) && ('+' != *pEnd))  {   //  Can crash cli here with e.g. 'ls -l'
00587                                 pEnd++;
00588                             }   //  problem occurs with input such as "- ", or "-a", seemingly anything dodgy following a '-' or a '+'
00589                             if  (((*pEnd == '-') || (*pEnd == '+')) &&(!isdigit(*(pEnd+1))) && ('.' !=*(pEnd+1)))
00590                                 pEnd = cmdline + clindex;   //  fixed by aborting remainder of line
00591                         }
00592                         a.respond = a.resp_always;
00593                         if  (((a.target_unit == BROADCAST) && (IAm == '0')) || (IAm == a.target_unit))
00594                             a.respond = true; //  sorted 26/4/18
00595                         //  All boards to obey BROADCAST command, only specific board to obey number prefixed command
00596                         if  ((a.target_unit == BROADCAST) || (IAm == a.target_unit))
00597                             commandlist[i].f(a);   //  execute command if addressed to this unit
00598                         i = a.numof_menu_items + 1;    //  to exit for loop
00599                     }   //  end of match found
00600                 }       // End of for numof_menu_items
00601                 if(i == a.numof_menu_items) {
00602 //                    a.com->printf("No Match Found for CMD [%s]\r\n", cmdline);
00603                     pc.printf("No Match Found for CMD [%s]\r\n", cmdline);
00604                     ESC_Error.set   (FAULT_COM_LINE_NOMATCH, 1);                      //  Set FAULT_EEPROM bit 0 if 24LC64 problem
00605                 }
00606             }           //  End of If have got some chars to lookup
00607             clindex = 0;
00608         }       // End of else key was CR, may or may not be command to lookup
00609     }           //  End of while (com->readable())
00610 }               //  end of command line interpreter core function
00611 
00612