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:
12:d1d21a2941ef
Parent:
11:bfb73f083009
Child:
13:ef7a06fa11de
--- a/main.cpp	Sat Jan 19 11:45:01 2019 +0000
+++ b/main.cpp	Mon Mar 04 17:51:08 2019 +0000
@@ -12,14 +12,23 @@
 
 /*
 Brushless_STM3 board
-
+Jan 2019    *   WatchDog implemented. Default is disabled, 'kd' command from TS controller enables and reloads
+            *   Tidied brushless_motor class, parameter passing now done properly
+            *   class   RControl_In created. Inputs now routed to new pins, can now use two chans both class   RControl_In and Servo out
+                (buggery board required for new inputs)
+            *   Added version string
+            *   Error handler written and included
+            *   Realised Nanotec motors are 6 pole, all others are 8 pole. Modified 'mode' to include 'motor poles' in EEPROM data, now speed reading correct for all
+            *   Reorganised EEPROM data - mode setting now much easier, less error prone
+            *   Maximum speed now one EEPROM option, range 1.0 to 25.0 MPH
+                
 New 29th May 2018 **
                 LMT01 temperature sensor routed to T1 - and rerouted to PC_13 as InterruptIn on T1 (ports A and B I think) not workable
 */
 
 
 /*  STM32F401RE - compile using NUCLEO-F401RE
-//  PROJECT -   Dual Brushless Motor Controller -   Jon Freeman     June 2018.
+//  PROJECT -   STM3_ESC Dual Brushless Motor Controller -   Jon Freeman     June 2018.
 
 AnalogIn to read each motor current
 
@@ -44,6 +53,7 @@
 #include    "F446ZE.h"              //  A thought for future version
 #endif
 /*  Global variable declarations */
+char   const    const_version_string[] = {"1.0.0\0"};  //  Version string, readable from serial ports
 volatile    uint32_t    fast_sys_timer      = 0;    //  gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US
 int         WatchDog    = WATCHDOG_RELOAD + 80; //  Allow extra few seconds at powerup
 bool        WatchDogEnable  = false;    //  Must recieve explicit instruction from pc or controller to enable
@@ -51,80 +61,31 @@
             driverpot_reading   = 0,    //  Global updated by interrupt driven read of Drivers Pot
             sys_timer           = 0,    //  gets incremented by our Ticker ISR every MAIN_LOOP_REPEAT_TIME_US
             AtoD_Semaphore      = 0;
-int         IAm;
+
 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];
+double      rpm2mph;
 
 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
+            last_temperature_count = 0;  //  global updated approx every 100ms after each LMT01 conversion completes
 /*  End of Global variable declarations */
 
 Ticker  tick_vread;     //  Device to cause periodic interrupts, used to time voltage readings etc
 Ticker  loop_timer;     //  Device to cause periodic interrupts, used to sync iterations of main programme loop
 Ticker  temperature_find_ticker;
 Timer   temperature_timer;
+
+#ifdef USING_DC_MOTORS
 Timer   dc_motor_kicker_timer;
 Timeout motors_restore;
+#endif
+
 RControl_In     RC_chan_1   (PC_14);
 RControl_In     RC_chan_2   (PC_15);   //  Instantiate two radio control input channels and specify which InterruptIn pin
-
-
-//  Interrupt Service Routines
-void    ISR_temperature_find_ticker ()      //  every 960 us, i.e. slightly faster than once per milli sec
-{
-    static  bool    readflag = false;
-    int t = temperature_timer.read_ms   ();
-    if  ((t == 5) && (!readflag))    {
-        last_temp_count = temp_sensor_count;
-        temp_sensor_count = 0;
-        readflag = true;
-    }
-    if  (t == 6)
-        readflag = false;
-}
+error_handling_Jan_2019     ESC_Error    ;         //  Provides array usable to store error codes.
 
-/** void    ISR_loop_timer  ()
-*   This ISR responds to Ticker interrupts at a rate of (probably) 32 times per second (check from constant declarations above)
-*   This ISR sets global flag 'loop_flag' used to synchronise passes around main programme control loop.
-*   Increments global 'sys_timer', usable anywhere as general measure of elapsed time
-*/
-void    ISR_loop_timer  ()      //  This is Ticker Interrupt Service Routine - loop timer - MAIN_LOOP_REPEAT_TIME_US
-{
-    loop_flag = true;   //  set flag to allow main programme loop to proceed
-    sys_timer++;        //  Just a handy measure of elapsed time for anything to use
-    if  ((sys_timer & 0x03) == 0)
-        flag_8Hz    = true;
-}
-
-/** void    ISR_voltage_reader  ()
-*   This ISR responds to Ticker interrupts every 'VOLTAGE_READ_INTERVAL_US' micro seconds
-*
-*   AtoD_reader() called from convenient point in code to take readings outside of ISRs
-*/
-
-void    ISR_voltage_reader  ()      //  This is Ticker Interrupt Service Routine ; few us between readings ; VOLTAGE_READ_INTERVAL_US    = 50
-{
-    AtoD_Semaphore++;
-    fast_sys_timer++;        //  Just a handy measure of elapsed time for anything to use
-}
-
-void    temp_sensor_isr ()      //  got rising edge from LMT01. ALMOST CERTAIN this misses some
-{
-    int t = temperature_timer.read_us   (); //  Must be being overrun by something, most likely culprit A-D reading ?
-    temperature_timer.reset ();
-    temp_sensor_count++;
-    if  (t > 18)            //  Yes proved some interrupts get missed, this fixes temperature reading
-        temp_sensor_count++;
-//    T2 = !T2;   //  scope hanger
-}
-
-//  End of Interrupt Service Routines
+eeprom_settings     mode    (SDA_PIN, SCL_PIN)  ;   //  This MUST come before Motors setup
 
 //uint32_t    HAtot = 0, HBtot = 0, A_Offset = 0, B_Offset = 0;
 /*
@@ -159,15 +120,63 @@
     KEEP_L_MASK_B, KEEP_H_MASK_B
 }   ;
 
-brushless_motor   MotorA  (MOT_A_I_ADC, APWMV, APWMI, A_tabl, _MAH1, _MAH2, _MAH3, PortA, PORT_A_MASK);
-brushless_motor   MotorB  (MOT_B_I_ADC, BPWMV, BPWMI, B_tabl, _MBH1, _MBH2, _MBH3, PortB, PORT_B_MASK);
+brushless_motor   MotorA  (MOT_A_I_ADC, APWMV, APWMI, A_tabl, _MAH1, _MAH2, _MAH3, PortA, PORT_A_MASK, ISHUNTA);
+brushless_motor   MotorB  (MOT_B_I_ADC, BPWMV, BPWMI, B_tabl, _MBH1, _MBH2, _MBH3, PortB, PORT_B_MASK, ISHUNTB);
 
 
-void    report_motor_types  ()      //  not very good test, shows 'Brushless' if Hall inputs read 1 to 6, 'DC' otherwise
+extern  cli_2019    pcc, tsc;   //  command line interpreters from pc and touch screen
+
+//  Interrupt Service Routines
+void    ISR_temperature_find_ticker ()      //  every 960 us, i.e. slightly faster than once per milli sec
+{
+    static  bool    readflag = false;
+    int t = temperature_timer.read_ms   ();
+    if  ((t == 5) && (!readflag))    {
+        last_temperature_count = temp_sensor_count;
+        temp_sensor_count = 0;
+        readflag = true;
+    }
+    if  (t == 6)
+        readflag = false;
+}
+
+/** void    ISR_loop_timer  ()
+*   This ISR responds to Ticker interrupts at a rate of (probably) 32 times per second (check from constant declarations above)
+*   This ISR sets global flag 'loop_flag' used to synchronise passes around main programme control loop.
+*   Increments global 'sys_timer', usable anywhere as general measure of elapsed time
+*/
+void    ISR_loop_timer  ()      //  This is Ticker Interrupt Service Routine - loop timer - MAIN_LOOP_REPEAT_TIME_US
 {
-    pc.printf   ("Mot A is %s, Mot B is %s\r\n", MotorA.motor_is_brushless() ? "Brushless":"DC", MotorB.motor_is_brushless() ? "Brushless":"DC");
+    loop_flag = true;   //  set flag to allow main programme loop to proceed
+    sys_timer++;        //  Just a handy measure of elapsed time for anything to use
+    if  ((sys_timer & 0x03) == 0)
+        flag_8Hz    = true;
 }
 
+/** void    ISR_voltage_reader  ()
+*   This ISR responds to Ticker interrupts every 'VOLTAGE_READ_INTERVAL_US' micro seconds
+*
+*   AtoD_reader() called from convenient point in code to take readings outside of ISRs
+*/
+void    ISR_voltage_reader  ()      //  This is Ticker Interrupt Service Routine ; few us between readings ; VOLTAGE_READ_INTERVAL_US    = 50
+{
+    AtoD_Semaphore++;
+    fast_sys_timer++;        //  Just a handy measure of elapsed time for anything to use
+}
+
+void    temp_sensor_isr ()      //  got rising edge from LMT01. ALMOST CERTAIN this misses some
+{
+    int t = temperature_timer.read_us   (); //  Must be being overrun by something, most likely culprit A-D reading ?
+    temperature_timer.reset ();
+    temp_sensor_count++;
+    if  (t > 18)            //  Yes proved some interrupts get missed, this fixes temperature reading
+        temp_sensor_count++;
+//    T2 = !T2;   //  scope hanger
+}
+
+//  End of Interrupt Service Routines
+
+
 void    setVI   (double v, double i)
 {
     MotorA.set_V_limit  (v);  //  Sets max motor voltage
@@ -176,40 +185,18 @@
     MotorB.set_I_limit  (i);
 }
 
-void    setV    (double v)
-{
-    MotorA.set_V_limit  (v);
-    MotorB.set_V_limit  (v);
-}
-
-void    setI    (double i)
-{
-    MotorA.set_I_limit  (i);
-    MotorB.set_I_limit  (i);
-}
-
 
 /**
-void    AtoD_reader ()  //  Call to here every VOLTAGE_READ_INTERVAL_US    = 50 once loop responds to flag set in isr
-Not part of ISR
+*   void    AtoD_reader ()  //  Call to here every VOLTAGE_READ_INTERVAL_US    = 50 once loop responds to flag set in isr
+*   Not part of ISR
 */
 void    AtoD_reader ()  //  Call to here every VOLTAGE_READ_INTERVAL_US    = 50 once loop responds to flag set in isr
 {
     static uint32_t i = 0;
-//    if  (MotorA.dc_motor)   {
-//        MotorA.low_side_on  ();
-//    }
-//    else    {
     if  (MotorA.tickleon)
         MotorA.high_side_off    ();
-//    }
-//    if  (MotorB.dc_motor)   {
-//        MotorB.low_side_on  ();
-//    }
-//    else    {
     if  (MotorB.tickleon)
         MotorB.high_side_off    ();
-//    }
     if  (AtoD_Semaphore > 20)   {
         pc.printf   ("WARNING - sema cnt %d\r\n", AtoD_Semaphore);
         AtoD_Semaphore = 20;
@@ -227,7 +214,7 @@
                 driverpot_reading >>= 1;
                 break;
             case    2:
-                MotorA.sniff_current    (); //  Initiate ADC reading into averaging table
+                MotorA.sniff_current    (); //  Initiate ADC current reading
                 break;
             case    3:
                 MotorB.sniff_current    ();
@@ -238,12 +225,10 @@
             i = 0;
     }   //  end of while (AtoD_Semaphore > 0)    {
     if  (MotorA.tickleon)   {
-//    if  (MotorA.dc_motor || MotorA.tickleon)   {
         MotorA.tickleon--;
         MotorA.motor_set    (); //  Reactivate any high side switches turned off above
     }
     if  (MotorB.tickleon)   {
-//    if  (MotorB.dc_motor || MotorB.tickleon)   {
         MotorB.tickleon--;
         MotorB.motor_set    ();
     }
@@ -295,11 +280,12 @@
     return  ((double) volt_reading) / 951.0;    //  divisor fiddled to make voltage reading correct !
 }
 
+
 void    mode_set_both_motors   (int mode, double val)      //  called from cli to set fw, re, rb, hb
 {
     MotorA.set_mode (mode);
     MotorB.set_mode (mode);
-    if  (mode == REGENBRAKE)    {
+    if  (mode == MOTOR_REGENBRAKE)    {
         if  (val > 1.0)
             val = 1.0;
         if  (val < 0.0)
@@ -310,36 +296,9 @@
     }
 }
 
-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)   ;
+
 
-/*struct  motorpairoptions    {   //  This to be user settable in eeprom, 32 bytes
-    uint8_t MotA_dir,   //  0 or 1
-            MotB_dir,   //  0 or 1
-            gang,       //  0 for separate control (robot mode), 1 for ganged loco bogie mode
-            serv1,      //  0, 1, 2 = Not used, Input, Output
-            serv2,      //  0, 1, 2 = Not used, Input, Output
-            cmd_source, //  0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2
-            last;
-}   ;
-*/
-int I_Am    ()      //  Returns boards id number as ASCII char
-{
-    return  IAm;
-}
-
-double  mph (int    rpm)
-{
-    if  (mode_good_flag)    {
-        return  rpm2mph * (double) rpm;
-    }
-    return  -1.0;
-}
-
+#ifdef  USING_DC_MOTORS
 void    restorer    ()
 {
     motors_restore.detach   ();
@@ -354,6 +313,7 @@
         MotorB.motor_set    ();
     }
 }
+#endif
 
 void    rcin_report ()  {
     double c1 = RC_chan_1.normalised();
@@ -429,14 +389,14 @@
         case    HAND_CONT_BRAKE_WAIT:   //  Only get here after direction input changed or newly validated at power on
             pc.printf   ("At HAND_CONT_BRAKE_WAIT\r\n");
             brake_effort = 0.05;    //  Apply braking not too fiercely
-            mode_set_both_motors    (REGENBRAKE, brake_effort);  //  Direction change 
+            mode_set_both_motors    (MOTOR_REGENBRAKE, brake_effort);  //  Direction change 
             hand_controller_state = HAND_CONT_BRAKE_POT;
             break;
 
         case    HAND_CONT_BRAKE_POT:        //  Only get here after one pass through HAND_CONT_BRAKE_WAIT but
             if  (brake_effort < 0.9)    {   //   remain in this state until driver has turned pott fully anticlockwise
                 brake_effort += 0.05;   //  ramp braking up to max over about one sec after direction change or validation
-                mode_set_both_motors    (REGENBRAKE, brake_effort);  //  Direction change or testing at power on
+                mode_set_both_motors    (MOTOR_REGENBRAKE, brake_effort);  //  Direction change or testing at power on
                 pc.printf   ("Brake effort %.2f\r\n", brake_effort);
             }
             else    {   //  once braking up to quite hard
@@ -450,7 +410,7 @@
             brake_effort = (Pot_Brake_Range - pot_position) / Pot_Brake_Range;
             if  (brake_effort < 0.0)
                 brake_effort = 0.5;
-            mode_set_both_motors    (REGENBRAKE, brake_effort);
+            mode_set_both_motors    (MOTOR_REGENBRAKE, brake_effort);
             old_pot_position = pot_position;
             hand_controller_state = HAND_CONT_STATE_BRAKING;
             pc.printf   ("Brake\r\n");
@@ -463,7 +423,7 @@
                 if  (worth_the_bother(pot_position, old_pot_position))  {
                     old_pot_position = pot_position;
                     brake_effort = (Pot_Brake_Range - pot_position) / Pot_Brake_Range;
-                    mode_set_both_motors    (REGENBRAKE, brake_effort);
+                    mode_set_both_motors    (MOTOR_REGENBRAKE, brake_effort);
 //                    pc.printf   ("Brake %.2f\r\n", brake_effort);
                 }
             }
@@ -472,9 +432,9 @@
         case    HAND_CONT_STATE_INTO_DRIVING:   //  Only get here after HAND_CONT_STATE_BRAKING
             pc.printf   ("Drive\r\n");
             if  (direction_new == 1)
-                mode_set_both_motors   (FORWARD, 0.0);  //  sets both motors
+                mode_set_both_motors   (MOTOR_FORWARD, 0.0);  //  sets both motors
             else
-                mode_set_both_motors   (REVERSE, 0.0);
+                mode_set_both_motors   (MOTOR_REVERSE, 0.0);
             hand_controller_state = HAND_CONT_STATE_DRIVING;
             break;
 
@@ -502,7 +462,6 @@
     int eighth_sec_count = 0;
     double  servo_angle = 0.0;  //  For testing servo outs
 
-
     Temperature_pin.fall (&temp_sensor_isr);
     Temperature_pin.mode (PullUp);
     //  Setup system timers to cause periodic interrupts to synchronise and automate volt and current readings, loop repeat rate etc
@@ -512,83 +471,53 @@
     //  Done setting up system interrupt timers
     temperature_timer.start ();
 
-    pc.baud     (9600);
-    com3.baud   (1200);
-    com2.baud   (19200);
-    setup_comms ();
+    pc.baud     (9600);     //  COM port to pc
+    com3.baud   (1200);     //  Once had an idea to use this for IR comms, never tried
+    com2.baud   (19200);    //  Opto isolated serial bus connecting 'n' STM3_ESC boards to 1 Brute Touch Screen controller
 
-    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;
-        k = rd_24LC64   (0, mode_bytes, 32);
-        if  (!k)
-            com2.printf ("Error reading from eeprom\r\n");
+    rpm2mph = 60.0                                                          //  to Motor Revs per hour;
+              * ((double)mode.rd(MOTPIN) / (double)mode.rd(WHEELGEAR))  //  Wheel revs per hour
+              * PI * ((double)mode.rd(WHEELDIA) / 1000.0)                  //  metres per hour
+              * 39.37 / (1760.0 * 36.0);                                      //  miles per hour
 
-//        int err = 0;
-        for (int i = 0; i < numof_eeprom_options; i++) {
-            if  ((mode_bytes[i] < option_list[i].min) || (mode_bytes[i] > option_list[i].max))  {
-                com2.printf ("EEROM error with %s\r\n", option_list[i].t);
-                pc.printf   ("EEROM error with %s\r\n", option_list[i].t);
-                if  (i == ID)   {   //  need to force id to '0'
-                    pc.printf   ("Bad board ID value %d, forcing to \'0\'\r\n");
-                    mode_bytes[ID] = '0';
-                }
-//                err++;
-            }
-//            else
-//                com2.printf ("%2x Good %s\r\n", buff[i], option_list[i].t);
-        }
-        rpm2mph = 0.0;
-        if  (mode_bytes[WHEELGEAR] == 0)    //  avoid making div0 error
-            mode_bytes[WHEELGEAR]++;
-//        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]);
-    }
+    MotorA.direction_set    (mode.rd(MOTADIR));     //  modes all setup in class   eeprom_settings {}  mode    ; constructor
+    MotorB.direction_set    (mode.rd(MOTBDIR));
+    MotorA.poles            (mode.rd(MOTAPOLES));   //  Returns true if poles 4, 6 or 8. Returns false otherwise
+    MotorB.poles            (mode.rd(MOTBPOLES));   //  Only two calls are here
+    MotorA.set_mode         (MOTOR_REGENBRAKE);
+    MotorB.set_mode         (MOTOR_REGENBRAKE);
+    setVI  (0.9, 0.5);              //  Power up with moderate regen braking applied
+
 //    T1 = 0;   Now WRONGLY hoped to be InterruptIn counting pulses from LMT01 temperature sensor
     T2 = 0; //  T2, T3, T4 As yet unused pins
 //    T3 = 0;
 //    T4 = 0;
 //    T5 = 0; now input from fw/re on remote control box
     T6 = 0;
-//    MotPtr[0]->set_mode (REGENBRAKE);
-    MotorA.set_mode (REGENBRAKE);
-    MotorB.set_mode (REGENBRAKE);
-    setVI  (0.9, 0.5);
 
-    if  ((last_temp_count > 160) && (last_temp_count < 2400))   //  in range -40 to +100 degree C
+    if  ((last_temperature_count > 160) && (last_temperature_count < 2400))   //  in range -40 to +100 degree C
         temp_sensor_exists  = true;
-//    pc.printf   ("Ready to go!, wheel gear in position %d\r\n", WHEELGEAR);
+#ifdef  USING_DC_MOTORS
     dc_motor_kicker_timer.start   ();
+#endif
     int old_hand_controller_direction = T5;
-    if  (mode_bytes[COMM_SRC] == 3)  {      //  Read fwd/rev switch 'T5', PA15 on 401
+    if  (mode.rd(COMM_SRC) == 3)  {      //  Read fwd/rev switch 'T5', PA15 on 401
         pc.printf   ("Pot control\r\n");
         if  (T5)
-            mode_set_both_motors   (FORWARD, 0.0);  //  sets both motors
+            mode_set_both_motors   (MOTOR_FORWARD, 0.0);  //  sets both motors
         else
-            mode_set_both_motors   (REVERSE, 0.0);
+            mode_set_both_motors   (MOTOR_REVERSE, 0.0);
     }
 
-    pc.printf   ("About to start!, mode_bytes[COMM_SRC]= %d\r\n", mode_bytes[COMM_SRC]);
-
+    pc.printf   ("About to start %s!, mode_bytes[COMM_SRC]= %d\r\n", const_version_string, mode.rd(COMM_SRC));
+    pc.printf   ("ESC_Error.all_good() ret'd %s\r\n", ESC_Error.all_good() ? "true" : "false");
+//    pc.printf   ("SystemCoreClock=%d, MAX_PWM_TICKS=%d\r\n", SystemCoreClock, MAX_PWM_TICKS);
+//    pcc.test    ()  ;
+//    tsc.test    ()  ;
     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_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
+            pcc.core    ()  ;   //  Proceed beyond here once loop_timer ticker ISR has set loop_flag true
+            tsc.core    ()  ;   //  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
         }                       //  32Hz original setting for loop repeat rate
         loop_flag = false;              //  Clear flag set by ticker interrupt handler. WHEN LAST CHECKED this was about every 32ms
@@ -596,7 +525,7 @@
         RC_chan_1.validate_rx();
         RC_chan_2.validate_rx();
 
-        switch  (mode_bytes[COMM_SRC])  {   //  Look to selected source of driving command, act on commands from wherever
+        switch  (mode.rd(COMM_SRC))  {   //  Look to selected source of driving command, act on commands from wherever
             case    0:  //  Invalid
                 break;
             case    COM1:  //  COM1    -   Primarily for testing, bypassed by command line interpreter
@@ -612,27 +541,33 @@
             case    RC_IN2:  //  RC_chan_2
                 break;
             default:
-                pc.printf   ("Unrecognised state %d\r\n", mode_bytes[COMM_SRC]);    //  set error flag instead here
+                if  (ESC_Error.read(FAULT_UNRECOGNISED_STATE))  {
+                    pc.printf   ("Unrecognised state %d\r\n", mode.rd(COMM_SRC));    //  set error flag instead here
+                    ESC_Error.set   (FAULT_UNRECOGNISED_STATE, 1);
+                }
                 break;
         }   //  endof   switch  (mode_bytes[COMM_SRC])  {
 
+#ifdef  USING_DC_MOTORS
         dc_motor_kicker_timer.reset   ();
-        MotorA.pulses_per_sec   ();   //  Needed to keep table updated to give reading in Hall transitions per second
-        MotorB.pulses_per_sec   ();   //  Read MotorX.PPS to read pulses per sec or MotorX.RPM to read motor RPM
-//        T4 = !T4;   //  toggle to hang scope on to verify loop execution
-        //  do stuff
+#endif
+        MotorA.speed_monitor_and_control   ();   //  Needed to keep table updated to give reading in Hall transitions per second
+        MotorB.speed_monitor_and_control   ();   //  Read MotorX.PPS to read pulses per sec or MotorX.RPM to read motor RPM
+
+#ifdef  USING_DC_MOTORS
         if  (MotorA.dc_motor)   {
-            MotorA.raw_V_pwm    (1);
+//            MotorA.raw_V_pwm    (1);
             MotorA.low_side_on  ();
         }
         if  (MotorB.dc_motor)   {
-            MotorB.raw_V_pwm    (1);
+//            MotorB.raw_V_pwm    (1);
             MotorB.low_side_on  ();
         }
         if  (MotorA.dc_motor || MotorB.dc_motor)    {
 //            motors_restore.attach_us    (&restorer, ttime);
             motors_restore.attach_us    (&restorer, 25);
         }
+#endif
 
         if  (flag_8Hz)  {   //  do slower stuff
             flag_8Hz    = false;
@@ -641,7 +576,8 @@
                 WatchDog--;
                 if  (WatchDog == 0) {   //  Deal with WatchDog timer timeout here
                     setVI  (0.0, 0.0);  //  set motor volts and amps to zero
-                    com2.printf ("TIMEOUT %2x\r\n", (I_Am() & 0x0f));   //  Potential problem of multiple units reporting at same time overcome by adding board number to WATCHDOG_RELOAD
+//                    com2.printf ("TIMEOUT %c\r\n", mode.rd(BOARD_ID));   //  Potential problem of multiple units reporting at same time overcome by adding board number to WATCHDOG_RELOAD
+                    pc.printf ("TIMEOUT %c\r\n", mode.rd(BOARD_ID));   //  Brute touch screen controller can do nothing with this
                 }                       //  End of dealing with WatchDog timer timeout
                 if  (WatchDog < 0)
                     WatchDog = 0;
@@ -649,16 +585,13 @@
             eighth_sec_count++;
             if  (eighth_sec_count > 6)    {   //  Send some status info out of serial port every second and a bit or thereabouts
                 eighth_sec_count = 0;
-                MotorA.current_calc ();     //  Updates readings in MotorA.I.min, MotorA.I.ave and MotorA.I.max
-                MotorB.current_calc ();
+                ESC_Error.report_any (false);   //  retain = false - reset error after reporting once
                 /*                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);
-//                com2.printf   ("V=%+.2f, Pot=%+.2f, HA %d, HB %d, IAmin %d, IAave %d, IAmax %d, IB %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);
             }
 #define SERVO_OUT_TEST
 #ifdef  SERVO_OUT_TEST