User Tools

Site Tools


builds:mcqueen:teensy1detroit
/**
  ESC controller software for PPPRS.  Runs on a teensy 3.1/3.2 at 3.3V and 96MHz

*/

//INCLUDES
//these are standard teensy libraries
#include <ADC.h>
#include <SD.h>
#include <EEPROM.h>
#include <Wire.h>
#include <FreqMeasureMulti.h>
//This is a pre-made library from sparkfun
#include <SparkFunLSM6DS3.h>
//This is a slight modification of Arduino's built-in servo library
#include <PulseServo.h>
//my custom PulseServo library sends out a pulse whenever I write to it, not on a predefined interval like the normal servo library
//it still has a predefined interval, but that is set artificially long so that anytime I write a new value to the servo it immediately sends out a pulse of that length
//removes the variable time delay of not knowing whether you just missed the boat on a servo pulse, I now send them out completely on-demand
//Yay for latency removal!
#define HWSERIAL Serial1

#define DEBUGMODE  false
float debuggedvalue = 0;
#define BLUETOOTH  false

//
//--------------------PIN DEFINITIONS
//
//communications
#define CPIN_SERIALRX     0
#define CPIN_SERIALTX     1
#define CPIN_SPISS        10
#define CPIN_SPIMOSI      11
#define CPIN_SPIMISO      12
#define CPIN_SPISCK       13
#define CPIN_I2CSDA       18
#define CPIN_I2CSCL       19
//digital
#define DPIN_BRAKE        2
#define DPIN_LED          3
#define DPIN_SPEED        5
#define DPIN_ESCOUT       6
#define DPIN_REVERSE      7
#define DPIN_ACC          9
//analog
#define APIN_ISHUNT       A0
#define APIN_IFUSE        A1
#define APIN_PACKV        A2
#define APIN_THROTTLE     A3
#define APIN_STEERING     A6

#define APIN_MOTORTEMP    A7
#define APIN_GEARTEMP     A8
#define APIN_FUSETEMP     A9
#define APIN_COOLANTTEMP  A10
#define APIN_BRAKETEMP    A11


#define APIN_ECUTEMP      38

#define AVAILABLE_1       5
#define AVAILABLE_2       A12 //Available if pin is bridged to digital pin 5
#define AVAILABLE_3       9
#define AVAILABLE_4       A15 //Available if pin is bridged to digital pin 9
#define AVAILABLE_5       A14

//----------------------------LOOP AND SAMPLE TIMING--------------------

#define SERVOMIN              1000              //LOOP AND SAMPLE TIMING  //absolute minimum value writable to the servo
#define SERVOMAX              2000              //LOOP AND SAMPLE TIMING  //absolute maximum value writable to the servo
#define MINPERIOD             2100   //LOOP AND SAMPLE TIMING  //minimum time between sending out pulses.  this enures we do not send out a new pulse before one is finished.
unsigned long prevmicros =    0;                //LOOP AND SAMPLE TIMING  //the last time we SHOULD HAVE completed a loop
unsigned long prevperiod =    0;                //LOOP AND SAMPLE TIMING  //This is the last time we ACTUALLY completed a loop
unsigned long prevmillis =    0;                //millis the last time we completed a loop
unsigned long prevsample =    0;                //the millis duration of the last sampling period.  used to calculate variable I2P


//--------------------------------THROTTLE INPUT AND OUTPUT----------------------------

#define THROTTLEMIN               530                       //THROTTLE INPUT AND OUTPUT  //MAX THROTTLE.  THROTTLE IS REVERSED FOR SAFETY WHEN DISCONNECTED.  Minimum ADC value that will be mapped for throttle purposes
#define THROTTLEMAX               950                      //THROTTLE INPUT AND OUTPUT  //MIN THROTTLE.  THROTTLE IS REVERSED FOR SAFETY WHEN DISCONNECTED.  Maximum ADC value that will be mapped for throttle purposes
unsigned int THROTTLERANGE =     THROTTLEMAX - THROTTLEMIN; //THROTTLE INPUT AND OUTPUT  //the total ADC counts difference in our min and max throttle readings
#define SERVOZERO                 1500                      //THROTTLE INPUT AND OUTPUT  //'Zero' point for servo library.
#define SERVODEAD                 1584                      //THROTTLE INPUT AND OUTPUT  //space between this and the zero band won't be sent because it will cause odd behavior with the ESC
#define LIMPLEVEL                 0.25f                        //THROTTLE INPUT AND OUTPUT  //percentage of full throttle allowed under full limp home mode (low voltage)
#define BRAKEPOWER                SERVOZERO                 //THROTTLE INPUT AND OUTPUT  //amount of negative throttle to apply when we hit the brakes.  right now, it's NOTHING.  Because regen be bad for the fuse!
#define RAMPUPRATE                12                      //THROTTLE INPUT AND OUTPUT  //uS of throttle rampup per 4mS of time.
#define LIMPCUTOFF                200                       //THROTTLE INPUT AND OUTPUT  //if we're below this at idle, we're in maximum limp home mode
#define LIMPHYST                  230                       //THROTTLE INPUT AND OUTPUT  //if we rebound above this, take us out of limp home mode.  anything between these 2 values scales our maximum throttle linearly between normal max and <LIMPLEVEL>
#define DEBOUNCE                  50                        //debounce time for the reverse switch because I'm too lazy to do good wire routing.  also the 'pause' time that ramp up is held at the minimum rate

unsigned long rampinguptime =     0;
unsigned long rampingdowntime =   0;
boolean rampingdown =             false;
boolean rampingup =               false;
boolean needrampup =              false;
boolean reversing =               0;
int traveldirection =             0;
#define GOFORWARD                 1
#define GOREVERSE                 -1
unsigned int limphyst;
unsigned int limpcutoff;
unsigned int limpthrottle;
int throttlepos =               SERVOZERO;    //the throttle we calculate.  Initialize with a throttle output of zero
int refthrottle =               SERVOZERO;
int testramp =                  SERVOZERO;
int lastthrottle =              SERVOZERO;    //the zero point for our servo band

unsigned int rampdownbase =       0; 

boolean limphome =                false;

#define MILLISPERRAMPUP   4
#define RAMPUPTABLESIZE   57   //used for indexing, so the actual number of entries is this plus one
const byte rampuprates_table[] PROGMEM = {1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 36, 39, 42, 45, 48, 51, 54, 58, 62, 66, 70, 75, 80, 85, 90, 95, 101, 107, 113, 120, 127, 134, 142, 150, 159, 168, 178, 188
                                         };

//--------------------------------CURRENT LIMITING----------------

#define TIME1X5                   90000      //CURRENT LIMITING  //time the fuse can tolerate 150% current.  It's a trap, this is less relevant than you think
#define TIME2X                    3000       //CURRENT LIMITING  
#define TIME3X                    300        //CURRENT LIMITING  
#define FULLSCALECURRENT          2860          //CURRENT LIMITING  //current at ADC count 1023, in 100mA increments
#define SHUNTOFFSET               -207
#define FUSEOFFSET                426
#define SHUNTMULTIPLIER           0.1623f
#define FUSEMULTIPLIER            0.1080f
#define SHUNTTHRESHOLD            22            //number of ADC counts to blank once we subtract by our offset.  if we're below this, we call it zero
#define FUSETHRESHOLD             1111          //number of ADC counts to blank once we subtract by our offset.  if we're below this, we call it zero
#define LIPOLIMIT                 295           //CURRENT LIMITING  //WITCHCRAFT! if we boot and our battery voltage is above this limit, assume we're running lipos and we need to reduce our fuse limit to 50A
#define NOVSENSE                  100
#define LIPOFUSE                  500           //CURRENT LIMITING  //this is our base fuse limit on LiPos.
#define LIFEFUSE                  600           //CURRENT LIMITING  //this is our base fuse limit on LiFes
#define REACTIONTIME              25            //the amount of millis we want to look into the future to check for possible overcurrent conditions
#define BUCKET3XSPEED             30            //don't allow access to the bucket 3x limit unless we're going this fast

unsigned int currentbase;                      //CURRENT LIMITING  //Numerical current for nominal limit
long current1x1;                       //CURRENT LIMITING  //is cooling off and we will 'harvest' I2P points back into our buckets to allow us to use higher current modes again.
unsigned int cur1x1display;
#define LIMITING_ON               1             //CURRENT LIMITING  //switch this to 0 in order to turn limiting off

unsigned long current1x5;                       //CURRENT LIMITING  //Numerical current for bucket 1 limit
unsigned int cur1x5display;
long i2p1x5;                           //CURRENT LIMITING  //133000000  //I^2 * t information is accumulated every time we complete a loop (pulse).  each current bucket has its own I2P bucket that can be filled, and this is bucket 1's limit
float i2p1x5divisor;

unsigned long current2x;                        //CURRENT LIMITING  //Numerical current for bucket 2 limit
unsigned int cur2xdisplay;
long i2p2x;                            //CURRENT LIMITING  //18000000   //I^2 * t information is accumulated every time we complete a loop (pulse).  each current bucket has its own I2P bucket that can be filled, and this is bucket 2's limit
float i2p2xdivisor;

unsigned long current3x;                        //CURRENT LIMITING  //Numerical current for bucket 3 limit
unsigned int cur3xdisplay;
long i2p3x;                            //CURRENT LIMITING  //6000000    //I^2 * t information is accumulated every time we complete a loop (pulse).  each current bucket has its own I2P bucket that can be filled, and this is bucket 3's limit
float i2p3xdivisor;

boolean full3x =                  false;        //CURRENT LIMITING  //Is bucket 3 full?
boolean full2x =                  false;        //CURRENT LIMITING  //Is bucket 2 full?
boolean full1x5 =                 false;        //CURRENT LIMITING  //Is bucket 1 full?
boolean empty3x =                 true;         //CURRENT LIMITING  //Is bucket 3 empty?
boolean empty2x =                 true;         //CURRENT LIMITING  //Is bucket 2 empty?
boolean empty1x5 =                true;         //CURRENT LIMITING  //Is bucket 1 empty?

long limit =                      LIPOFUSE;     //CURRENT LIMITING  // our current limit right now
byte limitbucket =                0;            //CURRENT LIMITING  //which bucket are we limiting from?  0 = not limiting.  1, 2, 3 = bucket 1x5, 2x, 3x
byte recoverybucket =             0;            //CURRENT LIMITING  //which bucket are we recovering to?  0 = not recovering.  1, 2, 3 = bucket 1x5, 2x, 3x
unsigned int refcurrent =         0;
long overcurrentratio =           0;
long lastloopcurrent =            0;
//--------------------------------------SPEED AND ODOMETRY------------

#define DISTANCEDIVISOR                 17     //SPEED AND ODOMETRY  //with our 54:10 output gearing, this is how many 1/10 miles we go per revolution of the intermediate shaft
#define DISTANCEMULTIPLIER              53
#define FEETDIVISOR                 5
#define FEETMULTIPLIER              7
#define SPEEDMULTIPLIER                 1491477
#define SPEEDTIMEOUT                    400000   //SPEED AND ODOMETRY  //number of uS to wait for a signal from the motor before declaring us stopped (0.4mph)
#define ODOMETERCHECKADDRESS            0x00
#define ODOMETERADDRESS                 0x02
#define SYSTEMWEIGHT                    120         //weight of the kart + driver, in Kg.
#define METRICSPEEDMULTIPLIER           51
#define METRICSPEEDDIVISOR              8
#define BLOCKINGSWITCHOVER              25          //max speed above which we use our current speed to select our reverse crossover
//^this speed helps us assume we have a working tachometer, so reverse works like it used to if our tach is broken.
#define REVERSECROSSOVER                6           //speed below which we can transfer to reverse from forward (or vice versa)
#define SPEEDPCT1    0.20f        //20%
#define SPEEDLVL1       6        //0.6mph
const unsigned int odometercheck =      0x0A5F;     //0000101001011111
//const unsigned int odometercheck =      0xF5A0;   //1111010110100000
volatile boolean timedout =             false;    //SPEED AND ODOMETRY
volatile boolean triggered =            false;    //SPEED AND ODOMETRY
volatile int numtriggers =     0;        //SPEED AND ODOMETRY
unsigned long motorlife =      0;        //SPEED AND ODOMETRY
unsigned long trignewtime =    0;        //SPEED AND ODOMETRY

boolean speedsensing =                  true;
unsigned int maxthrottle =              SERVOMAX;
boolean atmaxthrottle =                 false;
long maxthrottlestart =                 0;
#define NOSPEEDTIMEOUT                  3000      //number of mS to wait at our 'max' throttle without seeing any speed before we assume the speed sensor's broken and turn it off
unsigned int maxspeed =                 0;
unsigned long RPM =                     0;
unsigned long tripref =                 0;
unsigned long motorspeed =              0;        //SPEED AND ODOMETRY
unsigned long timeouttime =             0;        //SPEED AND ODOMETRY

const int speedpower_table [] PROGMEM = {0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200};

//----------------------------DATALOGGING AND DISPLAY------------
struct Datastruct {
  String dataname;
  String dataunits;
  boolean slowlogging;
  boolean fastlogging;
  boolean averaging;
  byte decimals;
  unsigned int diffthreshold;
  long val;
  long lastval;
  long avgval;
  unsigned int averagecycles;
};

#define LOGGEDVARIABLES        48

Datastruct mydata[LOGGEDVARIABLES + 1];
#define   LOG_TIME             0  //
#define   LOG_THROTIN          1  //
#define   LOG_THROTOUT         2  //
#define   LOG_RAMPRATE         3  //
#define   LOG_CURRENT          4  //
#define   LOG_FILTEREDCURRENT  5  //
#define   LOG_PREDCURRENT      6  //
#define   LOG_CURR2            7  //
#define   LOG_FUSERATIO        8  //
#define   LOG_LIMIT            9  //
#define   LOG_OVERCURRENT     10  //
#define   LOG_CAPACITY        11  //
#define   LOG_VOLTAGE         12  //
#define   LOG_OCV             13  //
#define   LOG_POWER           14  //
#define   LOG_ENERGY          15  //
#define   LOG_POUT            16  //
#define   LOG_EFFICIENCY      17  //
#define   LOG_SPEED           18  //
#define   LOG_AX              19  //
#define   LOG_AY              20  //
#define   LOG_AZ              21  //
#define   LOG_RX              22  //
#define   LOG_RY              23  //
#define   LOG_RZ              24  //
#define   LOG_KE              25  //
#define   LOG_TRIP            26  //
#define   LOG_ODO             27  //
#define   LOG_REVERSE         28  //
#define   LOG_BRAKE           29  //
#define   LOG_STEER           30  //
#define   LOG_FUSETEMP        31  //
#define   LOG_MOTORTEMP       32  //
#define   LOG_COOLANTTEMP     33  //
#define   LOG_GEARTEMP        34  //
#define   LOG_BRAKETEMP       35  //
#define   LOG_ECUTEMP         36  //
#define   LOG_FUSEIMP         37  //
#define   LOG_LIMBIN          38  //
#define   LOG_RECBIN          39  //
#define   LOG_BIN1            40  //
#define   LOG_BIN2            41  //
#define   LOG_BIN3            42  //
#define   LOG_I2P1            43  //
#define   LOG_I2P2            44  //
#define   LOG_I2P3            45  //
#define   LOG_LOOPS           46  //
#define   LOG_CPU             47  //

void initializedata()
{
  //THESE DO NOT NEED TO BE IN ANY SPECIFIC ORDER SINCE THEY GET THEIR INDICES FROM THE ABOVE TABLE
  //Storage Bucket Name         |Column Label        |Units      |Slow |Fast  |Avg  |Dec |Diff |Val|Lastval|Avgval|Avgcycles //
  mydata[LOG_TIME] =            {"TIME",             "s",        true , true , false, 3, 999,  0, 0, 0, 0};
  mydata[LOG_THROTIN] =         {"THROTTLE IN",      "%",        true , false, false, 1, 9,    0, 0, 0, 0};
  mydata[LOG_THROTOUT] =        {"THROTTLE OUT",     "%",        true , true , false, 1, 9,    0, 0, 0, 0};
  mydata[LOG_RAMPRATE] =        {"RAMP RATE",        "us/4ms",   false, false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_CURRENT] =         {"CURRENT",          "A",        false, true , true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_FILTEREDCURRENT] = {"FILTERED CURRENT", "A",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_PREDCURRENT] =     {"20ms PREDICTION",  "A",        false, false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_CURR2] =           {"FUSE HEATING",     "(A^2)*ms", false, false, true,  0, 9999, 0, 0, 0, 0};
  mydata[LOG_FUSERATIO] =       {"FUSE RATIO",       "%",        false, false, true,  1, 99,   0, 0, 0, 0};
  mydata[LOG_LIMIT] =           {"LIMIT",            "A",        true , false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_OVERCURRENT] =     {"OVERCURRENT",      "A",        false, false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_CAPACITY] =        {"CAPACITY",         "Ahr",      true , false, false, 2, 9,    0, 0, 0, 0};
  mydata[LOG_VOLTAGE] =         {"BATTERY",          "V",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_OCV] =             {"OCV",              "V",        false, false, true,  1, 0,    0, 0, 0, 0};
  mydata[LOG_POWER] =           {"BATTERY POWER",    "W",        true , false, true,  0, 99,   0, 0, 0, 0};
  mydata[LOG_ENERGY] =          {"ENERGY",           "Whr",      true , false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_POUT] =            {"POWER OUTPUT",     "W",        true , false, true,  0, 99,   0, 0, 0, 0};
  mydata[LOG_EFFICIENCY] =      {"EFFICIENCY",       "%",        false, false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_SPEED] =           {"SPEED",            "mph",      true , false, true,  1, 0,    0, 0, 0, 0};
  mydata[LOG_AX] =              {"ACCEL-X",          "g",        true , false, true,  2, 9,    0, 0, 0, 0};
  mydata[LOG_AY] =              {"ACCEL-Y",          "g",        true , false, true,  2, 9,    0, 0, 0, 0};
  mydata[LOG_AZ] =              {"ACCEL-Z",          "g",        false, false, true,  2, 9,    0, 0, 0, 0};
  mydata[LOG_RX] =              {"ROTATION-X",       "*/s",      false, false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_RY] =              {"ROTATION-Y",       "*/s",      false, false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_RZ] =              {"ROTATION-Z",       "*/s",      true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_KE] =              {"KINETIC ENERGY",   "J",        false, false, true,  1, 99,   0, 0, 0, 0};
  mydata[LOG_TRIP] =            {"TRIP",             "ft",       true , false, false, 0, 9,    0, 0, 0, 0};
  mydata[LOG_ODO] =             {"ODOMETER",         "mi",       true , false, false, 1, 0,    0, 0, 0, 0};
  mydata[LOG_REVERSE] =         {"GEAR",             " ",        false, false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_BRAKE] =           {"BRAKE",            " ",        true , false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_STEER] =           {"STEERING ANGLE",   "*",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_FUSETEMP] =        {"FUSE TEMP",        "C",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_MOTORTEMP] =       {"MOTOR TEMP",       "C",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_COOLANTTEMP] =     {"COOLANT TEMP",     "C",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_GEARTEMP] =        {"GEARBOX TEMP",     "C",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_BRAKETEMP] =       {"BRAKE TEMP",       "C",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_ECUTEMP] =         {"ECU TEMP",         "C",        true , false, true,  1, 9,    0, 0, 0, 0};
  mydata[LOG_FUSEIMP] =         {"FUSE IMPEDANCE",   "mOhm",     true , true , true,  3, 99,   0, 0, 0, 0};
  mydata[LOG_LIMBIN] =          {"LIMITING BIN",     "A",        false, false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_RECBIN] =          {"RECOVERY BIN",     "A",        true , false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_BIN1] =            {"BIN 1",            "%",        true , false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_BIN2] =            {"BIN 2",            "%",        true , false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_BIN3] =            {"BIN 3",            "%",        true , false, false, 0, 0,    0, 0, 0, 0};
  mydata[LOG_I2P1] =            {"I2P 1",            "(A^2)*ms", false, false, false, 0, 9999, 0, 0, 0, 0};
  mydata[LOG_I2P2] =            {"I2P 2",            "(A^2)*ms", false, false, false, 0, 9999, 0, 0, 0, 0};
  mydata[LOG_I2P3] =            {"I2P 3",            "(A^2)*ms", false, false, false, 0, 9999, 0, 0, 0, 0};
  mydata[LOG_LOOPS] =           {"LOOPS",            " ",        false, false, false, 0, 9999, 0, 0, 0, 0};
  mydata[LOG_CPU] =             {"CPU USAGE",        "%",        false, false, true,  0, 4,    0, 0, 0, 0};
}

const long digitsize_table[] = {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};

const int screwtemp_table[] = { -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999,
                                -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -250, -249, -246, -243, -240, -237, -235, -232, -229, -226, -224, -221, -219, -216, -214, -211, -209, -206, -204, -201, -199, -197, -194, -192, -190, -188, -185, -183, -181, -179, -177, -175, -172, -170, -168, -166, -164, -162,
                                -160, -158, -156, -154, -152, -150, -148, -146, -144, -143, -141, -139, -137, -135, -133, -131, -130, -128, -126, -124, -123, -121, -119, -117, -116, -114, -112, -110, -109, -107, -105, -104, -102, -101, -99, -97, -96, -94, -93, -91, -89, -88, -86, -85, -83, -82, -80, -79, -77, -76, -74, -73, -71, -70, -68,
                                -67, -65, -64, -62, -61, -60, -58, -57, -55, -54, -53, -51, -50, -48, -47, -46, -44, -43, -42, -40, -39, -38, -36, -35, -34, -32, -31, -30, -28, -27, -26, -24, -23, -22, -21, -19, -18, -17, -16, -14, -13, -12, -11, -9, -8, -7, -6, -4, -3, -2, -1, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26,
                                28, 29, 30, 31, 32, 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
                                112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
                                173, 174, 175, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 226, 227, 228, 229, 230,
                                231, 232, 233, 234, 235, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 283, 284, 285, 286, 287, 288,
                                289, 290, 291, 292, 293, 294, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348,
                                349, 350, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411,
                                412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 453, 454, 455, 456, 457, 458, 459, 460, 461, 463, 464, 465, 466, 467, 468, 469, 470, 472, 473, 474, 475, 476, 477, 478, 480, 481,
                                482, 483, 484, 485, 487, 488, 489, 490, 491, 493, 494, 495, 496, 497, 498, 500, 501, 502, 503, 505, 506, 507, 508, 509, 511, 512, 513, 514, 516, 517, 518, 519, 521, 522, 523, 524, 526, 527, 528, 529, 531, 532, 533, 535, 536, 537, 539, 540, 541, 542, 544, 545, 546, 548, 549, 550, 552, 553, 555, 556, 557, 559, 560, 561,
                                563, 564, 565, 567, 568, 570, 571, 572, 574, 575, 577, 578, 580, 581, 582, 584, 585, 587, 588, 590, 591, 593, 594, 596, 597, 599, 600, 602, 603, 605, 606, 608, 609, 611, 612, 614, 616, 617, 619, 620, 622, 623, 625, 627, 628, 630, 631, 633, 635, 636, 638, 640, 641, 643, 645, 646, 648, 650, 652, 653, 655, 657, 658, 660,
                                662, 664, 666, 667, 669, 671, 673, 675, 676, 678, 680, 682, 684, 686, 688, 689, 691, 693, 695, 697, 699, 701, 703, 705, 707, 709, 711, 713, 715, 717, 719, 721, 723, 725, 727, 729, 731, 734, 736, 738, 740, 742, 744, 747, 749, 751, 753, 756, 758, 760, 762, 765, 767, 769, 772, 774, 777, 779, 781, 784, 786, 789, 791, 794,
                                796, 799, 801, 804, 807, 809, 812, 815, 817, 820, 823, 825, 828, 831, 834, 837, 840, 842, 845, 848, 851, 854, 857, 860, 864, 867, 870, 873, 876, 879, 883, 886, 889, 893, 896, 900, 903, 907, 910, 914, 917, 921, 925, 929, 932, 936, 940, 944, 948, 952, 956, 960, 964, 968, 973, 977, 981, 986, 990, 995, 999, 1004, 1009,
                                1013, 1018, 1023, 1028, 1033, 1038, 1044, 1049, 1055, 1060, 1066, 1072, 1077, 1083, 1089, 1095, 1102, 1108, 1114, 1121, 1127, 1134, 1141, 1148, 1156, 1163, 1171, 1179, 1187, 1195, 1203, 1211, 1220, 1229, 1238, 1248, 1258, 1268, 1278, 1289, 1299, 1311, 1323, 1335, 1347, 1360, 1374, 1388, 1402, 1417, 1433,
                                1449, 1467, 1485, 1497, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999
                              };




const int fusetemp_table[] = { -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999,
                               -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -300, -299, -296, -293, -290, -287, -284, -281, -278, -276, -273, -270, -267, -265, -262, -260, -257, -254, -252, -249, -247, -244, -242, -240, -237, -235, -232, -230, -228,
                               -225, -223, -221, -218, -216, -214, -212, -210, -207, -205, -203, -201, -199, -197, -195, -193, -190, -188, -186, -184, -182, -180, -178, -176, -174, -172, -170, -168, -166, -165, -163, -161, -159, -157, -155, -153, -151, -150, -148, -146, -144, -142, -140, -139, -137, -135, -133, -132, -130, -128, -126,
                               -125, -123, -121, -120, -118, -116, -115, -113, -111, -110, -108, -106, -105, -103, -101, -100, -98, -97, -95, -93, -92, -90, -89, -87, -86, -84, -83, -81, -80, -78, -76, -75, -73, -72, -70, -69, -67, -66, -64, -63, -62, -60, -59, -57, -56, -54, -53, -51, -50, -49, -47, -46, -44, -43, -42, -40, -39, -37, -36, -35,
                               -33, -32, -30, -29, -28, -26, -25, -24, -22, -21, -20, -18, -17, -16, -14, -13, -12, -10, -9, -8, -6, -5, -4, -2, -1, 0, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34, 35, 37, 38, 39, 40, 42, 43, 44, 45, 46, 48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 59, 61, 62, 63, 64, 65, 66, 68, 69,
                               70, 71, 72, 73, 75, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, 96, 97, 99, 100, 101, 102, 103, 104, 105, 106, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 142, 143, 144, 145, 146, 147,
                               148, 149, 150, 151, 152, 153, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
                               216, 217, 218, 219, 220, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 278, 279, 280, 281, 282, 283,
                               284, 285, 286, 287, 288, 289, 290, 291, 292, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 345, 346, 347, 348, 349, 350, 351, 352,
                               353, 355, 356, 357, 358, 359, 360, 361, 362, 363, 365, 366, 367, 368, 369, 370, 371, 372, 374, 375, 376, 377, 378, 379, 380, 381, 383, 384, 385, 386, 387, 388, 389, 391, 392, 393, 394, 395, 396, 398, 399, 400, 401, 402, 403, 405, 406, 407, 408, 409, 410, 412, 413, 414, 415, 416, 417, 419, 420, 421, 422, 423, 425, 426,
                               427, 428, 429, 431, 432, 433, 434, 435, 437, 438, 439, 440, 441, 443, 444, 445, 446, 448, 449, 450, 451, 453, 454, 455, 456, 458, 459, 460, 461, 463, 464, 465, 466, 468, 469, 470, 471, 473, 474, 475, 476, 478, 479, 480, 482, 483, 484, 485, 487, 488, 489, 491, 492, 493, 495, 496, 497, 499, 500, 501, 503, 504, 505, 507,
                               508, 509, 511, 512, 513, 515, 516, 517, 519, 520, 521, 523, 524, 526, 527, 528, 530, 531, 533, 534, 535, 537, 538, 540, 541, 542, 544, 545, 547, 548, 550, 551, 552, 554, 555, 557, 558, 560, 561, 563, 564, 566, 567, 569, 570, 572, 573, 575, 576, 578, 579, 581, 582, 584, 585, 587, 588, 590, 592, 593, 595, 596, 598, 599,
                               601, 603, 604, 606, 607, 609, 611, 612, 614, 615, 617, 619, 620, 622, 624, 625, 627, 629, 630, 632, 634, 635, 637, 639, 641, 642, 644, 646, 647, 649, 651, 653, 654, 656, 658, 660, 662, 663, 665, 667, 669, 671, 672, 674, 676, 678, 680, 682, 684, 685, 687, 689, 691, 693, 695, 697, 699, 701, 703, 705, 707, 709, 711, 713,
                               715, 717, 719, 721, 723, 725, 727, 729, 731, 733, 735, 737, 739, 741, 743, 746, 748, 750, 752, 754, 756, 759, 761, 763, 765, 767, 770, 772, 774, 777, 779, 781, 784, 786, 788, 791, 793, 795, 798, 800, 803, 805, 808, 810, 812, 815, 818, 820, 823, 825, 828, 830, 833, 836, 838, 841, 844, 846, 849, 852, 854, 857, 860, 863,
                               866, 869, 871, 874, 877, 880, 883, 886, 889, 892, 895, 898, 900, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999,
                               1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999,
                               1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999
                             };

#define ECUTEMPOFFSET         250
#define ECUADCOFFSET          2463
#define ECUTEMPBIT            -1.7f
#define LOGBUFFERSIZE         2047      //DATALOGGING AND DISPLAY 
#define LOGINTERVAL           100        //DATALOGGING AND DISPLAY  //number of main loops we run through before submitting a datapoint for our log
#define LOGTIMEOUT            1000                                //maximum amount of time between logs, when logs are omitted for space saving.
#define PGOODADJUSTEDLIMIT    160       //DATALOGGING AND DISPLAY  //below this adjusted stack voltage, we should close the file and turn off logging because we've probably been turned off.
#define PGOODLIMIT            90       //DATALOGGING AND DISPLAY  //below this unadjusted voltage, we should close the file and turn off logging because we're probably going to brownout
#define MAXDIGITS             10
#define STEERCENTER           2052
#define STEERRATE             1
#define IMPEDANCECURRENT      250

boolean logging =             true;     //DATALOGGING AND DISPLAY
boolean fastlog =         false;    //DATALOGGING AND DISPLAY
char filebuffer[LOGBUFFERSIZE];         //DATALOGGING AND DISPLAY
char filename[24];                      //DATALOGGING AND DISPLAY
unsigned int bufflength =     0;        //DATALOGGING AND DISPLAY
unsigned long wattpulses =    0;        //DATALOGGING AND DISPLAY
boolean skiplog =             false;    //DATALOGGING AND DISPLAY
unsigned long amppulses =     0;        //DATALOGGING AND DISPLAY  //running counter we use for amphours
unsigned int paranumber =     1;        //DATALOGGING AND DISPLAY  //current parameter we're writing out on the serial port.  right now we have 11, so they each update at ~44Hz
unsigned int loopssincelog =  0;        //number of times we've looped without sending a datalog
#define IMUVARIABLES 7
byte IMUcounter = 0;

//------------------------GENERIC VARIABLES--------------------

#define ADCBITS           10
#define FULLSCALEADC      1023          //CURRENT LIMITING  //why yes, that is a 10-bit ADC in my pocket
#define AVERAGEBITS       3
#define ADCAVERAGING      8
#define CONVERSIONSPEED   ADC_HIGH_SPEED
#define SAMPLINGSPEED     ADC_HIGH_SPEED
#define PERCENT           100               //GENERIC VARIABLES  
#define FULLSCALEVOLTAGE  735               //maximum 100mV increments measurable by our divider.
float pctdivisor =        02.14f;           //GENERIC VARIABLES  //maps our input uS throttle output to a percentage
float thrdivisor =        0.598f;           //GENERIC VARIABLES  //maps our ADC-read throttle by this to get a uS throttle to output
float curdivisor =        1.80f;            //GENERIC VARIABLES  //maps our ADC-read current to 100-mA resolution
float voltdivisor =       0.839f;           //GENERIC VARIABLES  //maps our ADC-read voltage to get 100-mV resolution
float ahrdivisor =        0.000002778f;       //(10/3.600.000)
float whrdivisor =        0.000002778f;      // (10/3.600.000)

#define lifeimpedance     0.034f           //GENERIC VARIABLES  //impedance of the battery and wiring, used for calculating unloaded battery voltage  34mOhm
#define lipoimpedance     0.012f           //GENERIC VARIABLES  //impedance of the battery and wiring, used for calculating unloaded battery voltage  12mOhm.  LiPos are insane!
float battimpedance;
#define lipodivisor       0.83333333f      //GENERIC VARIABLES  //amount to multiply or dvide variables by to switch from LiFE toLiPo
#define safetyfactor      0.40f             //GENERIC VARIABLES  //ratio of theoretical I2t vs used I2t.  because if you fly too close to the sun, you get a blown fuse.
#define lipomultiplier    1.15625f         //GENERIC VARIABLES  //undervoltage ratio of lipos to lifes.  Slightly(slightly) different from the reciprocal of lipodivisor
float limpscale =         0.25f;

//
//INSTANTIATIONS
PulseServo myservo;          //the output is basically just a servo output, specially-controlled
LSM6DS3 myIMU ( I2C_MODE, 0x6B ); //Default constructor is I2C, addr 0x6B
FreqMeasureMulti myspeed;
File f;
ADC *adc = new ADC(); // adc object;


//------------------------ANALOG SAMPLING-------------------------------------
//
//  takes 8 samples of all our analog inputs,
//  and constrains them to our relevant data ranges
//
//------------------------ANALOG SAMPLING-------------------------------------
void sample()
{
  //make sure everything is zeroed out first
  mydata[LOG_CURRENT].val = 0;
  mydata[LOG_FUSERATIO].val = 0;
  for (int i = 0; i < 16; i++)
  {
    mydata[LOG_CURRENT].val += adc->analogRead(APIN_ISHUNT, ADC_0);
    mydata[LOG_FUSERATIO].val += adc->analogRead(APIN_IFUSE, ADC_0);
  }
  mydata[LOG_CURRENT].val += SHUNTOFFSET;
  mydata[LOG_FUSERATIO].val += FUSEOFFSET;

  if (mydata[LOG_CURRENT].val < SHUNTTHRESHOLD)
  {
    mydata[LOG_CURRENT].val = 0;
  }
  if (mydata[LOG_FUSERATIO].val < FUSETHRESHOLD)
  {
    mydata[LOG_FUSERATIO].val = 0;
  }






  mydata[LOG_VOLTAGE].val = adc->analogRead(APIN_PACKV, ADC_0);

  mydata[LOG_THROTIN].val = adc->analogRead (APIN_THROTTLE, ADC_0);
  mydata[LOG_STEER].val = adc->analogRead(APIN_STEERING, ADC_0);

  mydata[LOG_FUSETEMP].val = fusetemp_table[adc->analogRead(APIN_FUSETEMP, ADC_0)];
  mydata[LOG_MOTORTEMP].val = screwtemp_table[adc->analogRead(APIN_MOTORTEMP, ADC_0)];
  mydata[LOG_GEARTEMP].val = screwtemp_table[adc->analogRead(APIN_GEARTEMP, ADC_0)];
  mydata[LOG_BRAKETEMP].val = screwtemp_table[adc->analogRead(APIN_BRAKETEMP, ADC_0)];
  mydata[LOG_COOLANTTEMP].val = screwtemp_table[adc->analogRead(APIN_COOLANTTEMP, ADC_0)];


  mydata[LOG_BRAKE].val = 100 * !digitalRead(DPIN_BRAKE);
  mydata[LOG_REVERSE].val = !digitalRead(DPIN_REVERSE);


  //convert the current ADC readings to a numerical current
  if (((mydata[LOG_CURRENT].val * SHUNTMULTIPLIER) > 10) && ((mydata[LOG_FUSERATIO].val * FUSEMULTIPLIER) > 10))
  {
    mydata[LOG_FUSERATIO].val = (1000 * mydata[LOG_FUSERATIO].val) / mydata[LOG_CURRENT].val;

  }
  else
  {
    mydata[LOG_FUSERATIO].val = 1333;
  }
  mydata[LOG_FUSEIMP].val = 0.750f *  float(mydata[LOG_FUSERATIO].val);

  mydata[LOG_CURRENT].val *= SHUNTMULTIPLIER;                        //get our current flow to Amps range from ADC range

  mydata[LOG_FILTEREDCURRENT].val = ((3 *  mydata[LOG_FILTEREDCURRENT].val + lastloopcurrent) >> 3) +  ( mydata[LOG_CURRENT].val >> 1);
  amppulses += ( mydata[LOG_CURRENT].val * prevsample);                             //increment our capacity counter with our current reading
  mydata[LOG_VOLTAGE].val *= voltdivisor;            //first we need to multiply by our divisor
  mydata[LOG_POWER].val = ( mydata[LOG_CURRENT].val *  mydata[LOG_VOLTAGE].val) / 100;
  wattpulses += ( mydata[LOG_POWER].val * prevsample);                                  //watt-hours calculation thrown in here for shits and giggles

  if (false)//mydata[LOG_VOLTAGE].val < PGOODLIMIT)               //if we're logging and we detect a precipitous drop in voltage, close the file before data corruption occurs and stalls our throttle response for a few seconds.
  {
    mydata[LOG_THROTIN].val = 0;
    EEPROM.put(ODOMETERADDRESS, motorlife);

    if (logging)
    {
      logging = false;
      f.close();
    }
  }

  //this is where the magic happens
  //to get our I2P value, square the difference from our reading with the 1x1 limit
  mydata[LOG_CURR2].val =  mydata[LOG_CURRENT].val - long(current1x1);
  mydata[LOG_CURR2].val *=  mydata[LOG_CURR2].val;
  mydata[LOG_CURR2].val *= prevsample;
  mydata[LOG_CURR2].val /=  100;



  mydata[LOG_OCV].val =  mydata[LOG_VOLTAGE].val + float(battimpedance *  mydata[LOG_CURRENT].val);           //take a 'fake OCV' measurement using the impedance, current, and voltage
  if (false)//mydata[LOG_OCV].val  < limphyst)                                                    //if we're below our limp home threshold at all, do the following
  {
    if ( mydata[LOG_OCV].val < PGOODADJUSTEDLIMIT)
    {
      mydata[LOG_THROTIN].val = 0;
      EEPROM.put(ODOMETERADDRESS, motorlife);

      if (logging)
      {
        logging = false;
        f.close();
      }
    }
    limpthrottle = (constrain( mydata[LOG_OCV].val, limpcutoff, limphyst) - limpcutoff) * limpscale;               //bracket our voltage reading
    limpthrottle += LIMPLEVEL; //then map it to a 100%-<LIMPHYST>% reading based on how far we are to our absolute low cutoff
    limphome = true;                                                                  //make sure evryone knows we're in limp home mode
    //digitalWrite(DPIN_LED, HIGH);                                                       //and turn on the indicator LED

  }
  else
  {
    limphome = false;                                                                 //otherwise, take us out of limp mode
    //digitalWrite(DPIN_LED, LOW);                                                        //and turn off the indicator LED
  }

  //convert the throttle ADC readings to a servo duration
  mydata[LOG_THROTIN].val = mydata[LOG_THROTIN].val - THROTTLEMIN;                //invert the range (so that a disconnected pot = zero throttle)
  mydata[LOG_THROTIN].val = constrain( mydata[LOG_THROTIN].val, 0, THROTTLERANGE); //make sure we stay in bounds
  mydata[LOG_THROTIN].val *= mydata[LOG_THROTIN].val;
  mydata[LOG_THROTIN].val /= THROTTLERANGE;
  if (limphome)
  {
    mydata[LOG_THROTIN].val = ( mydata[LOG_THROTIN].val * limpthrottle); //make sure we stay in bounds
  }

  throttlepos = float( mydata[LOG_THROTIN].val) * thrdivisor;                              //run our divider to get us in the uS range rather than ADC range
  mydata[LOG_THROTIN].val = throttlepos * pctdivisor;                 //get our percent value here in the middle
  throttlepos += SERVODEAD;                               //lastly, add the offset of the zero position
  if (throttlepos == SERVODEAD)
  {
    throttlepos = SERVOZERO;
  }

}

//------------------------CURRENT AND THROTTLE LIMTING-------------------------------------
//
//  uses our current values and the contents of the I2P
//  buckets to determine what our limit should be.  Then
//  we alter the input throttle to hit our current limit.
//
//------------------------CURRENT AND THROTTLE LIMTING-------------------------------------
void control()
{
  //right away, we check whether we're over our 1x1 'forever' limit.  if we're at or below it, we do no limiting
  if ( mydata[LOG_CURRENT].val > long(current1x1))
  {
    //let the serial port know we're not recovering any current
    recoverybucket = 0;
    mydata[LOG_RECBIN].val = 0;
    limiting();
  }

  //if we're below the 1x1 limit, no limiting needs to happen
  else
  {
    analogWrite(DPIN_ACC, 85);
    //let the serial port know we're not limiting
    limitbucket = 0;
    mydata[LOG_LIMBIN].val = 0;
    //if our current is below our forever limit, we can recover I2P points
    if ( mydata[LOG_CURRENT].val < current1x1)
    {
      recover();
    }
    //if we're RIGHT ON our forever limit, no recovery and no limting takes place
    else
    {
      //let the serial port know we're not recovering
      recoverybucket = 0;
      mydata[LOG_RECBIN].val = 0;
    }
  }

  if (full3x || (speedsensing && ( mydata[LOG_SPEED].val < BUCKET3XSPEED)))
  {
    if (full2x)
    {
      if (full1x5)
      {
        limit = current1x1;            //our current limit is the middle
        mydata[LOG_LIMIT].val = cur1x1display;
      }
      else
      {
        limit = current1x5;
        mydata[LOG_LIMIT].val = cur1x5display;
      }
    }
    else
    {
      limit = current2x;
      mydata[LOG_LIMIT].val = cur2xdisplay;
    }
  }
  else
  {
    limit = current3x;
    mydata[LOG_LIMIT].val = cur3xdisplay;
  }

  mydata[LOG_RAMPRATE].val = 0;
  //if we've been bad and our current is too high, limit us
  mydata[LOG_PREDCURRENT].val =  mydata[LOG_CURRENT].val + ((( mydata[LOG_CURRENT].val - lastloopcurrent) * ((REACTIONTIME * 1) >> 0)) / long(prevsample));
  lastloopcurrent =  mydata[LOG_CURRENT].val;
  int looplimit = (limit * 9) >> 3;
  mydata[LOG_PREDCURRENT].val = constrain( mydata[LOG_PREDCURRENT].val, 0, (current3x * 5) >> 2);
  mydata[LOG_OVERCURRENT].val =  mydata[LOG_FILTEREDCURRENT].val - limit;

  if ( mydata[LOG_OVERCURRENT].val > (limit >> 4))
  {
    rampingup = false;
    unsigned long testthrottle = lastthrottle;
    long tempratio;
    if (!rampingdown)
    {
      rampingdown = true;
      rampingdowntime = millis();
      refthrottle = lastthrottle;
      overcurrentratio = (( 5 * ((limit * 17) >> 4) + 11 *  mydata[LOG_FILTEREDCURRENT].val) << 4 ) /  mydata[LOG_FILTEREDCURRENT].val;
      testthrottle = (refthrottle - SERVODEAD) * overcurrentratio;
      testthrottle = (testthrottle >> 8) + SERVODEAD;
    }
    else
    {
      tempratio = (( 5 * ((limit * 17) >> 4) + 11 *  mydata[LOG_FILTEREDCURRENT].val) << 4 ) /  mydata[LOG_FILTEREDCURRENT].val;
      if (millis() - rampingdowntime > REACTIONTIME)
      {
        refthrottle = lastthrottle;
        overcurrentratio = tempratio;
        rampingdowntime = millis();
      }

      if ( tempratio <= overcurrentratio)
      {
        testthrottle = (refthrottle - SERVODEAD) * tempratio;
        testthrottle = (testthrottle >> 8) + SERVODEAD;
      }

    }
    throttlepos = min(throttlepos, testthrottle);
  }
  else
  {
    if ((throttlepos < SERVODEAD) && (!speedsensing || (maxspeed < BLOCKINGSWITCHOVER))) //if our throttle reading is just at the bottom end of the range, set the output signal to zero and remove our forward and reverse blocking.
    {
      traveldirection = 0;
    }

    if (( mydata[LOG_PREDCURRENT].val >= looplimit) || rampingdown)
    {
      rampingdown = false;
      rampinguptime = millis();
      refthrottle = lastthrottle;
      rampingup = true;
    }
    testramp = lastthrottle + ((prevsample * 3 * (rampuprates_table[RAMPUPTABLESIZE] - rampuprates_table[RAMPUPTABLESIZE - 1])) / (2 * MILLISPERRAMPUP));
    if (rampingup)
    {
      unsigned long overlimit = (millis() - rampinguptime) / MILLISPERRAMPUP;
      if (overlimit > RAMPUPTABLESIZE)
      {
        rampingup = false;
      }
      else
      {
        testramp = refthrottle + pgm_read_byte(&(rampuprates_table[overlimit]));
      }
    }
    //if our last throttle reading wasn't zero, let us ramp up a bit
    if (throttlepos > testramp)
    {
      if (lastthrottle < SERVODEAD)
      {
        throttlepos = SERVODEAD;
      }
      else
      {
        throttlepos = testramp;
      }
    }
  }

  //this is set to detect a brake switch closing and choose how to apply our throttle

  if (mydata[LOG_BRAKE].val)
  {
    throttlepos = BRAKEPOWER;    //apply braking power to the RC line
    mydata[LOG_RAMPRATE].val = throttlepos - lastthrottle;
    lastthrottle = SERVOZERO;       //set all our ramp functions back to zero
  }
  else
  {
    //make sure we're only dealing with values within the usable throttle range
    if ((throttlepos > maxthrottle) && !((traveldirection == GOREVERSE) || ( mydata[LOG_REVERSE].val && !traveldirection)))
    {
      if (!atmaxthrottle)
      {
        atmaxthrottle = true;
        maxthrottlestart = millis();
      }
      else
      {
        if (speedsensing && (millis() - maxthrottlestart < NOSPEEDTIMEOUT))
        {
          throttlepos = maxthrottle;
        }
        else
        {
          speedsensing = false;
        }
      }

    }
    else
    {
      atmaxthrottle = false;
      if (throttlepos < SERVODEAD)
      {
        throttlepos = SERVOZERO;
      }
    }
    mydata[LOG_RAMPRATE].val = throttlepos - lastthrottle;

    lastthrottle = throttlepos;       //store this for our next time through the loop to reference
    mydata[LOG_BRAKE].val = 0;                    //otherwise, let the serial port know we're not braking
  }
  //this catches a throttle of technically less than zero (outside SERVODEAD-SERVOMAX range) and tells it to be zero for our display
  if (throttlepos < SERVODEAD)
  {
    mydata[LOG_THROTOUT].val = 0;
    throttlepos = SERVOZERO;
  }
  else
  {
    mydata[LOG_THROTOUT].val = (throttlepos - SERVODEAD) * pctdivisor;  //map our current throttle reading into percent.  takes less time than the map function (mapping takes for-fricken-ever).
  }


  if (mydata[LOG_REVERSE].val)                            //if the driver is holding the reverse button
  {

    if (traveldirection == GOFORWARD)                   //but if we're actually moving forward
    {
      //don't do anything differently.  the reverse button is held but we're moving forward, so our throttle should help us go forward
    }
    else                                              //if we're stopped (ready to reverse) or moving backwards already
    {
      throttlepos = (SERVOZERO << 1) - throttlepos; //we invert our throttle direction
      traveldirection = GOREVERSE;                    //and let our travel direction know we're reversing
    }             //this may get reset to 0 during our calculatevalues phase but eventually we'll latch into reverse
  }
  else                                                //if the reverse button is released
  {
    if (traveldirection == GOREVERSE)                   //if we're actually moving backward
    {
      throttlepos = (SERVOZERO << 1) - throttlepos;   //don't do anything differently.  the reverse button is released but we're moving backward, so our throttle should help us go backward
    }
    else                                              //otherwise, if we're moving forward or stopped,
    {
      traveldirection = GOFORWARD;                      //apply no throttle transform, and let our log know we're moving forward
    }
  }

  myservo.writeMicroseconds(throttlepos);  //finally, write the throttle output

}


//------------------------I2P LIMITING-------------------------------------
//
//  Determines which I2P bucket to limit
//  from (highest bucket first), then manages
//  what their quantity is and whether they
//  report as empty
//
//------------------------I2P LIMITING-------------------------------------
void limiting()
{
  //we can steal from bucket 3 ONLY IF bucket 3 isn't full.
  //additionally, both bucket 1 and 2 must be full for us to use this if our current isn't in bucket 3's range.
  //this allows us to put current of all levels in bucket 3 if need be, since it is the first bucket to recover.
  if ((!full3x) && (( mydata[LOG_CURRENT].val > current2x) || (full1x5 && full2x)))
  {
    limitbucket = 3;              //make sure the serial port know which bucket we're limiting from
    mydata[LOG_LIMBIN].val = cur3xdisplay;
    mydata[LOG_I2P3].val +=  mydata[LOG_CURR2].val;   //add our I2P to bucket 3
    empty3x = false;              //and clarify that it isn't empty
    if ( mydata[LOG_I2P3].val > i2p3x)         //if we've filled the bucket
    {
      full3x = true;              //tell everyone it's full
    }

  }
  else
  {
    //we can steal from bucket 2 ONLY IF bucket 2 isn't full.
    //additionally, bucket 1 must be full for us to use this if our current isn't in bucket 2's range.
    //this allows us to put current of bucket 1's and 2's level here if need be, since bucket 2 recovers capacity before bucket 1.
    if ((!full2x) && (( mydata[LOG_CURRENT].val > current1x5) || full1x5))
    {
      limitbucket = 2;              //make sure the serial port know which bucket we're limiting from
      mydata[LOG_LIMBIN].val = cur2xdisplay;
      mydata[LOG_I2P2].val +=  mydata[LOG_CURR2].val;   //add our I2P to bucket 3
      empty2x = false;              //and clarify that it isn't empty
      if ( mydata[LOG_I2P2].val > i2p2x)         //if we've filled the bucket
      {
        full2x = true;              //tell everyone it's full
      }

    }
    else
    {
      if (!full1x5)
      {
        //our current limit is the low end
        limitbucket = 1;              //make sure the serial port know which bucket we're limiting from
        mydata[LOG_LIMBIN].val = cur1x5display;
        mydata[LOG_I2P1].val +=  mydata[LOG_CURR2].val;  //add our I2P to bucket 3
        empty1x5 = false;             //and clarify that it isn't empty
        if ( mydata[LOG_I2P1].val > i2p1x5)       //if we've filled the bucket
        {
          full1x5 = true;             //tell everyone it's full
        }

      }
      else
      {
        limitbucket = 4;              //make sure the serial port know which bucket we're limiting from
        mydata[LOG_LIMBIN].val = cur1x1display;
      }
    }
  }

}


//------------------------I2P RECOVERY-------------------------------------
//
//  Determines which I2P bucket to recover
//  from (highest bucket first), then manages
//  what their quantity is and whether they
//  report as empty
//
//------------------------I2P RECOVERY-------------------------------------
void recover()                                //here we 'recover' I2P points back into our buckets based on the I2P difference
{
  if (!empty3x)                               //start by trying to empty bucket 3X
  {
    recoverybucket = 3;
    mydata[LOG_RECBIN].val = cur3xdisplay;
    if ( mydata[LOG_I2P3].val >  mydata[LOG_CURR2].val)            //if there's more in the bucket than we have to take out
    {
      mydata[LOG_I2P3].val -=  mydata[LOG_CURR2].val;             //subtract what we have to take out

    }
    else
    {
      mydata[LOG_I2P3].val = 0;                           //if there's more to remove than the bucket has,
      empty3x = true;                         //empty the bucket
    }
    if ( mydata[LOG_I2P3].val < ((i2p3x * 7) >> 3))
    {
      full3x = false;                         //and let the bucket know it's not full any more (but only do this at 25% availability)
    }
  }
  else
  {
    if (!empty2x)                             //next, try to empty bucket 2x
    {
      recoverybucket = 2;
      mydata[LOG_RECBIN].val = cur2xdisplay;
      if ( mydata[LOG_I2P2].val >  mydata[LOG_CURR2].val)          //if there's more in the bucket than we have to take out
      {
        mydata[LOG_I2P2].val -=  mydata[LOG_CURR2].val;           //subtract what we have to take out

      }
      else
      {
        mydata[LOG_I2P2].val = 0;                         //if there's more to remove than the bucket has,
        empty2x = true;                       //empty the bucket
      }
      if ( mydata[LOG_I2P2].val < ((i2p2x * 7) >> 3))
      {
        full2x = false;                       //and let the bucket know it's not full any more
      }
    }
    else
    {
      if (!empty1x5)                          //last, try to empty bucket 1x5
      {
        recoverybucket = 1;
        mydata[LOG_RECBIN].val = cur1x5display;
        if ( mydata[LOG_I2P1].val >  mydata[LOG_CURR2].val)       //if there's more in the bucket than we have to take out
        {
          mydata[LOG_I2P1].val -=  mydata[LOG_CURR2].val;        //subtract what we have to take out

        }
        else
        {
          mydata[LOG_I2P1].val = 0;                      //if there's more to remove than the bucket has,
          empty1x5 = true;                    //empty the bucket
        }
        if ( mydata[LOG_I2P1].val < ((i2p1x5 * 7) >> 3))
        {
          full1x5 = false;                    //and let the bucket know it's not full any more
        }
      }
      else
      {

        recoverybucket = 4;                   //if all buckets are empty, do nothing
        mydata[LOG_RECBIN].val = 0;
      }
    }
  }
}



void calculatevalues()
{

  mydata[LOG_BIN3].val =  mydata[LOG_I2P3].val * i2p3xdivisor;
  mydata[LOG_BIN3].val = constrain ( mydata[LOG_BIN3].val, 0, PERCENT);
  mydata[LOG_BIN2].val =  mydata[LOG_I2P2].val * i2p2xdivisor;
  mydata[LOG_BIN2].val = constrain ( mydata[LOG_BIN2].val, 0, PERCENT);
  mydata[LOG_BIN1].val =  mydata[LOG_I2P1].val * i2p1x5divisor;
  mydata[LOG_BIN1].val = constrain ( mydata[LOG_BIN1].val, 0, PERCENT);
  mydata[LOG_CAPACITY].val = amppulses * ahrdivisor;
  mydata[LOG_ENERGY].val = wattpulses * whrdivisor;
  mydata[LOG_TIME].val = millis();

  switch (IMUcounter) {
    case 0:
      mydata[LOG_ECUTEMP].val = 10.0 * myIMU.readTempC();
      mydata[LOG_ECUTEMP].val += 10.0 * myIMU.readTempC(); //ADDED FOR TIMING
      mydata[LOG_ECUTEMP].val /= 2;
      break;
    case 1:
      mydata[LOG_AX].val = 100.0 * myIMU.readFloatAccelY(); //X and Y axes are switched
      mydata[LOG_RX].val = 10.0 * myIMU.readFloatGyroY();   //X and Y axes are switched
      break;
    case 2:
      mydata[LOG_AY].val = 100.0 * myIMU.readFloatAccelX(); //X and Y axes are switched
      mydata[LOG_RY].val = 10.0 * myIMU.readFloatGyroX();   //X and Y axes are switched
      break;
    case 3:
      mydata[LOG_AZ].val = 100.0 * myIMU.readFloatAccelZ();
      mydata[LOG_RZ].val = 10.0 * myIMU.readFloatGyroZ();
      break;
    default:
      break;
  }
  IMUcounter++;
  if (IMUcounter >= 4)
  {
    IMUcounter = 0;
  }

  if (speedsensing)
  {

    if (myspeed.available())
    {
      if ((micros() - trignewtime) > (2 * SPEEDTIMEOUT))
      {
        mydata[LOG_SPEED].val = 0;
      }
      else
      {
        mydata[LOG_SPEED].val = 3.28f * myspeed.countToFrequency(myspeed.read());

        if ( mydata[LOG_SPEED].val > maxspeed)
        {
          maxspeed =  mydata[LOG_SPEED].val;
        }
      }
      trignewtime = micros();
      motorlife += myspeed.readCounts();
    }
    else
    {
      if ((micros() - trignewtime) > SPEEDTIMEOUT)
      {
        mydata[LOG_SPEED].val = 0;
      }
    }
    if (( mydata[LOG_SPEED].val > SPEEDLVL1) || !speedsensing)
    {
      maxthrottle = SERVOMAX;
    }
    else
    {
      maxthrottle = SERVODEAD + (SPEEDPCT1 * float(SERVOMAX - SERVODEAD));
    }

    if (traveldirection != 0)
    {
      if (maxspeed >= BLOCKINGSWITCHOVER)
      {
        if (abs( mydata[LOG_SPEED].val) <= REVERSECROSSOVER)
        {
          traveldirection = 0;
        }
      }
    }
    if (traveldirection == GOREVERSE && ( mydata[LOG_SPEED].val > 0))
    {
      mydata[LOG_SPEED].val = -mydata[LOG_SPEED].val;
    }
    mydata[LOG_TRIP].val = ((motorlife - tripref)  * FEETMULTIPLIER) >> FEETDIVISOR;

    if (((motorlife * DISTANCEMULTIPLIER) >> DISTANCEDIVISOR) > mydata[LOG_ODO].val)
    {
      EEPROM.put(ODOMETERADDRESS, motorlife);
      if (DEBUGMODE)
      {
        Serial.println("Saving Odometer");
      }
    }
    mydata[LOG_ODO].val = (motorlife * DISTANCEMULTIPLIER) >> DISTANCEDIVISOR;
    /**
      mydata[LOG_KE].val = abs(mydata[LOG_SPEED].val);
      mydata[LOG_KE].val *= mydata[LOG_KE].val;
      mydata[LOG_KE].val *= SYSTEMWEIGHT;
      mydata[LOG_KE].val *= METRICSPEEDMULTIPLIER;
      int numbitshifts = 0;
      if (mydata[LOG_KE].val > 33552896)
      {
      mydata[LOG_KE].val = mydata[LOG_KE].val >> 6;
      numbitshifts += 6;
      }
      mydata[LOG_KE].val *= 51;
      if (mydata[LOG_KE].val > 33552896)
      {
      mydata[LOG_KE].val = mydata[LOG_KE].val >> 6;
      numbitshifts += 6;
      }
      mydata[LOG_KE].val *= 51;
      mydata[LOG_KE].val = mydata[LOG_KE].val >> (19 + METRICSPEEDDIVISOR - numbitshifts);

      mydata[LOG_POUT].val = 1000 * (mydata[LOG_KE].val -  mydata[LOG_KE].lastval);
      mydata[LOG_POUT].val /= (mydata[LOG_TIME].val -  mydata[LOG_TIME].lastval);
    */
  }

}

//Unlike the bluetooth link, we want all our logged data for each data point to represent exactly the same point in time.
//so for logs, we normally skip this and only log if we've gone through <LOGINTERVAL> loops since our last log
//  most of the code is me dynamically removing leading zeros, because it's worth it to waste a couple micros removing data that will save me tens of micros blocking my important loop from running
//if I were really smart, I'd implement this as a generic function where I can feed it the data, the minimum number of digits, the maximum, and how many places it needs after the decimal.
//hey look at that, apparently I'm getting smarter!
//this is now a single function that iterates through all our variables, checks to see if they should be logged, removes leading zeroes, and adds a decimal point where necessary (specified in log tables)

//ARRRGH HERE BE UNCOMMENTED CODE
void bufferData()
{
  if (false) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println(debuggedvalue);
  }
  if ((loopssincelog >= LOGINTERVAL) || fastlog)
  {

    loopssincelog = 0;

    unsigned int digits = 0;
    long tempdiff = 0;
    long tempvall = 0;
    long comparator = 0;
    unsigned int decimals = 0;
    boolean firstvalue = true;
    //digitalWrite(DPIN_LED, HIGH);
    int buffbuff = bufflength;
    for (int i = 0; i < LOGGEDVARIABLES; i++)
    {

      if ((!fastlog && mydata[i].slowlogging) || (fastlog && mydata[i].fastlogging))
      {
        if (firstvalue)
        {
          firstvalue = false;
        }
        else
        {
          filebuffer[bufflength] = ',';
          bufflength++;
        }

        if (mydata[i].val < 0)
        {
          filebuffer[bufflength] = '-';
          mydata[i].val = -mydata[i].val;
          bufflength++;
        }
        tempvall = mydata[i].val;

        if (tempvall !=  mydata[i].lastval)
        {
          int tempint = mydata[i].diffthreshold;
          if (!tempint)
          {
            skiplog = false;
          }
          else
          {
            tempdiff = tempvall -  mydata[i].lastval;
            tempdiff  = abs(tempdiff);
            if (tempdiff > tempint)
            {
              skiplog = false;
            }
          }

        }
        decimals = mydata[i].decimals;
        digits = MAXDIGITS;
        for (int n = digits; n > ((mydata[i].decimals) + 1); n--)
        {
          if (tempvall < digitsize_table[n])
          {
            digits--;
          }
          else
          {
            n = 1; //force us to exit this loop
          }
        }
        for (int n = 1; n <= digits; n++)
        {

          long templong = tempvall;
          for (int p = 10; p > 1; p--)
          {
            if (templong >= digitsize_table[p])
            {
              templong -= digitsize_table[p];
              p++;
            }
          }
          filebuffer[bufflength + digits - n] = '0' + templong;
          tempvall /= 10;
        }

        if (decimals > 0)
        {
          for (int n = decimals; n > 0; n--)
          {
            filebuffer[bufflength + digits + n - decimals] = filebuffer[bufflength + digits + n - (decimals + 1)];
          }
          filebuffer[bufflength + digits - (decimals)] = '.';
          bufflength++;

        }
        bufflength += digits;
      }

    }
    //skiplog = false;
    if (!skiplog)
    {
      for (int i = 0; i < LOGGEDVARIABLES; i++)
      {
        mydata[i].lastval = mydata[i].val;
      }
      filebuffer[bufflength] = '\r';
      bufflength++;
      filebuffer[bufflength] = '\n';
      bufflength++;
    }
    else
    {
      bufflength = buffbuff;
    }
  }
}



void logData()
{
  if (bufflength > 0)                           //if there's a card in and we actually have stuff to log
  {
    if (DEBUGMODE) {
      Serial.write((uint8_t *)filebuffer, bufflength);
    }
    //digitalWrite(DPIN_LED, HIGH);
    f.write((uint8_t *)filebuffer, bufflength);      //write the buffer
    if (mydata[LOG_THROTIN].val)
    {
      f.flush();
    }
    bufflength = 0;                                          //and set its length back to 0
    mydata[LOG_LOOPS].val = 0;
    //digitalWrite(DPIN_LED, HIGH);
  }
}

//writes the first lines of information so we can read our data back in Datplot
void writeHeaders()
{
  bufflength = 0;

  boolean firstvalue = true;

  for (int i = 0; i < LOGGEDVARIABLES; i++)
  {
    if ((!fastlog && mydata[i].slowlogging) || (fastlog && mydata[i].fastlogging))
    {
      if (firstvalue)
      {
        firstvalue = false;
      }
      else
      {
        filebuffer[bufflength] = ',';
        bufflength++;
      }
      for (int n = 0; n < (sizeof(mydata[i].dataname) - 1); n++)
      {
        if ((mydata[i].dataname).charAt(n))
        {
          filebuffer[bufflength] = (mydata[i].dataname).charAt(n);
          bufflength++;
        }
        else
        {
          n = sizeof(mydata[i].dataname);
        }
      }
    }
  }
  filebuffer[bufflength] = '\r';
  bufflength++;
  filebuffer[bufflength] = '\n';
  bufflength++;
  mydata[LOG_THROTIN].val = 0;

  logData();
  firstvalue = true;

  for (int i = 0; i < LOGGEDVARIABLES; i++)
  {
    if ((!fastlog && mydata[i].slowlogging) || (fastlog && mydata[i].fastlogging))
    {
      if (firstvalue)
      {
        firstvalue = false;
      }
      else
      {
        filebuffer[bufflength] = ',';
        bufflength++;
      }
      for (int n = 0; n < (sizeof(mydata[i].dataunits) - 1); n++)
      {
        if ((mydata[i].dataunits).charAt(n))
        {
          filebuffer[bufflength] = (mydata[i].dataunits).charAt(n);
          bufflength++;
        }
        else
        {
          n = sizeof(mydata[i].dataunits);
        }

      }
    }
  }
  filebuffer[bufflength] = '\r';
  bufflength++;
  filebuffer[bufflength] = '\n';
  bufflength++;

  logData();
  f.flush();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////MAIN FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////MAIN FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////MAIN FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////MAIN FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////MAIN FUNCTIONS

void setup()                                             // run once, when the sketch starts
{

  initializedata();

  adc->setAveraging(4); // set number of averages
  adc->setResolution(10); // set bits of resolution
  adc->setConversionSpeed(ADC_HIGH_SPEED); // change the conversion speed
  adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed

  adc->setAveraging(4, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  adc->setReference(ADC_REF_1V2, ADC_1);
  adc->setConversionSpeed(ADC_MED_SPEED, ADC_1); // change the conversion speed
  adc->setSamplingSpeed(ADC_MED_SPEED, ADC_1); // change the sampling speed


  byte outpins[] = {CPIN_SERIALTX, CPIN_SPISS, CPIN_SPIMOSI, CPIN_SPISCK, CPIN_I2CSDA, CPIN_I2CSCL, DPIN_LED, DPIN_ESCOUT, DPIN_ACC};      //all output pins
  for ( int i = 0; i < sizeof(outpins); i++) {
    pinMode(outpins[i], OUTPUT);                             //turn to output
    digitalWrite(outpins[i], LOW);
  }
  digitalWrite(CPIN_SPISS, HIGH);

  //set our inputs
  byte inpins[] = {CPIN_SERIALRX, CPIN_SPIMISO};         //all input pins
  for ( int i = 0; i < sizeof(inpins); i++) {
    pinMode(inpins[i], INPUT);                            //set tham all to input
  }
  byte pulluppins[] = {DPIN_BRAKE, DPIN_SPEED, DPIN_REVERSE};        //all input pins with pullups
  for ( int i = 0; i < sizeof(pulluppins); i++) {
    pinMode(pulluppins[i], INPUT_PULLUP);                            //set tham all to input with pullup
  }

  /**byte apins[12] = {APIN_FUSETEMP, APIN_GEARTEMP, APIN_MOTORTEMP, APIN_COOLANTTEMP, APIN_ISHUNT, APIN_IFUSE, APIN_THROTTLE, APIN_PACKV, APIN_BRAKETEMP, APIN_STEERING, APIN_ECUTEMP}; //all analog input pins
    for ( int i = 0; i < 11; i++) {
    pinMode(apins[i], INPUT);                            //set tham all to input
    }*/


  //Most important thing is making sure the controller is set to zero throttle when it boots.  seriously, there are protections in the ESC but redundancy is always good for safety
  myservo.attach(DPIN_ESCOUT, SERVOMIN, SERVOMAX);
  myservo.writeMicroseconds(SERVOZERO);
  if (DEBUGMODE) {
    Serial.begin(921600);
    while (digitalRead(DPIN_REVERSE))
    {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Waiting for Reverse Press");
      delay(1000);
    }
    while (!digitalRead(DPIN_REVERSE))
    {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Waiting for Reverse Release");
      delay(1000);
    }
    Serial.println("Begin");
  }
  if (BLUETOOTH)
  {
    HWSERIAL.begin(115200);
    HWSERIAL.print('<');
  }

  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("Setting up IMU");
  }
  myIMU.settings.gyroEnabled = 1;  //Can be 0 or 1
  myIMU.settings.gyroRange = 2000;   //Max deg/s.  Can be: 125, 245, 500, 1000, 2000
  myIMU.settings.gyroSampleRate = 833;   //Hz.  Can be: 13, 26, 52, 104, 208, 416, 833, 1666
  myIMU.settings.gyroBandWidth = 200;  //Hz.  Can be: 50, 100, 200, 400;
  myIMU.settings.gyroFifoEnabled = 0;  //Set to include gyro in FIFO
  myIMU.settings.gyroFifoDecimation = 1;  //set 1 for on /1

  myIMU.settings.accelEnabled = 1;
  myIMU.settings.accelRange = 16;      //Max G force readable.  Can be: 2, 4, 8, 16
  myIMU.settings.accelSampleRate = 833;  //Hz.  Can be: 13, 26, 52, 104, 208, 416, 833, 1666, 3332, 6664, 13330
  myIMU.settings.accelBandWidth = 200;  //Hz.  Can be: 50, 100, 200, 400;
  myIMU.settings.accelFifoEnabled = 0;  //Set to include accelerometer in the FIFO
  myIMU.settings.accelFifoDecimation = 1;  //set 1 for on /1
  myIMU.settings.tempEnabled = 1;

  //Non-basic mode settings
  myIMU.settings.commMode = 1;

  //FIFO control settings
  myIMU.settings.fifoThreshold = 100;  //Can be 0 to 4096 (16 bit bytes)
  myIMU.settings.fifoSampleRate = 50;  //Hz.  Can be: 10, 25, 50, 100, 200, 400, 800, 1600, 3300, 6600
  myIMU.settings.fifoModeWord = 0;  //FIFO mode.
  //FIFO mode.  Can be:
  //  0 (Bypass mode, FIFO off)
  //  1 (Stop when full)
  //  3 (Continuous during trigger)
  //  4 (Bypass until trigger)
  //  6 (Continous mode)
  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("IMU Settings Complete");
  }
  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("IMU Booting");
  }
  myIMU.begin();
  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("IMU Boot Complete");
  }
  //update our divisors

  pctdivisor = 1000.0f / (SERVOMAX - SERVODEAD);
  thrdivisor = float(SERVOMAX - SERVODEAD) / float(THROTTLERANGE);
  curdivisor = float(FULLSCALECURRENT) / FULLSCALEADC;
  voltdivisor = float(FULLSCALEVOLTAGE) / FULLSCALEADC;





  //take a battery reading and determine whether we should be obeying 50A fuse limits or 60A limits
  //we need some delay time here since detection is . . . iffy
  delay(500);
  mydata[LOG_VOLTAGE].val = 0;

  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("Determining Fuse Limit");
  }
  for (int i = 0; i < 8; i++)
  {
    mydata[LOG_VOLTAGE].val += analogRead(APIN_PACKV);
  }
  mydata[LOG_VOLTAGE].val = mydata[LOG_VOLTAGE].val >> 3;
  mydata[LOG_VOLTAGE].val *= voltdivisor;
  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.print("Battery Voltage: ");
    Serial.println(mydata[LOG_VOLTAGE].val, DEC);
  }

  if (mydata[LOG_VOLTAGE].val < LIPOLIMIT)
  {
    if (mydata[LOG_VOLTAGE].val < NOVSENSE)
    {
      if (DEBUGMODE) {
        Serial.print(millis(), DEC);
        Serial.print(" : ");
        Serial.println("Voltage Sense Failure, 50A");
      }
      currentbase = LIPOFUSE;
      battimpedance = 0;
      limphyst = 0;
      limpcutoff = 0;
    }
    else
    {
      if (DEBUGMODE) {
        Serial.print(millis(), DEC);
        Serial.print(" : ");
        Serial.println("LiFe Fuse, 60A");
      }
      currentbase = LIFEFUSE;
      battimpedance = lifeimpedance;
      limphyst = LIMPHYST;
      limpcutoff = LIMPCUTOFF;
    }
  }
  else
  {
    if (DEBUGMODE) {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("LiPo Fuse, 50A");
    }
    currentbase = LIPOFUSE;
    battimpedance = lipoimpedance;
    limphyst = LIMPHYST * lipomultiplier;
    limpcutoff = LIMPCUTOFF * lipomultiplier;
  }

  //This is the really important part, since I tell you how I'm modeling the fuse
  //now the fun begins.  we have base values for our limiting info in our variable declarations at the top, but these DO need to change based on which battery pack I run so I need to calculate them all here anyway
  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("Setting Fuse Modeling Parameters");
  }
  //our 110% current is exactly 110% of our base current.  duh.  For reasons currently unknown, I have decided to calculate this my multiplying by 11 then dividing by 10.  same for the others.  since we're just booting up and our ESC takes longer to boot up, we don't care too much about time
  current1x1 = currentbase * 11;
  current1x1 = current1x1 / 10;
  cur1x1display = current1x1 / 10;


  //now I'm only going to explain this for the 150% bucket because the others are calculated the same
  //our 150% current is . . . 150% of our base current.  shocker.
  current1x5 = currentbase * 15;
  current1x5 = current1x5 / 10;
  cur1x5display = current1x5 / 10;
  //the amount of I2P we can put into a bucket is the fuse blow time at that limit (divided by our period time) times the difference between the current AT that level and the current at the forever level, squared.
  i2p1x5 = float(safetyfactor * float(TIME1X5)) * (sq(current1x5 - current1x1) / 100);

  //look up I2t heating of resistors.  also the fuse datasheet is helpful here, and littelfuse has a separate app note on fuse heating that may cause me to throw this entire code out. and go with something more math-based.

  current2x = currentbase * 2;
  cur2xdisplay = current2x / 10;
  i2p2x = float(safetyfactor * float(TIME2X)) * (sq(current2x - current1x1) / 100);


  current3x = currentbase * 3;
  cur3xdisplay = current3x / 10;
  i2p3x = float(safetyfactor * float(TIME3X)) * (sq(current3x - current1x1) / 100);


  //and of course, since the fuse heating of one bucket contributes also to the fuse heating of another bucket, we need to resize our buckets based on how much the fuse could have heated up before you got into your current bucket
  //this all works out in the end since a lower current can use a higher bucket if it's the only thing left.
  i2p1x5 -= i2p2x;
  i2p2x -= i2p3x;

  i2p1x5divisor = float(PERCENT) / float(i2p1x5);
  i2p2xdivisor = float(PERCENT) / float(i2p2x);
  i2p3xdivisor = float(PERCENT) / float(i2p3x);

  limpscale = (float(1) - LIMPLEVEL) / float(limphyst - limpcutoff);

  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("Fuse parameters set");
  }

  if (!digitalRead(DPIN_REVERSE))
  {
    //if we boot up with reverse held down, we log fewer variables, but more often
    fastlog = true;
    if (DEBUGMODE) {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Fast Logging Mode");
    }
  }
  else
  {
    fastlog = false;
    if (DEBUGMODE) {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Slow Logging Mode");
    }
  }


  //assume that we shouldnt be logging, unless we can open a file
  logging = false;
  if (SD.begin(CPIN_SPISS))
  {
    strcpy(filename, "PRSLOG00.csv");                      //basic file name template to look for
    for (filename[6] = '0'; filename[6] <= '9'; filename[6]++)
    { //change the tens and ones to look for a new file name
      for (filename[7] = '0'; filename[7] <= '9'; filename[7]++)
      { //hopefully we can find one where a file doesn;t already exist
        if (!SD.exists(filename))                                            //if we do . . .
        {
          f = SD.open(filename, FILE_WRITE);                                  //make it our new file
          if (f)
          {
            logging = true;
            if (DEBUGMODE) {
              Serial.print(millis(), DEC);
              Serial.print(" : ");
              Serial.print("Filename: ");
              Serial.write(filename, 12);
              Serial.println("");
            }

            //save these bytes because we're going to force them to '9' to kick us out of this loop
            filename[6] = '9';                                           //we found a name for our new file
            filename[7] = '9';
          }
          else
          {
            if (DEBUGMODE) {
              Serial.print(millis(), DEC);
              Serial.print(" : ");
              Serial.println("File Error");
            }
          }
        }
      }
    }
  }
  else
  {
    if (DEBUGMODE) {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Logging Not Started");
    }
  }
  digitalWrite(DPIN_LED, HIGH);
  delay(50);
  if (logging)
  {
    if (DEBUGMODE) {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Writing Headers");
    }
    writeHeaders();
  }




  //initialize our running amp-pulses tally
  amppulses = 0;
  wattpulses = 0;
  //if the controller boots up and the throttle is held down, wait here until it returns to zero
  if (analogRead(APIN_THROTTLE) > THROTTLEMIN)
  {
    if (DEBUGMODE) {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Waiting for safe throttle");
    }
    digitalWrite(DPIN_LED, HIGH);
    while (analogRead(APIN_THROTTLE) > THROTTLEMIN)
    {

    }
    digitalWrite(DPIN_LED, LOW);
  }


  numtriggers = 0;
  limit = current3x;
  unsigned int tempvali = 0;
  if (logging)
  {
    loopssincelog = LOGINTERVAL;
    skiplog = false;//lets us know that we need to make a log right away
  }
  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("Retrieving lifetime odometer");
  }
  EEPROM.get(ODOMETERCHECKADDRESS, tempvali);
  if (true)
  {
    unsigned long tempodo = 0;
    EEPROM.get(ODOMETERADDRESS, tempodo);
    tripref = tempodo;
    motorlife = tempodo;
  }
  else
  {
    if (DEBUGMODE) {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Invalid Odometer Reading");
    }
    if (DEBUGMODE) {
      Serial.print(millis(), DEC);
      Serial.print(" : ");
      Serial.println("Setting Valid Odometer");
    }
    motorlife = 1150000;  //~46 miles?
    tripref = motorlife;
    unsigned long tempodo = motorlife;
    EEPROM.put(ODOMETERCHECKADDRESS, odometercheck);
    EEPROM.put(ODOMETERADDRESS, tempodo);
  }

  myspeed.begin(5, FREQMEASUREMULTI_INTERLEAVE);
  digitalWrite(DPIN_LED, LOW);
  delay(100);
  if (DEBUGMODE) {
    Serial.print(millis(), DEC);
    Serial.print(" : ");
    Serial.println("GO!");
  }
  if (BLUETOOTH)
  {
    Serial.print('<');
  }
  //digitalWrite(DPIN_LED, LOW);
  //Our loop timing reference starts now.  GO!
  prevmicros = micros();
  prevmillis = millis();
}

void loop() {                                            //main loop
  //take a sample
  sample(); //80uS
  calculatevalues(); //510uS

  control();    //20uS //act on the sample

  if (!skiplog)
  {
    logData();        //20uS
    skiplog = true;
  }
  mydata[LOG_LOOPS].val = prevsample;
  bufferData(); //add to the existing log//120uS


  //pause here to enforce our loop timing
  unsigned long startwait = micros();
  //digitalWrite(DPIN_LED, HIGH);
  //these look like they do conflicting things, but the different ways they're incremented actually let us have a maximum and a minimum loop time
  //if we waste a lot of time somewhere (I'm looking at you, datalog), this lets us 'catch up' on our timing


  while ((micros() - prevperiod) < MINPERIOD) {}
  //digitalWrite(DPIN_LED, LOW);

  prevsample = millis() - prevmillis;
  prevmillis += prevsample;
  loopssincelog += prevsample;

  prevmicros = prevperiod;
  prevperiod = micros();
  mydata[LOG_CPU].val = (PERCENT * (startwait - prevmicros)) / (prevperiod - prevmicros);
  digitalWrite(DPIN_LED, !logging);
}
builds/mcqueen/teensy1detroit.txt · Last modified: 2017/07/31 11:46 by admin