Jon Freeman / Mbed 2 deprecated Brute_TS_Controller_2018_11

Dependencies:   TS_DISCO_F746NG mbed Servo LCD_DISCO_F746NG BSP_DISCO_F746NG QSPI_DISCO_F746NG AsyncSerial FastPWM

Files at this revision

API Documentation at this revision

Comitter:
JonFreeman
Date:
Mon Jan 14 16:39:41 2019 +0000
Parent:
11:a573664b1a59
Child:
13:390430631255
Commit message:
Tidied, better documented, more OOP, interim release

Changed in this revision

Electric_Loco.h Show annotated file Show diff for this revision Revisions of this file
SD_DISCO_F746NG.lib Show diff for this revision Revisions of this file
cli_TS_nortos.cpp Show annotated file Show diff for this revision Revisions of this file
error_handler.cpp Show annotated file Show diff for this revision Revisions of this file
esc_comms.cpp Show annotated file Show diff for this revision Revisions of this file
graphics.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
movingcoilmeter.cpp Show annotated file Show diff for this revision Revisions of this file
movingcoilmeter.h Show annotated file Show diff for this revision Revisions of this file
qspi_mem.cpp Show annotated file Show diff for this revision Revisions of this file
touch_handler.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/Electric_Loco.h	Sat Jun 23 09:37:41 2018 +0000
+++ b/Electric_Loco.h	Mon Jan 14 16:39:41 2019 +0000
@@ -1,5 +1,7 @@
 #include "mbed.h"
-/*  Updated 9 May 2018
+#include "movingcoilmeter.h"
+#include <cctype>
+/*  Updated January 2019
     Jon Freeman
 
   5" and 7.25" gauge Electric Locomotive Controller - ST DISCO-F746NG
@@ -7,11 +9,11 @@
 
 Display shows 'analogue' moving coil meter movements for :
     Locomotive speed Miles per Hour
-    System voltage (range 20v - 90v or thereabouts)
-    Power Watts delivered to drive motors.
+    System voltage (range 10v - 63v or thereabouts)
+    Power Watts delivered to drive motors or dumped.
 
-Touch screen has three 'buttons', these are currently unused, and are where the meter movements show.
-Idea is to use two for two horns,
+Touch screen has three 'buttons', and are where the meter movements show.
+Two smaller meter buttons for two horns, larger speedo button currently unsused
 
 Display has 'slider' touch control. This drives the loco.
 Control in central position when not driving or drfting.
@@ -20,7 +22,6 @@
 Take finger off and control drifts down to central 'neutral' position.
 */
 
-#define QSPI
 
 #define MAX_TOUCHES 6       //  Touch screen can decode up to this many simultaneous finger press positions
 #define NEUTRAL_VAL   150   //  Number of pixels
@@ -30,78 +31,146 @@
 #define SLIDERW 50          //  pixel width of slider
 #define SLIDERH 268         //  pixel height of slider
 
-//  To get speedo reading correctly, need to use correct gear ratio and wheel size info
-//#define BOGIE_5_INCH
-#define BOGIE_7_and_a_quarter_INCH
+#define VOLTMETER_X 68      //  Voltmeter screen position
+#define VOLTMETER_Y 68
+#define AMMETER_X   68      //  Ammeter screen position - Now replaced by Power meter
+#define AMMETER_Y   202
+#define SPEEDO_X    274     //  Speedometer screen position
+#define SPEEDO_Y    135
+//#define V_A_SIZE    54      //  Size of voltmeter and ammeter
+//#define SPEEDO_SIZE 112
+
+#define V_A_SIZE    56      //  Size of voltmeter and ammeter
+#define SPEEDO_SIZE 114
 
+#define SPEEDO_BODY_COLOUR  LCD_COLOR_BLACK
+#define SPEEDO_DIAL_COLOUR  LCD_COLOR_WHITE
+#define SPEEDO_TEXT_COLOUR  LCD_COLOR_BLUE
+
+#define VMETER_BODY_COLOUR  LCD_COLOR_BLACK
+#define VMETER_DIAL_COLOUR  LCD_COLOR_WHITE
+#define VMETER_TEXT_COLOUR  LCD_COLOR_BLUE
+
+#define AMETER_BODY_COLOUR  LCD_COLOR_BLACK
+#define AMETER_DIAL_COLOUR  LCD_COLOR_WHITE
+#define AMETER_TEXT_COLOUR  LCD_COLOR_BLUE
 const   int
-
     BUTTON_RAD  = (SLIDERW / 2) - 4,    //  radius of circular 'knob' in slider control
     MIN_POS     = BUTTON_RAD + 5,               //  top of screen
     MAX_POS     = SLIDERH - (BUTTON_RAD + 1),   //  bottom of screen
-    CIRC_CTR = SLIDERX + BUTTON_RAD + 4;
+    CIRC_CTR    = SLIDERX + BUTTON_RAD + 4;
 
-static const double
-#ifdef  BOGIE_7_and_a_quarter_INCH
-    MOTOR_PINION_T  = 17.0, //  motor pinion teeth, wheel gear teeth and wheel dia required to calculate speed and distance.
-    WHEEL_GEAR_T    = 76.0,
-    WHEEL_DIA_MM    = 147.0,
-#endif
-#ifdef  BOGIE_5_INCH
-    MOTOR_PINION_T  = 27.0, //  motor pinion teeth, wheel gear teeth and wheel dia required to calculate speed and distance.
-    WHEEL_GEAR_T    = 85.0,
-    WHEEL_DIA_MM    = 98.0,
-#endif
-    PI = 4.0 * atan(1.0),
-    WHEEL_CIRCUMFERENCE_METRE = PI * WHEEL_DIA_MM / 1000.0,
-    PULSES_PER_WHEEL_REV    = 32.0 * WHEEL_GEAR_T / MOTOR_PINION_T,
-    PULSES_PER_METRE        = PULSES_PER_WHEEL_REV / WHEEL_CIRCUMFERENCE_METRE,
-    rpm2mph         = 60.0                                      //  = Motor Revs per hour;
-                  * (MOTOR_PINION_T / WHEEL_GEAR_T)   //  = Wheel rev per hour
-                  * WHEEL_CIRCUMFERENCE_METRE         //  = metres per hour
-                  * 39.37                             //  = inches per hour
-                  / (1760 * 36)                       //  = miles per hour
-                  ;
+static const double    PI       = 4.0 * atan(1.0);
+static const double    TWO_PI   = 8.0 * atan(1.0);
 
-const   double LOCO_HANDBRAKE_ESCAPE_SPEED = 0.5;
-
+enum    {HI_HORN, LO_HORN};
 enum    {NO_DPS, ONE_DP};
 //  Assign unique number to every button we may use, and keep count of total number of them
-enum        {
-            ENTER, SLIDER, SPEEDO_BUT, VMETER_BUT, AMETER_BUT,
-            NUMOF_BUTTONS}  ;   //  button names
-enum        {
-            STATES, INACTIVE, RUN, NEUTRAL_DRIFT, REGEN_BRAKE, PARK, HANDBRAKE_SLIPPING};
+enum    {ENTER, SLIDER_BUTTON, SPEEDO_BUTTON, VMETER_BUTTON, AMETER_BUTTON,NUMOF_BUTTONS}  ;   //  button names
+enum    {STATES, RUN, NEUTRAL_DRIFT, REGEN_BRAKE, RUN_DOWN, INTO_RUN, INTO_REGEN_BRAKE, INTO_NEUTRAL_DRIFT};
+enum    {SLIDER_PRESS, SLIDER_RELEASE, SLIDER_AUTOREP};
 
-struct  slide   {   int position;    int    oldpos; int state; int direction;   bool recalc_run;    bool handbrake_slipping;    
-                    double handbrake_effort;   double   loco_speed;  }   ;
 struct  point   {   int x;    int y;  }   ;
-struct  key     {   int keynum; int x;  int y;  bool pressed;  }   ;
-struct  ky_bd   {   int count,  slider_y; key ky[MAX_TOUCHES + 1];   bool  sli;   }  ;
+struct  keystr  {   int keynum; int x;  int y;  }   ;
+struct  ky_bd   {   int count,  slider_y; keystr key[MAX_TOUCHES + 1];   bool  sli;   }  ;
 
-const   int MAX_PARAMS = 20;
+class   screen_touch_handler
+    {   
+        ky_bd   kybd_a, kybd_b;                 //  alternating present - previous keyboard structures
+        ky_bd * present_kybd, * previous_kybd;  //  pointers
+        bool    in_list  (struct ky_bd & , int )    ;
+        void    motor_power ()   ;
+        void    flush   ()  ;
+        int     viscous_drag   (int, double, double)  ;
+        int     position;    
+        int     oldpos; 
+        int     next_state;
+    public:
+        int     direction;   
+        void    DrawSlider    ()  ;
+        void    HandleFingerInput   ()  ;
+        screen_touch_handler  ()  ;     //  default constructor
+    }   ;
+
+const   int MAX_PARAMS = 30;
+const   int MAX_CMD_LINE_LEN    = 180;
+const   int MAX_ESCS = 12;
+
 struct  parameters  {
-    struct kb_command const * clist;
-    char    cmd_line[120];
-    char    * cmd_line_ptr;
-    int32_t position_in_list, numof_dbls, target_unit, numof_menu_items, com_no, cl_index, gp_i;
+    int32_t numof_menu_items, numof_cl_values_read;
     double  dbl[MAX_PARAMS];
-    bool    respond;
 }   ;
 
-class   genio   {   //  23/06/2018  generic io handler
+enum    {
+    FAULT_0,
+    FAULT_BOARD_ID_IN_MSG,
+    FAULT_TS,
+    FAULT_PC,
+    FAULT_COM,
+    FAULT_COM_NO_MATCH,
+    FAULT_COM_LINE_LEN,
+    FAULT_QSPI,
+    FAULT_ODOMETER,
+    FAULT_MAX,
+    NUMOF_REPORTABLE_TS_ERRORS
+    }   ;
+
+class   error_handling_Jan_2019
+{
+    int32_t    TS_fault[NUMOF_REPORTABLE_TS_ERRORS]    ;   //  Some number of reportable error codes, accessible through set and read members
     public:
-    uint32_t    count;  //  Running total of times called
-    bool        available;  //  set true when number rec'd, set false by reading it
-    int         d_type;      //  loaded on setup indicating expected data type
-    int         d_len;       //  loaded on setup indicating expected num of parameters to handle
-    uint32_t    ui[MAX_PARAMS];
-    double      dbl[MAX_PARAMS];
-    genio   ()  {}   //  default constructor
-    genio   (int, int)  ;   //  more useful constructor
-    void    store   (struct parameters &)  ;   //
-    bool    read    (uint32_t **)  ;
-    bool    read    (double &)  ;
+    error_handling_Jan_2019 ()  {   //  default constructor
+        for (int i = 0; i < (sizeof(TS_fault) / sizeof(int32_t)); i++)
+            TS_fault[i] = 0;
+    }
+    void        set   (uint32_t, int32_t)   ;
+    void        clr   (uint32_t)   ;
+    uint32_t    read  (uint32_t)   ;
+    bool        all_good    ()  ;
+    void        report_any  ()  ;
+}   ;
+
+class   command_line_interpreter_core  {
+    parameters  a   ;   //  as opposed to clicore(&parameters)
+    struct kb_command const * clist;
+    int32_t portio, cl_index, target_unit;
+    char    cmd_line[MAX_CMD_LINE_LEN];
+    char    * cmd_line_ptr;
+    int clreadable    ();
+    int clgetc        ();
+    void clputc        (int);
+  public:
+    command_line_interpreter_core   ()  {};   //  default constructor
+    command_line_interpreter_core   (int, int, struct kb_command const * )  ;    //{   //  constructor including io port id value
+    void    sniff   ()  ;   //  The function to call often to sniff out commands from command line and act upon them
+}   ;
+
+class   STM3_ESC_Interface
+{
+    int         board_IDs   [MAX_ESCS],    //  allow up to 4 boards in practical system, size for 10+ as 10 discrete ID nums exist
+                board_count,
+                reqno;
+public:
+    double  last_V, last_I, mph, esc_speeds[MAX_ESCS];
+    STM3_ESC_Interface  ()  {   //  Constructor
+        board_count = 0;
+        reqno = 0;
+        last_V = last_I = mph = 0.0;
+        for (int i = 0; i < MAX_ESCS; i++)  {
+            board_IDs[i] = 0;
+            esc_speeds[i] = 0.0;
+        }
+    }
+    bool    request_mph     ()  ;       //  Issue "'n'mph\r" to one particular STM3_ESC board to request RPM   22/06/2018
+    void    mph_update      (double)  ; //  touched 08/01/2019
+    void    set_board_ID    (int)   ;   //  called in response to 'whon' coming back from a STM3_ESC
+    void    search_for_escs ()  ;       //  one-off call during startup to identify all connected STM3_ESCs
+    void    get_boards_list (int *) ;   //  copies board list
+    void    set_V_limit     (double)    ;   //  Set max motor voltage
+    void    set_I_limit     (double)    ;   //  Set max motor current
+    void    message         (char *)    ;   //  Broadcast message to all STM3_ESCs
+    void    message         (int, char *)    ;   //  Send message to one individual STM3_ESC
 }   ;
 
 
+
--- a/SD_DISCO_F746NG.lib	Sat Jun 23 09:37:41 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://developer.mbed.org/teams/ST/code/SD_DISCO_F746NG/#cf12f3d37081
--- a/cli_TS_nortos.cpp	Sat Jun 23 09:37:41 2018 +0000
+++ b/cli_TS_nortos.cpp	Mon Jan 14 16:39:41 2019 +0000
@@ -1,87 +1,131 @@
 //Loco_TS_2018
 #include "mbed.h"
-//#include "BufferedSerial.h"
 #include "AsyncSerial.hpp"
 #include <cctype>
 #include "Electric_Loco.h"
 using namespace std;
 
-//extern  int I_Am    ()  ;      //  Returns boards id number as ASCII char '0', '1' etc. Code for Broadcast = '\r'
-//typedef double  fl_typ;  //
-
 const int   BROADCAST   = '\r';
 
 //  WithOUT RTOS
 extern  Serial pc;
-extern  AsyncSerial com;
-extern  void    send_test   ()  ;
+extern  AsyncSerial com2escs;
+extern  error_handling_Jan_2019   Controller_Error    ;
+extern  const   char   const_version_string[] ;  //  Version string, readable from serial ports
 
-genio::genio    (int dtype, int dlen)  {
+extern  STM3_ESC_Interface    My_STM3_ESC_boards   ;
+
+/*genio::genio    (int dtype, int dlen)  {    //  constructor
     d_type = dtype;
     d_len = dlen;
     for (int i = 0; i < MAX_PARAMS; i++)    {
-        ui[i] = 0;
+        si[i] = 0;
         dbl[i] = 0.0;
     }
-    count = 0;
-    available = false;
+    count = 0;  //  running total of times called
+    available = false;  //  bool
+    //available = 0; // if using integer rather than bool
 }
 
-void    genio::store   (struct parameters & a)  {   //
-    count++;
+void    genio::store   (struct parameters & a)  {   //  copies up to MAX_PARAMS into int and double arrays
+    count++;            //  number of times this response rec'd this session
     available = true;
-    for (int i = 0; i < d_len; i++) {
-        ui[i] = (int32_t)a.dbl[i];
+    for (int i = 0; i < d_len; i++) {   //  dlen //  loaded on setup indicating expected num of parameters to handle. Why not just use MAX_PARAMS ?
+        si[i] = (int32_t)a.dbl[i];
         dbl[i] = a.dbl[i];
     }
 }
 
-bool    genio::read    (uint32_t ** addr)  {
+bool    genio::read    (int32_t ** addr)  {
     if  (!available)
         return  false;
     available = false;
-    *addr = ui;
+    *addr = si;         //      int32_t    ui[MAX_PARAMS];
     return  true;
 }
+*/
+
+command_line_interpreter_core::command_line_interpreter_core    (int p, int menulen, struct kb_command const * clistp)  {
+    clist = clistp;
+    a.numof_menu_items = menulen;
+    a.numof_cl_values_read = 0;
+    portio = p;
+    cl_index = 0;
+}
+
+int     command_line_interpreter_core::clreadable    ()  {
+    if  (portio == 0)
+        return  pc.readable();
+    return  com2escs.readable();
+}  
+
+void    command_line_interpreter_core::clputc    (int c)  {
+    if  (portio == 0)
+        pc.putc (c);
+    else
+        com2escs.putc   (c);
+}  
+
+int     command_line_interpreter_core::clgetc    ()  {
+    if  (portio == 0)
+        return  pc.getc();
+    return  com2escs.getc();
+}  
+
 
 void    null_cmd (struct parameters & a)
 {
     pc.printf   ("At null_cmd, parameters : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
 }
 
-
 void    menucmd (struct parameters & a);
 
 void    kd_cmd (struct parameters & a)  //  kick the watchdog
 {
 }
 
-extern  void    rpm_push    (struct parameters & a)  ;
-extern  void    mph_push    (struct parameters & a)  ;
-void    uprpm_cmd (struct parameters & a)  //  controller rec'd rpm from driver boards
-{
-    rpm_push    (a);
-//    a.gp_i ^= 1;    //  toggle lsb
-}
-
 void    upmph_cmd (struct parameters & a)  //  controller rec'd mph from driver boards
 {
-    mph_push    (a);
-//    a.gp_i ^= 1;    //  toggle lsb
-}
-
-void    uprdi_cmd (struct parameters & a)  //  controller rec'd rpm from driver boards
-{
-//    pc.printf   ("RPM %d %d\r\n", (int)a.dbl[0], (int)a.dbl[1]);
+    My_STM3_ESC_boards.mph_update  (a.dbl[1]);
 }
 
 
-/*void    who_cmd (struct parameters & a)
+void    TSver_cmd (struct parameters & a)  //   Report current TS software version to pc
 {
-    int i = I_Am    ();
-    if  (I_Am() == a.target_unit)
-        pc.printf ("Hi there, I am %c\r\n", a.target_unit);
-}*/
+    pc.printf   ("ver %s\r\n", const_version_string);
+}
+
+void    boards_cmd  (struct parameters & a) {
+    int boards[MAX_ESCS], count = 0;
+    My_STM3_ESC_boards.get_boards_list   (boards);
+    while  (boards[count] && count < MAX_ESCS)
+        count++;
+    pc.printf   ("Board Count = %d\r\n", count);
+    if  (count) {
+        pc.printf   ("Boards found ^");
+        for (int i = 0; i < count; i++)
+            pc.printf   ("%c ", boards[i]);
+        pc.printf   ("\r\n");
+    }
+    else
+        pc.printf   ("No STM3_ESC boards found\r\n");
+}
+
+void    who_cmd (struct parameters & a)  //   Have read "whon" back from STM3_ESC where 'n' is STM3_Esc board ID number (ascii digit)
+{
+    int v = (int)a.dbl[0] | '0';
+    if  (a.numof_cl_values_read == 1 && isdigit(v)) {
+//        pc.printf   ("who%c, vals read %d\r\n", (int)a.dbl[0] | '0', a.numof_cl_values_read);
+        My_STM3_ESC_boards.set_board_ID  (v);//        set_board_id    (v);  //  just to test and prove can not add same board twice
+    }
+    else
+        pc.printf   ("Garbage response to 'who', numof vals %d, value %c\r\n", a.numof_cl_values_read, v);
+}
+
+void    ver_req_cmd (struct parameters & a)  //   Touch Screen controller requesting version string from one specific ESC board
+{
+    pc.printf   ("ver_req_cmd not yet implemented\r\n");
+}
 
 struct kb_command  {
     const char * cmd_word;         //  points to text e.g. "menu"
@@ -92,142 +136,32 @@
 struct  kb_command const 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},
+    {"ver", "report const_version_string", TSver_cmd},        //  Report TS controller version
+    {"boards", "List STM3_ESC boards connected", boards_cmd},
     {"kd", "kick the dog", kd_cmd},
-    {"rpm", "rpm reading from 2 motors", uprpm_cmd},
     {"nu", "do nothing", null_cmd},
 };
 
-const int numof_menu_items = sizeof(command_list) / sizeof(kb_command);
-void    menucmd (struct parameters & a)
-{
-//    struct kb_command const * cl = command_list;
-    pc.printf("\r\n\nTS_2018 Locomotive Touch Screen Controller\r\nAt menucmd function - listing commands:-\r\n");
-    for(int i = 0; i < numof_menu_items; i++)
-        pc.printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
-    pc.printf("End of List of Commands\r\n");
-}
-
 struct  kb_command const loco_command_list[] = {
     {"ls", "Lists available commands", menucmd},
     {"?", "Lists available commands, same as ls", menucmd},
-    {"rpm", "rpm reading from 2 motors", uprpm_cmd},
+    {"who", "Reads 'whon' response from STM3_ESC where 'n' is board num ascii from eeprom", who_cmd},
+    {"ver", "report const_version_string", ver_req_cmd},    //  Request software version of one specific ESC board
     {"mph", "bogie mph reading", upmph_cmd},    //  22/06/2018
-    {"rdi", "rpm reading from 2 motors", uprdi_cmd},
     {"nu", "do nothing", null_cmd},
 }   ;
 
-struct parameters pccom, lococom;
 
-/*struct  parameters  {
-    struct kb_command const * clist;
-    char    cmd_line[120];
-    char    * cmd_line_ptr;
-    int32_t position_in_list, numof_dbls, target_unit, numof_menu_items;
-    double  dbl[MAX_PARAMS];
-    bool    respond;
-}   ;
-*/
-int getc    (int which_port)
-{
-    if  (which_port == 0)
-        return  pc.getc ();
-    if  (which_port == 1)
-        return  com.getc    ();
-    return  -1;
-}
-int readable    (int which_port)
-{
-    if  (which_port == 0)
-        return  pc.readable ();
-    if  (which_port == 1)
-        return  com.readable    ();
-    return  -1;
-
-}
-
-void setup_pccom ()
-{
-    pccom.clist = command_list;
-    pccom.numof_menu_items = sizeof(command_list) / sizeof(kb_command);
-    pccom.com_no    = 0;
-    pccom.cl_index  = 0;
-    pccom.gp_i      = 0;    //  general puropse integer, not used to 30/4/2018
-}
-
-void    setup_lococom ()
-{
-    lococom.clist = loco_command_list;
-    lococom.numof_menu_items = sizeof(loco_command_list) / sizeof(kb_command);
-    lococom.com_no  = 1;
-    lococom.cl_index    = 0;
-    lococom.gp_i        = 0;    //  general puropse integer, toggles 0 / 1 to best guess source of rpm
-}
-
-void    clicore (struct parameters & a)
+void    menucmd (struct parameters & a)
 {
-    int ch;
-    char * pEnd;
-    while   (readable(a.com_no))    {
-        ch = getc   (a.com_no);
-        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;    //  Broadcast
-            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'
-                    //pc.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.clist[i].cmd_word);
-                    if(strncmp(a.clist[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++;
-                            }
-                        }
-                        //pc.printf   ("\r\n");   //  Not allowed as many may output this.
-                        //for (int k = 0; k < param_block.numof_dbls; k++)
-                        //    pc.printf   ("Read %.3f\r\n", param_block.dbl[k]);
-//                            param_block.times[i] = clock();
-//                            if  ((param_block.target_unit == BROADCAST) && (I_Am() == '0'))
-//                                param_block.respond = true;
-                        a.clist[i].f(a);   //  execute command
-                        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)
-                    pc.printf("No Match Found for CMD [%s]\r\n", a.cmd_line);
-            }           //  End of If have got some chars to lookup
-            //pc.printf("\r\n>");
-            a.cl_index = 0;
-        }               // End of else key was CR, may or may not be command to lookup
-    }                   //  End of while (pc.readable())
+    pc.printf("\r\n\nTS_2018 Locomotive Touch Screen Controller\r\nAt menucmd function - listing commands:-\r\n");
+    for(int i = 0; i < a.numof_menu_items; i++)
+        pc.printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
+    pc.printf("End of List of Commands\r\n");
 }
 
 
-/*
+/**
 New - March 2018
 Using opto isolated serial port, paralleled up using same pair to multiple boards running this code.
 New feature - commands have optional prefix digit 0-9 indicating which unit message is addressed to.
@@ -235,68 +169,68 @@
 Only units recognising its address from prefix digit may respond. This avoids bus contention.
 But for BROADCAST commands, '0' may respond on behalf of the group
 */
-//void    command_line_interpreter    (void const *argument)
-/*void    command_line_interpreter    ()
-{
-    const int MAX_CMD_LEN = 120;
-    static  char    cmd_line[MAX_CMD_LEN + 4];
-    static  int     cl_index = 0;
+command_line_interpreter_core   pcli    (0, sizeof(command_list) / sizeof(kb_command), command_list);
+command_line_interpreter_core   ploco   (1, sizeof(loco_command_list) / sizeof(kb_command), loco_command_list);
+
+void    command_line_interpreter_core::sniff    ()  {   //  look for recieved commands and act upon them
     int ch;
-    char * pEnd, * cmd_line_ptr;
-    static struct  parameters  param_block  ;
-    while  (pc.readable()) {
-        ch = pc.getc();
-//            if  (cl_index > MAX_CMD_LEN)  {   //  trap out stupidly long command lines
-//                pc.printf   ("Error!! Stupidly long cmd line\r\n");
-//                cl_index = 0;
-//            }
-        if(ch != '\r')  //  was this the 'Enter' key?
-            cmd_line[cl_index++] = ch;  //  added char to command being assembled
+    char * pEnd;
+    while   (clreadable())    {
+        ch = clgetc   ();
+        if(ch != '\r')  {//  was this the 'Enter' key?
+            if  (ch != '\n')    {   //  Ignore line feeds
+                cmd_line[cl_index++] = ch;  //  added char to command being assembled
+                if  (cl_index >= MAX_CMD_LINE_LEN-2)  {
+                    cl_index = 0;     //  Line is garbage anyway, so might as well junk the lot ?
+                    pc.printf   ("cl_index=%d TOO LONG in clicore\r\n", cl_index);
+                    Controller_Error.set    (FAULT_COM_LINE_LEN, 1);
+                }
+            }       //  endof if(ch != '\n')    ignore line feeds
+        }           //  endof if(ch != '\r')    
         else    {   //  key was CR, may or may not be command to lookup
-            param_block.target_unit = BROADCAST;    //  Broadcast
+            target_unit = BROADCAST;    //  Broadcast
             cmd_line_ptr = cmd_line;
             cmd_line[cl_index] = 0; //  null terminate command string
             if(cl_index)    {   //  If have got some chars to lookup
                 int i, wrdlen;
                 if  (isdigit(cmd_line[0]))  {   //  Look for command with prefix digit
                     cmd_line_ptr++;     //  point past identified digit prefix
-                    param_block.target_unit = cmd_line[0];  //  '0' to '9'
-                    //pc.printf ("Got prefix %c\r\n", cmd_line[0]);
+                    target_unit = cmd_line[0];  //  '0' to '9'
+                    //pc.printf ("Got prefix %c\r\n", a.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(clist[i].cmd_word);
+                    if(strncmp(clist[i].cmd_word, cmd_line_ptr, wrdlen) == 0 && !isalpha(cmd_line_ptr[wrdlen]))  {   //  If match found
                         for (int k = 0; k < MAX_PARAMS; k++)    {
-                            param_block.dbl[k] = 0.0;
+                            a.dbl[k] = 0.0;
                         }
-                        param_block.position_in_list = i;
-                        param_block.numof_dbls = 0;
+                        //position_in_list = i;
+                        a.numof_cl_values_read = 0;
                         pEnd = cmd_line_ptr + wrdlen;
                         while   (*pEnd)  {          //  Assemble all numerics as doubles
-                            param_block.dbl[param_block.numof_dbls++] = strtod    (pEnd, &pEnd);
+                            a.dbl[a.numof_cl_values_read++] = strtod    (pEnd, &pEnd);
                             while   (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd)  {
                                 pEnd++;
                             }
                         }
                         //pc.printf   ("\r\n");   //  Not allowed as many may output this.
-                        //for (int k = 0; k < param_block.numof_dbls; k++)
+                        //for (int k = 0; k < param_block.a.numof_cl_values_read; k++)
                         //    pc.printf   ("Read %.3f\r\n", param_block.dbl[k]);
 //                            param_block.times[i] = clock();
 //                            if  ((param_block.target_unit == BROADCAST) && (I_Am() == '0'))
 //                                param_block.respond = true;
-                        command_list[i].f(param_block);   //  execute command
-                        i = numof_menu_items + 1;    //  to exit for loop
+                        clist[i].f(a);   //  execute command
+                        i = a.numof_menu_items + 1;    //  to exit for loop
                     }   //  end of match found
                 }       // End of for numof_menu_items
-                if(i == numof_menu_items)
+                if(i == a.numof_menu_items) {
                     pc.printf("No Match Found for CMD [%s]\r\n", cmd_line);
+                    Controller_Error.set    (FAULT_COM_NO_MATCH, 1);
+                }
             }           //  End of If have got some chars to lookup
-            //pc.printf("\r\n>");
             cl_index = 0;
         }               // End of else key was CR, may or may not be command to lookup
     }                   //  End of while (pc.readable())
-//        Thread::wait(20);  //  Using RTOS on this project
-//    }
-}
-*/
+}  
 
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/error_handler.cpp	Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,78 @@
+#include "mbed.h"
+#include "Electric_Loco.h"
+/*class   error_handling_Jan_2019
+{
+    int32_t    TS_fault[NUMOF_REPORTABLE_TS_ERRORS]    ;   //  Some number of reportable error codes, accessible through set and read members
+    public:
+    error_handling_Jan_2019 ()  {   //  default constructor
+        for (int i = 0; i < (sizeof(TS_fault) / sizeof(int32_t)); i++)
+            TS_fault[i] = 0;
+    }
+    void        set   (uint32_t, int32_t)   ;
+    uint32_t    read  (uint32_t)   ;
+    bool        all_good    ()  ;
+    void        report_any  ()  ;
+}   ;
+*/
+
+const char * FaultList[] = {
+/*
+    FAULT_0,
+    FAULT_BOARD_ID_IN_MSG,
+    FAULT_TS,
+    FAULT_PC,
+    FAULT_COM,
+    FAULT_COM_NO_MATCH,
+    FAULT_COM_LINE_LEN,
+    FAULT_QSPI,
+    FAULT_ODOMETER,
+    FAULT_MAX,
+    NUMOF_REPORTABLE_TS_ERRORS
+*/
+    "Zero",
+    "BoardID in msg",
+    "TouchScreen",
+    "pc",
+    "com no match",
+    "com line len",
+    "qspi",
+    "odometer",
+    "max",
+    "endoflist",
+    " ",
+    }   ;
+
+bool    error_handling_Jan_2019::all_good   ()  {
+    for (int i = 0; i < NUMOF_REPORTABLE_TS_ERRORS; i++)
+        if  (TS_fault[i])
+            return  false;
+    return  true;
+}
+
+/**void    error_handling_Jan_2019::set  (uint32_t err_no, int32_t bits_to_set) {
+    Used to set bits in error int
+    Uses OR to set new bits without clearing other bits set previously
+*/
+void    error_handling_Jan_2019::set  (uint32_t err_no, int32_t bits_to_set) {
+    TS_fault[err_no] |= bits_to_set;    //  Uses OR to set new bits without clearing other bits set previously
+}
+
+/**void    error_handling_Jan_2019::clr  (uint32_t err_no) {
+    Used to clear all bits in error int
+*/
+void    error_handling_Jan_2019::clr  (uint32_t err_no) {
+    TS_fault[err_no] = 0;
+}
+
+uint32_t    error_handling_Jan_2019::read (uint32_t err_no) {
+    return  TS_fault[err_no];
+}
+
+extern  Serial  pc;
+void    error_handling_Jan_2019::report_any ()  {
+    for (int i = 0; i < NUMOF_REPORTABLE_TS_ERRORS; i++)  {
+        if  (TS_fault[i])
+            pc.printf   ("Error report, number %d, value %d, %s\r\n", i, TS_fault[i], FaultList[i]);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/esc_comms.cpp	Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,113 @@
+#include "mbed.h"
+#include "Electric_Loco.h"
+#include "AsyncSerial.hpp"
+
+extern  Serial  pc;
+extern  AsyncSerial com2escs;
+extern  error_handling_Jan_2019   Controller_Error    ;         //  Provides array usable to store error codes.
+extern  volatile    bool    trigger_32ms;
+extern  command_line_interpreter_core   pcli, ploco;    //  pcli handles comms with pc, ploco handles comms with STM3_ESC boards
+
+void    STM3_ESC_Interface::message (int board, char * msg)  //  Send message to one individual STM3_ESC
+{
+    if  (!(isdigit(board)))   {
+        pc.printf   ("Error in STM3_ESC_Interface::message, '%c' not valid board ID\r\n");
+        Controller_Error.set    (FAULT_BOARD_ID_IN_MSG, -1);
+        return  ;
+    }
+    com2escs.putc    (board);
+    message         (msg);
+}
+
+void    STM3_ESC_Interface::message (char * msg)  //  Broadcast message to all STM3_ESCs
+{
+        com2escs.printf    (msg);
+}
+
+void    STM3_ESC_Interface::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;
+    com2escs.printf  ("v%d\r", (int)(last_V * 99.0));
+}
+
+void    STM3_ESC_Interface::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
+    com2escs.printf  ("i%d\r", (int)(last_I * 99.0));
+}
+
+void    STM3_ESC_Interface::get_boards_list (int * dest)    {
+    for (int i = 0; i < MAX_ESCS; i++)
+        dest[i] = board_IDs[i];
+}
+
+void    STM3_ESC_Interface::search_for_escs ()  {   //  Seek out all STM3_ESC boards connected to TS controller
+    char    whotxt[] = "0who\r\0";
+    for (int i = 0; i < MAX_ESCS; i++)
+        board_IDs[i] = 0;
+    board_count = 0;
+    pc.printf   ("Searching for connected STM3_ESC boards - ");
+    while   (!trigger_32ms) 
+        ploco.sniff  ();    //  Allow any previous STM3_ESC comms opportunity to complete
+    while   (whotxt[0] <= '9')   {       //  Sniff out system, discover motor controllers connected
+        trigger_32ms = false;
+        message  (whotxt);   //  Issue '0who' etc
+        whotxt[0]++;
+        while   (!trigger_32ms) {     //  Give time for STM3_ESC board to respond
+            pcli.sniff  ();     //  Check commands from pc also
+            ploco.sniff  ();    //  This is where responses to 'who' get picked up and dealt with
+        }
+    }       //  Completed quick sniff to identify all connected STM3 ESC boards
+    if  (board_count)   {
+        pc.printf   ("Found %d boards, IDs ", board_count);
+        for (int i = 0; i < board_count; i++)
+            pc.printf   ("%c ", board_IDs[i]);
+        pc.printf   ("\r\n");
+    }
+    else    
+        pc.printf   ("None found\r\n");
+}
+
+void    STM3_ESC_Interface::set_board_ID (int a)   {    //  called in response to 'whon' coming back from a STM3_ESC
+    board_count = 0;    //  reset and recalculate board_count
+    while   (board_IDs[board_count])  {
+        if  (board_IDs[board_count++] == a)   {
+//            pc.printf   ("set_board_ID %c already listed\r\n", a);
+            return;
+        }
+    }
+    board_IDs[board_count++] = a;
+}
+
+bool    STM3_ESC_Interface::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;
+    message (board_IDs[reqno++], "mph\r");
+    return  true;
+}
+
+//void    STM3_ESC_Interface::mph_update(struct parameters & a)  {    //  Puts new readings into mem  22/06/2018
+void    STM3_ESC_Interface::mph_update(double mph_reading)  {    //  Puts new readings into mem  22/06/2018
+    static int identified_board = 0;
+    esc_speeds[identified_board++] = mph_reading;               //  A single mph reading returned from one STM3ESC. a.dbl[0] gives esc number
+    if  (identified_board >= board_count)    
+        identified_board = 0;                       //  Circular buffer for number of STM3ESC boards sniffed and found
+    double  temp = 0.0;
+        for (int j = 0; j < board_count; j++)
+            temp += esc_speeds[j];
+    if  (board_count < 1)           //  Avoid possible DIV0 error
+        mph = 0.0;
+    else
+        mph = temp / board_count;                       //  Updated average of most recent mph readings recieved
+}
+
--- a/graphics.cpp	Sat Jun 23 09:37:41 2018 +0000
+++ b/graphics.cpp	Mon Jan 14 16:39:41 2019 +0000
@@ -3,358 +3,53 @@
 #include "LCD_DISCO_F746NG.h"
 #include "Electric_Loco.h"
 
-#define VOLTMETER_X 68      //  Voltmeter screen position
-#define VOLTMETER_Y 68
-#define AMMETER_X   68      //  Ammeter screen position - Now replaced by Power meter
-#define AMMETER_Y   202
-#define SPEEDO_X    274     //  Speedometer screen position
-#define SPEEDO_Y    135
-#define V_A_SIZE    54      //  Size of voltmeter and ammeter
-#define SPEEDO_SIZE 112
-
-#define SPEEDO_BODY_COLOUR  LCD_COLOR_BLACK
-#define SPEEDO_DIAL_COLOUR  LCD_COLOR_WHITE
-#define SPEEDO_TEXT_COLOUR  LCD_COLOR_BLUE
-
-#define VMETER_BODY_COLOUR  LCD_COLOR_BLACK
-#define VMETER_DIAL_COLOUR  LCD_COLOR_WHITE
-#define VMETER_TEXT_COLOUR  LCD_COLOR_BLUE
-
-#define AMETER_BODY_COLOUR  LCD_COLOR_BLACK
-#define AMETER_DIAL_COLOUR  LCD_COLOR_WHITE
-#define AMETER_TEXT_COLOUR  LCD_COLOR_BLUE
 
 extern  LCD_DISCO_F746NG    lcd;
 extern  TS_DISCO_F746NG     touch_screen;
 extern  Serial pc;
 
-static const int    char_widths[]   = {5, 7, 11, 14, 17, 17}    ,
-                    meter_radius_min = 30, meter_radius_max = 120;
+/*
+moving_coil_meter   Voltmeter   (   LCD_COLOR_BLACK,    //  Frame / body colour
+                                    LCD_COLOR_WHITE,    //  Dial face colour
+                                    LCD_COLOR_RED,      //  Moving needle colour
+                                    LCD_COLOR_BLUE,     //  Text colour for Units e.g. 'V' and e.g. '32.7'
+                                    LCD_COLOR_MAGENTA,  //  Scale graduations colour
+                                    VOLTMETER_X,        //  X co-ord, centre of meter
+                                    VOLTMETER_Y,        //  Y co-ord, centre of meter
+                                    V_A_SIZE,           //  Meter is square with rounded corners. This is meter dial face radius
+                                    22.0,               //  Scale not limited to e.g. 0 to 10. This is reading at anti-clock limit
+                                    59.0,               //  This is reading at full scale deflection
+                                    1.25 * PI,          //  Angle of needle at anti-clockwise limit
+                                    -0.25 * PI ,        //  Angle of needle at full scale deflection (clockwise max)
+                                    30,                 //  Number of scale graduation marks drwan
+                                    "V",                //  Text for Units, e.g. 'V' or 'MPH'
+                                    ONE_DP,             //  NO_DPS or ONE_DP - supports only no decimal places or one
+                                    false) ;            //  true to show '+' or '-', false to supress sign display
+*/
+/*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
+
+*/
 
 
-//  Uses our own generated sine and cosines from lookup table.  For some unexplained reason, using inbuilt sin and cos fns cause display flicker !
-//extern double  jcos  (double angle);    //  Used in DrawNeedle, plain sin and cos functions cause display flicker !!
-//extern double  jsin  (double angle);
-#define jcos    cos
-#define jsin    sin
-/*void    costabgen  (int points)  {
-    double  angle = 0.0;
-    while   (angle < 2.1 * PI)  {
-        pc.printf   ("Angle %f, my cos %+f, c cos %+f\r\n", angle, jcos(angle), cos(angle));
-//        pc.printf   ("Angle %f, my sin %+f, c sin %+f\r\n", angle, jsin(angle), sin(angle));
-        angle += PI / 24;
-    }
-//    double  angle;
-*//*    int step, perline = 0;
-    double  interval = PI / 2.0 / (double)points;
-    pc.printf   ("//At costabgen with %d points\r\n", points);
-    pc.printf   ("static const double costab[] = {\r\n");
-    for (step = 0; step <= points; step++)  {
-        angle   = interval * (double)step;
-//        pc.printf   ("cos %+.3f = %+.3f\r\n", angle, cos(angle));
-        if  (++perline == 8)  {
-            pc.printf   ("%+.6f,\r\n", cos(angle));
-            perline = 0;
-        }
-        else
-            pc.printf   ("%+.6f, ", cos(angle));
-        wait    (0.025);
-    }
-    pc.printf   ("0.0\t}\t;\r\n//End of costab\r\n");
-*/
-//}
-
-/**
-  * @brief  Fills a triangle (between 3 points).
-  * @param  x1: Point 1 X position
-  * @param  y1: Point 1 Y position
-  * @param  x2: Point 2 X position
-  * @param  y2: Point 2 Y position
-  * @param  x3: Point 3 X position
-  * @param  y3: Point 3 Y position
-  * @retval None
-  */
-static void FillTriangle(uint16_t x1, uint16_t x2, uint16_t x3, uint16_t y1, uint16_t y2, uint16_t y3)
-{
-    int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0,
-            yinc1 = 0, yinc2 = 0, den = 0, num = 0, num_add = 0, num_pixels = 0,
-            curpixel = 0;
-
-    deltax = abs(x2 - x1);        /* The difference between the x's */
-    deltay = abs(y2 - y1);        /* The difference between the y's */
-    x = x1;                       /* Start x off at the first pixel */
-    y = y1;                       /* Start y off at the first pixel */
-
-    if (x2 >= x1) {               /* The x-values are increasing */
-        xinc1 = 1;
-        xinc2 = 1;
-    } else {                      /* The x-values are decreasing */
-        xinc1 = -1;
-        xinc2 = -1;
-    }
-
-    if (y2 >= y1) {               /* The y-values are increasing */
-        yinc1 = 1;
-        yinc2 = 1;
-    } else {                      /* The y-values are decreasing */
-        yinc1 = -1;
-        yinc2 = -1;
-    }
-
-    if (deltax >= deltay) {       /* There is at least one x-value for every y-value */
-        xinc1 = 0;                  /* Don't change the x when numerator >= denominator */
-        yinc2 = 0;                  /* Don't change the y for every iteration */
-        den = deltax;
-        num = deltax / 2;
-        num_add = deltay;
-        num_pixels = deltax;         /* There are more x-values than y-values */
-    } else {                      /* There is at least one y-value for every x-value */
-        xinc2 = 0;                  /* Don't change the x for every iteration */
-        yinc1 = 0;                  /* Don't change the y when numerator >= denominator */
-        den = deltay;
-        num = deltay / 2;
-        num_add = deltax;
-        num_pixels = deltay;         /* There are more y-values than x-values */
-    }
-
-    for (curpixel = 0; curpixel <= num_pixels; curpixel++) {
-        lcd.DrawLine(x, y, x3, y3);
-
-        num += num_add;              /* Increase the numerator by the top of the fraction */
-        if (num >= den) {           /* Check if numerator >= denominator */
-            num -= den;               /* Calculate the new numerator value */
-            x += xinc1;               /* Change the x as appropriate */
-            y += yinc1;               /* Change the y as appropriate */
-        }
-        x += xinc2;                 /* Change the x as appropriate */
-        y += yinc2;                 /* Change the y as appropriate */
-    }
-}
-
-double  anglefix    (double a)  {       //  Ensures 0.0 <= angle <= + two PI
-    while   (a > PI) a   -= 2.0 * PI;
-    while   (a < 0.0) a   += 2.0 * PI;
-    return  a;
-}
-
-class    moving_coil_meter
-{
-    int     meter_radius, cent_x, cent_y, needle_len, scale_ticks,
-            disc_colour,    needle_colour,  scale_colour,   text_colour, body_colour, dec_places;
-    double  start_angle, end_angle, old_angle, value_min, value_max, rad_per_value, swept_angle, value_range;
-    double  Value;  //  This is the one that determines pointer angle
-    bool    draw_sign;
-
-    void    DrawNeedle          (double alpha, int colour)  ;
-    void    DrawScaleGraduations(int colour)  ;
-    double  get_pointer_angle   (double value)  ;
-    int     get_font            ()  ;
-
-public:
-
-    moving_coil_meter   ()  {   //  constructor
-        meter_radius = 100;
-        value_min = -1.0;
-        value_max = 1.0;
-        cent_x = cent_y = 150;
-        disc_colour     = LCD_COLOR_BLACK;
-        needle_colour   = LCD_COLOR_WHITE;
-        scale_colour    = LCD_COLOR_MAGENTA;
-        text_colour     = LCD_COLOR_RED;
-        body_colour     = LCD_COLOR_CYAN;
-        old_angle       = 0.0;
-    }
-
-    bool    setup   (int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang, int scaleticks, char * units, int decimal_places, bool sign)  ;
-    void    set_colours   (int bod_colour, int bgcol, int needlecol, int textcolour, int scalecol)  ;
-    void    set_value   (double v)  ;
-}   Voltmeter,  Powermeter,    Speedo;     //  3 instances of moving coil meter graphic
-
-void    moving_coil_meter::set_colours   (int bod_col, int bgcol, int needlecol, int textcol, int scalecol)  {
-    body_colour = bod_col;
-    disc_colour = bgcol;
-    needle_colour   = needlecol;
-    text_colour = textcol;
-    scale_colour    = scalecol;
-}
-
-void    moving_coil_meter::DrawNeedle  (double alpha, int colour)
-{
-    point  pixpts[4];
-    int save_colour, ssa, sca;
-    alpha   = anglefix  (alpha);
-    double  shortln = (needle_len / 18.7),
-            sina    = jsin(alpha),
-            cosa    = jcos(alpha);
-
-    save_colour = lcd.GetTextColor  ();
-    ssa = (int)(shortln * sina);
-    sca = (int)(shortln * cosa);
-    old_angle = alpha;
-    pixpts[0].x = cent_x - ssa;//(int)(shortln * sin(alpha));
-    pixpts[0].y = cent_y - sca;//(int)(shortln * cos(alpha));
-    pixpts[1].x = cent_x + (int)(needle_len * cosa);
-    pixpts[1].y = cent_y - (int)(needle_len * sina);  //  - as increasing y is downwards
-    pixpts[2].x = cent_x + ssa;//(int)(shortln * sin(alpha));
-    pixpts[2].y = cent_y + sca;//(int)(shortln * cos(alpha));
-    lcd.SetTextColor    (colour);
-    lcd.FillCircle      (cent_x, cent_y, (int)(needle_len / 15.0));
-    FillTriangle    (pixpts[0].x, pixpts[1].x, pixpts[2].x, pixpts[0].y, pixpts[1].y, pixpts[2].y);
-    lcd.SetTextColor    (save_colour);
-}
-
-void    moving_coil_meter::DrawScaleGraduations (int colour)
-{
-    int save_colour = lcd.GetTextColor  ();
-    int i, radius_inner = (int) meter_radius - 2, radius_outer = (int) (meter_radius * 0.9);
-    double  ang, cosang, sinang, angle_step;
-    lcd.SetTextColor    (colour);
-    ang = start_angle;
-    angle_step  = (start_angle - end_angle) / scale_ticks;
-    for (i = 0; i <= scale_ticks; i++)   {   //
-        cosang  = cos(ang);
-        sinang  = sin(ang);
-        lcd.DrawLine    (cent_x + radius_outer * cosang, cent_y - radius_outer * sinang, cent_x + radius_inner * cosang, cent_y - radius_inner * sinang);
-        ang -= angle_step;
-    }
-    lcd.SetTextColor    (save_colour);
-}
 
 void    displaytext    (int x, int y, const int font, char * txt)   ;
 
-bool    moving_coil_meter::setup   (int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang, int scaleticks, char * units, int decimal_places, bool sign)
-{
-    bool    retval = true;
-    int font, charwid, x_offset;
-    if  (size < meter_radius_min || size > meter_radius_max)
-        return  false;
-    meter_radius = size;
-    if  (meter_radius > cx || meter_radius > cy)
-        return  false;
-    int corner_rad          = meter_radius / 6,
-        screw_hole_offset   = meter_radius * 92 / 100,
-        screw_rad           = meter_radius / 13;
-    cent_x = cx;
-    cent_y = cy;
-
-    start_angle = start_ang;
-    end_angle = end_ang;
-    value_min = lo;
-    value_max = hi;
-    scale_ticks = scaleticks;
-    swept_angle = abs(start_angle - end_angle);
-    value_range = (value_max - value_min);
-    rad_per_value = swept_angle / value_range;
-    dec_places  = decimal_places;
-    draw_sign   = sign;
-
-    needle_len = (int)(0.87 * (double)meter_radius);
-    int oldcolour1 = lcd.GetTextColor   ();
-    int oldcolour2 = lcd.GetBackColor   ();
-    lcd.SetTextColor    (body_colour);
-    //  Draw meter body as solid square with rounded corners, complete with mounting screw holes !
-    lcd.FillRect    (cent_x - meter_radius, cent_y - meter_radius - corner_rad, meter_radius * 2, corner_rad);
-    lcd.FillRect    (cent_x - meter_radius, cent_y + meter_radius, meter_radius * 2, corner_rad + 1);
-    lcd.FillRect    (cent_x - meter_radius - corner_rad, cent_y - meter_radius, 1 +(meter_radius + corner_rad) * 2, meter_radius * 2);
-    lcd.FillCircle  (cent_x - meter_radius, cent_y - meter_radius, corner_rad);  //  meter box has rounded corners
-    lcd.FillCircle  (cent_x - meter_radius, cent_y + meter_radius, corner_rad);
-    lcd.FillCircle  (cent_x + meter_radius, cent_y - meter_radius, corner_rad);
-    lcd.FillCircle  (cent_x + meter_radius, cent_y + meter_radius, corner_rad);
-    lcd.SetTextColor    (LCD_COLOR_DARKGRAY);
-    lcd.FillCircle  (cent_x - screw_hole_offset, cent_y - screw_hole_offset, screw_rad);  //  panel mounting screw holes near corners
-    lcd.FillCircle  (cent_x - screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
-    lcd.FillCircle  (cent_x + screw_hole_offset, cent_y - screw_hole_offset, screw_rad);
-    lcd.FillCircle  (cent_x + screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
-    lcd.SetTextColor    (disc_colour);
-    lcd.FillCircle      (cent_x, cent_y, meter_radius);
-    DrawScaleGraduations (scale_colour);   //drew the green trace around active needle-sweep angle
-
-    font = get_font ();
-    charwid = char_widths[font];
-    x_offset = charwid * strlen(units) / 2;
-    lcd.SetTextColor    (text_colour);
-    lcd.SetBackColor    (disc_colour);
-//    displaytext (cent_x - x_offset, cent_y + (meter_radius * 7) / 19, font, units);
-    displaytext (cent_x - x_offset, cent_y + (meter_radius * 6) / 19, font, units);
-    lcd.SetBackColor    (oldcolour2);
-    lcd.SetTextColor    (oldcolour1);
-    return  retval;
+extern  uint32_t    odometer_out  ()    ;
+void    rewrite_odometer    ()  {
+    char    dist[20];
+    sprintf (dist, "%06dm", odometer_out());   //  12th June 2018 changed 05 to 06 to allow correct display of tot distance > 99999 metres
+    lcd.SetTextColor    (LCD_COLOR_BLACK);
+    displaytext (241, 224, 2, dist);
 }
 
-int     moving_coil_meter::get_font ()
-{
-    int font = meter_radius - meter_radius_min;
-    font /= 17;
-    if  (font > 4)
-        font = 4;
-    if  (font < 2)
-        font = 2;
-    return  font;
-}
-
-double  moving_coil_meter::get_pointer_angle    (double v)
-{
-    double   vabvmin, retval;
-    if   (v < value_min) v = value_min;
-    if   (v > value_max) v = value_max;
-    Value = v;   //  clipped copy of supplied value
-    vabvmin = v - value_min;
-    retval   = start_angle - (vabvmin * rad_per_value);
-    return   anglefix   (retval);
-}
-
-void    moving_coil_meter::set_value   (double meter_read_value)
-{
-    char    txt[32];
-    int x_offset, font, charwid, lenchk;//,
-    DrawNeedle  (old_angle, disc_colour);                   //  un-draw needle
-    DrawNeedle  (get_pointer_angle   (meter_read_value), needle_colour)  ; //  re-draw needle
- //   if  (draw_sign) {
-    if  (dec_places == ONE_DP)
-        sprintf (txt, " %+.1f \0", meter_read_value);
-    else
-        sprintf (txt, " %+.0f \0", meter_read_value);
-    lenchk = strlen(txt);
-    if  (!draw_sign)    {
-        for (int i = 1; i < lenchk; i++)
-            txt[i] = txt[i + 1];
-//        lenchk--;     //  Stupidly, this gives the display flicker blight
-    }
-    lenchk = strlen(txt);//  Stupidly, repeating this instead does NOT give the display flicker blight
-    font = get_font();
-    charwid = char_widths[font];
-    x_offset = charwid * lenchk / 2;
-    lcd.SetTextColor    (text_colour);
-    lcd.SetBackColor    (disc_colour);
-    if  (lenchk > 0 && lenchk < 9)
-        displaytext (cent_x - x_offset, cent_y + (meter_radius * 11) / 19, font, txt);
-}
-//bool    moving_coil_meter::setup   (int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang,
-//                                    int scale_ticks, char * units)
-void    vm_set  ()   //x   y  size minv  maxv  min angle   max angle,
-{
-    Speedo.set_colours  (SPEEDO_BODY_COLOUR, SPEEDO_DIAL_COLOUR, LCD_COLOR_RED, SPEEDO_TEXT_COLOUR, LCD_COLOR_BLACK);
-    Speedo.setup    (SPEEDO_X, SPEEDO_Y, SPEEDO_SIZE, 0.0, 12.0, 1.25 * PI, -0.25 * PI , 12, "MPH", ONE_DP, false);
-    Voltmeter.set_colours  (LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_COLOR_RED, LCD_COLOR_BLUE, LCD_COLOR_MAGENTA);
-    Voltmeter.setup (VOLTMETER_X, VOLTMETER_Y, V_A_SIZE, 22.0, 59.0, 1.25 * PI, -0.25 * PI , 30, "V", ONE_DP, false);
-    Powermeter.set_colours  (LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_COLOR_RED, LCD_COLOR_BLUE, LCD_COLOR_BLUE);
-    Powermeter.setup   (AMMETER_X, AMMETER_Y, V_A_SIZE, -1400.0, 1400.0, 1.25 * PI, -0.25 * PI , 14, "Watt", NO_DPS, false);
-}
-
-//void    update_meters  (double speed, double current, double voltage)
-void    update_meters  (double speed, double power, double voltage)
-{
-//    Powermeter.set_value(voltage * current);
-    Powermeter.set_value(power);
-    Voltmeter.set_value (voltage);
-    Speedo.set_value    (speed);
-}
-
-
-
 struct  rect    {   struct point a, b; }   ;
 
-struct  butt_on  {
+struct  button_specs  {
     struct  rect    area;
     int border_colour,  body_colour;
     bool    in_use, pressed;//, released;
@@ -362,7 +57,7 @@
     char txt2[12];
 }   ;
 
-struct  butt_on   button[NUMOF_BUTTONS];
+struct  button_specs   button[NUMOF_BUTTONS];
 
 int get_button_press    (struct point & pt) ;
 int get_but_p   (int x, int y)
@@ -373,14 +68,25 @@
     return  get_button_press    (p);
 }
 
-
+/**
+void    read_keypresses    (struct ky_bd & a)
+Sets values in struct ky_bd, containing :
+        struct  ky_bd   {   int count,  slider_y; keystr key[MAX_TOUCHES + 1];   bool  sli;   }  ;
+            struct  keystr  {   int keynum; int x;  int y;  }   ;
+    sets a.count to number of fingers found pressing buttons
+    fills a.key[].keynum with list of 'a.count' button return codes
+    fills corresponding a.key[].x and a.key[].y with finger coordinates
+    if button is SLIDER_BUTTON
+        sets a.sli true else sets false
+        sets a.slider_y with new slider y coordinate - 0 at top
+*/
 void    read_keypresses    (struct ky_bd & a)
 {
     int x;
     a.count = 0;
     a.sli   = false;
     for (x = 0; x < MAX_TOUCHES; x++)
-        a.ky[x].keynum = -1;
+        a.key[x].keynum = -1;
     int touches, but;
     TS_StateTypeDef TS_State;
     touch_screen.GetState(&TS_State);
@@ -388,12 +94,12 @@
     for (int h = 0; h < touches; h++)   {
         but = get_but_p  (TS_State.touchX[h], TS_State.touchY[h]);
         if  (but > - 1) {
-            a.ky[a.count].keynum = but;
-            a.ky[a.count].x     = TS_State.touchX[h];
-            a.ky[a.count].y     = TS_State.touchY[h];
-            if  (but == SLIDER) {
+            a.key[a.count].keynum = but;
+            a.key[a.count].x     = TS_State.touchX[h];
+            a.key[a.count].y     = TS_State.touchY[h];
+            if  (but == SLIDER_BUTTON) {
                 a.sli   = true;
-                a.slider_y  = a.ky[a.count].y;
+                a.slider_y  = a.key[a.count].y;
             }
             a.count++;
         }
@@ -425,7 +131,7 @@
     lcd.SetBackColor(obc);
 }
 
-void    draw_button (struct butt_on & bu)
+void    draw_button (struct button_specs & bu)
 {
     int oldbgcolour;
     lcd.SetTextColor    (bu.body_colour);
@@ -451,7 +157,7 @@
     if  (but < 0 || but > NUMOF_BUTTONS)    {
         pc.printf   ("Button out of range in draw_button_hilight %d\r\n", but)  ;
     } else    {
-        struct   butt_on * bu = &button[but];
+        struct   button_specs * bu = &button[but];
         int oldbgcolour = lcd.GetBackColor();//, minx, miny, maxx, maxy;
         lcd.SetTextColor(colour);
         lcd.DrawRect(bu->area.a.x - 1, bu->area.a.y - 1, bu->area.b.x - bu->area.a.x + 2, bu->area.b.y - bu->area.a.y + 2);
@@ -461,13 +167,13 @@
     }
 }
 
-void    draw_button (struct butt_on & bu, int body_colour)
+void    draw_button (struct button_specs & bu, int body_colour)
 {
     bu.body_colour = body_colour;
     draw_button (bu);
 }
 
-void    setup_button    (struct butt_on & bu, int x1, int y1, int dx, int dy, int bord, int body, char * txt1, char * txt2)
+void    setup_button    (struct button_specs & bu, int x1, int y1, int dx, int dy, int bord, int body, char * txt1, char * txt2)
 {
     static const int margin = 3;
     int xsize = lcd.GetXSize();
@@ -490,40 +196,23 @@
     draw_button(bu);
 }
 
-bool    ifpressed   (int key)
+/*bool    ifpressed   (int key)
 {
     return  button[key].pressed;
 }
-
-bool    is_button_pressed   (struct point & pt, struct butt_on & bu)
+*/
+bool    is_button_pressed   (struct point & pt, struct button_specs & bu)
 {
     if  (bu.in_use)  {
-        if  (bu.area.a.x < pt.x && bu.area.b.x > pt.x
-                && bu.area.a.y < pt.y && bu.area.b.y > pt.y)
+        if  (   bu.area.a.x < pt.x 
+                && bu.area.b.x > pt.x
+                && bu.area.a.y < pt.y 
+                && bu.area.b.y > pt.y)
             return  true;
     }
     return  false;
 }
 
-bool    keyrelease  (int key)
-{
-    bool    rv = false;
-    if  (button[key].pressed)   {
-        rv = true;
-        button[key].pressed = false;
-    }
-    return  rv;
-}
-void    setpressed  (int key, bool torf)
-{
-    button[key].pressed = torf;
-}
-void    setinuse    (int key, bool torf)
-{
-    button[key].in_use = torf;
-}
-
-
 int get_button_press    (struct point & pt)
 {
     for (int j = 0; j < NUMOF_BUTTONS; j++)
@@ -534,91 +223,71 @@
 
 void    setup_buttons   ()
 {
-    setup_button    (button[SPEEDO_BUT],
+    setup_button    (button[SPEEDO_BUTTON],
                     SPEEDO_X - SPEEDO_SIZE, SPEEDO_Y - SPEEDO_SIZE,
                     SPEEDO_SIZE * 2, SPEEDO_SIZE * 2, SPEEDO_BODY_COLOUR,   LCD_COLOR_RED,   " X", "")  ;
-    setup_button    (button[VMETER_BUT],
+    setup_button    (button[VMETER_BUTTON],
                     VOLTMETER_X - V_A_SIZE, VOLTMETER_Y - V_A_SIZE, V_A_SIZE * 2, V_A_SIZE * 2, VMETER_BODY_COLOUR,   LCD_COLOR_RED,   " Y", "")  ;
-    setup_button    (button[AMETER_BUT],
+    setup_button    (button[AMETER_BUTTON],
                     AMMETER_X - V_A_SIZE, AMMETER_Y - V_A_SIZE, V_A_SIZE * 2, V_A_SIZE * 2, AMETER_BODY_COLOUR,   LCD_COLOR_RED,   " Z", "")  ;
-    setup_button    (button[SLIDER],   SLIDERX, SLIDERY, SLIDERW, SLIDERH, LCD_COLOR_BLUE,   LCD_COLOR_MAGENTA,   "", "")  ;
+    setup_button    (button[SLIDER_BUTTON],   SLIDERX, SLIDERY, SLIDERW, SLIDERH, LCD_COLOR_BLUE,   LCD_COLOR_MAGENTA,   "", "")  ;
 }
 
-
-void SliderGraphic (struct slide & q)   {
-    int
-    colr,
-    oldbgcolr   = lcd.GetBackColor   (),
-    oldtxtcolr  = lcd.GetTextColor   ();
+void    screen_touch_handler::DrawSlider    ()  {
+    uint32_t
+        colr,
+        oldbgcolr   = lcd.GetBackColor   (),
+        oldtxtcolr  = lcd.GetTextColor   ();
     char    txt[4];
     txt[1] = 0;
-    if  (q.position > MAX_POS)
-        q.position = MAX_POS;
-    if  (q.position < MIN_POS)
-        q.position = MIN_POS;
-    if  (q.position == NEUTRAL_VAL)
-        q.state = NEUTRAL_DRIFT;
-    if  (q.position > NEUTRAL_VAL)
-        q.state = REGEN_BRAKE;
-    if  (q.position < NEUTRAL_VAL)
-        if  (q.state == REGEN_BRAKE)    {   //  Ensure transition from BRAKE to RUN passes through NEUTRAL
-            q.position = NEUTRAL_VAL;
-            q.state = NEUTRAL_DRIFT;
-        }
-        else
-            q.state = RUN;
-    if  (q.position == MAX_POS) {
-        if  (q.loco_speed < LOCO_HANDBRAKE_ESCAPE_SPEED)
-            q.state = PARK;
-        else    {
-            q.state = REGEN_BRAKE;
-            q.position--;
-        }
-    }
-    if  (q.position != q.oldpos)    {
+    if  (position > MAX_POS)
+        position = MAX_POS;
+    if  (position < MIN_POS)
+        position = MIN_POS;
+    if  (position != oldpos)    {
         //  Draw slider background colour rectangle overwriting previous circles
         //  Redraw black vertical
         //  Draw new circles
         //  Write text char
         lcd.SetTextColor(LCD_COLOR_MAGENTA);
-        lcd.FillRect    (SLIDERX + 1, q.oldpos - BUTTON_RAD, SLIDERW - 2, SLIDERW);
+        lcd.FillRect    (SLIDERX + 1, oldpos - BUTTON_RAD, SLIDERW - 2, SLIDERW);
         lcd.SetTextColor(LCD_COLOR_BLACK);
         lcd.FillRect    (SLIDERX + (SLIDERW / 2) - 3, 6, 7, SLIDERH - 8);
-        q.oldpos = q.position;
+        oldpos = position;
         lcd.SetTextColor(LCD_COLOR_WHITE);
-        lcd.DrawCircle  (CIRC_CTR, q.position, BUTTON_RAD);  //  seel also FillCircle
-        lcd.DrawCircle  (CIRC_CTR, q.position, BUTTON_RAD - 1);
-        switch  (q.state)  {
+        lcd.DrawCircle  (CIRC_CTR, position, BUTTON_RAD);  //  seel also FillCircle
+        lcd.DrawCircle  (CIRC_CTR, position, BUTTON_RAD - 1);
+        switch  (next_state)  {
+            case    RUN_DOWN:
             case    RUN:
                 txt[0] = 'R';
                 colr = LCD_COLOR_GREEN;
                 break;
+            case    INTO_NEUTRAL_DRIFT:
             case    NEUTRAL_DRIFT:
+            case    INTO_RUN:
                 txt[0] = 'N';
                 colr = LCD_COLOR_BLUE;
                 break;
             case    REGEN_BRAKE:
+            case    INTO_REGEN_BRAKE:
                 txt[0] = 'B';
                 colr = LCD_COLOR_ORANGE;
                 break;
-            case    PARK:
-                txt[0] = 'P';
-                colr = LCD_COLOR_RED;
-                break;
             default:
                 txt[0] = 'X';
                 colr = LCD_COLOR_CYAN;
+//                pc.printf   ("State %d\r\n", next_state);
         }   //  End of switch
         lcd.SetTextColor(colr);
-        lcd.FillCircle  (CIRC_CTR, q.position, BUTTON_RAD - 2);
+        lcd.FillCircle  (CIRC_CTR, position, BUTTON_RAD - 2);
         lcd.SetBackColor  (colr);
         lcd.SetTextColor(LCD_COLOR_YELLOW);
-        displaytext(SLIDERX + 17, q.position - 10, 4, txt);   //  largest font
+        displaytext(SLIDERX + 17, position - 10, 4, txt);   //  largest font
         lcd.SetBackColor  (LCD_COLOR_BLACK);
     }           //  End of else
     lcd.SetTextColor (oldtxtcolr);
     lcd.SetBackColor (oldbgcolr);
-//    pc.printf   ("SliderG %d, %d, %d\r\n", q.position, q.oldpos, q.state);
 }
 
 
--- 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 ()
 
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/movingcoilmeter.cpp	Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,252 @@
+#include "mbed.h"
+#include "TS_DISCO_F746NG.h"
+#include "LCD_DISCO_F746NG.h"
+#include "Electric_Loco.h"
+
+extern  LCD_DISCO_F746NG    lcd;
+static const int    char_widths[]   = {5, 7, 11, 14, 17, 17}    ,
+                    meter_radius_min = 30, meter_radius_max = 120;
+extern  void    displaytext    (int x, int y, const int font, char * txt)   ;
+
+void    moving_coil_meter::DrawNeedle  (double alpha, int colour)
+{
+    uint16_t    x1, x2, x3, y1, y2, y3;
+    int save_colour, ssa, sca;
+    while   (alpha > PI) alpha   -= TWO_PI;
+    while   (alpha < 0.0) alpha  += TWO_PI;
+    double  shortln = (needle_len / 18.7),
+            sina    = sin(alpha),
+            cosa    = cos(alpha);
+
+    save_colour = lcd.GetTextColor  ();
+    ssa = (int)(shortln * sina);
+    sca = (int)(shortln * cosa);
+    old_angle = alpha;
+    x1 = cent_x - ssa;//(int)(shortln * sin(alpha));
+    y1 = cent_y - sca;//(int)(shortln * cos(alpha));
+    x2 = cent_x + (int)(needle_len * cosa);
+    y2 = cent_y - (int)(needle_len * sina);  //  - as increasing y is downwards
+    x3 = cent_x + ssa;//(int)(shortln * sin(alpha));
+    y3 = cent_y + sca;//(int)(shortln * cos(alpha));
+    lcd.SetTextColor    (colour);
+    lcd.FillCircle      (cent_x, cent_y, (int)(needle_len / 15.0));
+//    FillTriangle    (x1, x2, x3, y1, y2, y3);
+
+//static void FillTriangle(uint16_t x1, uint16_t x2, uint16_t x3, uint16_t y1, uint16_t y2, uint16_t y3)
+//{
+    int16_t deltax = 0, deltay = 0, x, y, xinc1 = 0, xinc2 = 0,
+            yinc1 = 0, yinc2 = 0, den = 0, num = 0, num_add = 0, num_pixels = 0,
+            curpixel = 0;
+
+    deltax = abs(x2 - x1);        /* The difference between the x's */
+    deltay = abs(y2 - y1);        /* The difference between the y's */
+    x = x1;                       /* Start x off at the first pixel */
+    y = y1;                       /* Start y off at the first pixel */
+
+    if (x2 >= x1) {               /* The x-values are increasing */
+        xinc1 = 1;
+        xinc2 = 1;
+    } else {                      /* The x-values are decreasing */
+        xinc1 = -1;
+        xinc2 = -1;
+    }
+
+    if (y2 >= y1) {               /* The y-values are increasing */
+        yinc1 = 1;
+        yinc2 = 1;
+    } else {                      /* The y-values are decreasing */
+        yinc1 = -1;
+        yinc2 = -1;
+    }
+
+    if (deltax >= deltay) {       /* There is at least one x-value for every y-value */
+        xinc1 = 0;                  /* Don't change the x when numerator >= denominator */
+        yinc2 = 0;                  /* Don't change the y for every iteration */
+        den = deltax;
+        num = deltax / 2;
+        num_add = deltay;
+        num_pixels = deltax;         /* There are more x-values than y-values */
+    } else {                      /* There is at least one y-value for every x-value */
+        xinc2 = 0;                  /* Don't change the x for every iteration */
+        yinc1 = 0;                  /* Don't change the y when numerator >= denominator */
+        den = deltay;
+        num = deltay / 2;
+        num_add = deltax;
+        num_pixels = deltay;         /* There are more y-values than x-values */
+    }
+
+    for (curpixel = 0; curpixel <= num_pixels; curpixel++) {
+        lcd.DrawLine(x, y, x3, y3);
+
+        num += num_add;              /* Increase the numerator by the top of the fraction */
+        if (num >= den) {           /* Check if numerator >= denominator */
+            num -= den;               /* Calculate the new numerator value */
+            x += xinc1;               /* Change the x as appropriate */
+            y += yinc1;               /* Change the y as appropriate */
+        }
+        x += xinc2;                 /* Change the x as appropriate */
+        y += yinc2;                 /* Change the y as appropriate */
+    }
+//}
+    lcd.SetTextColor    (save_colour);
+}
+
+void    moving_coil_meter::LED (int which_led, int colour)  {   //  Meter mounting screw holes serve as LEDs. LCD_COLOR_DARKGRAY for off
+    lcd.SetTextColor    (colour);
+    switch  (which_led) {
+        case    0:  //  NW
+            lcd.FillCircle  (cent_x - screw_hole_offset, cent_y - screw_hole_offset, screw_rad);  //  panel mounting screw holes near corners
+            break;
+        case    1:  //  SW
+            lcd.FillCircle  (cent_x - screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
+            break;
+        case    2:  //  NE
+            lcd.FillCircle  (cent_x + screw_hole_offset, cent_y - screw_hole_offset, screw_rad);
+            break;
+        case    3:  //  SE
+            lcd.FillCircle  (cent_x + screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
+            break;
+        default:
+            break;
+    }
+}
+
+void    moving_coil_meter::redraw ()
+{
+    int oldcolour1 = lcd.GetTextColor   ();
+    int oldcolour2 = lcd.GetBackColor   ();
+    lcd.SetTextColor    (body_colour);
+    //  Draw meter body as solid square with rounded corners, complete with mounting screw holes !
+
+    int font, charwid, x_offset;
+//    int corner_rad          = (meter_radius / 6),
+//        screw_hole_offset   = (meter_radius * 92 / 100),
+//        screw_rad           = (meter_radius / 13);
+
+    lcd.FillRect    (cent_x - meter_radius, cent_y - meter_radius - corner_rad, meter_radius * 2, corner_rad);
+    lcd.FillRect    (cent_x - meter_radius, cent_y + meter_radius, meter_radius * 2, corner_rad + 1);
+    lcd.FillRect    (cent_x - meter_radius - corner_rad, cent_y - meter_radius, 1 +(meter_radius + corner_rad) * 2, meter_radius * 2);
+    lcd.FillCircle  (cent_x - meter_radius, cent_y - meter_radius, corner_rad);  //  meter box has rounded corners
+    lcd.FillCircle  (cent_x - meter_radius, cent_y + meter_radius, corner_rad);
+    lcd.FillCircle  (cent_x + meter_radius, cent_y - meter_radius, corner_rad);
+    lcd.FillCircle  (cent_x + meter_radius, cent_y + meter_radius, corner_rad);     //  done drawing meter body
+/*    lcd.SetTextColor    (LCD_COLOR_DARKGRAY);
+    lcd.FillCircle  (cent_x - screw_hole_offset, cent_y - screw_hole_offset, screw_rad);  //  panel mounting screw holes near corners
+    lcd.FillCircle  (cent_x - screw_hole_offset, cent_y + screw_hole_offset, screw_rad);    //  Jan 2019 now drawn as LEDs below
+    lcd.FillCircle  (cent_x + screw_hole_offset, cent_y - screw_hole_offset, screw_rad);
+    lcd.FillCircle  (cent_x + screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
+*/
+    lcd.SetTextColor    (disc_colour);
+    lcd.FillCircle      (cent_x, cent_y, meter_radius);     //  main meter dial face
+
+    int save_colour = lcd.GetTextColor  ();
+    int radius_inner = (int) meter_radius - 2, radius_outer = (int) (meter_radius * 0.9);
+    double  ang, cosang, sinang, angle_step;
+    lcd.SetTextColor    (scale_colour);
+    angle_step  = (start_angle - end_angle) / scale_ticks;
+    ang = start_angle;
+    for (int i = 0; i <= scale_ticks; i++)   {   //    DrawScaleGraduations
+        cosang  = cos(ang);
+        sinang  = sin(ang);
+        lcd.DrawLine    (cent_x + radius_outer * cosang, cent_y - radius_outer * sinang, cent_x + radius_inner * cosang, cent_y - radius_inner * sinang);
+        ang -= angle_step;
+    }
+
+    font = get_font_size ();
+    charwid = char_widths[font];
+    x_offset = charwid * strlen(unit_txt) / 2;
+    lcd.SetTextColor    (text_colour);
+    lcd.SetBackColor    (disc_colour);
+    displaytext (cent_x - x_offset, cent_y + (meter_radius * 6) / 19, font, unit_txt);
+    lcd.SetBackColor    (oldcolour2);
+    lcd.SetTextColor    (oldcolour1);
+    
+    LED (0, LCD_COLOR_RED);     //  NW
+    LED (1, LCD_COLOR_GREEN);   //  SW
+    LED (2, LCD_COLOR_BLUE);    //  NE
+    LED (3, LCD_COLOR_YELLOW);  //  SE
+}
+
+
+moving_coil_meter::moving_coil_meter   (    int bod_col, int bgcol, int needlecol, int textcol, int scalecol,
+                                            int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang, 
+                                            int scaleticks, char * units, int decimal_places, bool sign)
+{
+    strncpy (unit_txt, units, 8);
+    body_colour = bod_col;
+    disc_colour = bgcol;
+    needle_colour   = needlecol;
+    text_colour = textcol;
+    scale_colour    = scalecol;
+    if  (size < meter_radius_min)
+        size = meter_radius_min;
+    if  (size > meter_radius_max)
+        size = meter_radius_max;
+    meter_radius = size;
+    cent_x = cx;
+    cent_y = cy;
+    start_angle = start_ang;
+    end_angle = end_ang;
+    value_min = lo;
+    value_max = hi;
+    scale_ticks = scaleticks;
+    swept_angle = abs(start_angle - end_angle);
+    value_range = (value_max - value_min);
+    dec_places  = decimal_places;
+    draw_sign   = sign;     //  bool
+    needle_len = (int)(0.87 * (double)meter_radius);
+    corner_rad          = (meter_radius / 6),
+    screw_hole_offset   = (meter_radius * 92 / 100),
+    screw_rad           = (meter_radius / 13);
+}
+
+int     moving_coil_meter::get_font_size ()
+{
+    int font = meter_radius - meter_radius_min;
+    font /= 17;
+    if  (font > 4)
+        font = 4;
+    if  (font < 2)
+        font = 2;
+    return  font;
+}
+
+double  anglefix    (double a)  {       //  Ensures 0.0 <= angle <= + two PI
+    while   (a > PI) a   -= TWO_PI;
+    while   (a < 0.0) a   += TWO_PI;
+    return  a;
+}
+
+double  moving_coil_meter::get_pointer_angle    (double v)
+{
+//    double  a;
+    if   (v < value_min) v = value_min;
+    if   (v > value_max) v = value_max;
+    return   anglefix   (start_angle - ((v - value_min) * swept_angle / value_range))    ;
+}
+
+void    moving_coil_meter::set_value   (double meter_read_value)
+{
+    char    txt[32];
+    int x_offset, font, lenchk;//,
+//    double  pointer_angle;
+    DrawNeedle  (old_angle, disc_colour);                   //  un-draw needle
+    DrawNeedle  (get_pointer_angle   (meter_read_value), needle_colour)  ; //  re-draw needle
+    if  (dec_places == ONE_DP)
+        sprintf (txt, " %+.1f \0", meter_read_value);
+    else
+        sprintf (txt, " %+.0f \0", meter_read_value);
+    lenchk = strlen(txt);
+    if  (!draw_sign)    {
+        for (int i = 1; i < lenchk; i++)
+            txt[i] = txt[i + 1];
+        lenchk--;     //  Stupidly, this gives the display flicker blight
+    }
+//    lenchk = strlen(txt);//  Stupidly, repeating this instead does NOT give the display flicker blight
+    font = get_font_size    ();
+    x_offset = char_widths[font] * lenchk / 2;
+    lcd.SetTextColor    (text_colour);
+    lcd.SetBackColor    (disc_colour);
+    if  (lenchk > 0 && lenchk < 9)
+        displaytext (cent_x - x_offset, cent_y + (meter_radius * 11) / 19, font, txt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/movingcoilmeter.h	Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,56 @@
+#ifndef JON_FREEMAN_MOVING_COIL_METER
+#define JON_FREEMAN_MOVING_COIL_METER
+#include "mbed.h"
+
+/**
+class    moving_coil_meter
+Create moving coil meter graphic using STM32F746G-DISCO.
+Usage example :
+
+top line of constructor :
+moving_coil_meter::moving_coil_meter   (    int bod_col, int bgcol, int needlecol, int textcol, int scalecol,
+                                            int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang, 
+                                            int scaleticks, char * units, int decimal_places, bool sign)
+
+moving_coil_meter   Voltmeter   (   LCD_COLOR_BLACK,    //  Frame / body colour
+                                    LCD_COLOR_WHITE,    //  Dial face colour
+                                    LCD_COLOR_RED,      //  Moving needle colour
+                                    LCD_COLOR_BLUE,     //  Text colour for Units e.g. 'V' and e.g. '32.7'
+                                    LCD_COLOR_MAGENTA,  //  Scale graduations colour
+                                    VOLTMETER_X,        //  X co-ord, centre of meter
+                                    VOLTMETER_Y,        //  Y co-ord, centre of meter
+                                    V_A_SIZE,           //  Meter is square with rounded corners. This is meter dial face radius
+                                    22.0,               //  Scale not limited to e.g. 0 to 10. This is reading at anti-clock limit
+                                    59.0,               //  This is reading at full scale deflection
+                                    1.25 * PI,          //  Angle of needle at anti-clockwise limit
+                                    -0.25 * PI ,        //  Angle of needle at full scale deflection (clockwise max)
+                                    30,                 //  Number of scale graduation marks drwan
+                                    "V",                //  Text for Units, e.g. 'V' or 'MPH'
+                                    ONE_DP,             //  NO_DPS or ONE_DP - supports only no decimal places or one
+                                    false) ;            //  true to show '+' or '-', false to supress sign display
+
+*/
+class    moving_coil_meter
+{
+    int     meter_radius, cent_x, cent_y, needle_len, scale_ticks,
+            disc_colour,    needle_colour,  scale_colour,   text_colour, body_colour, dec_places;
+    int corner_rad,         //          = (meter_radius / 6),
+        screw_hole_offset,  //   = (meter_radius * 92 / 100),
+        screw_rad   ;       //           = (meter_radius / 13);
+    char    unit_txt[8];
+    double  start_angle, end_angle, old_angle, value_min, value_max, swept_angle, value_range;
+    bool    draw_sign;
+
+    void    DrawNeedle          (double alpha, int colour)  ;
+    double  get_pointer_angle   (double value)  ;
+    int     get_font_size       ()  ;
+
+public:
+    moving_coil_meter   () {} ;//  default constructor
+    moving_coil_meter   (int, int, int, int, int, int, int, int, double, double, double, double, int, char *, int, bool) ;
+    void    set_value   (double v)  ;
+    void    redraw  ()  ;
+    void    LED (int, int)  ;
+}   ;
+
+#endif
--- a/qspi_mem.cpp	Sat Jun 23 09:37:41 2018 +0000
+++ b/qspi_mem.cpp	Mon Jan 14 16:39:41 2019 +0000
@@ -1,9 +1,10 @@
 #include "mbed.h"
 #include "Electric_Loco.h"
 
-#ifdef  QSPI    //  Defined in Electric_Loco.h
 #include "QSPI_DISCO_F746NG.h"
 
+extern  error_handling_Jan_2019   Controller_Error    ;
+
 struct  log_element {
     uint32_t    pulsetot;   //  Total distance ever in metres
     uint16_t    powerW;     //  during previous second
@@ -28,29 +29,24 @@
         (pQSPI_Info.ProgPagesNumber    != N25Q128A_SECTOR_SIZE))
     {
       error("Get informations FAILED\r\n");
-      return    false;
+      return    false;  //  Controller_Error.set gets called from main    ;
     }
     else
     {
-        pc.printf("Get informations PASSED\r\n");
+/*        pc.printf("Get N25Q128A QSPI mem informations PASSED\r\n");
         pc.printf   ("FLASH_SIZE\t\t%d\r\n", N25Q128A_FLASH_SIZE);
         pc.printf   ("ERASE_SECTOR_SIZE\t%d\r\n", N25Q128A_SUBSECTOR_SIZE);
         pc.printf   ("PROG_PAGE_SIZE\t\t%d\r\n", N25Q128A_PAGE_SIZE);
         pc.printf   ("Erase sectors number\t%d\r\n", N25Q128A_SUBSECTOR_SIZE);
-        pc.printf   ("N25Q128A_SECTOR_SIZE\t%d\r\n", N25Q128A_SECTOR_SIZE);
+        pc.printf   ("N25Q128A_SECTOR_SIZE\t%d\r\n", N25Q128A_SECTOR_SIZE); */
         return    true;
     }
 }
 
-int ask_QSPI_OK ()  {
-    return  QSPI_OK;
-}
-
-int qspiinit    ()  {
-    int r = qspi.Init();
-    if  (r != QSPI_OK)
-        pc.printf   ("QSPI Init failed.\r\n");
-    return  r;
+bool    test_qspi   ()  {
+    if  ((qspi.Init() == QSPI_OK) && (qspimemcheck    ()))
+        return  true;
+    return  false;
 }
 
 void    show_bank   (uint32_t    bank)   {
@@ -58,6 +54,7 @@
     struct  log_element * p = (log_element *)bu;
     if  (qspi.Read(bu, bank << 12, 4096) != QSPI_OK)  {
         pc.printf   ("Error reading qspi mem in show_bank\r\n");
+        Controller_Error.set    (FAULT_QSPI, 1);
         return  ;
     }
     pc.printf   ("Listing records in bank %d\r\n", bank);
@@ -94,14 +91,15 @@
         bank = START_BANK;
         while   (bank < START_BANK + BANKS_4K && !free_elem_found)    {
             if  (qspi.Read(buff, bank << 12, 4096) != QSPI_OK)  {
-                pc.printf   ("Error reading qspi mem\r\n");
+                Controller_Error.set    (FAULT_QSPI, 1);
+//                pc.printf   ("Error reading qspi mem\r\n");
             }
             for (ptr = 0; !free_elem_found && ptr < 4096; ptr += sizeof(struct log_element)) {
                 free_elem_found = test_element_free  (&buff[ptr]);
                 if  (free_elem_found)   {
                     obank = bank;
                     optr = ptr;
-                    pc.printf   ("Found free element at bank %d, ptr %x\r\n", bank, ptr);
+//                    pc.printf   ("Found free element at bank %d, ptr %x\r\n", bank, ptr);
                 }
                 else    {   //  Not free element found
                     lptr = ptr;
@@ -116,7 +114,7 @@
 //        historic_distance = p->pulsetot;  //  This needs replacing
         total_distance = p->pulsetot;   //  New May 2018, total_distance is metres. Update info arrives in mm.
 
-        pc.printf   ("Constructor found free elem at bank %d, position %d, previous total %ld, volts %.3f\r\n", bank, ptr, p->pulsetot, ((double)p->volts) / 500.0);
+//        pc.printf   ("Constructor found free elem at bank %d, position %d, previous total %ld, volts %.3f\r\n", bank, ptr, p->pulsetot, ((double)p->volts) / 500.0);
     }   //  endof constructor
 }   odometer;
 
@@ -208,4 +206,3 @@
 uint32_t    odometer_out  ()    {
     return  odometer.out();
 }
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/touch_handler.cpp	Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,269 @@
+#include "mbed.h"
+#include "Electric_Loco.h"
+#include "TS_DISCO_F746NG.h"
+#include "LCD_DISCO_F746NG.h"
+
+/**
+void    read_keypresses    (struct ky_bd & a)
+Sets values in struct ky_bd, containing :
+        struct  ky_bd   {   int count,  slider_y; keystr key[MAX_TOUCHES + 1];   bool  sli;   }  ;
+            struct  keystr  {   int keynum; int x;  int y;  }   ;
+    sets a.count to number of fingers found pressing buttons
+    fills a.key[].keynum with list of 'a.count' button return codes
+    fills corresponding a.key[].x and a.key[].y with finger coordinates
+    if button is SLIDER_BUTTON
+        sets a.sli true else sets false
+        sets a.slider_y with new slider y coordinate - 0 at top
+*/
+extern  STM3_ESC_Interface    My_STM3_ESC_boards   ;
+extern  void    throttle    (double p)  ;            // New Apr 2018 ; servo adjusts throttle lever on Honda GX120
+extern  void    read_keypresses    (struct ky_bd & a)   ;
+extern  void    draw_button_hilight     (int but, int colour)   ;
+extern  void    draw_button (struct button_specs & bu, int body_colour) ;
+extern  void    horn    (int which, int onoff)  ;
+
+extern  Serial  pc;
+/**
+int     screen_touch_handler::viscous_drag   (int up_viscosity, int dn_viscosity)    ;
+    Usage
+        position = viscous_drag (a, b);
+    Uses latest slider_y and present 'position'
+    Returns a value for new position moved in direction by amount determined by difference but reduced more for high viscosity
+    This allows for different rates of control movement for rise and fall
+*/
+int     screen_touch_handler::viscous_drag   (int ip, double up_drag, double dn_drag)    {
+    int s = (ip - position);    //  ip is final destination position of slider on scale
+    if  (!s)            //  If no movement
+        return  position;
+    int rv;
+    double  d = (double) s;
+    if  (up_drag < 0.1) up_drag = 0.1;
+    if  (dn_drag < 0.1) dn_drag = 0.1;  //  avoiding DIV0 errors
+    if  (s < 0) {   //  Finger has moved up the slider
+        rv = position - 1 + (int)(d / up_drag); 
+    }
+    if  (s > 0) {   //  Finger has moved down the slider
+        rv = position + 1 + (int)(d / dn_drag);
+    }
+    return  rv;
+}
+
+void    screen_touch_handler::flush   ()    {   //  clear keyboard buffers
+    kybd_a.count    = kybd_b.count  = 0;
+    for (int i = 0; i <= MAX_TOUCHES; i++)  {
+        kybd_a.key[i].keynum    = kybd_b.key[i].keynum = 0;
+    }
+}
+/*
+Dealing with Keys Pressed
+    This time   and Last time   AUTOREPEAT
+    This time   and !Last time  NEW_PRESS
+    !This time  and Last time   NEW_RELEASE
+    !This time  and !Last time  not detectable as key not in either list
+*/
+
+/**
+bool    in_list  (struct ky_bd & a, int key)
+Scans current keyboard buffer searching for 'key'.
+Returns true if found, false otherwise
+*/
+bool    screen_touch_handler::in_list  (struct ky_bd & a, int key)
+{
+    int i = 0;
+    while   (i < a.count)
+        if  (key == a.key[i++].keynum)
+            return  true;
+    return  false;
+}
+
+void    screen_touch_handler::motor_power    ()  {  //  uses position to set motor volts and amps and Honda throttle
+    int b = NEUTRAL_VAL - position;    //  now got integer going positive for increasing power demand
+    double  torque_req = ((double) b) / (double)(NEUTRAL_VAL - MIN_POS);  //  in range 0.0 to 1.0
+    if  (torque_req < 0.0)  torque_req = 0.0;
+    if  (torque_req > 1.0)  torque_req = 1.0;
+//                pc.printf   ("torque_rec = %.3f, last_V = %.3f\r\n", torque_req, last_V);
+    My_STM3_ESC_boards.set_I_limit (torque_req);
+    if  (torque_req < 0.05) {
+        My_STM3_ESC_boards.set_V_limit (My_STM3_ESC_boards.last_V / 2.0);
+        throttle    (torque_req * 6.0);
+    }
+    else    {   //  torque_rec >= 0.05
+        throttle    (0.3 + (torque_req / 2.0));
+        if  (My_STM3_ESC_boards.last_V < 0.99)
+            My_STM3_ESC_boards.set_V_limit (My_STM3_ESC_boards.last_V + 0.05);   //  ramp voltage up rather than slam to max
+    }   //  endof if/else  (torque_req < 0.05) {
+}
+
+
+void    screen_touch_handler::HandleFingerInput   ()    {
+    int key;
+    if  (present_kybd == &kybd_b)    {       //  swap keyboard buffers
+        present_kybd = &kybd_a;
+        previous_kybd = &kybd_b;
+    }   else    {
+        present_kybd = &kybd_b;
+        previous_kybd = &kybd_a;
+    }
+    read_keypresses   (*present_kybd);
+
+//    slider_state_machine    (); //  Takes care of all actions required of slider
+
+//void    screen_touch_handler::slider_state_machine    ()  {
+    int old_position = position;    //  Used at end to decide if screen update required
+    switch  (next_state)    {
+
+        case    NEUTRAL_DRIFT:  //  slider is at 'N', loco not being driven or braked but may well be rolling
+            if  (present_kybd->sli && previous_kybd->sli) {   //  finger is definitely on slider, found this time and last time
+                if  (present_kybd->slider_y < NEUTRAL_VAL)
+                    next_state = INTO_RUN;
+                if  (present_kybd->slider_y > NEUTRAL_VAL)
+                    next_state = INTO_REGEN_BRAKE;
+            }
+            break;        //    endof case    NEUTRAL_DRIFT
+
+        case    INTO_NEUTRAL_DRIFT:
+            My_STM3_ESC_boards.set_I_limit (0.0);  
+            My_STM3_ESC_boards.set_V_limit (0.0);  
+            throttle    (0.0);                      //  Honda revs to tickover
+            position = NEUTRAL_VAL;     //  'position' is copy of slider_y after application of any viscous damping
+            next_state = NEUTRAL_DRIFT;
+            break;
+
+        case    INTO_RUN:   //  cut motor power and set direction of travel
+            if  (present_kybd->sli) {   //  finger is on slider
+                throttle    (0.33);
+                if  (direction) My_STM3_ESC_boards.message  ("fw\r");   //  Forward run command
+                else            My_STM3_ESC_boards.message  ("re\r");   //  Reverse run command
+                next_state  = RUN;
+            }
+            break;        //    endof case    INTO_RUN
+
+        case    INTO_REGEN_BRAKE:   //  cut motor power and set 'rb'
+            My_STM3_ESC_boards.message  ("rb\r");   //  Regen Brake command
+            throttle    (0.0);                      //  Wind Honda engine revs down to tickover
+            next_state  = REGEN_BRAKE;
+            break;        //    endof case    INTO_REGEN_BRAKE
+
+        case    REGEN_BRAKE:    //  established in regenerative braking mode, loco may or may not be rolling
+            if  (present_kybd->sli) {   //  finger is on slider. No action without finger
+                if (position != present_kybd->slider_y)   {   //  update braking effort according to finger movement
+                    position = viscous_drag (present_kybd->slider_y, 5.0, 5.0);
+                    if  (position <= NEUTRAL_VAL)       //  if position risen out of REGEN_BRAKE region
+                        next_state = INTO_NEUTRAL_DRIFT;
+                    else    {       //  position is within REGEN_BRAKE area
+                        double  brake_effort = ((double)(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
+                        My_STM3_ESC_boards.set_V_limit (sqrt(brake_effort));   //  sqrt gives more linear feel to control
+                        My_STM3_ESC_boards.set_I_limit (1.0);
+                    }   //  endof else position is within REGEN_BRAKE area
+                }       //  endof if finger moved
+            }           //  endof finger is on slider
+            break;        //    endof case    REGEN_BRAKE
+
+        case    RUN:            //  remains in this state while finger remains on slider in run range. Drive effort determined by finger position
+            if  (My_STM3_ESC_boards.last_V < 0.99)
+                My_STM3_ESC_boards.set_V_limit (My_STM3_ESC_boards.last_V + 0.05);   //  ramp voltage up rather than slam to max
+            if  (present_kybd->sli) {   //  finger is on slider
+                if (position != present_kybd->slider_y)   {   //  update driving power according to finger movement
+                    position = viscous_drag (present_kybd->slider_y, 45.0, 20.0);
+                    if  (position >= NEUTRAL_VAL)       //  if position falen below RUN region
+                        next_state = INTO_NEUTRAL_DRIFT;
+                    else               //  Finger is still in RUN range
+                        motor_power ();     //  class member already has position, sets volts, amps and throttle
+                }       //  endof update driving power according to finger movement
+            }           //  endof finger is on slider
+            else    {   //  finger not on slider
+                throttle    (0.0);      //  Honda revs down to tickover
+                next_state = RUN_DOWN;
+            }
+            break;        //    endof case    RUN
+
+        case    RUN_DOWN:       //  if finger removed during RUN, slider is to auto-glide back down to 'N'
+            if  (present_kybd->sli)     //  finger is on slider
+                    next_state  = RUN;
+            else    {                   //  finger is not on slider
+                position = viscous_drag (NEUTRAL_VAL, 45.0, 20.0);
+                if  (position >= NEUTRAL_VAL)   
+                    next_state = INTO_NEUTRAL_DRIFT;
+                else
+                    motor_power ();
+            }
+            break;        //    endof RUN_DOWN
+
+    }   //  endof       switch  (next_state)    {
+
+    if  (position != old_position)  {   //  partial screen rewrite is required
+        DrawSlider  ();
+    }
+//}   //  endof   void    slider_state_machine    ()  {
+
+
+    if  (present_kybd->count || previous_kybd->count)   {   //  at least one key pressed this time or last time
+
+        for (int i = 0; i < present_kybd->count; i++)   {   //  PRESENT Do for all keys pressed on this pass
+            key = present_kybd->key[i].keynum;
+            if  (in_list(*previous_kybd, key))   {           //  AUTOREPEAT  This key is being pressed now and last time also
+//                pc.printf   ("Autorep key %d\r\n", key);
+            }           //  endof   AUTOREPEAT  This key is being pressed now and last time also
+            else    {   //  NEW_PRESS key is a new keypress
+//                pc.printf   ("New Press %d\r\n", key);
+                draw_button_hilight     (key, LCD_COLOR_YELLOW)  ;
+                switch  (key)    {    //  Handle new touch screen button presses here - single action per press, not autorepeat
+                    case    SLIDER_BUTTON:  //  All slider action handled in state machine above
+                        break;
+                    case    SPEEDO_BUTTON:  //  No action currently assigned to speedo button
+                        break;
+                    case    VMETER_BUTTON:  //  Turn on high tone horn
+                        horn    (HI_HORN, 1);
+                        break;
+                    case    AMETER_BUTTON:  //  Turn on low tone horn
+                        horn    (LO_HORN, 1);
+                        break;
+                    default:
+                        pc.printf   ("Unhandled keypress %d\r\n", key);
+                        break;
+                }       //  endof   switch  (key) endof Handle new touch screen button presses here - single action per press, not autorepeat
+            }           //  endof   NEW_PRESS key is a new keypress
+        }       //  endof     PRESENT Do for all keys pressed on this pass
+
+        for (int i = 0; i < previous_kybd->count; i++)   {   //  Do for all keys pressed on previous pass
+            key = previous_kybd->key[i].keynum;
+            if  (!in_list(*present_kybd, key))   {           //  NEW_RELEASE
+//                pc.printf   ("New Release %d\r\n", key);
+                draw_button_hilight     (key, LCD_COLOR_DARKBLUE)  ;
+                switch  (key)    {    //  Handle new touch screen button RELEASEs here - single action per press, not autorepeat
+                    case    SLIDER_BUTTON:  //
+                        break;
+                    case    SPEEDO_BUTTON:  //
+                        break;
+                    case    VMETER_BUTTON:  //
+                        horn    (HI_HORN, 0);    //  Turn hi-tone horn off   (voltmeter key)
+                        break;
+                    case    AMETER_BUTTON:  //
+                        horn    (LO_HORN, 0);    //  Turn lo-tone horn off   (powermeter key)
+                        break;
+                    default:
+                        pc.printf   ("Unhandled key release %d\r\n", key);
+                        break;
+                }       //  endof   switch  (button)
+            }                                               //  endof   NEW_RELEASE
+/*            else    {     //  DO NOT NEED SECOND FIND OF AUTOREPEAT
+                pc.printf   ("Autorep2\r\n");
+            }*/
+        }       //  endof     Do for all keys pressed on previous pass
+    }           //  endof   at least one key pressed this time or last time
+    else    {   //  no keys being pressed on this pass or previous pass
+//        pc.printf   ("no key being pressed\r\n");
+    }           //  endof   no keys being pressed on this pass or previous pass
+}       //  endof   void    screen_touch_handler::HandleFingerInput   ()    {
+
+    screen_touch_handler::screen_touch_handler  ()  {   //  default constructor
+        present_kybd    = & kybd_a;
+        previous_kybd   = & kybd_b;
+        oldpos  = 0;
+        position    = MAX_POS - 2;
+        next_state  = INTO_REGEN_BRAKE;
+        flush   ();
+    }
+