STM3 ESC dual brushless motor controller. 10-60v, motor power rating tiny to kW. Ganged or independent motor control As used in 'The Brute' locomotive - www.jons-workshop.com

Dependencies:   mbed BufferedSerial Servo FastPWM

Files at this revision

API Documentation at this revision

Comitter:
JonFreeman
Date:
Sun Jun 17 06:59:37 2018 +0000
Parent:
6:f289a49c1eae
Child:
8:93203f473f6e
Commit message:
Firmware for STM3 Twin Brushless Motor Electronic Speed Controller; Snapshot at 17th June 2018

Changed in this revision

DualBLS.h Show annotated file Show diff for this revision Revisions of this file
F401RE.h Show annotated file Show diff for this revision Revisions of this file
F446ZE.h Show annotated file Show diff for this revision Revisions of this file
cli_BLS_nortos.cpp Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/DualBLS.h	Tue Jun 05 07:19:39 2018 +0000
+++ b/DualBLS.h	Sun Jun 17 06:59:37 2018 +0000
@@ -1,5 +1,3 @@
-//#define POWER_OF_TWO 12     // Range is 4 to 13, is log2N
-//typedef float ffty;     // Choice of float or double    float is HUGELY FASTER than double
 const   int     HANDBRAKE   = 0,
                 FORWARD     = 8,
                 REVERSE     = 16,
@@ -34,9 +32,9 @@
     {0, 2, 2, "Servo2 0, 1, 2 = Not used, Input, Output"},
     {1, 5, 2, "Command source 0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2"},
     {'1', '9', '0', "Alternative ID ascii '1' to '9'"}, //  defaults to '0' before eerom setup for first time
-    {50, 250, 98,  "Wheel diameter mm"},   //  New 01/06/2018
-    {10, 250, 27,  "Motor pinion"},   //  New 01/06/2018
-    {50, 250, 85,  "Wheel gear"},   //  New 01/06/2018
+    {20, 253, 98,  "Wheel diameter mm"},   //  New 01/06/2018
+    {10, 253, 27,  "Motor pinion"},   //  New 01/06/2018
+    {20, 253, 85,  "Wheel gear"},   //  New 01/06/2018
 }   ;
 const int    numof_eeprom_options    = sizeof(option_list) / sizeof (struct optpar);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/F401RE.h	Sun Jun 17 06:59:37 2018 +0000
@@ -0,0 +1,143 @@
+//  Hoped to select servo functions from user info stored on EEROM. Too difficult. Do not define servo as in and out
+
+//  Port A -> MotorA, Port B -> MotorB
+const   uint16_t
+AUL = 1 << 0,   //  Feb 2018 Now using DGD21032 mosfet drivers via 74HC00 pwm gates (low side) - GOOD, works well with auto-tickle of high side drivers
+AVL = 1 << 6,   //  These are which port bits connect to which mosfet driver
+AWL = 1 << 4,
+
+AUH = 1 << 1,
+AVH = 1 << 7,
+AWH = 1 << 8,
+
+AUV =   AUH | AVL,  //  Each of 6 possible output energisations made up of one hi and one low
+AVU =   AVH | AUL,
+AUW =   AUH | AWL,
+AWU =   AWH | AUL,
+AVW =   AVH | AWL,
+AWV =   AWH | AVL,
+
+KEEP_L_MASK_A   = AUL | AVL | AWL,
+KEEP_H_MASK_A   = AUH | AVH | AWH,
+
+BRA = AUL | AVL | AWL,  //  All low side switches on (and all high side off) for braking
+
+BUL = 1 << 0,   //  Likewise for MotorB but different port bits on different port
+BVL = 1 << 1,
+BWL = 1 << 2,
+
+BUH = 1 << 10,
+BVH = 1 << 12,
+BWH = 1 << 13,
+
+BUV =   BUH | BVL,
+BVU =   BVH | BUL,
+BUW =   BUH | BWL,
+BWU =   BWH | BUL,
+BVW =   BVH | BWL,
+BWV =   BWH | BVL,
+
+KEEP_L_MASK_B   = BUL | BVL | BWL,
+KEEP_H_MASK_B   = BUH | BVH | BWH,
+
+BRB = BUL | BVL | BWL,
+
+PORT_A_MASK = AUL | AVL | AWL | AUH | AVH | AWH,            //  NEW METHOD FOR DGD21032 MOSFET DRIVERS
+PORT_B_MASK = BUL | BVL | BWL | BUH | BVH | BWH;
+
+PortOut MotA    (PortA, PORT_A_MASK);   //  Activate output ports to motor drivers
+PortOut MotB    (PortB, PORT_B_MASK);
+
+//  Pin 1   VBAT    NET +3V3
+
+//DigitalIn   J3         (PC_13, PullUp);//  Pin 2   Jumper pulls to GND, R floats Hi
+InterruptIn   Temperature_pin   (PC_13);//  Pin 2   June 2018 - taken for temperature sensor - hard wired to T1 due to wrong thought T1 could be InterruptIn
+
+
+//  Pin 3   PC14-OSC32_IN   NET O32I
+//  Pin 4   PC15-OSC32_OUT  NET O32O
+//  Pin 5   PH0-OSC_IN      NET PH1
+//  Pin 6   PH1-OSC_OUT     NET PH1
+//  Pin 7   NRST            NET NRST
+AnalogIn    Ain_DriverPot   (PC_0); //  Pin 8   Spare Analogue in, net SAIN fitted with external pull-down
+AnalogIn    Ain_SystemVolts (PC_1); //  Pin 9
+AnalogIn    Motor_A_Current (PC_2); //  Pin 10
+AnalogIn    Motor_B_Current (PC_3); //  Pin 11
+//  Pin 12 VSSA/VREF-   NET GND
+//  Pin 13 VDDA/VREF+   NET +3V3
+//  Pin 14  Port_A AUL
+//  Pin 15  Port_A AUH
+//  Pins 16, 17 BufferedSerial pc
+BufferedSerial  pc          (PA_2, PA_3, 512, 4, NULL);    //  Pins 16, 17    tx, rx to pc via usb lead
+//  Pin 18  VSS     NET GND
+//  Pin 19  VDD     NET +3V3
+//  Pin 20  Port_A AWL
+//  Pin 21  DigitalOut led1(LED1);
+DigitalOut  LED           (PA_5); //  Pin 21
+//  Pin 22  Port_A AVL
+//  Pin 23  Port_A AVH
+InterruptIn  MBH2      (PC_4); //  Pin 24
+InterruptIn  MBH3      (PC_5); //  Pin 25
+//  Pin 26  Port_B BUL
+//  Pin 27  Port_B BVL
+//  Pin 28  Port_B BWL
+//  Pin 29  Port_B BUH
+//  Pin 30  VCAP1
+//  Pin 31  VSS
+//  Pin 32  VDD
+//  Pin 33  Port_B BVH
+//  Pin 34  Port_B BWH
+DigitalOut  T4        (PB_14);    //  Pin 35
+DigitalOut  T3        (PB_15);    //  Pin 36
+//  BufferedSerial com2 pins 37 Tx, 38 Rx
+BufferedSerial  com2          (PC_6, PC_7);    //  Pins 37, 38  tx, rx to XBee module
+FastPWM     A_MAX_V_PWM     (PC_8, 1),  //  Pin 39                  pwm3/3
+            A_MAX_I_PWM     (PC_9, 1); //  pin 40, prescaler value  pwm3/4
+//InterruptIn MotB_Hall   (PA_8); //  Pin 41
+//  Pin 41  Port_A AWH
+//  BufferedSerial com3 pins 42 Tx, 43 Rx
+//InterruptIn tryseredge  (PA_9);
+BufferedSerial  com3        (PA_9, PA_10);    //    Pins 42, 43  tx, rx to any aux module
+//  PA_9 is Tx. I wonder, can we also use InterruptIn on this pin to generate interrupts on tx bit transitions ? Let's find out !
+//  No.
+
+//  Feb 2018 Pins 44 and 45 now liberated, could use for serial or other uses
+//BufferedSerial  extra_ser   (PA_11, PA_12);    //  Pins 44, 45  tx, rx to XBee module
+DigitalOut  T2  (PA_11);    //  Pin 44
+// was DigitalOut  T1  (PA_12);    //  Pin 45
+
+
+//InterruptIn T1  (PA_12);    //  Pin 45 now input counting pulses from LMT01 temperature sensor
+//  InterruptIn DOES NOT WORK ON PA_12. Boards are being made, will have to wire link PA12 to PC13
+DigitalIn   T1    (PA_12);
+////InterruptIn T1  (PC_13);    //  Pin 45 now input counting pulses from LMT01 temperature sensor
+
+
+
+//  Pin 46  SWDIO
+//  Pin 47  VSS
+//  Pin 48  VDD
+//  Pin 49  SWCLK
+
+//Was DigitalOut  T5  (PA_15); //  Pin 50
+DigitalIn   T5  (PA_15); //  Pin 50 now fwd/rev from remote control box if fitted
+InterruptIn MAH1    (PC_10);    //  Pin 51
+InterruptIn MAH2    (PC_11);    //  Pin 52
+InterruptIn MAH3    (PC_12);    //  Pin 53
+InterruptIn MBH1    (PD_2);     //  Pin 54
+DigitalOut  T6      (PB_3);     //  Pin 55
+FastPWM     B_MAX_V_PWM     (PB_4, 1),  //  Pin 56                  pwm3/3
+            B_MAX_I_PWM     (PB_5, 1); //  pin 57, prescaler value  pwm3/4
+
+I2C i2c                     (PB_7, PB_6);   //  Pins 58, 59 For 24LC64 eeprom
+//  Pin 60  BOOT0
+
+//  Servo pins, 2 off. Configured as Input to read radio control receiver
+//  If used as servo output, code gives pin to 'Servo' - seems to work
+InterruptIn Servo1_i    (PB_8); //  Pin 61  to read output from rc rx
+InterruptIn Servo2_i    (PB_9); //  Pin 62  to read output from rc rx
+
+//  Pin 63  VSS
+//  Pin 64  VDD
+//  SYSTEM CONSTANTS
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/F446ZE.h	Sun Jun 17 06:59:37 2018 +0000
@@ -0,0 +1,145 @@
+//  5TH JUNE 2018 NONE OF THIS IS RIGHT YET !!
+
+//  Hoped to select servo functions from user info stored on EEROM. Too difficult. Do not define servo as in and out
+
+//  Port A -> MotorA, Port B -> MotorB
+const   uint16_t
+AUL = 1 << 0,   //  Feb 2018 Now using DGD21032 mosfet drivers via 74HC00 pwm gates (low side) - GOOD, works well with auto-tickle of high side drivers
+AVL = 1 << 6,   //  These are which port bits connect to which mosfet driver
+AWL = 1 << 4,
+
+AUH = 1 << 1,
+AVH = 1 << 7,
+AWH = 1 << 8,
+
+AUV =   AUH | AVL,  //  Each of 6 possible output energisations made up of one hi and one low
+AVU =   AVH | AUL,
+AUW =   AUH | AWL,
+AWU =   AWH | AUL,
+AVW =   AVH | AWL,
+AWV =   AWH | AVL,
+
+KEEP_L_MASK_A   = AUL | AVL | AWL,
+KEEP_H_MASK_A   = AUH | AVH | AWH,
+
+BRA = AUL | AVL | AWL,  //  All low side switches on (and all high side off) for braking
+
+BUL = 1 << 0,   //  Likewise for MotorB but different port bits on different port
+BVL = 1 << 1,
+BWL = 1 << 2,
+
+BUH = 1 << 10,
+BVH = 1 << 12,
+BWH = 1 << 13,
+
+BUV =   BUH | BVL,
+BVU =   BVH | BUL,
+BUW =   BUH | BWL,
+BWU =   BWH | BUL,
+BVW =   BVH | BWL,
+BWV =   BWH | BVL,
+
+KEEP_L_MASK_B   = BUL | BVL | BWL,
+KEEP_H_MASK_B   = BUH | BVH | BWH,
+
+BRB = BUL | BVL | BWL,
+
+PORT_A_MASK = AUL | AVL | AWL | AUH | AVH | AWH,            //  NEW METHOD FOR DGD21032 MOSFET DRIVERS
+PORT_B_MASK = BUL | BVL | BWL | BUH | BVH | BWH;
+
+PortOut MotA    (PortA, PORT_A_MASK);   //  Activate output ports to motor drivers
+PortOut MotB    (PortB, PORT_B_MASK);
+
+//  Pin 1   VBAT    NET +3V3
+
+//DigitalIn   J3         (PC_13, PullUp);//  Pin 2   Jumper pulls to GND, R floats Hi
+InterruptIn   Temperature_pin   (PC_13);//  Pin 2   June 2018 - taken for temperature sensor - hard wired to T1 due to wrong thought T1 could be InterruptIn
+
+
+//  Pin 3   PC14-OSC32_IN   NET O32I
+//  Pin 4   PC15-OSC32_OUT  NET O32O
+//  Pin 5   PH0-OSC_IN      NET PH1
+//  Pin 6   PH1-OSC_OUT     NET PH1
+//  Pin 7   NRST            NET NRST
+AnalogIn    Ain_DriverPot   (PC_0); //  Pin 8   Spare Analogue in, net SAIN fitted with external pull-down
+AnalogIn    Ain_SystemVolts (PC_1); //  Pin 9
+AnalogIn    Motor_A_Current (PC_2); //  Pin 10
+AnalogIn    Motor_B_Current (PC_3); //  Pin 11
+//  Pin 12 VSSA/VREF-   NET GND
+//  Pin 13 VDDA/VREF+   NET +3V3
+//  Pin 14  Port_A AUL
+//  Pin 15  Port_A AUH
+//  Pins 16, 17 BufferedSerial pc
+BufferedSerial  pc          (PA_2, PA_3, 512, 4, NULL);    //  Pins 16, 17    tx, rx to pc via usb lead
+//  Pin 18  VSS     NET GND
+//  Pin 19  VDD     NET +3V3
+//  Pin 20  Port_A AWL
+//  Pin 21  DigitalOut led1(LED1);
+DigitalOut  LED           (PA_5); //  Pin 21
+//  Pin 22  Port_A AVL
+//  Pin 23  Port_A AVH
+InterruptIn  MBH2      (PC_4); //  Pin 24
+InterruptIn  MBH3      (PC_5); //  Pin 25
+//  Pin 26  Port_B BUL
+//  Pin 27  Port_B BVL
+//  Pin 28  Port_B BWL
+//  Pin 29  Port_B BUH
+//  Pin 30  VCAP1
+//  Pin 31  VSS
+//  Pin 32  VDD
+//  Pin 33  Port_B BVH
+//  Pin 34  Port_B BWH
+DigitalOut  T4        (PB_14);    //  Pin 35
+DigitalOut  T3        (PB_15);    //  Pin 36
+//  BufferedSerial com2 pins 37 Tx, 38 Rx
+BufferedSerial  com2          (PC_6, PC_7);    //  Pins 37, 38  tx, rx to XBee module
+FastPWM     A_MAX_V_PWM     (PC_8, 1),  //  Pin 39                  pwm3/3
+            A_MAX_I_PWM     (PC_9, 1); //  pin 40, prescaler value  pwm3/4
+//InterruptIn MotB_Hall   (PA_8); //  Pin 41
+//  Pin 41  Port_A AWH
+//  BufferedSerial com3 pins 42 Tx, 43 Rx
+//InterruptIn tryseredge  (PA_9);
+BufferedSerial  com3        (PA_9, PA_10);    //    Pins 42, 43  tx, rx to any aux module
+//  PA_9 is Tx. I wonder, can we also use InterruptIn on this pin to generate interrupts on tx bit transitions ? Let's find out !
+//  No.
+
+//  Feb 2018 Pins 44 and 45 now liberated, could use for serial or other uses
+//BufferedSerial  extra_ser   (PA_11, PA_12);    //  Pins 44, 45  tx, rx to XBee module
+DigitalOut  T2  (PA_11);    //  Pin 44
+// was DigitalOut  T1  (PA_12);    //  Pin 45
+
+
+//InterruptIn T1  (PA_12);    //  Pin 45 now input counting pulses from LMT01 temperature sensor
+//  InterruptIn DOES NOT WORK ON PA_12. Boards are being made, will have to wire link PA12 to PC13
+DigitalIn   T1    (PA_12);
+////InterruptIn T1  (PC_13);    //  Pin 45 now input counting pulses from LMT01 temperature sensor
+
+
+
+//  Pin 46  SWDIO
+//  Pin 47  VSS
+//  Pin 48  VDD
+//  Pin 49  SWCLK
+
+//Was DigitalOut  T5  (PA_15); //  Pin 50
+DigitalIn   T5  (PA_15); //  Pin 50 now fwd/rev from remote control box if fitted
+InterruptIn MAH1    (PC_10);    //  Pin 51
+InterruptIn MAH2    (PC_11);    //  Pin 52
+InterruptIn MAH3    (PC_12);    //  Pin 53
+InterruptIn MBH1    (PD_2);     //  Pin 54
+DigitalOut  T6      (PB_3);     //  Pin 55
+FastPWM     B_MAX_V_PWM     (PB_4, 1),  //  Pin 56                  pwm3/3
+            B_MAX_I_PWM     (PB_5, 1); //  pin 57, prescaler value  pwm3/4
+
+I2C i2c                     (PB_7, PB_6);   //  Pins 58, 59 For 24LC64 eeprom
+//  Pin 60  BOOT0
+
+//  Servo pins, 2 off. Configured as Input to read radio control receiver
+//  If used as servo output, code gives pin to 'Servo' - seems to work
+InterruptIn Servo1_i    (PB_8); //  Pin 61  to read output from rc rx
+InterruptIn Servo2_i    (PB_9); //  Pin 62  to read output from rc rx
+
+//  Pin 63  VSS
+//  Pin 64  VDD
+//  SYSTEM CONSTANTS
+
--- 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
-//    }
+//    }*/
 }
 
 
--- a/main.cpp	Tue Jun 05 07:19:39 2018 +0000
+++ b/main.cpp	Sun Jun 17 06:59:37 2018 +0000
@@ -6,7 +6,7 @@
 
 /*
 New 29th May 2018 - YET TO CODE FOR - Fwd/Rev line from possible remote hand control box has signal routed to T5
-                Also new LMT01 temperature sensor routed to T1 - and rerouted to PC_13 at InterruptIn on T1 (ports A and B I think) not workable
+                Also new LMT01 temperature sensor routed to T1 - and rerouted to PC_13 as InterruptIn on T1 (ports A and B I think) not workable
 */
 
 
@@ -28,154 +28,11 @@
 
 
 */
-//#if defined (TARGET_NUCLEO_F446ZE)
-#if defined (TARGET_NUCLEO_F401RE)
-
-//  Hoped to select servo functions from user info stored on EEROM. Too difficult. Do not define servo as in and out
-
-//  Port A -> MotorA, Port B -> MotorB
-const   uint16_t
-AUL = 1 << 0,   //  Feb 2018 Now using DGD21032 mosfet drivers via 74HC00 pwm gates (low side) - GOOD, works well with auto-tickle of high side drivers
-AVL = 1 << 6,   //  These are which port bits connect to which mosfet driver
-AWL = 1 << 4,
-
-AUH = 1 << 1,
-AVH = 1 << 7,
-AWH = 1 << 8,
-
-AUV =   AUH | AVL,  //  Each of 6 possible output energisations made up of one hi and one low
-AVU =   AVH | AUL,
-AUW =   AUH | AWL,
-AWU =   AWH | AUL,
-AVW =   AVH | AWL,
-AWV =   AWH | AVL,
-
-KEEP_L_MASK_A   = AUL | AVL | AWL,
-KEEP_H_MASK_A   = AUH | AVH | AWH,
-
-BRA = AUL | AVL | AWL,  //  All low side switches on (and all high side off) for braking
-
-BUL = 1 << 0,   //  Likewise for MotorB but different port bits on different port
-BVL = 1 << 1,
-BWL = 1 << 2,
-
-BUH = 1 << 10,
-BVH = 1 << 12,
-BWH = 1 << 13,
-
-BUV =   BUH | BVL,
-BVU =   BVH | BUL,
-BUW =   BUH | BWL,
-BWU =   BWH | BUL,
-BVW =   BVH | BWL,
-BWV =   BWH | BVL,
-
-KEEP_L_MASK_B   = BUL | BVL | BWL,
-KEEP_H_MASK_B   = BUH | BVH | BWH,
-
-BRB = BUL | BVL | BWL,
-
-PORT_A_MASK = AUL | AVL | AWL | AUH | AVH | AWH,            //  NEW METHOD FOR DGD21032 MOSFET DRIVERS
-PORT_B_MASK = BUL | BVL | BWL | BUH | BVH | BWH;
-
-PortOut MotA    (PortA, PORT_A_MASK);   //  Activate output ports to motor drivers
-PortOut MotB    (PortB, PORT_B_MASK);
-
-//  Pin 1   VBAT    NET +3V3
-
-//DigitalIn   J3         (PC_13, PullUp);//  Pin 2   Jumper pulls to GND, R floats Hi
-InterruptIn   Temperature_pin   (PC_13);//  Pin 2   June 2018 - taken for temperature sensor - hard wired to T1 due to wrong thought T1 could be InterruptIn
-
-
-//  Pin 3   PC14-OSC32_IN   NET O32I
-//  Pin 4   PC15-OSC32_OUT  NET O32O
-//  Pin 5   PH0-OSC_IN      NET PH1
-//  Pin 6   PH1-OSC_OUT     NET PH1
-//  Pin 7   NRST            NET NRST
-AnalogIn    Ain_DriverPot   (PC_0); //  Pin 8   Spare Analogue in, net SAIN fitted with external pull-down
-AnalogIn    Ain_SystemVolts (PC_1); //  Pin 9
-AnalogIn    Motor_A_Current (PC_2); //  Pin 10
-AnalogIn    Motor_B_Current (PC_3); //  Pin 11
-//  Pin 12 VSSA/VREF-   NET GND
-//  Pin 13 VDDA/VREF+   NET +3V3
-//  Pin 14  Port_A AUL
-//  Pin 15  Port_A AUH
-//  Pins 16, 17 BufferedSerial pc
-BufferedSerial  pc          (PA_2, PA_3, 512, 4, NULL);    //  Pins 16, 17    tx, rx to pc via usb lead
-//  Pin 18  VSS     NET GND
-//  Pin 19  VDD     NET +3V3
-//  Pin 20  Port_A AWL
-//  Pin 21  DigitalOut led1(LED1);
-DigitalOut  LED           (PA_5); //  Pin 21
-//  Pin 22  Port_A AVL
-//  Pin 23  Port_A AVH
-InterruptIn  MBH2      (PC_4); //  Pin 24
-InterruptIn  MBH3      (PC_5); //  Pin 25
-//  Pin 26  Port_B BUL
-//  Pin 27  Port_B BVL
-//  Pin 28  Port_B BWL
-//  Pin 29  Port_B BUH
-//  Pin 30  VCAP1
-//  Pin 31  VSS
-//  Pin 32  VDD
-//  Pin 33  Port_B BVH
-//  Pin 34  Port_B BWH
-DigitalOut  T4        (PB_14);    //  Pin 35
-DigitalOut  T3        (PB_15);    //  Pin 36
-//  BufferedSerial com2 pins 37 Tx, 38 Rx
-BufferedSerial  com2          (PC_6, PC_7);    //  Pins 37, 38  tx, rx to XBee module
-FastPWM     A_MAX_V_PWM     (PC_8, 1),  //  Pin 39                  pwm3/3
-            A_MAX_I_PWM     (PC_9, 1); //  pin 40, prescaler value  pwm3/4
-//InterruptIn MotB_Hall   (PA_8); //  Pin 41
-//  Pin 41  Port_A AWH
-//  BufferedSerial com3 pins 42 Tx, 43 Rx
-//InterruptIn tryseredge  (PA_9);
-BufferedSerial  com3        (PA_9, PA_10);    //    Pins 42, 43  tx, rx to any aux module
-//  PA_9 is Tx. I wonder, can we also use InterruptIn on this pin to generate interrupts on tx bit transitions ? Let's find out !
-//  No.
-
-//  Feb 2018 Pins 44 and 45 now liberated, could use for serial or other uses
-//BufferedSerial  extra_ser   (PA_11, PA_12);    //  Pins 44, 45  tx, rx to XBee module
-DigitalOut  T2  (PA_11);    //  Pin 44
-// was DigitalOut  T1  (PA_12);    //  Pin 45
-
-
-//InterruptIn T1  (PA_12);    //  Pin 45 now input counting pulses from LMT01 temperature sensor
-//  InterruptIn DOES NOT WORK ON PA_12. Boards are being made, will have to wire link PA12 to PC13
-DigitalIn   T1    (PA_12);
-////InterruptIn T1  (PC_13);    //  Pin 45 now input counting pulses from LMT01 temperature sensor
-
-
-
-//  Pin 46  SWDIO
-//  Pin 47  VSS
-//  Pin 48  VDD
-//  Pin 49  SWCLK
-
-//Was DigitalOut  T5  (PA_15); //  Pin 50
-DigitalIn   T5  (PA_15); //  Pin 50 now fwd/rev from remote control box if fitted
-InterruptIn MAH1    (PC_10);    //  Pin 51
-InterruptIn MAH2    (PC_11);    //  Pin 52
-InterruptIn MAH3    (PC_12);    //  Pin 53
-InterruptIn MBH1    (PD_2);     //  Pin 54
-DigitalOut  T6      (PB_3);     //  Pin 55
-FastPWM     B_MAX_V_PWM     (PB_4, 1),  //  Pin 56                  pwm3/3
-            B_MAX_I_PWM     (PB_5, 1); //  pin 57, prescaler value  pwm3/4
-
-I2C i2c                     (PB_7, PB_6);   //  Pins 58, 59 For 24LC64 eeprom
-//  Pin 60  BOOT0
-
-//  Servo pins, 2 off. Configured as Input to read radio control receiver
-//  If used as servo output, code gives pin to 'Servo' - seems to work
-InterruptIn Servo1_i    (PB_8); //  Pin 61  to read output from rc rx
-InterruptIn Servo2_i    (PB_9); //  Pin 62  to read output from rc rx
-
-//  Pin 63  VSS
-//  Pin 64  VDD
-//  SYSTEM CONSTANTS
-
+#if defined (TARGET_NUCLEO_F401RE)  //  CPU in 64 pin LQFP
+#include    "F401RE.h"
 #endif
-#if defined (TARGET_NUCLEO_F446ZE)
+#if defined (TARGET_NUCLEO_F446ZE)  //  CPU in 144 pin LQFP
+#include    "F446ZE.h"
 #endif
 /*  Global variable declarations */
 volatile    uint32_t    fast_sys_timer      = 0;    //  gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US
@@ -188,11 +45,14 @@
 bool        loop_flag   = false;    //  made true in ISR_loop_timer, picked up and made false again in main programme loop
 bool        flag_8Hz    = false;    //  As loop_flag but repeats 8 times per sec
 bool        temp_sensor_exists = false;
+bool        eeprom_flag; //  gets set according to 24LC674 being found or not
+bool        mode_good_flag  = false;
 char        mode_bytes[36];
 
 uint32_t    temp_sensor_count = 0,  //  incremented by every rising edge from LMT01
             last_temp_count = 0;  //  global updated approx every 100ms after each LMT01 conversion completes
 //    struct  single_bogie_options    bogie;    
+    double  rpm2mph = 0.0;  //  gets calculated from eeprom mode entries if present
 /*  End of Global variable declarations */
 
 Ticker  tick_vread;     //  Device to cause periodic interrupts, used to time voltage readings etc
@@ -364,7 +224,7 @@
 motor   MotorA  (&MotA, &A_MAX_V_PWM, &A_MAX_I_PWM, A_tabl, AHarr);
 motor   MotorB  (&MotB, &B_MAX_V_PWM, &B_MAX_I_PWM, B_tabl, BHarr);
 
-motor * MotPtr[8];  //  Array of pointers to some number of motor objects
+//motor * MotPtr[8];  //  Array of pointers to some number of motor objects
 
 motor::motor    (PortOut * P , FastPWM * _maxV_ , FastPWM * _maxI_ , const uint16_t * lutptr, InterruptIn ** Hall)        //  Constructor
 {   //  Constructor
@@ -700,7 +560,9 @@
     }
 }
 
-extern  void    command_line_interpreter    ()  ;
+extern  void    setup_comms ()  ;
+extern  void    command_line_interpreter_pc    ()  ;
+extern  void    command_line_interpreter_loco    ()  ;
 extern  int     check_24LC64   ()  ;   //  Call from near top of main() to init i2c bus
 extern  bool    wr_24LC64  (int mem_start_addr, char * source, int length)   ;
 extern  bool    rd_24LC64  (int mem_start_addr, char * dest, int length)   ;
@@ -719,6 +581,12 @@
     return  IAm;
 }
 
+double  mph (int    rpm)    {
+    if  (mode_good_flag)    {
+        return  rpm2mph * (double) rpm;
+    }
+    return  -1.0;
+}
 
 int main()
 {
@@ -726,8 +594,8 @@
 
     MotA   = 0;     //  Output all 0s to Motor drive ports A and B
     MotB   = 0;
-    MotPtr[0] = &MotorA;    //  Pointers to motor class objects
-    MotPtr[1] = &MotorB;
+//    MotPtr[0] = &MotorA;    //  Pointers to motor class objects
+//    MotPtr[1] = &MotorB;
     
     Temperature_pin.fall (&temp_sensor_isr);
     Temperature_pin.mode (PullUp);
@@ -767,23 +635,18 @@
     pc.baud     (9600);
     com3.baud   (1200);
     com2.baud   (19200);
+    setup_comms ();
 
+    IAm = '0';
     if  (check_24LC64() != 0xa0)  { //  searches for i2c devices, returns address of highest found
         pc.printf   ("Check for 24LC64 eeprom FAILED\r\n");
         com2.printf   ("Check for 24LC64 eeprom FAILED\r\n");
+        eeprom_flag = false;
     }
     else   {        //  Found 24LC64 memory on I2C
+        eeprom_flag = true;
         bool k;
-//        static const char ramtst[] = "I found the man sir!";
-//        j = wr_24LC64  (0x1240, (char*)ramtst, strlen(ramtst));
-//        for (int i = 0; i < TXTBUFSIZ; i++)    buff[i] = 0;     //  Clear buffer
-//        //  need a way to check i2c busy - YES implemented ack_poll
-//        k = rd_24LC64  (0x1240, buff, strlen(ramtst));
-//        pc.printf("Ram test returned [%s], wr ret'd [%s], rd ret'd [%s]\r\n", buff, j ? "true" : "false", k ? "true" : "false");
-//        com2.printf("Ram test returned [%s], wr ret'd [%s], rd ret'd [%s]\r\n", buff, j ? "true" : "false", k ? "true" : "false");
         k = rd_24LC64   (0, mode_bytes, 32);
-//        if  (k)
-//            com2.printf ("Good read from eeprom\r\n");
         if  (!k)
             com2.printf ("Error reading from eeprom\r\n");
 
@@ -796,11 +659,16 @@
 //            else
 //                com2.printf ("%2x Good %s\r\n", buff[i], option_list[i].t);
         }
-        IAm = '0';
+        rpm2mph = 0.0;
         if  (err == 0)  {
+            mode_good_flag = true;
             MotorA.direction_set    (mode_bytes[MOTADIR]);
             MotorB.direction_set    (mode_bytes[MOTBDIR]);
             IAm = mode_bytes[ID];
+            rpm2mph = 60.0                                                          //  to Motor Revs per hour;
+                    * ((double)mode_bytes[MOTPIN] / (double)mode_bytes[WHEELGEAR])  //  Wheel revs per hour
+                    * PI * ((double)mode_bytes[WHEELDIA] / 1000.0)                  //  metres per hour
+                    * 39.37 / (1760.0 * 36.0);                                      //  miles per hour
         }
            //  Alternative ID 1 to 9
 //        com2.printf ("Alternative ID = 0x%2x\r\n", buff[6]);
@@ -826,7 +694,7 @@
     Servo   Servo2  (PB_9)  ;
     Servos[1] = & Servo2;
     
-    pc.printf   ("last_temp_count = %d\r\n", last_temp_count);  //  Has had time to do at least 1 conversion
+//    pc.printf   ("last_temp_count = %d\r\n", last_temp_count);  //  Has had time to do at least 1 conversion
     if  ((last_temp_count > 160) && (last_temp_count < 2400))   //  in range -40 to +100 degree C
         temp_sensor_exists  = true;
 /*
@@ -851,10 +719,12 @@
             break;
     }
     */
-    pc.printf   ("Ready to go!, wheel gear in position %d\r\n", WHEELGEAR);
+//    pc.printf   ("Ready to go!, wheel gear in position %d\r\n", WHEELGEAR);
+    pc.printf   ("About to start!\r\n");
     while   (1) {      //  Loop forever, repeats synchroised by waiting for ticker Interrupt Service Routine to set 'loop_flag' true
         while   (!loop_flag)  {         //  Most of the time is spent in this loop, repeatedly re-checking for commands from pc port
-            command_line_interpreter    ()  ;   //  Proceed beyond here once loop_timer ticker ISR has set loop_flag true
+            command_line_interpreter_pc    ()  ;   //  Proceed beyond here once loop_timer ticker ISR has set loop_flag true
+            command_line_interpreter_loco    ()  ;   //  Proceed beyond here once loop_timer ticker ISR has set loop_flag true
             AtoD_reader ();                     //  Performs A to D conversions at rate set by ticker interrupts
         }
         loop_flag = false;              //  Clear flag set by ticker interrupt handler
@@ -877,12 +747,12 @@
                 eighth_sec_count = 0;
                 MotorA.current_calc ();     //  Updates readings in MotorA.I.min, MotorA.I.ave and MotorA.I.max
                 MotorB.current_calc ();
-                if  (temp_sensor_exists)    {
+/*                if  (temp_sensor_exists)    {
                     double  tmprt = (double) last_temp_count;
                     tmprt /= 16.0;
                     tmprt -= 50.0;
                     pc.printf   ("Temp %.2f\r\n", tmprt);
-                }
+                }*/
 //                com2.printf   ("V=%+.2f, Pot=%+.2f, HA %d, HB %d, IAmin %d, IAave %d, IAmax %d, IB %d, Arpm %d, Brpm %d\r\n", Read_BatteryVolts(), Read_DriverPot(), MotorA.read_Halls  (), MotorB.read_Halls  (), MotorA.I.min, MotorA.I.ave, MotorA.I.max, MotorB.I.ave, (Apps * 60) / 24, (Bpps * 60) / 24);
             }
         }   //  End of if(flag_8Hz)