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

Update 17th August 2020 Radio control inputs completed

Revision:
7:6deaeace9a3e
Parent:
6:f289a49c1eae
Child:
8:93203f473f6e
--- a/cli_BLS_nortos.cpp	Tue Jun 05 07:19:39 2018 +0000
+++ b/cli_BLS_nortos.cpp	Sun Jun 17 06:59:37 2018 +0000
@@ -12,14 +12,16 @@
 const int   BROADCAST   = '\r';
 const   int MAX_PARAMS = 20;
 struct  parameters  {
-    int32_t position_in_list,   //  set but not used Apr 2018, contains i for i'th menu item
-//            last_time,          //  gets reading from clock() ; not known to be useful or reliable
-            numof_dbls,
-            target_unit;
+    struct kb_command const * command_list;
+    BufferedSerial * com;   //  pc or com2
+    char    cmd_line[120];
+    char    * cmd_line_ptr;
+    int32_t position_in_list, numof_dbls, target_unit, numof_menu_items, cl_index, gp_i;
     double  dbl[MAX_PARAMS];
-    bool    respond;
+    bool    respond, resp_always;
 }   ;
 
+struct parameters pccom, lococom;
 //  WithOUT RTOS
 extern  BufferedSerial com2, pc;
 extern  void    send_test   ()  ;
@@ -28,12 +30,12 @@
 extern  void    setI    (double i)  ;
 extern  void    read_last_VI    (double * val)  ;   //  only for test from cli
 
-BufferedSerial * com;
+//BufferedSerial * com;
 
 void    null_cmd (struct parameters & a)
 {
     if  (a.respond) 
-        com->printf   ("At null_cmd, board ID %c, parameters : First %.3f, second %.3f\r\n", I_Am(), a.dbl[0], a.dbl[1]);
+        a.com->printf   ("At null_cmd, board ID %c, parameters : First %.3f, second %.3f\r\n", I_Am(), a.dbl[0], a.dbl[1]);
 }
 
 extern  void    mode_set   (int mode, double val)  ;   //  called from cli to set fw, re, rb, hb
@@ -44,7 +46,7 @@
     if  (a.respond) {
         double  r[4];
         read_supply_vi  (r);    //  get MotorA.I.ave, MotorB.I.ave, Battery volts
-        com->printf ("rdi%.0f %.0f %.1f\r", r[0], r[1], r[2]);  //  Format good to be unpicked by cli in touch screen controller
+        a.com->printf ("rdi%.0f %.0f %.1f\r", r[0], r[1], r[2]);  //  Format good to be unpicked by cli in touch screen controller
     }
 }
 
@@ -53,7 +55,7 @@
     if  (a.respond) {
         double  r[6];
         read_last_VI    (r);
-        com->printf ("rvi%.2f %.2f %.2f %.2f\r", r[0], r[1], r[2], r[3]);
+        a.com->printf ("rvi%.2f %.2f %.2f %.2f\r", r[0], r[1], r[2], r[3]);
     }
 }
 
@@ -70,7 +72,7 @@
 void    rb_cmd (struct parameters & a)      //  Regen brake command
 {
     double b = a.dbl[0] / 100.0;
-//    com->printf   ("Applying brake %.3f\r\n", b);
+//    a.com->printf   ("Applying brake %.3f\r\n", b);
     mode_set   (REGENBRAKE, b);
 //    apply_brake (b);
 }
@@ -84,9 +86,9 @@
     for (int i = 0; i < 32; i++)
         t[i] = 0xff;
     for (int i = 0; i < 8191; i += 32)  {
-        com->printf (".");
+        a.com->printf (".");
         if  (!wr_24LC64   (i, t, 32))
-            com->printf ("eeprom write prob\r\n");
+            a.com->printf ("eeprom write prob\r\n");
     }
 }
 /*struct  motorpairoptions    {   //  This to be user settable in eeprom, 32 bytes
@@ -107,37 +109,37 @@
 
 void    mode_cmd (struct parameters & a)       //  With no params, reads eeprom contents. With params sets eeprom contents
 {
-    if  (a.target_unit == BROADCAST) {
-//        com->printf ("At mode_cmd, can not use BROADCAST with mode_cmd\r\n");
+    if  (a.target_unit == BROADCAST || !a.resp_always) {
+//        a.com->printf ("At mode_cmd, can not use BROADCAST with mode_cmd\r\n");
     } else    {
         char    t[36];
-        com->printf ("At mode_cmd with node %d\r\n", a.target_unit);
+        a.com->printf ("At mode_cmd with node %d\r\n", a.target_unit);
         rd_24LC64   (0, t, 32);
-        com->printf ("Numof params=%d\r\n", a.numof_dbls);
+        a.com->printf ("Numof params=%d\r\n", a.numof_dbls);
         for (int i = 0; i < numof_eeprom_options; i++)
-            com->printf ("%2x\t%s\r\n", t[i], option_list[i].t);
+            a.com->printf ("%2x\t%s\r\n", t[i], option_list[i].t);
         if  (a.numof_dbls == 0) {   //  Read present contents, do not write
-            com->printf ("That's it\r\n");
+            a.com->printf ("That's it\r\n");
         } else    { //  Write new shit to eeprom
-            com->printf ("\r\n");
+            a.com->printf ("\r\n");
             if  (a.numof_dbls != numof_eeprom_options) {
-                com->printf ("params required = %d, you offered %d\r\n", numof_eeprom_options, a.numof_dbls);
+                a.com->printf ("params required = %d, you offered %d\r\n", numof_eeprom_options, a.numof_dbls);
             } else    { //  Have been passed correct number of parameters
                 int b;
-                com->printf("Ready to write params to eeprom\r\n");
+                a.com->printf("Ready to write params to eeprom\r\n");
                 for (int i = 0; i < numof_eeprom_options; i++) {
                     b = (int)a.dbl[i];  //  parameter value to check against limits
                     if  (i == 6)    //  Alternative ID must be turned to ascii
                         b |= '0';
                     if  ((b < option_list[i].min) || (b > option_list[i].max))  {   //  if parameter out of range
-                        com->printf("Warning - Parameter = %d, out of range, setting to default %d\r\n", b, option_list[i].def);
+                        a.com->printf("Warning - Parameter = %d, out of range, setting to default %d\r\n", b, option_list[i].def);
                         b = option_list[i].def;
                     }
-                    com->printf ("0x%2x\t%s\r\n", (t[i] = b), option_list[i].t);
+                    a.com->printf ("0x%2x\t%s\r\n", (t[i] = b), option_list[i].t);
                 }
                 wr_24LC64   (0, t, numof_eeprom_options);
                 memcpy  (mode_bytes,t,32);
-                com->printf("Parameters set in eeprom\r\n");
+                a.com->printf("Parameters set in eeprom\r\n");
             }
         }
     }
@@ -149,8 +151,8 @@
 void    hb_cmd (struct parameters & a)
 {
     if  (a.respond) {
-        com->printf   ("numof params = %d\r\n", a.numof_dbls);
-        com->printf   ("Hand Brake : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
+        a.com->printf   ("numof params = %d\r\n", a.numof_dbls);
+        a.com->printf   ("Hand Brake : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
     }
     mode_set   (HANDBRAKE, 0.0);
 }
@@ -158,13 +160,13 @@
 extern  uint32_t    last_temp_count;
 void    temperature_cmd  (struct parameters & a)  {
     if  (a.respond) {
-        com->printf ("tem%c %d\r\n", mode_bytes[ID], (last_temp_count / 16) - 50);
+        a.com->printf ("tem%c %d\r\n", mode_bytes[ID], (last_temp_count / 16) - 50);
     }
 }
 
 void    bogie_constants_report_cmd  (struct parameters & a)  {
     if  (a.respond) {
-        com->printf ("bc%c %d %d %d\r\n", mode_bytes[ID], mode_bytes[WHEELDIA], mode_bytes[MOTPIN], mode_bytes[WHEELGEAR]);
+        a.com->printf ("bc%c %d %d %d\r\n", mode_bytes[ID], mode_bytes[WHEELDIA], mode_bytes[MOTPIN], mode_bytes[WHEELGEAR]);
     }
 }
 
@@ -173,11 +175,19 @@
 {
     if  (a.respond) {
         uint32_t dest[3];
-        read_RPM    (dest);
-        com->printf  ("rpm%d %d\r", dest[0], dest[1]);
+        read_RPM    (dest);     //  gets rpm for each motor
+        a.com->printf  ("rpm%d %d\r", dest[0], dest[1]);
     }
 }
 
+extern  double  rpm2mph ;
+void    mph_cmd (struct parameters & a) //  to report miles per hour
+{
+        uint32_t dest[3];
+        read_RPM    (dest);     //  gets rpm for each motor
+        a.com->printf ("mph%c %.3f\r\n", mode_bytes[ID], (double)(dest[0] + dest[1]) * rpm2mph / 2.0);
+}
+
 void    menucmd (struct parameters & a);
 
 void    vi_cmd (struct parameters & a)
@@ -197,21 +207,21 @@
 void    i_cmd (struct parameters & a)
 {
 //    if  (a.respond)
-//        com->printf   ("In setI, setting I to %.2f\r\n", a.dbl[0]);
+//        a.com->printf   ("In setI, setting I to %.2f\r\n", a.dbl[0]);
     setI   (a.dbl[0] / 100.0);
 }
 
 void    kd_cmd (struct parameters & a)  //  kick the watchdog
 {
     WatchDog = WATCHDOG_RELOAD + (I_Am() & 0x0f);
-//    com->printf ("Poked %d up Dog\r\n", WatchDog);
+//    a.com->printf ("Poked %d up Dog\r\n", WatchDog);
 }
 
 void    who_cmd (struct parameters & a)
 {
     int i = I_Am    ();
     if  (I_Am() == a.target_unit)
-        com->printf ("who%c\r\n", a.target_unit);
+        a.com->printf ("who%c\r\n", a.target_unit);
 }
 
 struct kb_command  {
@@ -220,7 +230,7 @@
     void (*f)(struct parameters &);   //  points to function
 }  ;
 
-struct  kb_command const command_list[] = {
+struct  kb_command const loco_command_list[] = {
     {"ls", "Lists available commands", menucmd},
     {"?", "Lists available commands, same as ls", menucmd},
     {"fw", "forward", fw_cmd},
@@ -242,14 +252,55 @@
     {"nu", "do nothing", null_cmd},
 };
 
-const int numof_menu_items = sizeof(command_list) / sizeof(kb_command);
+//const int numof_loco_menu_items = sizeof(loco_command_list) / sizeof(kb_command);
+
+
+struct  kb_command const pc_command_list[] = {
+    {"ls", "Lists available commands", menucmd},
+    {"?", "Lists available commands, same as ls", menucmd},
+    {"fw", "forward", fw_cmd},
+    {"re", "reverse", re_cmd},
+    {"rb", "regen brake 0 to 99 %", rb_cmd},
+    {"hb", "hand brake", hb_cmd},
+    {"v", "set motors V percent RANGE 0 to 100", v_cmd},
+    {"i", "set motors I percent RANGE 0 to 100", i_cmd},
+    {"vi", "set motors V and I percent RANGE 0 to 100", vi_cmd},
+    {"who", "search for connected units, e.g. 3who returs 'Hi there' if found", who_cmd},
+    {"mode", "read or set params in eeprom", mode_cmd},
+    {"erase", "set eeprom contents to all 0xff", erase_cmd},
+    {"tem", "report temperature", temperature_cmd},
+    {"kd", "kick the dog, reloads WatchDog", kd_cmd},
+    {"rpm", "read motor pair speeds", rpm_cmd},
+    {"mph", "read loco speed miles per hour", mph_cmd},
+    {"rvi", "read most recent values sent to pwms", rvi_cmd},
+    {"rdi", "read motor currents and power voltage", rdi_cmd},
+    {"bc", "bogie constants - wheel dia, motor pinion, wheel gear", bogie_constants_report_cmd},
+    {"nu", "do nothing", null_cmd},
+};
+
+void    setup_comms ()  {
+    pccom.com = & pc;
+    pccom.command_list = pc_command_list;
+    pccom.numof_menu_items = sizeof(pc_command_list) / sizeof(kb_command);
+    pccom.cl_index  = 0;
+    pccom.gp_i      = 0;    //  general puropse integer, not used to 30/4/2018
+    pccom.resp_always   = true;
+    lococom.com = & com2;
+    lococom.command_list = loco_command_list;
+    lococom.numof_menu_items = sizeof(loco_command_list) / sizeof(kb_command);
+    lococom.cl_index    = 0;
+    lococom.gp_i        = 0;    //  general puropse integer, toggles 0 / 1 to best guess source of rpm
+    lococom.resp_always = false;
+}
+
+
 void    menucmd (struct parameters & a)
 {
     if  (a.respond) {
-        com->printf("\r\n\nDouble Brushless Motor Driver 2018\r\nAt menucmd function - listing commands:-\r\n");
-        for(int i = 0; i < numof_menu_items; i++)
-            com->printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
-        com->printf("End of List of Commands\r\n");
+        a.com->printf("\r\n\nDouble Brushless Motor Driver 2018\r\nAt menucmd function - listing commands:-\r\n");
+        for(int i = 0; i < a.numof_menu_items; i++)
+            a.com->printf("[%s]\t\t%s\r\n", a.command_list[i].cmd_word, a.command_list[i].explan);
+        a.com->printf("End of List of Commands\r\n");
     }
 }
 
@@ -262,9 +313,77 @@
 But for BROADCAST commands, '0' may respond on behalf of the group
 */
 //void    command_line_interpreter    (void const *argument)
+void    cli_core    (struct parameters & a) {
+    const int MAX_CMD_LEN = 120;
+    int ch, IAm = I_Am();
+    char * pEnd;//, * cmd_line_ptr;
+    while  (a.com->readable()) {
+        ch = a.com->getc();
+        if  (a.cl_index > MAX_CMD_LEN)  {   //  trap out stupidly long command lines
+            a.com->printf   ("Error!! Stupidly long cmd line\r\n");
+            a.cl_index = 0;
+        }
+        if(ch != '\r')  //  was this the 'Enter' key?
+            a.cmd_line[a.cl_index++] = ch;  //  added char to command being assembled
+        else    {   //  key was CR, may or may not be command to lookup
+            a.target_unit = BROADCAST;    //  Set to BROADCAST default once found command line '\r'
+            a.cmd_line_ptr = a.cmd_line;
+            a.cmd_line[a.cl_index] = 0; //  null terminate command string
+            if(a.cl_index)    {   //  If have got some chars to lookup
+                int i, wrdlen;
+                if  (isdigit(a.cmd_line[0]))  {   //  Look for command with prefix digit
+                    a.cmd_line_ptr++;     //  point past identified digit prefix
+                    a.target_unit = a.cmd_line[0];  //  '0' to '9'
+                    //com->printf ("Got prefix %c\r\n", cmd_line[0]);
+                }
+                for (i = 0; i < a.numof_menu_items; i++)   {   //  Look for input match in command list
+                    wrdlen = strlen(a.command_list[i].cmd_word);
+                    if(strncmp(a.command_list[i].cmd_word, a.cmd_line_ptr, wrdlen) == 0 && !isalpha(a.cmd_line_ptr[wrdlen]))  {   //  If match found
+                        for (int k = 0; k < MAX_PARAMS; k++)    {
+                            a.dbl[k] = 0.0;
+                        }
+                        a.position_in_list = i;
+                        a.numof_dbls = 0;
+                        pEnd = a.cmd_line_ptr + wrdlen;
+                        while   (*pEnd)  {          //  Assemble all numerics as doubles
+                            a.dbl[a.numof_dbls++] = strtod    (pEnd, &pEnd);
+                            while   (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd)  {
+                                pEnd++;
+                            }
+                        }
+                        //com->printf   ("\r\n");   //  Not allowed as many may output this.
+                        //for (int k = 0; k < param_block.numof_dbls; k++)
+                        //    com->printf   ("Read %.3f\r\n", param_block.dbl[k]);
+//                            param_block.times[i] = clock();
+//                        a.respond = false;
+                        a.respond = a.resp_always;
+                        if  (((a.target_unit == BROADCAST) && (IAm == '0')) || (IAm == a.target_unit))
+                            a.respond = true; //  sorted 26/4/18
+                        //  All boards to obey BROADCAST command, only specific board to obey number prefixed command
+                        if  ((a.target_unit == BROADCAST) || (IAm == a.target_unit))
+                            a.command_list[i].f(a);   //  execute command if addressed to this unit
+                        i = a.numof_menu_items + 1;    //  to exit for loop
+                    }   //  end of match found
+                }       // End of for numof_menu_items
+                if(i == a.numof_menu_items)
+                    a.com->printf("No Match Found for CMD [%s]\r\n", a.cmd_line);
+            }           //  End of If have got some chars to lookup
+            //com->printf("\r\n>");
+            a.cl_index = 0;
+        }               // End of else key was CR, may or may not be command to lookup
+    }                   //  End of while (com->readable())
+}
+
+void    command_line_interpreter_pc    ()   {
+    cli_core    (pccom);
+}
+void    command_line_interpreter_loco    () {
+    cli_core    (lococom);
+}
+
 void    command_line_interpreter    ()
 {
-    const int MAX_CMD_LEN = 120;
+/*    const int MAX_CMD_LEN = 120;
     static  char    cmd_line[MAX_CMD_LEN + 4];
     static  int     cl_index = 0;
     int ch, IAm = I_Am();
@@ -291,9 +410,9 @@
                     param_block.target_unit = cmd_line[0];  //  '0' to '9'
                     //com->printf ("Got prefix %c\r\n", cmd_line[0]);
                 }
-                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_ptr, wrdlen) == 0 && !isalpha(cmd_line_ptr[wrdlen]))  {   //  If match found
+                for (i = 0; i < a.numof_menu_items; i++)   {   //  Look for input match in command list
+                    wrdlen = strlen(a.command_list[i].cmd_word);
+                    if(strncmp(a.command_list[i].cmd_word, a.cmd_line_ptr, wrdlen) == 0 && !isalpha(a.cmd_line_ptr[wrdlen]))  {   //  If match found
                         for (int k = 0; k < MAX_PARAMS; k++)    {
                             param_block.dbl[k] = 0.0;
                         }
@@ -328,7 +447,7 @@
         }               // End of else key was CR, may or may not be command to lookup
     }                   //  End of while (com->readable())
 //        Thread::wait(20);  //  Using RTOS on this project
-//    }
+//    }*/
 }