Touch screen drivers control dashboard for miniature locomotive. Features meters for speed, volts, power. Switches for lights, horns. Drives multiple STM3_ESC brushless motor controllers for complete brushless loco system as used in "The Brute" - www.jons-workshop.com

Dependencies:   TS_DISCO_F746NG mbed Servo LCD_DISCO_F746NG BSP_DISCO_F746NG QSPI_DISCO_F746NG AsyncSerial FastPWM

Revision:
12:a25bdf135348
Parent:
11:a573664b1a59
Child:
14:6bcec5ac21ca
--- a/main.cpp	Sat Jun 23 09:37:41 2018 +0000
+++ b/main.cpp	Mon Jan 14 16:39:41 2019 +0000
@@ -1,14 +1,15 @@
 /*
-April 2018  -   Jon Freeman
+November 2018  -   Jon Freeman
+Cloned from Loco_TS_2018_06 on 23rd November 2018
 
-Touch Screen controller communicates with 1, 2, ... n Twin BLDC Controller boards via opto-isolated serial port.
+Touch Screen controller communicates with 1, 2, ... n Brushless STM3 Controller boards via opto-isolated serial port.
 
 9 pid D connector retained but wiring NOT compatible with 2017.
 This time pins are : -
 1   Not Used on TS2018, connection from Twin BLDC Controller only - Pot wiper
 2   Not Used on TS2018, connection from Twin BLDC Controller only - GND
 3   Not Used on TS2018, connection from Twin BLDC Controller only - Weak +5 (top of pot)
-4   Not Used on TS2018, connection from Twin BLDC Controller only - Possible Fwd / Rev switched between pins 2 and 3 above
+4   Not Used on TS2018, connection from Twin BLDC Controller only - Fwd / Rev switched between pins 2 and 3 above
 5   TS2018 high voltage output - power up signal to Twin BLDC Controllers, +10 to + 70 Volt (full supply via 3k3 0.5W safety resistor)
 6   Twin BLDC Rx- <- TS2018 Tx data     ||GND to avoid exposing TS +5v rail to the outside world
 7   Twin BLDC Rx+ <- TS2018 +5v         ||Tx\ to avoid exposing TS +5v rail to the outside world, INVERTED Txd idles lo
@@ -21,24 +22,47 @@
 #include "Servo.h"
 #include "TS_DISCO_F746NG.h"
 #include "LCD_DISCO_F746NG.h"
-#include <cctype>
+
+char   const_version_string[] = {"1.0.0\0"};  //  Version string, readable from serial ports
+
+LCD_DISCO_F746NG        lcd ;               //  LCD display
+TS_DISCO_F746NG         touch_screen    ;   //  Touch Screen
+screen_touch_handler    slider  ;           //  Loco driver's slider control
 
-LCD_DISCO_F746NG    lcd;
-TS_DISCO_F746NG     touch_screen;
+//  see movingcoilmeter.h ffi
+moving_coil_meter   Voltmeter   (   LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_COLOR_RED, LCD_COLOR_BLUE, LCD_COLOR_MAGENTA,
+                                    VOLTMETER_X, VOLTMETER_Y, V_A_SIZE, 22.0, 61.0, 1.25 * PI, -0.25 * PI , 20, "V", ONE_DP, false),  
+                    Powermeter  (   LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_COLOR_RED, LCD_COLOR_BLUE, LCD_COLOR_BLUE,
+                                    AMMETER_X, AMMETER_Y, V_A_SIZE, -1400.0, 1400.0, 1.25 * PI, -0.25 * PI , 14, "Watt", NO_DPS, false),    
+                    Speedo      (   SPEEDO_BODY_COLOUR, SPEEDO_DIAL_COLOUR, LCD_COLOR_RED, SPEEDO_TEXT_COLOUR, LCD_COLOR_BLACK,
+                                    SPEEDO_X, SPEEDO_Y, SPEEDO_SIZE, 0.0, 12.0, 1.25 * PI, -0.25 * PI , 12, "MPH", ONE_DP, false);
+                                //  3 instances of moving coil meter graphic
 
-//FastPWM     maxv    (D12, 1), 
-//            maxi    (D11, 1); //  pin, prescaler value
-Serial      pc      (USBTX, USBRX);    //  AsyncSerial does not work here. Comms to 'PuTTY' or similar comms programme on pc
+error_handling_Jan_2019     Controller_Error    ;         //  Provides array usable to store error codes.
+STM3_ESC_Interface          My_STM3_ESC_boards   ;
+
+extern  command_line_interpreter_core   pcli, ploco;    //  pcli handles comms with pc, ploco handles comms with STM3_ESC boards
 
-DigitalOut  reverse_pin     (D7);   //  These pins no longer used to set mode and direction, now commands issued to com
-DigitalOut  forward_pin     (D9);   //was D6, these two decode to fwd, rev, regen_braking and park
+/*
+STRANGE BEHAVIOUR WARNING !
+This project requires two serial ports.
+The following combination of one 'Serial' and one 'AsyncSerial' is the only combination found to work !
+MODSERIAL has not been adapted to F746NG, will not compile.
+Does compile with BufferedSerial but crashes the whole thing. No startup.
+*/
 
-DigitalOut  I_sink1          (D14);  //  a horn
-DigitalOut  I_sink2          (D15);  //  lamp
-DigitalOut  I_sink3          (D3);  //  lamp
-DigitalOut  I_sink4          (D4);
-DigitalOut  I_sink5          (D5);
-DigitalOut led_grn           (LED1); //  the only on board user led
+Serial      pc      (USBTX, USBRX);    //  AsyncSerial does not work here. Comms to 'PuTTY' or similar comms programme on pc
+AsyncSerial com2escs (A4, A5);   //  Com port to opto isolators on Twin BLDC Controller boards. Only AsyncSerial works here
+
+//DigitalOut  reverse_pin     (D7);   //  These pins no longer used to set mode and direction, now commands issued to com
+//DigitalOut  forward_pin     (D9);   //was D6, these two decode to fwd, rev, regen_braking and park
+
+DigitalOut  I_sink1         (D14);  //  a horn
+DigitalOut  I_sink2         (D15);  //  lamp
+DigitalOut  I_sink3         (D3);  //  lamp
+DigitalOut  I_sink4         (D4);
+DigitalOut  I_sink5         (D5);
+DigitalOut  led_grn         (LED1); //  the only on board user led
 
 DigitalIn   f_r_switch      (D2);   //  was D0, Reads position of centre-off ignition switch
 //DigitalIn   spareio_d8      (D8);
@@ -52,190 +76,42 @@
 //AnalogIn    spare_ain4      (A4);   //  hardware on pcb for these 3 spare analogue inputs - not used to Apr 2017
 //AnalogIn    spare_ain5      (A5); //  causes display flicker !
 
-
-AsyncSerial  com (A4, A5);   //  Com port to opto isolators on Twin BLDC Controller boards
-//AsyncSerial  ir  (D1, D0);    //  Second port does work, but gives the old broken-up display flicker nonsense problem
+Servo   servo1    (D6);     //  Model control servo used to adjust Honda engine speed
 
-Servo   servo1    (D6);     //  Now used to adjust Honda speed
-
-extern  uint32_t    odometer_out  ()    ;   //  Read latest total of metres travelled ever
+extern  bool    test_qspi   ()  ;
 extern  bool    odometer_zero   ()  ;   //  Returns true on success
 extern  bool    odometer_update  (uint32_t pulsetot, uint16_t pow, uint16_t volt)  ;   //  Hall pulse total updated once per sec and saved in blocks of 4096 bytes on QSPI onboard memory
 
-extern  int     get_button_press    (struct point & pt) ;
-extern  void    displaytext    (int x, int y, const int font, uint32_t BCol, uint32_t TCol, char * txt) ;
-extern  void    displaytext    (int x, int y, const int font, char * txt)   ;
-extern  void    displaytext    (int x, int y, char * txt)   ;
 extern  void    setup_buttons    ()  ;
-extern  void    draw_numeric_keypad (int    colour) ;
-extern  void    draw_button_hilight     (int bu, int colour)  ;
-extern  void    read_presses    (int * a)   ;
-extern  void    read_keypresses    (struct ky_bd & a)   ;
-extern  void    SliderGraphic (struct slide & q)   ;
-extern  void    vm_set  ()  ;
-extern  void    update_meters  (double, double, double)  ;
-
-extern  void    setup_pccom ()  ;
-extern  void    setup_lococom ()  ;
-extern  void    clicore (struct parameters & a) ;
-extern  struct  parameters pccom, lococom;
+extern  void    rewrite_odometer    ()  ;
 
 static  const   int
-    DAMPER_DECAY        = 42,   //  Small num -> fast 'viscous damper' on dead-mans function with finger removed from panel
     MAF_PTS             = 140,      //  Moving Average Filter points. Filters reduce noise on volatage and current readings
     FWD                 = 0,
     REV                 = ~FWD;
 
-//from .h    struct  slide   {   int position;    int    oldpos; int state; int direction;   bool recalc_run;    bool handbrake_slipping;    double handbrake_effort;   double   loco_speed  }   ;
-struct slide    slider  ;
-
+int32_t     V_maf[MAF_PTS + 2],    I_maf[MAF_PTS + 2],  maf_ptr = 0;    //
+volatile    uint32_t    sys_timer_32Hz = 0;
+double      recent_distance = 0.0;
 
-int     V_maf[MAF_PTS + 2],    I_maf[MAF_PTS + 2],  maf_ptr = 0;    //  ********* These should be uint16_t
-uint32_t    sys_timer_32Hz = 0;
-double  last_V = 0.0, last_I = 0.0, recent_distance = 0.0;
-
-bool    qtrsec_trig                 = false;
-bool    trigger_current_read        = false;
+bool        qtrsec_trig             = false;
+bool        trigger_current_read    = false;
 volatile    bool    trigger_32ms    = false;
 
-class   speed_2018
-{
-    static const int    SPEED_AVE_PTS   = 5;    //  AVE_PTS - points in moving average filters
-    double      mph_maf         [(SPEED_AVE_PTS + 1)][4];   //  22/06/2018 Allow for up to 4 ESC boards
-    uint32_t    speed_maf_mem   [(SPEED_AVE_PTS + 1) * 2][8],   //  Allow for up to 8 axles
-                axle_total  [8],    //  allow up to 8 axles
-                mafptr,
-                mphmafptr,
-                board_IDs   [4],    //  allow up to 4 boards
-                board_count,
-                b, reqno;
-public:
-    speed_2018  ()  {   //  Constructor
-        memset(speed_maf_mem, 0, sizeof(speed_maf_mem));
-        for (int i = 0; i < sizeof(axle_total) / sizeof(uint32_t); i++)
-            axle_total[i] = 0;
-        mafptr = 0;
-        mphmafptr = 0;
-        board_count = 0;
-        b = 0;
-        reqno = 0;
-    }
-    bool    request_rpm ()  ;
-    bool    request_mph ()  ;   //  22/06/2018
-    void    rpm_update(struct parameters & a)  ;
-    void    mph_update(struct parameters & a)  ;    //  22/06/2018
-    void    set_board_IDs (uint32_t *)   ;
-    double  mph ()  ;
-}
-   speed  ;
-
-void    speed_2018::set_board_IDs (uint32_t * a)   {
-    board_count = 0;
-    while   (a[board_count])  {
-        board_IDs[board_count] = a[board_count];
-        board_count++;
-    }
-    pc.printf   ("set_board_IDs %d\r\n", board_count);
-}
-
-double  speed_2018::mph ()  {
-    if  (!board_count)  {
-//        pc.printf   ("No boards\r\n");
-        return  0.0;
-    }
-    int t[8] = {0,0,0,0,0,0,0,0};
-    for (int i = 0; i < SPEED_AVE_PTS; i++) {
-        for (int j = 0; j < board_count * 2; j++)
-            t[j] += speed_maf_mem[i][j];
-    }
-    int j = 0;
-    for (int i = 0; i < board_count * 2; i++) {
-        j += t[i];
-        axle_total[i] = t[i];
-    }
-    return  (rpm2mph * ((double) j) / (SPEED_AVE_PTS * board_count * 2));
-}
-
-bool    speed_2018::request_rpm ()  {   //  Issue "'n'rpm\r" to BLDC board to request RPM
-    if  (board_IDs[0] == 0)
-        return  false;      //  No boards identified
-    if  (board_IDs[reqno] == 0)
-        reqno = 0;
-    com.putc    (board_IDs[reqno++]);
-    com.printf  ("rpm\r");
-    return  true;
-}
-
-bool    speed_2018::request_mph ()  {   //  Issue "'n'mph\r" to BLDC board to request RPM   22/06/2018
-    if  (board_IDs[0] == 0)
-        return  false;      //  No boards identified
-    if  (board_IDs[reqno] == 0)
-        reqno = 0;
-    com.putc    (board_IDs[reqno++]);
-    com.printf  ("mph\r");
-    return  true;
-}
-
-void    speed_2018::rpm_update(struct parameters & a)  {    //  Puts new readings into mem
-    speed_maf_mem   [mafptr][b++] = (uint32_t)a.dbl[0];
-    speed_maf_mem   [mafptr][b++] = (uint32_t)a.dbl[1];
-    if  ((b + 1) >= (board_count * 2))    {
-        b = 0;
-        mafptr++;
-        if  (mafptr >= SPEED_AVE_PTS)   
-            mafptr = 0;
-    }
-}
-
-void    speed_2018::mph_update(struct parameters & a)  {    //  Puts new readings into mem  22/06/2018
-//    mph_maf   [mphmafptr][b++] = a.dbl[1];
-    pc.printf   ("%2fmph\r\n");
-//    speed_maf_mem   [mafptr][b++] = (uint32_t)a.dbl[1];
-/*    if  (b >= board_count)    {
-        b = 0;
-        mphmafptr++;
-        if  (mphmafptr >= SPEED_AVE_PTS)   
-            mphmafptr = 0;
-    }*/
-}
-
-
-void    rpm_push    (struct parameters & a)  {  //  Latest RPM reports from Dual BLDC Motor Controllers arrive here
-    speed.rpm_update   (a);
-//    pc.printf   ("RPM%d %d, mph %.1f\r\n", (int)a.dbl[0], (int)a.dbl[1], speed2.mph());
-}
-
-void    mph_push    (struct parameters & a)  {  //  Latest RPM reports from Dual BLDC Motor Controllers arrive here 22/06/2018
-    speed.mph_update   (a);
-//    pc.printf   ("RPM%d %d, mph %.1f\r\n", (int)a.dbl[0], (int)a.dbl[1], speed2.mph());
-}
-
-
-
-void    set_V_limit (double p)  //  Sets max motor voltage
-{
-    if  (p < 0.0)
-        p = 0.0;
-    if  (p > 1.0)
-        p = 1.0;
-    last_V = p;
-    com.printf  ("v%d\r", (int)(p * 99.0));
-}
-
-void    set_I_limit (double p)     //  Sets max motor current
-{
-    if  (p < 0.0)
-        p = 0.0;
-    if  (p > 1.0)
-        p = 1.0;
-    last_I = p;     //  New 30/4/2018 ; no use for this yet, included to be consistent with V
-    com.printf  ("i%d\r", (int)(p * 99.0));
-}
-
-double  read_ammeter ()
+double  read_voltmeter   ()
 {
     int32_t a = 0;
     for (int b = 0; b < MAF_PTS; b++)
+        a += V_maf[b];
+    a /= MAF_PTS;
+    double v = (double) a;
+    return  (v / 932.0) + 0.0;  //  fiddled to suit resistor values
+}
+
+double  read_ammeter ()     //  Returns amps taken by STM3escs - amps dumped due to over-voltage
+{                           //  Could make sense to read this at up to 32 times per second
+    int32_t a = 0;          //  MAF data almost completely renewed at this rate
+    for (int b = 0; b < MAF_PTS; b++)   //  MAF updated every 250us, MAF size = MAF_PTS (once set to 140, probably still is)
         a += I_maf[b];
     a /= MAF_PTS;
     a -= 0x4000;
@@ -243,16 +119,46 @@
     return  i / 200.0;      //  Fiddled to get current reading close enough
 }
 
-double  read_voltmeter   ()
-{
-    int a = 0;
-    for (int b = 0; b < MAF_PTS; b++)
-        a += V_maf[b];
-    a /= MAF_PTS;
-    double v = (double) a;
-    return  (v / 932.0) + 0.0;  //  fiddled to suit resistor values
+const   int BIGMAFSIZ = 8;
+
+class   ammeter_reading  {
+    double  bigImaf[BIGMAFSIZ];
+    int     bigImafptr;
+    double  amps_longave,   //  internal use only
+            amps_latest,    //  update() called @ 32Hz. Value stored here is average over most recent 3125us
+            amps_filtered;  //  Average of the BIGMAFSIZ most recent samples stored in latest
+public:    
+    ammeter_reading ()  {   //  constructor
+        bigImafptr = 0;
+        amps_longave = amps_latest = amps_filtered = 0.0;
+        for (int i = 0; i < BIGMAFSIZ; i++)
+            bigImaf[i] = 0.0;
+    }
+    void    update  ()  ;   //  Read ammeter core, store most recent 32ms or so worth in amps_latest, 250ms average in amps_filtered
+    double  latest  ()  ;
+    double  filtered()  ;
+}   Ammeter ;
+
+double  ammeter_reading::latest     ()  {
+    return  amps_latest;
 }
 
+double  ammeter_reading::filtered   ()  {
+    return  amps_filtered;
+}
+
+void  ammeter_reading::update   ()  {
+    amps_latest = read_ammeter();
+    amps_longave -= bigImaf[bigImafptr];
+    bigImaf[bigImafptr] = amps_latest;
+    amps_longave += amps_latest;
+    bigImafptr++;
+    if  (bigImafptr >= BIGMAFSIZ)
+        bigImafptr = 0;
+    amps_filtered = amps_longave / BIGMAFSIZ;
+}
+
+
 //  Interrupt Service Routines
 
 void    ISR_current_reader  (void)      //  FIXED at 250us
@@ -260,12 +166,12 @@
     static  int ms32 = 0, ms250 = 0;
     trigger_current_read    = true; //  every 250us, i.e. 4kHz  NOTE only sets trigger here, readings taken in main loop
     ms32++;
-    if  (ms32 > 124)    {   //  31.25ms, not 32ms, is 32Hz
+    if  (ms32 >= 125)    {   //  31.25ms, not 32ms, is 32Hz
         ms32 = 0;
         sys_timer_32Hz++;   //  , usable anywhere as general measure of elapsed time
         trigger_32ms = true;
         ms250++;
-        if  (ms250 > 7) {
+        if  (ms250 >= 8) {
             ms250 = 0;
             qtrsec_trig = true;
         }
@@ -274,84 +180,10 @@
 
 //  End of Interrupt Service Routines
 
-
-bool    inlist  (struct ky_bd & a, int key)
-{
-    int i = 0;
-    while   (i < a.count)  {
-        if  (key == a.ky[i].keynum)
-            return  true;
-        i++;
-    }
-    return  false;
-}
-
-
-void    stuff_to_do_every_250us  ()     //  Take readings of system voltage and current
-{
-    if  (!trigger_current_read)
-        return;
-    trigger_current_read = false;
-    int ch;
-    ch++;
-    I_maf[maf_ptr] = ht_amps_ain.read_u16();    //  Read ACS709 ammeter module
-    V_maf[maf_ptr] = ht_volts_ain.read_u16();   //  Read system voltage
-    maf_ptr++;
-    if  (maf_ptr > MAF_PTS - 1)
-        maf_ptr = 0;
-}
-
-void    set_run_mode    (int    mode)
-{   //  NOTE Nov 2017 - Handbrake not implemented
-    if  (mode == HANDBRAKE_SLIPPING)    slider.handbrake_slipping = true;
-    else                                slider.handbrake_slipping = false;
-    switch  (mode) {
-            //            STATES, INACTIVE, RUN, NEUTRAL_DRIFT, REGEN_BRAKE, PARK};
-//        case    HANDBRAKE_SLIPPING:
-//            break;
-        case    PARK:     //  PARKED new rom code IS now finished.
-//            forward_pin = 0;
-//            reverse_pin = 0;
-            slider.state = mode;
-            set_V_limit     (0.075); // was 0.1
-            set_I_limit (slider.handbrake_effort);
-//            char    tmp[16];
-//            sprintf (tmp, "vi7 %d\r", (int)(slider.handbrake_effort * 99.0));
-//            com.printf  ("%s", tmp);
-            break;
-        case    REGEN_BRAKE:    //  BRAKING, pwm affects degree
-            com.printf  ("rb\r");
-//            forward_pin = 1;
-//            reverse_pin = 1;
-            slider.state = mode;
-            break;
-        case    NEUTRAL_DRIFT:
-            slider.state = mode;
-            set_I_limit (0.0);      //  added after first test runs, looking for cause of mechanical startup snatch
-            set_V_limit (0.0);      //  added after first test runs, looking for cause of mechanical startup snatch
-            break;
-        case    RUN:
-            if  (slider.direction)  {
-                com.printf  ("fw\r");
-//                forward_pin = 0;
-//                reverse_pin = 1;
-            } else    {
-                com.printf  ("re\r");
-//                forward_pin = 1;
-//                reverse_pin = 0;
-            }
-            slider.state = mode;
-            break;
-        default:
-            break;
-    }
-}
-
-
 void    throttle    (double p)  {            // New Apr 2018 ; servo adjusts throttle lever on Honda GX120
-const   double  THR_MAX = 0.92;
-const   double  THR_MIN = 0.09;
-const   double  RANGE = THR_MAX - THR_MIN;
+    const   double  THR_MAX = 0.92;     //  Values tweaked to suit servo and linkage fitted to loco power unit
+    const   double  THR_MIN = 0.09;
+    const   double  RANGE = (THR_MAX - THR_MIN);
     if  (p > 1.0)
         p = 1.0;
     if  (p < 0.0)
@@ -363,383 +195,141 @@
 }
 
 
+void    horn    (int which, int onoff)  {
+    if  (which == HI_HORN)
+        I_sink5 = onoff;
+    else
+        I_sink4 = onoff;
+}
+
+
 void    lights  (int onoff)  {
     I_sink2 = onoff;    //  lamp right
     I_sink3 = onoff;    //  lamp left
 }
 
-int main()
+
+void    draw_normal_run_screen  ()  {
+    lcd.Clear(LCD_COLOR_LIGHTGRAY);
+    setup_buttons(); //  draws buttons
+    slider.DrawSlider ();
+    //  Draw 3 analogue meter movements, speedo, voltmeter, ammeter
+    Speedo.redraw();
+    Voltmeter.redraw();
+    Powermeter.redraw();
+}
+
+
+int main()  //  Programme entry point
 {
-    int qtr_sec = 0, seconds = 0, minutes = 0;
-    double  electrical_power_Watt = 0.0;
-    ky_bd   kybd_a, kybd_b;
-    memset  (&kybd_a, 0, sizeof(kybd_a));
-    memset  (&kybd_b, 0, sizeof(kybd_b));
+    int     qtr_sec = 0, seconds = 0, minutes = 0;
+    double  electrical_power_Watt = 0.0, volts = 0.0;
+
     pc.baud (9600);
-    com.baud (19200);
-    pc.printf   ("\r\n\n\nLoco_TS_2018 Loco Controller starting\r\n");
+    com2escs.baud (19200);
 
     I_sink1 = 0;    //  turn outputs off
     I_sink2 = 0;    //  lamp right
     I_sink3 = 0;    //  lamp left
-    I_sink4 = 0;
-    I_sink5 = 0;
+    I_sink4 = 0;    //  low horn
+    I_sink5 = 0;    //  high horn
     spareio_d10.mode(PullUp);
 
-    Ticker  tick250us;
+    Ticker  tick250us;      //  Master 4kHz interrupt timebase
 
 //  Setup User Interrupt Vectors
-    tick250us.attach_us (&ISR_current_reader, 250);    //  count 125 of these to trig 31.25ms
-#ifdef  QSPI
-
-extern  int qspifindfree    (uint8_t* dest, uint32_t addr)  ;
-extern  int ask_QSPI_OK ()  ;
-extern  bool    qspimemcheck    ()  ;
-extern  int qspiinit    ()  ;
-    int qspi_ok = ask_QSPI_OK   ();
-//extern  int qspieraseblock  (uint32_t addr)  ;
-//extern  int qspiwr  (uint8_t* src, uint32_t addr) ;
-//extern  int qspiwr  (uint8_t* src, uint32_t addr, uint32_t len) ;
-//extern  int qspird  (uint8_t* dest, uint32_t addr, uint32_t len)  ;
+    tick250us.attach_us (&ISR_current_reader, 250);    //  count 125 interrupts to trig 31.25ms
 
-//#define BUFFER_SIZE         ((uint32_t)32)
-//#define QSPI_BUFFER_SIZE         ((uint32_t)4270)           //  Big enough for 4096 byte block
-//#define WRITE_READ_ADDR     ((uint32_t)0x0050)
-//#define WRITE_READ_ADDR     ((uint32_t)0x0010)
-//#define WRITE_READ_ADDR2    ((uint32_t)0x0020)
-//#define WRITE_READ_ADDR3    ((uint32_t)0x4030)
-//#define QSPI_BASE_ADDR      ((uint32_t)0x90000000)
-
-    //                                  123456789012345
-//    uint8_t WriteBuffer[QSPI_BUFFER_SIZE] = "Hello World !!!\0";
-//    uint8_t ReadBuffer[QSPI_BUFFER_SIZE];
-//    const uint8_t MemInitString[] = "Electric Locomotive Controller - Jon Freeman B. Eng. Hons - November 2017\0";  
-//    const uint8_t Ifound_String[] = "I found the man sir, god I wish I hadn't!\0";  
-    
-//    pc.printf   ("[%s]\r\n", MemInitString);
-//    pc.printf   ("[%s]\r\n", Ifound_String);
-//    pc.printf("\n\nQSPI demo started\r\n");
+//  QSPI memory is now in constant use for odometer
+    if  (!test_qspi())
+        Controller_Error.set    (FAULT_QSPI, -1);   //        pc.printf   ("Problem with qspimemcheck\r\n");
 
-    // Check initialization
-    if (qspiinit() != qspi_ok)
-      error("Initialization FAILED\r\n");
-    else
-      pc.printf("Initialization PASSED\r\n");
-    
-    // Check memory informations
-    if  (!qspimemcheck    ())
-        pc.printf   ("Problem with qspimemcheck\r\n");
-/*    // Erase memory
-    qspieraseblock  (WRITE_READ_ADDR);
-    qspieraseblock  (WRITE_READ_ADDR2);
-    qspieraseblock  (WRITE_READ_ADDR3);
-    qspieraseblock  (0x02000);
-    // Write memory
-    qspiwr(WriteBuffer, WRITE_READ_ADDR);
-    qspird(ReadBuffer, WRITE_READ_ADDR, 20);
-    qspiwr((uint8_t*)"Oh what a joy it is.", 0x02000);
-    qspird(ReadBuffer, 0x02000, 20);
-    qspieraseblock  (0x02000);
-*/    // Read memory
-//    if (qspi.Read(ReadBuffer, WRITE_READ_ADDR, 11) != QSPI_OK)
-/*    qspird(ReadBuffer, WRITE_READ_ADDR, 256);
-    qspird(ReadBuffer, WRITE_READ_ADDR + 1, 256);
-    qspird(ReadBuffer, 0, 256);
+    slider.direction = f_r_switch ? REV : FWD;      //  Only place in the code where direction gets set. Centre-Off power switch REV-OFF-FWD.
 
-    // Jon's play with Write memory
-    qspiwr  ((uint8_t*)MemInitString, WRITE_READ_ADDR2);
-    qspiwr  ((uint8_t*)Ifound_String, WRITE_READ_ADDR3);
-
-    qspird(ReadBuffer, 0, 256); //  shows correct write of "Electric Locomotive Controller" after "Hello World !!!"
-    qspird(ReadBuffer, WRITE_READ_ADDR2, 250);
-
-    qspird(ReadBuffer, WRITE_READ_ADDR3, 250);
-    pc.printf   ("\r\r\r\n");
-    qspiwr  ((uint8_t*)"Today I have to pack the car full of all sorts of stuff including 7.25 gauge loco and take it up to Begbrook to show members of Bristol Society of Model and Experimental Engineers!", 2000);
-    qspird(ReadBuffer, 0, 4096);
-
-    int pos = qspifindfree    (ReadBuffer, 0);
-    pc.printf   ("qspifindfree reports %d\r\n", pos);
-*/
+    My_STM3_ESC_boards.set_I_limit (0.0);
+    My_STM3_ESC_boards.set_V_limit (0.0);
+    My_STM3_ESC_boards.message  ("rb\r");
+    throttle    (0.0);      //  Set revs to idle. Start engine and warm up before powering up control
+    pc.printf   ("\r\n\n\nJon's Loco_TS_2018 Loco Controller ver %s starting, direction %s\r\n", const_version_string, slider.direction ? "Forward":"Reverse");
 
-#endif
-
-    if  (!f_r_switch)    {
-        slider.direction = FWD; //  make decision from key switch position here
-        com.printf  ("fw\r");
-    }
-    else    {
-        slider.direction = REV; //  make decision from key switch position here
-        com.printf  ("re\r");
-    }
-    set_I_limit (0.0);
-    set_V_limit (0.0);
-    throttle    (0.0);      //  Set revs to idle. Start engine and warm up before powering up control
-    setup_pccom   ();
-    setup_lococom ();
-    pc.printf   ("Jon's Touch Screen Loco 2018 sytem starting up %s\r\n", slider.direction ? "Forward":"Reverse");
     uint8_t lcd_status = touch_screen.Init(lcd.GetXSize(), lcd.GetYSize());
     if (lcd_status != TS_OK) {
-        lcd.Clear(LCD_COLOR_RED);
-        lcd.SetBackColor(LCD_COLOR_RED);
-        lcd.SetTextColor(LCD_COLOR_WHITE);
-        lcd.DisplayStringAt(0, LINE(5), (uint8_t *)"TOUCHSCREEN INIT FAIL", CENTER_MODE);
-        wait    (20);
-    } else {
-        lcd.Clear(LCD_COLOR_DARKBLUE);
-        lcd.SetBackColor(LCD_COLOR_GREEN);
-        lcd.SetTextColor(LCD_COLOR_WHITE);
-        lcd.DisplayStringAt(0, LINE(5), (uint8_t *)"TOUCHSCREEN INIT OK", CENTER_MODE);
-    }
-
-    lcd.SetFont(&Font16);
-    lcd.Clear(LCD_COLOR_LIGHTGRAY);
-    setup_buttons(); //  draws buttons
-
-    slider.oldpos = 0;
-    slider.loco_speed = 0.0;
-    slider.handbrake_effort = 0.1;
-    slider.position = MAX_POS - 2;   //  Low down in REGEN_BRAKE position - NOT to power-up in PARK
-    SliderGraphic  (slider);    //  sets slider.state to value determined by slider.position
-    set_run_mode    (REGEN_BRAKE);    //  sets slider.mode
-
-    lcd.SetBackColor(LCD_COLOR_DARKBLUE);
-
-    vm_set();   //  Draw 3 analogue meter movements, speedo, voltmeter, ammeter
+        Controller_Error.set    (FAULT_TS, -1);
+    } 
+    lcd.Clear(LCD_COLOR_DARKBLUE);
+    lcd.SetBackColor(LCD_COLOR_GREEN);
+    lcd.SetTextColor(LCD_COLOR_WHITE);
+    lcd.DisplayStringAt(0, LINE(5), (uint8_t *)"TOUCHSCREEN INIT OK", CENTER_MODE);
 
 //    if  (odometer_zero   ())
 //        pc.printf   ("TRUE from odometer_zero\r\n");
 //    else
 //        pc.printf   ("FALSE from odometer_zero\r\n");
-    double  torque_req = 0.0;
-    bool    toggle32ms = false;
-    //  Main loop
 
-    uint32_t boards_fitted[8], bfptr = 0;
-    for (int i = 0; i < 8; i++)
-        boards_fitted[i] = 0;
-    sys_timer_32Hz  = 0;
-
-    int     last_digit = 0, board_cnt = 0, ch;
-    while   (sys_timer_32Hz < 12)   {       //  Sniff out system, discover motor controllers connected
-        while   (!trigger_32ms)
-            clicore    (pccom);
-        trigger_32ms = false;
-        if  (sys_timer_32Hz < 11)   {       //  issue "0who\r", "1who\r" ... "9who\r"
-            com.putc    ((sys_timer_32Hz - 1) | '0');
-            com.printf  ("who\r");
-        }
-        while   (com.readable())  {
-            ch = com.getc();
-            if  (ch != '\r') {
-                if  (isdigit(ch))
-                    last_digit = ch;
-            }
-            else    {   //  got '\r' at end of line
-                if  (isdigit(last_digit))
-                    boards_fitted[board_cnt++] = last_digit;
-                last_digit = 0;
-            }
-        }
-    }
-
-//    boards_fitted[0] = '4';
-//    boards_fitted[1] = '5';
-
-    while   (boards_fitted[bfptr])    { //  This works, identified BLDC Motor Controller board ID chars '0' to '9' listed in boards_fitted[]
-        pc.printf   ("Board %c found\r\n", boards_fitted[bfptr++]);
-    }
-    speed.set_board_IDs   (boards_fitted);    //  store number and IDs of Dual BLDC boards identified
-//    bfptr = 0;
-    clicore (pccom);
-//    pc.printf   ("pcmenuitems %d, commenuitems %d\r\n", pccom.numof_menu_items, lococom.numof_menu_items);
-    //  Done setup, time to roll !
-    
     lights  (1);    //  Headlights ON!
 
-    while (1) {
-
-        struct ky_bd * present_kybd, * previous_kybd;
-        bool    sliderpress = false;
-        clicore (pccom);   //  Do any actions from command line via usb link
-
-        stuff_to_do_every_250us     ()  ;   //  Only does work when trigger_current_read flag has been set by ISR
+    My_STM3_ESC_boards.search_for_escs ();  //  Build list of connected STM3_ESC IDs
 
-        if  (trigger_32ms == true)  {       //  Stuff to do every 32 milli secs
-            trigger_32ms = false;
-            clicore (lococom);
-            toggle32ms = !toggle32ms;
-            if  (toggle32ms)    {
-                present_kybd = &kybd_a;
-                previous_kybd = &kybd_b;
-            }   else    {
-                present_kybd = &kybd_b;
-                previous_kybd = &kybd_a;
-            }
-            read_keypresses   (*present_kybd);
-            sliderpress = false;
-            slider.recalc_run = false;
-            int j = 0;
-//        if  (present2->count > previous_kybd->count)            pc.printf   ("More presses\r\n");
-//        if  (present2->count < previous_kybd->count)            pc.printf   ("Fewer presses\r\n");
-            if  (present_kybd->count || previous_kybd->count)   {   //  at least one key pressed this time or last time
-                int k;
-                double  dbl;
-//            pc.printf   ("Keys action may be required");
-                //  if key in present and ! in previous, found new key press to handle
-                //  if key ! in present and in previous, found new key release to handle
-                if  (inlist(*present_kybd, SLIDER))  {                      //  Finger is on slider, so Update slider graphic here
-                    sliderpress = true;
-                    k = present_kybd->slider_y;     //  get position of finger on slider
-                    if  (slider.state == RUN && k != slider.position)       //  Finger has moved within RUN range
-                        slider.recalc_run   = true;
-                    if  (slider.state == RUN && k >= NEUTRAL_VAL)   {       //  Finger has moved from RUN to BRAKE range
-                        slider.position = k = NEUTRAL_VAL;          //  kill drive for rapid reaction to braking
-                        throttle    (0.0);
-                    }
+/*    Controller_Error.set    (3, 99);
+    pc.printf   ("%lx red\r\n", LCD_COLOR_RED);     //LCD_COLOR is 0xffrrggbb
+    pc.printf   ("%lx grn\r\n", LCD_COLOR_GREEN);
+    pc.printf   ("%lx blu\r\n", LCD_COLOR_BLUE);
+    pc.printf   ("%lx blk\r\n", LCD_COLOR_BLACK);
+    pc.printf   ("%lx white\r\n", LCD_COLOR_WHITE);
+*/
+    draw_normal_run_screen  ();
 
-                    else    {           //  nice slow non-jerky glidey movement required
-                        dbl = (double)(k - slider.position);
-                        dbl /= 13.179;  //  Where did 13.179 come from ?
-                        if  (dbl < 0.0)
-                            dbl -= 1.0;
-                        if  (dbl > 0.0)
-                            dbl += 1.0;
-                        slider.position += (int)dbl;
-                    }
-                    SliderGraphic   (slider);    //  sets slider.state to value determined by slider.position
-                    set_run_mode    (slider.state);
-                    draw_button_hilight     (SLIDER, LCD_COLOR_YELLOW)  ;
+    pc.printf   ("Controller_Error.all_good() ret'd %s\r\n", Controller_Error.all_good() ? "true" : "false");
 
-                    if  (slider.state == REGEN_BRAKE) {
-                        double  brake_effort = ((double)(slider.position - NEUTRAL_VAL)
-                                                / (double)(MAX_POS - NEUTRAL_VAL));
-                        //  brake_effort normalised to range 0.0 to 1.0
-                        brake_effort *= 0.98;  //  upper limit to braking effort, observed effect before was quite fierce
-//                        pc.printf   ("Brake effort %.2f\r\n", brake_effort);
-                        /* set_pwm (brake_effort); */
-                        set_V_limit (sqrt(brake_effort));   //  sqrt gives more linear feel to control
-                        set_I_limit (1.0);
-                        throttle    (0.0);
-                    }
-                }   else    {   //                pc.printf   ("Slider not touched\r\n");
-                }
+    while (1) {     //  main prog loop
 
-                j = 0;
-                while   (j < present_kybd->count)   {   //  handle new key presses
-                    k = present_kybd->ky[j++].keynum;
-                    if  (inlist(*present_kybd, k))    {
-                        switch  (k)    {    //  Here for auto-repeat type key behaviour
-                            case    21:     //  key is 'voltmeter'
-//                                set_V_limit (last_V * 1.002 + 0.001);
-                                break;
-                            case    22:     //  key is 'ammeter'
-//                                set_V_limit (last_V * 0.99);
-                                break;
-                        }   //  endof switch (k)
-                    }       //  endof if (inlist(*present2, k)) {
-                    if  (inlist(*present_kybd, k) && !inlist(*previous_kybd, k))    {   //  New key press detected
-                        pc.printf   ("Handle Press %d\r\n", k);
-                        draw_button_hilight     (k, LCD_COLOR_YELLOW)  ;
-                        switch  (k)    {    //  Handle new touch screen button presses here - single action per press, not autorepeat
-                            case    SPEEDO_BUT:  //
-                                pc.printf   ("Speedometer key pressed %d\r\n", k);
-                                break;
-                            case    VMETER_BUT:  //
-//                                pc.printf   ("Voltmeter key pressed %d\r\n", k);
-                                I_sink5 = 1;    //  Turn on hi-horn
-                                break;
-                            case    AMETER_BUT:  //
-//                                pc.printf   ("Ammeter key pressed %d\r\n", k);
-                                I_sink4 = 1;    //  Turn on lo-horn
-                                break;
-                            default:
-                                pc.printf   ("Unhandled keypress %d\r\n", k);
-                                break;
-                        }       //  endof   switch  (button)
-                    }
-                }           //  endof while - handle new key presses
-                j = 0;
-                while   (j < previous_kybd->count)   {   //  handle new key releases
-                    k = previous_kybd->ky[j++].keynum;
-                    if  (inlist(*previous_kybd, k) && !inlist(*present_kybd, k))    {
-                        pc.printf   ("Handle Release %d\r\n", k);
-                        draw_button_hilight     (k, LCD_COLOR_DARKBLUE)  ;
-                        switch  (k)    {    //  Handle new touch screen button RELEASes here - single action per press, not autorepeat
-                            case    SPEEDO_BUT:  //
-                                pc.printf   ("Speedometer key released %d\r\n", k);
-                                break;
-                            case    VMETER_BUT:  //
-                                I_sink5 = 0;    //  Turn hi-tone horn off
-//                                pc.printf   ("Voltmeter key released %d\r\n", k);
-                                break;
-                            case    AMETER_BUT:  //
-                                I_sink4 = 0;    //  Turn lo-tone horn off
-//                                pc.printf   ("Ammeter key released %d\r\n", k);
-                                break;
-                            default:
-                                pc.printf   ("Unhandled keyreleas %d\r\n", k);
-                                break;
-                        }       //  endof   switch  (button)
-                    }
-                }       //  endof while - handle new key releases
-            }   //  endof at least one key pressed this time or last time
+        pcli.sniff  ();   //  Do any actions from command line serial port via usb link
 
-            if  (sliderpress == false)  {           //  need to glide dead-mans function towards neutral here
-                if  (slider.position < NEUTRAL_VAL)    {
-                    slider.position += 1 + (NEUTRAL_VAL - slider.position) / DAMPER_DECAY;
-                    SliderGraphic   (slider);
-                    slider.recalc_run = true;
-                }
-            }
+        if  (trigger_current_read)  {     //  flag set in interrupt handler every 250us
+            trigger_current_read = false;
+            I_maf[maf_ptr] = ht_amps_ain.read_u16();    //  Read raw ACS709 ammeter module
+            V_maf[maf_ptr] = ht_volts_ain.read_u16();   //  Read raw system voltage
+            maf_ptr++;
+            if  (maf_ptr > MAF_PTS - 1)
+                maf_ptr = 0;
+        }                       //  endof stuff to do every 250us
 
-            if  (slider.recalc_run) {   //  range of slider.position in RUN mode is min_pos_() to NEUTRAL_VAL - 1
-                slider.recalc_run = false;  //  All RUN power and pwm calcs done here
-                int b = slider.position;
-                if  (b > NEUTRAL_VAL)
-                    b = NEUTRAL_VAL;
-                if  (b < MIN_POS)   //  if finger position is above top of slider limit
-                    b = MIN_POS;
-                b = NEUTRAL_VAL - b;    //  now got integer going positive for increasing power demand
-                torque_req = (double) b;
-                torque_req /= (NEUTRAL_VAL - MIN_POS);  //  in range 0.0 to 1.0
-                pc.printf   ("torque_rec = %.3f, last_V = %.3f\r\n", torque_req, last_V);
-                set_I_limit (torque_req);
-                if  (torque_req < 0.05) {
-                    set_V_limit (last_V / 2.0);
-                    throttle    (torque_req * 6.0);
-                }
-                else    {
-                    throttle    (0.3 + (torque_req / 2.0));
-                    if  (last_V < 0.99)
-                        set_V_limit (last_V + 0.05);   //  ramp voltage up rather than slam to max
-                }
-            }
-        }       //  endof doing 32ms stuff
+        if  (trigger_32ms == true)  {       //  Stuff to do every 31.25 milli secs (32Hz)
+            trigger_32ms = false;
+            ploco.sniff ();         //  Only call within main loop, checks message responses from STM3_ESC boards
+            Ammeter.update ();      //  This updates Ammeter 'latest' and 'filtered' variables every 31.25ms
+            slider.HandleFingerInput   ();  //  Do everything concerning fingers on touch screen
+        }                                  //  endof doing 32Hz stuff
 
         if  (qtrsec_trig == true)  {    //  do every quarter second stuff here
             qtrsec_trig = false;
-            double  speedmph = speed.mph(), amps = read_ammeter(), volts = read_voltmeter();
-            slider.loco_speed = speedmph;
-            electrical_power_Watt = volts * amps;   //  visible throughout main
-            update_meters  (speedmph, electrical_power_Watt, volts)  ;   //  displays speed, volts and power (volts times amps)
+            volts = read_voltmeter();    //  voltage and current readings updated @ 250us, these are averaged over 35ms or so
+            electrical_power_Watt = volts * Ammeter.filtered();   //  visible throughout main
+            //  Update meters
+            Powermeter.set_value(electrical_power_Watt);
+            Voltmeter.set_value (volts);
+            Speedo.set_value    (My_STM3_ESC_boards.mph);
+
             led_grn = !led_grn;
+/*
+Handbrake more sensibly implemented on STM3_ESC boards ?
+
             if  (slider.state == PARK)   {
-                if  (speedmph > LOCO_HANDBRAKE_ESCAPE_SPEED / 4.0)    {
+                if  (My_STM3_ESC_boards.mph > LOCO_HANDBRAKE_ESCAPE_SPEED / 4.0)    {
                     slider.handbrake_effort *= 1.1;
                     if  (slider.handbrake_effort > 0.55)    slider.handbrake_effort = 0.55;
                     set_run_mode    (PARK);
                     pc.printf   ("Handbrake slipping, effort %.2f\r\n", slider.handbrake_effort);
                 }
-                if  (speedmph < 0.02)    {
+                if  (My_STM3_ESC_boards.mph < 0.02)    {
                     slider.handbrake_effort *= 0.9;
                     if  (slider.handbrake_effort < 0.05)    slider.handbrake_effort = 0.05;
                     set_run_mode    (PARK);
                     pc.printf   ("Handbrake not slipping, effort %.2f\r\n", slider.handbrake_effort);
                 }
             }
-            speed.request_rpm   (); //  issues "'n'rpm\r", takes care of cycling through available boards in sequence
-//            speed.request_mph   (); //  issues "'n'rpm\r", takes care of cycling through available boards in sequence
+*/
+            My_STM3_ESC_boards.request_mph   (); //  issues "'n'rpm\r", takes care of cycling through available boards in sequence
 //            switch  (qtr_sec)   {   //  Can do sequential stuff quarter second apart here
 //            }   //  End of switch qtr_sec
             qtr_sec++;
@@ -747,29 +337,27 @@
             if(qtr_sec > 3) {
                 qtr_sec = 0;
                 seconds++;
-                com.printf  ("kd\r");       //  Kick the WatchDog timers in the Twin BLDC drive boards
                 if  (seconds > 59)  {
                     seconds = 0;
                     minutes++;
                     //  do once per minute stuff here
+                    Controller_Error.report_any ();
                 }   //  fall back into once per second
-#ifdef  QSPI
-            recent_distance += (speedmph * (111.76 * 4.0));    //  Convert mph to distance mm travelled in one second
-            uint32_t    new_metres = ((uint32_t)recent_distance) / 1000;
-            recent_distance -= (double)(new_metres * 1000);
-            if  (!odometer_update (new_metres, (uint16_t)electrical_power_Watt, (uint16_t)(volts * 500.0)))
-                pc.printf   ("Probs with odometer_update");
-            char    dist[20];
-//            sprintf (dist, "%05d m", odometer_out());
-//            displaytext (236, 226, 2, dist);
-            sprintf (dist, "%06dm", odometer_out());   //  12th June 2018 changed 05 to 06 to allow correct display of tot distance > 99999 metres
-//            displaytext (236, 226, 2, dist);
-            displaytext (241, 224, 2, dist);
-#endif
+                if  (seconds & 1)
+                    Speedo.LED  (0, LCD_COLOR_DARKGRAY);
+                else
+                    Speedo.LED  (0, LCD_COLOR_RED);
+                My_STM3_ESC_boards.message  ("kd\r");       //  Kick the WatchDog timers in the Twin BLDC drive boards
+                recent_distance += (My_STM3_ESC_boards.mph * (111.76 * 4.0));    //  Convert mph to distance mm travelled in one second
+                uint32_t    new_metres = ((uint32_t)recent_distance) / 1000;
+                recent_distance -= (double)(new_metres * 1000);
+                if  (!odometer_update (new_metres, (uint16_t)electrical_power_Watt, (uint16_t)(volts * 500.0))) {
+                    pc.printf   ("Probs with odometer_update");
+                    Controller_Error.set    (FAULT_ODOMETER, 1);
+                }
+                rewrite_odometer    ()  ;   //  Update text on speedo dial face
             }   //  endof if(qtr_sec > 3
         }       //  endof if  (qtrsec_trig == true)  {
     }           //  endof while(1) main programme loop
-} 
+}               //  endof main ()
 
-
-