Dual Brushless Motor ESC, 10-62V, up to 50A per motor. Motors ganged or independent, multiple control input methods, cycle-by-cycle current limit, speed mode and torque mode control. Motors tiny to kW. Speed limit and other parameters easily set in firmware. As used in 'The Brushless Brutalist' locomotive - www.jons-workshop.com. See also Model Engineer magazine June-October 2019.

Dependencies:   mbed BufferedSerial Servo PCT2075 FastPWM

Update 17th August 2020 Radio control inputs completed

Revision:
16:d1e4b9ad3b8b
Parent:
14:acaa1add097b
Child:
17:cc9b854295d6
--- a/24LC64_eeprom.cpp	Sat Nov 30 18:40:30 2019 +0000
+++ b/24LC64_eeprom.cpp	Tue Jun 09 09:20:19 2020 +0000
@@ -6,39 +6,50 @@
     //  Code for 24LC64 8k x 8 bit eeprom
     //  Code based on earlier work using memory FM24W256, also at i2c address 0xa0;
 
-const int addr_rd = 0xa1;  //  set bit 0 for read, clear bit 0 for write 24LC64
-const int addr_wr = 0xa0;  //  set bit 0 for read, clear bit 0 for write 24LC64
-const int ACK     = 1;
-
-struct  optpar  {
-    int min, max, def;  //  min, max, default
-    const char * t;     //  description
+const int   addr_rd = 0xa1;  //  set bit 0 for read, clear bit 0 for write 24LC64
+const int   addr_wr = 0xa0;  //  set bit 0 for read, clear bit 0 for write 24LC64
+const int   ACK     = 1;
+/*struct  optpar  {
+    int     min, max, de_fault;  //  min, max, default
+    const char * txt;     //  description
 }   ;
-struct  optpar option_list2[] = {
-    {0, 1, 1, "MotorA direction 0 or 1"},
-    {0, 1, 0, "MotorB direction 0 or 1"},
-    {4, 8, 8, "MotorA poles 4 or 6 or 8"},
-    {4, 8, 8, "MotorB poles 4 or 6 or 8"},
-    {1, 4, 1, "MotorA 0R05 ohm current shunt resistors 1 to 4"},
-    {1, 4, 1, "MotorB 0R05 ohm current shunt resistors 1 to 4"},
-    {0, 1, 0, "Servo1 0 = Not used, 1= Output"},
-    {0, 1, 0, "Servo2 0 = Not used, 1= Output"},
-    {0, 1, 0, "RC Input1 0 = Not used, 1= Output"},
-    {0, 1, 0, "RC Input2 0 = Not used, 1= Output"},
-    {2, 5, 2, "Command source 2= COM2 (Touch Screen), 3= Pot, 4= RC Input1, 5= RC Input2"},
-    {'1', '9', '0', "Alternative ID ascii '1' to '9'"}, //  defaults to '0' before eerom setup for first time
-    {10, 250, 60,   "Top speed MPH * 10 range 1.0 to 25.0"},    //  New Jan 2019 TOP_SPEED
-    {50, 253, 98,   "Wheel diameter mm"},   //  New 01/06/2018
-    {10, 253, 27,   "Motor pinion"},   //  New 01/06/2018
-    {50, 253, 85,   "Wheel gear"},   //  New 01/06/2018
-    {0, 100, 0, "Future 1"},
-    {0, 100, 0, "Future 2"},
-    {0, 100, 0, "Future 3"},
-    {0, 100, 0, "Future 4"},
-    {0, 100, 0, "Future 5"},
+*/
+struct  optpar option_list[] = {
+    {0, 1, 1,     "MotorA direction [0 or 1]"},       //  MOTADIR
+    {0, 1, 0,     "MotorB direction [0 or 1]"},       //  MOTBDIR
+    {4, 8, 8,     "MotorA poles 4 or 6 or 8"},      //  MOTAPOLES
+    {4, 8, 8,     "MotorB poles 4 or 6 or 8"},      //  MOTBPOLES
+    {1, 4, 1,     "MotorA 0R05 shunt Rs 1 to 4"},        //  ISHUNTB
+    {1, 4, 1,     "MotorB 0R05 shunt Rs 1 to 4"},        //  ISHUNTB
+    {10, 50, 20,  "POT_REGBRAKE_RANGE percentage 10 to 50"},     //  POT_REGBRAKE_RANGE
+    {0, 1, 0,     "Servo1 out 0 = Disabled, 1= Output enabled"},        //  SVO1
+    {0, 1, 0,     "Servo2 out 0 = Disabled, 1= Output enabled"},        //  SVO2
+    {0, 2, 0,     "RC Input1 0 = Not used, 1= Uni_dir, 2= Bi_dir"},     //  RCIN1
+    {0, 2, 0,     "RC Input2 0 = Not used, 1= Uni_dir, 2= Bi_dir"},     //  RCIN2
+    {2, 6, 2,     "Command source 2= COM2 (Touch Screen), 3= Pot, 4= RCIn1, 5= RCIn2, 6=RCin_both"},     //  COMM_SRC
+    {'1', '9', '0',     "Alternative ID ascii '1' to '9'"}, //  BOARD_ID    defaults to '0' before eerom setup for first time
+    {10, 250, 60,       "Top speed MPH, range 1.0 to 25.0"},    //  New Jan 2019 TOP_SPEED
+    {50, 253, 98,       "Wheel diameter mm"},   //  New 01/06/2018
+    {10, 253, 27,       "Motor pinion"},   //  New 01/06/2018
+    {50, 253, 85,       "Wheel gear"},   //  New 01/06/2018     ** NOTE this and above two used to calculate RPM to MPH **
+    {0, 255, 12,     "RC in 1 trim"},    //  New Dec 2019 RCI1_TRIM read as 2's complement (-128 to +127), user enters -128 to +127
+    {0, 255, 12,     "RC in 2 trim"},    //  New Dec 2019 RCI2_TRIM read as 2's complement (-128 to +127), user enters -128 to +127
+    {10, 50, 20,     "RCIN_REGBRAKE_RANGE stick range percent 10 to 50"},     //  RCIN_REGBRAKE_RANGE
+    {10, 90, 50,     "RCIN_STICK_ATTACK rate percent 10 to 90"},     //  RCIN_STICK_ATTACK
+    {0, 1, 0,     "RCIN1 direction swap 0 normal, 1 reverse"},     //  RCIN1REVERSE
+    {0, 1, 0,     "RCIN2 direction swap 0 normal, 1 reverse"},     //  RCIN2REVERSE
+    {11, 63, 48,    "Nominal system voltage, used to calculate current scaling"},    //   NOM_SYSTEM_VOLTS
+    {5, 91, 90,    "Brake Effectiveness percentage"},
+    {1, 5, 1,       "Baud rate, [1=9k6, 2=19k2, 3=38k4, 4=78k6, 5=115k2] = "},   //  BAUD 1=9k6, 2=19k2, 3=38k4, 4=78k6, 5=115k2
+    {0, 100, 0,     "Future 11"},
+    {0, 100, 0,     "Future 12"},
+    {0, 100, 0,     "Future 13"},
+    {0, 100, 0,     "Future 14"},
+    {0, 100, 0,     "Future 15"},
+    {0, 100, 0,     "Future 16"},
 }   ;
 
-const   int    numof_eeprom_options2    = sizeof(option_list2) / sizeof (struct optpar);
+const   int    numof_eeprom_options    = sizeof(option_list) / sizeof (struct optpar);
 
 
 
@@ -50,8 +61,9 @@
     uint32_t    i2c_device_count;
     uint32_t    i2c_device_list[12];    //  max 12 i2c devices
     char        settings    [36];
-    bool        rd_24LC64  (int start_addr, char * dest, int length)    ;
-    bool        wr_24LC64  (int start_addr, char * dest, int length)    ;
+    double      rpm2mphdbl, user_brake_rangedbl, Vnomdbl;
+    bool        rd_24LC64  (uint32_t start_addr, char * dest, uint32_t length)    ;
+    bool        wr_24LC64  (uint32_t start_addr, char * dest, uint32_t length)    ;
     bool        set_24LC64_internal_address (int    start_addr) ;
     bool        ack_poll    ()  ;
   public:
@@ -60,13 +72,224 @@
     char        rd  (uint32_t)  ;           //  Read one setup char value from private buffer 'settings'
     bool        wr  (char, uint32_t)  ;     //  Write one setup char value to private buffer 'settings'
     bool        save    ()  ;               //  Write 'settings' buffer to EEPROM
+    bool        set_defaults    ();         //  Put default settings into EEPROM and local buffer
     uint32_t    errs    ()  ;               //  Return errors
+    const char *      t   (uint32_t);
+    int         min (uint32_t)   ;
+    int         max (uint32_t)   ;
+    int         def (uint32_t)   ;
+    bool        in_range   (char val, uint32_t p)  ;
+    void        edit   (double * dbl, uint32_t numof_dbls)    ;
+    double      user_brake_range ()  ;
+    double      top_speed ()  ;
+    double      rpm2mph ()  ;
+    double      rpm2mph (double)  ;
 }   ;
 */
 
+uint32_t    eeprom_settings::baud    ()  {
+    const   uint32_t    brates[] = {0, 9600, 19200, 38400, 76800, 11520};
+    return  brates[settings[BAUD]];
+}
+
+double      eeprom_settings::top_speed ()  {
+    return  top_speeddbl;
+}
+
+double  eeprom_settings::brake_effectiveness    ()  {
+    return  brake_eff;
+}
+
+double  eeprom_settings::user_brake_range    ()  {
+    return  user_brake_rangedbl;
+}
+
+double  eeprom_settings::Vnom    ()  {
+    return  Vnomdbl;
+}
+
+double  eeprom_settings::rpm2mph    ()  {
+    return  rpm2mphdbl;
+}
+
+double  eeprom_settings::rpm2mph    (double rpm)  {
+    return  rpm2mphdbl * rpm;
+}
+
+void    eeprom_settings::edit   (double * dbl, uint32_t numof_dbls)    {
+extern  void    set_RCIN_offsets    ()  ;
+    const char* labs[3] = {"Disabled","Uni_directional","Bi_directional"};
+    char    temps[MAX_CLI_PARAMS+1];  //  max num of parameters entered from pc terminal
+    uint32_t i;
+    double  user_scratch;
+    double  topspeed;   //  New Jan 2019 - set max loco speed
+    pc.printf   ("\r\nus - User Settings data in EEPROM\r\nSyntax 'us' with no parameters lists current state.\r\n");
+    if  (numof_dbls > 0)  {           //  If more than 0 parameters supplied
+        if  (numof_dbls > MAX_CLI_PARAMS)
+            numof_dbls = MAX_CLI_PARAMS;
+        for (i = 0; i < numof_dbls; i++)
+            temps[i] = (char)dbl[i];          //  recast doubles to char
+        while   (MAX_CLI_PARAMS > i)
+            temps[i++] = 0;
+        i = temps[0];
+        switch  (i) {   //  First number read from user response after "mode"
+            case    11:      //  MotorA_dir [0 or 1], MotorB_dir [0 or 1]
+                wr(temps[1], MOTADIR);
+                wr(temps[2], MOTBDIR);
+                break;
+            case    12:      //  MotorA_poles [4,6,8], MotorB_poles [4,6,8]
+                if  (temps[1] == 4 || temps[1] == 6 || temps[1] == 8)
+                    wr(temps[1], MOTAPOLES);
+                if  (temps[2] == 4 || temps[2] == 6 || temps[2] == 8)
+                    wr(temps[2], MOTBPOLES);
+                break;
+            case    13:      //  MotorA_ current sense resistors [1 to 4], MotorB_ current sense resistors [1 to 4]
+                wr(temps[1], ISHUNTA);     //  Corrected since published
+                wr(temps[2], ISHUNTB);
+                break;
+            case    14:
+                wr(temps[1], BRAKE_EFFECTIVENESS);
+                break;
+            case    15:
+                pc.printf   ("POT_REGBRAKE_RANGE value entered = %d\r\n", temps[1]);
+                if  (!in_range(temps[1], POT_REGBRAKE_RANGE))
+                    temps[1] = def(POT_REGBRAKE_RANGE);
+                wr    (temps[1], POT_REGBRAKE_RANGE);
+                break;
+            case    16:      //  2 Servo1 [0 or 1], Servo2 [0 or 1]
+                wr(temps[1], SVO1);
+                wr(temps[2], SVO2);
+                break;
+            case    17:      //  3 RCIn1 [0 or 1], RCIn2 [0 or 1]
+                wr(temps[1], RCIN1);
+                wr(temps[2], RCIN2);
+                break;
+            case    18:
+                wr(temps[1], RCIN1REVERSE);
+                break;
+            case    19:
+                wr(temps[1], RCIN2REVERSE);
+                break;
+/*            case    33:      //  Not shown in menu, just to test stuff searching for strtod bug, to be deleted later
+                pc.printf   ("Testing to fix strtod bug, ");
+                i = 0;
+                while   (numof_dbls--)
+                    pc.printf   ("%.3f, ", dbl[i++]);
+                pc.printf   ("TheEnd\r\n");
+                break;*/
+            case    21:      //  3 RCIn1 trim [-128 to +127]
+            case    22:      //  3 RCIn2 trim [-128 to +127]
+                user_scratch = dbl[1];
+                if  (user_scratch > 127.0)  user_scratch = 127.0;
+                if  (user_scratch < -128.0) user_scratch = -128.0;
+                wr    (((signed char) user_scratch), i == 21 ? RCI1_TRIM : RCI2_TRIM);
+                set_RCIN_offsets    ()  ;
+                break;
+            case    23:     //  RCIN_REGBRAKE_RANGE
+                wr    (temps[1], RCIN_REGBRAKE_RANGE);
+                break;
+            case    24:      //  RCIN_STICK_ATTACK
+                wr    (temps[1], RCIN_STICK_ATTACK);
+                break;
+            case    25:      //  4 Board ID '0' to '9'
+                if  (temps[1] <= 9)    //  pointless to compare unsigned integer with zero
+                    wr('0' | temps[1], BOARD_ID);
+                break;
+            case    26:      //  TOP_SPEED
+                topspeed = dbl[1];
+                if  (topspeed > 25.0)   topspeed = 25.0;
+                if  (topspeed < 1.0)    topspeed = 1.0;
+                wr((char)(topspeed * 10.0), TOP_SPEED);
+                break;
+            case    27:      //  5 Wheel dia mm, Motor pinion teeth, Wheel gear teeth
+                wr(temps[1], WHEELDIA);
+                wr(temps[2], MOTPIN);
+                wr(temps[3], WHEELGEAR);
+                break;
+            case    28:      //    {2, 5, 2, "Command source 2= COM2 (Touch Screen), 3= Pot, 4= RC Input1, 5= RC Input2, 6=RC1+2 Robot"},
+                if  (temps[1] > 1 && temps[1] <= 6)
+                    wr(temps[1], COMM_SRC);
+                break;
+            case    29: //  Nominal System Voltage
+                wr    (temps[1], NOM_SYSTEM_VOLTS);
+                break;
+            case    83: //  set to defaults
+                set_defaults   ();
+                break;
+            case    30: //  BAUD
+                wr  (temps[1], BAUD);
+                break;
+/*            case    9:      //  9 Save settings
+                save   ();
+                pc.printf   ("Saving settings to EEPROM\r\n");
+                break;*/
+            default:
+                pc.printf   ("Not found - user setting %d\r\n", i);
+                i = 0;
+                break;
+        }       //  endof   switch
+        if  (i) {
+            save    ();
+            pc.printf   ("Saving settings to EEPROM\r\n");
+        }
+    }           //  endof   //  If more than 0 parameters supplied
+    else    {   //  command was just "mode" on its own
+        pc.printf   ("No Changes\r\n");
+    }
+    pc.printf   ("us 11\t%s = %d, %s = %d\r\n", t(MOTADIR), settings[MOTADIR], t(MOTBDIR), settings[MOTBDIR]);
+    pc.printf   ("us 12\t%s = %d, %s = %d\r\n", t(MOTAPOLES), settings[MOTAPOLES], t(MOTBPOLES), settings[MOTBPOLES]);
+    pc.printf   ("us 13\tNumof motor current shunt resistors [%d to %d], MotorA = %d, MotorB = %d\r\n", min(ISHUNTA), max(ISHUNTA), settings[ISHUNTA], settings[ISHUNTB]);
+    pc.printf   ("us 14\t%s [min %d, max %d] = %d\r\n", t(BRAKE_EFFECTIVENESS), min(BRAKE_EFFECTIVENESS), max(BRAKE_EFFECTIVENESS), settings[BRAKE_EFFECTIVENESS]);
+    pc.printf   ("us 15\t%s[%d to %d] = %d\r\n", t(POT_REGBRAKE_RANGE), min(POT_REGBRAKE_RANGE), max(POT_REGBRAKE_RANGE), settings[POT_REGBRAKE_RANGE]);
+    pc.printf   ("us 16\tServo1 [0 or 1] = %d %s, Servo2 [0 or 1] = %d %s\r\n", settings[SVO1], settings[SVO1] == 0 ? "Disabled":"Enabled", settings[SVO2], settings[SVO2] == 0 ? "Disabled":"Enabled");
+    pc.printf   ("us 17\tRCIn1 [0 disable, 1 Uni_dir, 2 Bi_dir] = %d, %s\r\n\tRCIn2 [0 disable, 1 Uni_dir, 2 Bi_dir] = %d, %s\r\n", settings[RCIN1], labs[settings[RCIN1]], settings[RCIN2], labs[rd(RCIN2)]);
+    pc.printf   ("us 18\t%s RCIN1 = %d, %s\r\n", t(RCIN1REVERSE), settings[RCIN1REVERSE], settings[RCIN1REVERSE] == 0 ? "NORMAL":"REVERSE");
+    pc.printf   ("us 19\t%s RCIN2 = %d, %s\r\n", t(RCIN2REVERSE), settings[RCIN2REVERSE], settings[RCIN2REVERSE] == 0 ? "NORMAL":"REVERSE");
+    pc.printf   ("us 21\tRCIn1 two's comp trim, [-128 to +127] = %d\r\n", (signed char) settings[RCI1_TRIM]);
+    pc.printf   ("us 22\tRCIn2 two's comp trim, [-128 to +127] = %d\r\n", (signed char) settings[RCI2_TRIM]);
+    pc.printf   ("us 23\tRCIn Regen braking uses this pcntage of movement range, [%d to %d] = %d\r\n",min(RCIN_REGBRAKE_RANGE), max(RCIN_REGBRAKE_RANGE), settings[RCIN_REGBRAKE_RANGE]);
+    pc.printf   ("us 24\tRCIn Stick move Attack rate, [%d to %d] = %d\r\n",min(RCIN_STICK_ATTACK), max(RCIN_STICK_ATTACK), settings[RCIN_STICK_ATTACK]);
+    pc.printf   ("us 25\tBoard ID ['0' to '9'] = '%c'\r\n", settings[BOARD_ID]);
+    pc.printf   ("us 26\t%s = %.1f\r\n", t(TOP_SPEED), double(settings[TOP_SPEED]) / 10.0);
+//                  WHEELDIA, MOTPIN, WHEELGEAR, used in converting RPM to MPH
+    pc.printf   ("us 27\t%s = %d, %s = %d, %s = %d\r\n", t(WHEELDIA), settings[WHEELDIA], t(MOTPIN), settings[MOTPIN], t(WHEELGEAR), settings[WHEELGEAR]);
+    pc.printf   ("us 28\tCommand Src [%d] - 2=COM2 (Touch Screen), 3=Pot, 4=RC In1, 5=RC In2, 6=RC1+2\r\n", settings[COMM_SRC]);
+    pc.printf   ("us 29\t%s = %d\r\n", t(NOM_SYSTEM_VOLTS), settings[NOM_SYSTEM_VOLTS]);
+    pc.printf   ("us 30\t%s %d\r\n", t(BAUD), settings[BAUD]);
+    pc.printf   ("us 83\tSet to defaults\r\n");
+//    pc.printf   ("us 9\tSave settings\r\r\n");
+}
+
+bool    eeprom_settings::in_range   (char val, uint32_t p)  {
+    if  ((val >= option_list[p].min) && (val <= option_list[p].max))
+        return  true;
+    return  false;
+}
+uint32_t    eeprom_settings::min (uint32_t i)    {
+    if  (i >= numof_eeprom_options)
+        i = numof_eeprom_options - 1;
+    return  option_list[i].min;
+}
+uint32_t    eeprom_settings::max (uint32_t i)    {
+    if  (i >= numof_eeprom_options)
+        i = numof_eeprom_options - 1;
+    return  option_list[i].max;
+}
+uint32_t    eeprom_settings::def (uint32_t i)    {
+    if  (i >= numof_eeprom_options)
+        i = numof_eeprom_options - 1;
+    return  option_list[i].de_fault;
+}
+
+const char *  eeprom_settings::t  (uint32_t    i)  {
+    if  (i >= numof_eeprom_options)
+        i = numof_eeprom_options - 1;
+    return  option_list[i].txt;
+}
+
 bool        eeprom_settings::set_defaults    () {         //  Put default settings into EEPROM and local buffer
-    for (int i = 0; i < numof_eeprom_options2; i++)
-        settings[i] = option_list2[i].def;       //  Load defaults and 'Save Settings'
+    for (int i = 0; i < numof_eeprom_options; i++)
+        settings[i] = option_list[i].de_fault;       //  Load defaults and 'Save Settings'
     return  save    ();
 }
 
@@ -88,19 +311,20 @@
     errors = i2c_device_count = 0;
     for (int i = 0; i < 36; i++)
         settings[i] = 0;
-    for (int i = 0; i < 12; i++)
+    for (int i = 0; i < MAX_I2C_DEVICES; i++)
         i2c_device_list[i] = 0;
     i2c.frequency(400000);      //  Speed 400000 max.
-//    int last_found = 0, q;      //  Note address bits 3-1 to match addr pins on device
     int q;
     for (int i = 0; i < 255; i += 2)    {   //  Search for devices at all possible i2c addresses
         i2c.start();
         q = i2c.write(i);   //  may return error code 2 when no start issued
         switch  (q) {
             case    ACK:
-                pc.printf   ("I2C device found at 0x%x\r\n", i);
-//                last_found = i;
                 i2c_device_list[i2c_device_count++] = i;
+                if  (i2c_device_count >= MAX_I2C_DEVICES)   {
+                    i = 300;    //  break out
+                    pc.printf   ("Too many i2c devices %d\r\n", i2c_device_count);
+                }
             case    2:      //  Device not seen at this address
             break;
             default:
@@ -110,7 +334,6 @@
         }
     }
     i2c.stop();
-//    if  (errors || last_found != 0xa0)    {
     if  (errors || !do_we_have_i2c(0xa0))    {
         pc.printf   ("Error - EEPROM not seen %d\r\n", errors);
         errors |= 0xa0;
@@ -120,25 +343,39 @@
         errors = 0;
         if  (!rd_24LC64   (0, settings, 32))
             ESC_Error.set   (FAULT_EEPROM, 2);                      //  Set FAULT_EEPROM bit 1 if 24LC64 problem
-        for (int i = 0; i < numof_eeprom_options2; i++) {
-            if  ((settings[i] < option_list2[i].min) || (settings[i] > option_list2[i].max))  {
-                pc.printf   ("EEROM error with %s\r\n", option_list2[i].t);
+        for (int i = 0; i < numof_eeprom_options; i++) {
+            if  ((settings[i] < option_list[i].min) || (settings[i] > option_list[i].max))  {
+                pc.printf   ("EEROM error with %s\r\n", option_list[i].txt);
+                settings[i] = option_list[i].de_fault;       //  Load default for faulty entry
                 errors++;
             }
         }
     }
     ESC_Error.set   (FAULT_EEPROM, errors);  //  sets nothing if 0
-    if  (errors > 1) {
-        pc.printf   ("Bad settings found at startup. Restoring defaults\r\n");
-        for (int i = 0; i < numof_eeprom_options2; i++)
-            settings[i] = option_list2[i].def;       //  Load defaults and 'Save Settings'
+//    if  (errors > 1) {    ??why > 1 ?
+    if  (errors > 0) {
+//        pc.printf   ("Bad settings found at startup. Restoring defaults\r\n");
+//        for (int i = 0; i < numof_eeprom_options2; i++)
+//            settings[i] = option_list[i].de_fault;       //  Load defaults and 'Save Settings'
         if  (!wr_24LC64  (0, settings, 32))         //  Save settings
-            pc.printf   ("Error saving EEPROM in mode19\r\n");
+            pc.printf   ("Error saving EEPROM in user_settings19\r\n");
     }
-    else    //  0 or 1 error max found
-        pc.printf   ("At startup, settings errors = %d\r\n", errors);
+    rpm2mphdbl = 60.0                                                          //  to Motor Revs per hour;
+              * ((double)settings[MOTPIN] / (double)settings[WHEELGEAR])  //  Wheel revs per hour
+              * PI * ((double)settings[WHEELDIA] / 1000.0)                  //  metres per hour
+              * 39.37 / (1760.0 * 36.0);                                      //  miles per hour
+    update_dbls ();
+//    else    //  0 or 1 error max found
+//        pc.printf   ("At startup, settings errors = %d\r\n", errors);
 }       //  endof constructor
     
+void    eeprom_settings::update_dbls ()  {
+    user_brake_rangedbl = (double)settings[POT_REGBRAKE_RANGE] / 100.0;
+    Vnomdbl = (double)settings[NOM_SYSTEM_VOLTS];
+    brake_eff = (double)settings[BRAKE_EFFECTIVENESS] / 100.0;
+    top_speeddbl = (double)settings[TOP_SPEED] / 10.0;
+}
+
 bool    eeprom_settings::ack_poll    ()  {   //  wait short while for any previous memory operation to complete
     const int poll_tries    = 40;
     int poll_count = 0;
@@ -171,7 +408,7 @@
     return  true;
 }
 
-bool eeprom_settings::rd_24LC64  (int start_addr, char * dest, int length)   {
+bool eeprom_settings::rd_24LC64  (uint32_t start_addr, char * dest, uint32_t length)   {
     int acknak = ACK;
     if(length < 1)
         return false;
@@ -193,7 +430,7 @@
     return  true;
 }
 
-bool    eeprom_settings::wr_24LC64  (int start_addr, char * source, int length)   {
+bool    eeprom_settings::wr_24LC64  (uint32_t start_addr, char * source, uint32_t length)   {
     int err = 0;
     if(length < 1 || length > 32)   {
         pc.printf   ("Length out of range %d in wr_24LC64\r\n", length);
@@ -217,20 +454,33 @@
 
 char    eeprom_settings::rd  (uint32_t i)  {           //  Read one setup char value from private buffer 'settings'
     if  (i > 31)    {
-        pc.printf   ("ERROR Attempt to read setting %d\r\n");
+        pc.printf   ("ERROR Attempt to read setting %d\r\n", i);
         return  0;
     }
     return  settings[i];
 }
 
-bool    eeprom_settings::wr  (char c, uint32_t i)  {           //  Read one setup char value from private buffer 'settings'
-    if  (i > 31)
+/*
+bool    eeprom_settings::in_range   (char val, uint32_t p)  {
+    if  ((val >= option_list[p].min) && (val <= option_list[p].max))
+        return  true;
+    return  false;
+}
+*/
+bool    eeprom_settings::wr  (char val, uint32_t p)  {           //  Write one setup char value to private buffer 'settings'
+    if  (p > 31)
         return  false;
-    settings[i] = c;
-    return  true;
+    if  ((val >= min(p)) && (val <= max(p)))    {
+        settings[p] = val;
+        return  true;
+    }
+    settings[p] = def(p);
+//    pc.printf   ("Wrong in wr, %s\r\n", t(p));
+    return  false;
 }
 
 bool    eeprom_settings::save    ()  {               //  Write 'settings' buffer to EEPROM
+    update_dbls ();
     return  wr_24LC64   (0, settings, 32);
 }