| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390 | //SCULLCOM HOBBY ELECTRONICS//ELECTRONIC DC LOAD PROJECT//Software Version 35 (4x5 Matrix Keypad Version - modified key layout)//14th April 2018#include <SPI.h>                              //include SPI library (Serial Peripheral Interface)#include <Wire.h>                             //include I2C library#include <LiquidCrystal_I2C.h>                // F Malpartida's NewLiquidCrystal library                                              // https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/NewliquidCrystal_1.3.4.zip#include <math.h>                             //#include <Adafruit_MCP4725.h>                 //Adafruit DAC library  https://github.com/adafruit/Adafruit_MCP4725#include <MCP342x.h>                          //Steve Marple library avaiable from    https://github.com/stevemarple/MCP342x#include <MCP79410_Timer.h>                   //Scullcom Hobby Electronics library  http://www.scullcom.com/MCP79410Timer-master.zip#include <EEPROM.h>                           //include EEPROM library used for storing setup data#include <Keypad.h>                           //http://playground.arduino.cc/Code/Keypadconst byte ROWS = 5;                          //five rowsconst byte COLS = 4;                          //four columns//define the symbols on the buttons of the keypadschar hexaKeys[ROWS][COLS] = {  {'<','0','>','F'},  {'7','8','9','E'},  {'4','5','6','D'},  {'1','2','3','C'},  {'A','B','#','*'}};byte rowPins[ROWS] = {9, 10, 11, 12, 14}; //connect to the row pin outs of the keypadbyte colPins[COLS] = {5, 6, 7, 8}; //connect to the column pin outs of the keypad//initialize an instance of class NewKeypadKeypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);char customKey;char decimalPoint;                            //used to test for more than one press of * key (decimal point)Adafruit_MCP4725 dac;                         //constructoruint8_t address = 0x68;                       //0x68 is the default address for the MCP3426 deviceMCP342x adc = MCP342x(address);const byte MCP79410_ADDRESS = 0x6f;           //0x6f is the default address for the MCP79410 Real Time Clock ICMCP79410_Timer timer = MCP79410_Timer(MCP79410_ADDRESS);//Set the pins on the I2C chip used for LCD connections//ADDR,EN,R/W,RS,D4,D5,D6,D7LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7);    //0x27 is the default address of the LCD with I2C bus moduleconst byte pinA = 2;                          //digital pin (also interrupt pin) for the A pin of the Rotary Encoder (changed to digital pin 2)const byte pinB = 4;                          //digital pin for the B pin of the Rotary Encoderconst byte CursorPos = 17;                    //analog pin A3 used as a digital pin to set cursor position (rotary encoder push button)const byte LoadOnOff = 15;                    //analog pin A1 used as a digital pin to set Load ON/OFFconst byte TriggerPulse = 16;                 //analog pin A2 used as a digital pin for trigger pulse input in transient modeconst byte fan = 3;                           //digital pin 3 for fan control output (changed to Digital pin 3)const byte temperature = A6;                  //analog pin used for temperature output from LM35 (was A0 previously but changed)int temp;                                     //float BatteryLife = 0;                        //float BatteryLifePrevious = 0;                //float Seconds = 0;                            //time variable used in Battery Capacity Mode (BC)float SecondsLog = 0;                         //variable used for data logging of the time in secondsfloat BatteryCutoffVolts;                     //used to set battery discharge cut-off voltagefloat MaxBatteryCurrent = 1.0;                //maximum battery current allowed for Battery Capacity Testingint stopSeconds;                              //store for seconds when timer stoppedint CP = 8;                                   //cursor start positionboolean toggle = false;                       //used for toggle of Load On/Off buttonunsigned long controlVoltage = 0;             //used for DAC to control MOSFETlong current = 0;                             //variable used by ADC for measuring the currentlong voltage = 0;                             //variable used by ADC for measuring the voltagefloat reading = 0;                            //variable for Rotary Encoder value divided by 1000float setCurrent = 0;                         //variable used for the set current of the loadfloat setPower = 0;                           //variable used for the set power of the loadfloat setResistance = 0;                      //variable used for the set resistance of the loadfloat setCurrentCalibrationFactor = 0.980;    //calibration adjustment - set as required if needed (was 1.000)float displayCurrentCal = 0.000;              //calibration correction for LCD current display (was 0.040)int Load = 0;                                 //Load On/Off flagfloat setControlCurrent = 0;                  //variable used to set the temporary store for control current requiredint VoltsDecimalPlaces = 3;                   //number of decimal places used for Voltage display on LCDfloat ActualVoltage = 0;                      //variable used for Actual Voltage reading of Loadfloat ActualCurrent = 0;                      //variable used for Actual Current reading of Loadfloat ActualPower = 0;                        //variable used for Actual Power reading of Loadfloat ResistorCutOff = 999;                   //maximum Resistor we want to deal with in softwarefloat BatteryCurrent;                         //float LoadCurrent;                            //int CurrentCutOff = EEPROM.read(0x00);int PowerCutOff = EEPROM.read(0x20);int tempCutOff = EEPROM.read(0x40);int setReading = 0;                           //int ControlVolts = 0;                         //used to set output currentfloat OutputVoltage = 0;                      //String Mode ="  ";                            //used to identify which modeint modeSelected = 0;                         //Mode status flagint lastCount = 50;                           //volatile float encoderPosition = 0;           //volatile unsigned long factor= 0;             //number of steps to jumpvolatile unsigned long encoderMax = 999000;   //sets maximum Rotary Encoder value allowed CAN BE CHANGED AS REQUIRED (was 50000)float LiPoCutOffVoltage = 3.0;                //set cutoff voltage for LiPo batteryfloat LiFeCutOffVoltage = 2.8;                //set cutoff voltage for LiFe batteryfloat NiCdCutOffVoltage = 1.0;                //set cutoff voltage for NiCd batteryfloat ZiZnCutOffVoltage = 1.0;                //set cutoff voltage for ZiZn batteryfloat PbAcCutOffVoltage = 1.75;               //set cutoff voltage for PbAc batteryString BatteryType ="    ";byte exitMode = 0;                           //used to exit battery selection menu and return to CC Modechar numbers[10];                            //keypad number entry - Plenty to store a representation of a floatbyte index = 0;int z = 1;                                   //was previously 0float x = 0;int y = 0;int r = 0;float LowCurrent = 0;               //the low current setting for transcient modefloat HighCurrent = 0;              //the high current setting for transcient modeunsigned long transientPeriod;      //used to store pulse time period in transcient pulse modeunsigned long current_time;         //used to store the current time in microsecondsunsigned long last_time;            //used to store the time of the last transient switch in micro secondsboolean transient_mode_status;      //used to maintain the state of the trascient mode (false = low current, true = high current)float transientList [10][2];        //array to store Transient List dataint total_instructions;             //used in Transient List Modeint current_instruction;            //used in Transient List Mode//--------------------------------Interrupt Routine for Rotary Encoder------------------------void isr(){  static unsigned long lastInterruptTime = 0;  unsigned long interruptTime = millis();  if (interruptTime - lastInterruptTime > 5) {  //    if (digitalRead(pinB) == LOW)    {      encoderPosition = encoderPosition - factor;    }else{      encoderPosition = encoderPosition + factor;    }    encoderPosition = min(encoderMax, max(0, encoderPosition));  // sets maximum range of rotary encoder    lastInterruptTime = interruptTime;  }    }//---------------------------------Initial Set up---------------------------------------void setup() {  Serial.begin(9600);                                      //used for testing only   Wire.begin();                                            //join i2c bus (address optional for master)  Wire.setClock(400000L);                                  //sets bit rate to 400KHz  MCP342x::generalCallReset();                             //Reset devices  delay(1);                                                //MC342x needs 300us to settle, wait 1ms - (may not be required)  pinMode (pinA, INPUT);  pinMode (pinB, INPUT);  pinMode (CursorPos, INPUT_PULLUP);  pinMode (LoadOnOff, INPUT_PULLUP);   pinMode (TriggerPulse, INPUT_PULLUP);  pinMode (fan, OUTPUT);  TCCR2B = (TCCR2B & 0b11111000) | 1;                      //change PWM to above hearing (Kenneth Larvsen recommendation)  pinMode (temperature, INPUT);   analogReference(INTERNAL);                               //use Arduino internal reference for tempurature monitoring  attachInterrupt(digitalPinToInterrupt(pinA), isr, LOW);  dac.begin(0x61);                                         //the DAC I2C address with MCP4725 pin A0 set high  dac.setVoltage(0,false);                                 //reset DAC to zero for no output current set at Switch On  lcd.begin(20, 4);                                        //set up the LCD's number of columns and rows   lcd.setBacklightPin(3,POSITIVE);                         // BL, BL_POL  lcd.setBacklight(HIGH);                                  //set LCD backlight on   lcd.clear();                                             //clear LCD display  lcd.setCursor(6,0);                                      //set LCD cursor to column 0, row 4  lcd.print("SCULLCOM");                                   //print SCULLCOM to display with 5 leading spaces (you can change to your own)  lcd.setCursor(1,1);                                      //set LCD cursor to column 0, row 1 (start of second line)  lcd.print("Hobby Electronics");                          //print Hobby Electronics to display (you can change to your own)  lcd.setCursor(1,2);  lcd.print("DC Electronic Load"); //  lcd.setCursor(0,3);  lcd.print("Ver. 35 (5x4 Keypad)"); //  delay(2000);                                             //3000 mSec delay for intro display  lcd.clear();                                             //clear dislay  setupLimits();  delay(3000);  lcd.clear();  last_time = 0;                                           //set the last_time to 0 at the start (Transicent Mode)  transient_mode_status = false;                           //set the initial transient mode status (false = low, true = high);  setCurrent = LowCurrent;                                 //first set the current to the low current value (Transicent Mode)   lcd.setCursor(8,0);  lcd.print("OFF");                                        //indicate that LOAD is off at start up  Current();                                               //sets initial mode to be CC (Constant Current) at Power Up    customKey = customKeypad.getKey();  }//------------------------------------------Main Program Loop---------------------------------void loop() { if(CurrentCutOff > 5){                                 //Test and go to user set limits if required    userSetUp();     }else{    readKeypadInput();                                     //read Keypad entry    if (digitalRead(LoadOnOff) == LOW) {    LoadSwitch();                                          //Load on/off  }    transient();                                           //test for Transient Mode    lcd.setCursor(18,3);                                   //sets display of Mode indicator at bottom right of LCD  lcd.print(Mode);                                       //display mode selected on LCD (CC, CP, CR or BC)  if(Mode != "TC" && Mode != "TP" && Mode != "TT" && Mode != "TL"){      //if NOT transient mode then Normal Operation    reading = encoderPosition/1000;                        //read input from rotary encoder     maxConstantCurrentSetting();                           //set maxiumum Current allowed in Constant Current Mode (CC)    powerLevelCutOff();                                    //Check if Power Limit has been exceeded      temperatureCutOff();                                   //check if Maximum Temperature is exceeded      batteryCurrentLimitValue();                            //Battery Discharge Constant Current Limit Value in BC Mode    displayEncoderReading();                               //display rotary encoder input reading on LCD    lastCount = encoderPosition;                           //store rotary encoder current position    CursorPosition();                                      //check and change the cursor position if cursor button pressed  }else{    transientLoadToggle();                                  //Start Transient Mode  }      readVoltageCurrent();                                  //routine for ADC's to read actual Voltage and Current  ActualReading();                                       //Display actual Voltage, Current readings and Actual Wattage    dacControl();  dacControlVoltage();                                   //sets the drive voltage to control the MOSFET  batteryCapacity();                                     //test if Battery Capacity (BC) mode is selected - if so action  fanControl();                                          //call heatsink fan control  }}//------------------------------------------------Read Keypad Input-----------------------------------------------------void readKeypadInput (void) {  customKey = customKeypad.getKey();  //  if (customKey != NO_KEY){                             //only used for testing keypad (uncomment if required for testing keypad)// Serial.print("customKey = ");                          //only used for testing keypad (uncomment if required for testing keypad)// Serial.println(customKey);                             //only used for testing keypad (uncomment if required for testing keypad)//  }                                                     //only used for testing keypad (uncomment if required for testing keypad)if(customKey == 'E'){                                     //check for Load on/off  LoadSwitch();}  if(customKey == 'D'){                                   //check if Set-Up Mode Selected   delay(200);  toggle = false;                                         //switch Load OFF  userSetUp();  encoderPosition = 0;                                    //reset encoder reading to zero  index = 0;  z = 1;                                                  //sets column position for LCD displayed character  decimalPoint = (' ');                                   //clear decimal point text character reset}   if(customKey == 'C'){                                   //check if Transient Mode Selected  toggle = false;                                         //switch Load OFF  transientType();   encoderPosition = 0;                                    //reset encoder reading to zero  index = 0;  z = 1;                                                  //sets column position for LCD displayed character  decimalPoint = (' ');                                   //clear decimal point text character reset  }                 if(customKey == 'A'){                                   //check if Constant Current button pressed  toggle = false;                                         //switch Load OFF  lcd.setCursor(8,0);  lcd.print("OFF");  Current();                                              //if selected go to Constant Current Selected routine  encoderPosition = 0;                                    //reset encoder reading to zero  index = 0;  z = 1;                                                  //sets column position for LCD displayed character  decimalPoint = (' ');                                   //clear decimal point test character reset  }           if(customKey == 'B'){                                   //check if Constant Power button pressed  toggle = false;                                         //switch Load OFF  lcd.setCursor(8,0);  lcd.print("OFF");   Power();                                                //if selected go to Constant Power Selected routine  encoderPosition = 0;                                    //reset encoder reading to zero  index = 0;  z = 1;                                                  //sets column position for LCD displayed character  decimalPoint = (' ');                                   //clear decimal point test character reset  }            if(customKey == '#'){                                   //check if Constant Resistance button pressed    toggle = false;                                         //switch Load OFF  lcd.setCursor(8,0);  lcd.print("OFF");    Resistance();                                           //if selected go to Constant Resistance Selected routine  encoderPosition = 0;                                    //reset encoder reading to zero  index = 0;  z = 1;                                                  //sets column position for LCD displayed character  decimalPoint = (' ');                                   //clear decimal point test character reset  }  if(customKey == '*'){                                   //check if Battery Capacity button pressed  dac.setVoltage(0,false);                                //Ensures Load is OFF - sets DAC output voltage to 0  toggle = false;                                         //switch Load OFF  batteryType();                                          //select battery type  index = 0;  z = 1;                                                  //sets column position for LCD displayed character  decimalPoint = (' ');                                   //clear decimal point test character reset    if (exitMode == 1){                                   //if NO battery type selected revert to CC Mode    lcd.setCursor(8,0);    lcd.print("OFF");    Current();                                            //if selected go to Constant Current Selected routine    encoderPosition = 0;                                  //reset encoder reading to zero    customKey = 'A';    }    else    {    lcd.setCursor(16,2);    lcd.print(BatteryType);                               //print battery type on LCD     lcd.setCursor(8,0);    lcd.print("OFF");    timer.reset();                                        //reset timer    BatteryLifePrevious = 0;    CP = 9;                                               //set cursor position    BatteryCapacity();                                    //go to Battery Capacity Routine    }  }if (Mode != "BC"){  if(customKey >= '0' && customKey <= '9'){               //check for keypad number input       numbers[index++] = customKey;       numbers[index] = '\0';       lcd.setCursor(z,3);                                //                          lcd.print(customKey);                              //show number input on LCD       z = z+1;     }    if(customKey == '>'){                                   //check if decimal button key pressed      if (decimalPoint != ('>')){                         //test if decimal point entered twice - if so skip       numbers[index++] = '.';      numbers[index] = '\0';      lcd.setCursor(z,3);      lcd.print(".");      z = z+1;      decimalPoint = ('>');                             //used to indicate decimal point has been input        }      }  if(customKey == '<'){                                 //clear input entry     index = 0;     z = 1;                                             //sets column position for LCD displayed character     lcd.setCursor(z,3);     lcd.print("     ");     numbers[index] = '\0';                             //     decimalPoint = (' ');                              //clear decimal point test character reset    }  if(customKey == 'F') {                                //use entered number    x = atof(numbers);              reading = x;         encoderPosition = reading*1000;         index = 0;         numbers[index] = '\0';         z = 1;                                         //sets column position for LCD displayed character         lcd.setCursor(0,3);         lcd.print("        ");         decimalPoint = (' ');                          //clear decimal point test character reset          }    }  }//----------------------Limit Maximum Current Setting-----------------------------------------void maxConstantCurrentSetting (void) {  if (Mode == "CC" && reading > CurrentCutOff){           //Limit maximum Current Setting  reading = CurrentCutOff;  encoderPosition = (CurrentCutOff * 1000);               //keep encoder position value at maximum Current Limit  lcd.setCursor(0,3);  lcd.print("                    ");                      //20 spaces to clear last line of LCD   }  if (Mode == "CP" && reading > PowerCutOff) {             //Limit maximum Current Setting        reading = PowerCutOff;        encoderPosition = (PowerCutOff * 1000);            //keep encoder position value at maximum Current Limit        lcd.setCursor(0,3);        lcd.print("                    ");                   //20 spaces to clear last line of LCD     }   if (Mode == "CR" && reading > ResistorCutOff ) {             //Limit maximum Current Setting        reading = ResistorCutOff;        encoderPosition = (ResistorCutOff * 1000);            //keep encoder position value at maximum Current Limit        lcd.setCursor(0,3);        lcd.print("                    ");                   //20 spaces to clear last line of LCD     }}//----------------------Power Level Cutoff Routine-------------------------------------------void powerLevelCutOff (void) {  if (ActualPower  > PowerCutOff){                        //Check if Power Limit has been exceed  reading = 0;  encoderPosition = 0;   lcd.setCursor(0,3);  lcd.print("                    ");  lcd.setCursor(0,3);  lcd.print("Exceeded Power");  lcd.setCursor(8,0);  lcd.print("OFF");  toggle = false;                                         //switch Load Off  }}//----------------------Battery Constant Current Limit Value------------------------------------------void batteryCurrentLimitValue (void) {  if (Mode == "BC" && reading > MaxBatteryCurrent){  reading = MaxBatteryCurrent;  encoderPosition = (MaxBatteryCurrent*1000);            //keep encoder position value at 1000mA  }}//----------------------Display Rotary Encoder Input Reading on LCD---------------------------void displayEncoderReading (void) {    lcd.setCursor(8,2);                                      //start position of setting entry    if ( ( Mode == "CP" || Mode == "CR" ) && reading < 100 ) {        lcd.print("0");    }        if (reading < 10) {                                      //add a leading zero to display if reading less than 10        lcd.print("0");     }    if ( Mode == "CP" || Mode == "CR" ) {        lcd.print (reading, 2);                              //show input reading from Rotary Encoder on LCD    } else {        lcd.print (reading, 3);    }    lcd.setCursor (CP, 2);                                   //sets cursor position    lcd.cursor();                                            //show cursor on LCD}//--------------------------Cursor Position-------------------------------------------------------//Change cursor the position routinevoid CursorPosition(void) {    // Defaults for two digits before decimal and 3 after decimal point    int unitPosition = 9;    //Power and Resistance modes can be 3 digit before decimal but only 2 decimals    if ( Mode == "CP" || Mode == "CR" ) {        unitPosition = 10;            }    if (digitalRead(CursorPos) == LOW) {            delay(200);                                          //simple key bounce delay          CP = CP + 1;        if (CP == unitPosition + 1 ) {            CP = CP + 1;        }    }        if (CP > 13)  { CP = unitPosition; }                     //No point in turning tens and hundreds    if (CP == unitPosition +4 ) { factor = 1; }    if (CP == unitPosition +3 ) { factor = 10; }    if (CP == unitPosition +2 ) { factor = 100; }    if (CP == unitPosition )    { factor = 1000; }}//---------------------------------------------Read Voltage and Current--------------------------------------------------------------void readVoltageCurrent (void) {    MCP342x::Config status;// Initiate a conversion; convertAndRead() will wait until it can be read  adc.convertAndRead(MCP342x::channel1, MCP342x::oneShot,           MCP342x::resolution16, MCP342x::gain1,                         //"gain1" means we have select the input amp of the ADC to x1           1000000, voltage, status);  // Initiate a conversion; convertAndRead() will wait until it can be read  adc.convertAndRead(MCP342x::channel2, MCP342x::oneShot,           MCP342x::resolution16, MCP342x::gain4,                         //"gain4" means we have select the input amp of the ADC to x4           1000000, current, status);  }//-----------------------------------Calculate Actual Voltage and Current and display on LCD-----------------------------------------void ActualReading(void) {  ActualCurrent = (((current*2.048)/32767) * 2.5);        //calculate load current  currentDisplayCal();                                    //LCD display current calibration correction  ActualVoltage = (((voltage*2.048)/32767) * 50.4);       //calculate load voltage upto 100v (was 50)  ActualPower = ActualVoltage*ActualCurrent;  if (ActualPower <=0){    ActualPower = 0;  } if (ActualVoltage <=0.0){                              //added to prevent negative readings on LCD due to error  ActualVoltage = 0.0; } if (ActualCurrent <= 0.0){                             //added to prevent negative readings on LCD due to error  ActualCurrent = 0.0; }  lcd.setCursor(0,1);        if ( ActualCurrent < 10.0 ) {        lcd.print(ActualCurrent,3);    } else {        lcd.print(ActualCurrent,2);    }        lcd.print("A");    lcd.print(" ");        if (ActualVoltage < 10.0) {        lcd.print(ActualVoltage, 3);    } else {        lcd.print(ActualVoltage, 2);    }        lcd.print("V");    lcd.print(" ");         if (ActualPower < 100 ) {        lcd.print(ActualPower,2);    } else {        lcd.print(ActualPower,1);    }    lcd.print("W");    lcd.print(" ");}//-----------------------DAC Control Voltage for Mosfet---------------------------------------void dacControlVoltage (void) {  if (Mode == "CC"){  setCurrent = reading*1000;                                //set current is equal to input value in Amps  setReading = setCurrent;                                  //show the set current reading being used  setControlCurrent = setCurrent * setCurrentCalibrationFactor;  controlVoltage = setControlCurrent;   }  if (Mode == "CP"){  setPower = reading*1000;                                  //in Watts  setReading = setPower;  setCurrent = setPower/ActualVoltage;  setControlCurrent = setCurrent * setCurrentCalibrationFactor;  controlVoltage = setControlCurrent;                       //  }  if (Mode == "CR"){  setResistance = reading;                                  //in ohms  setReading = setResistance;  setCurrent = (ActualVoltage)/setResistance*1000;  setControlCurrent = setCurrent * setCurrentCalibrationFactor;  controlVoltage = setControlCurrent;   }if (Mode == "TC" || Mode == "TP" || Mode == "TT" || Mode == "TL"){                            //Transient Modes  setControlCurrent = (setCurrent * 1000) * setCurrentCalibrationFactor;  controlVoltage = setControlCurrent;   }}//-------------------------------------Battery Capacity Discharge Routine----------------------------------------------------void batteryCapacity (void) {  if (Mode == "BC"){        setCurrent = reading*1000;                             //set current is equal to input value in Amps    setReading = setCurrent;                               //show the set current reading being used    setControlCurrent = setCurrent * setCurrentCalibrationFactor;    controlVoltage = setControlCurrent;    lcd.setCursor(0,3);    lcd.print (timer.getTime());                           //start clock and print clock time    Seconds = timer.getTotalSeconds();                     //get totals seconds      LoadCurrent = ActualCurrent;                           //if timer still running use present Actual Current reading    if (timer.status() == 2){                              //if timer is halted then use last Actual Current reading before timer stopped      LoadCurrent = BatteryCurrent;      }     BatteryLife = (LoadCurrent*1000)*(Seconds/3600);       //calculate battery capacity in mAh    lcd.setCursor(9,3);    BatteryLife = round(BatteryLife);    if(BatteryLife >= BatteryLifePrevious){                //only update LCD (mAh) if BatteryLife has increased        if (BatteryLife < 10) {                              //add a 3 leading zero to display if reading less than 10      lcd.print("000");      }      if (BatteryLife >= 10 && BatteryLife <100){          //add a 2 leading zero to display      lcd.print("00");        }      if (BatteryLife >= 100 && BatteryLife <1000){        //add a 1 leading zero to display      lcd.print("0");       }      lcd.print(BatteryLife,0);    lcd.setCursor(13,3);    lcd.print("mAh");    BatteryLifePrevious = BatteryLife;                      //update displayed battery capacity on LCD    }   }  if (Mode == "BC" && ActualVoltage <= BatteryCutoffVolts){ //stops clock if battery reached cutoff level and switch load off  BatteryCurrent = ActualCurrent;  dac.setVoltage(0,false);                                  //reset DAC to zero for no output current set at switch on                                               toggle = false;                                           //Load is toggled OFF  lcd.setCursor(8,0);  lcd.print("OFF");                                         //indicate that LOAD is off at start up  timer.stop();  }      if (Mode == "BC" && Load == 1){                       //Routine used for data logging in Battery Capacity Mode          if (Seconds != SecondsLog){                       //only send serial data if time has changed            SecondsLog = Seconds;            Serial.print (SecondsLog);                    //sends serial data of time in seconds            Serial.print (",");                             //sends a comma as delimiter for logged data            Serial.println (ActualVoltage);                   //sends serial data of Voltage reading                       }          }}//--------------------------------------------------Fan Control----------------------------------------------------------void fanControl (void) {  temp = analogRead(temperature);  temp = temp * 0.107421875;                                // convert to Celsius    if (temp >= 40){                                        //if temperature 40 degree C or above turn fan on.    digitalWrite(fan, HIGH);   }  else {    digitalWrite(fan, LOW);                               //otherwise turn fan turned off  }    lcd.setCursor(16,0);  lcd.print(temp);                                        //display temperature of heatsink on LCD  lcd.print((char)0xDF);  lcd.print("C");}  //-----------------------Toggle Current Load ON or OFF------------------------------void LoadSwitch(void) {    delay(200);                                              //simple key bounce delay      if(toggle)    {      lcd.setCursor(8,0);      lcd.print("OFF");      current_instruction = 0;                            //reset current instruction for Transient List Mode to zero      last_time = 0;                                      //reset last time to zero      transientPeriod = 0;                                //reset transient period time to zero      setCurrent = 0;                                     //reset setCurrent to zero      toggle = !toggle;      Load = 0;            }    else    {      lcd.setCursor(8,0);      lcd.print("ON ");      lcd.setCursor(0,3);      lcd.print("                    ");                 //clear bottom line of LCD      toggle = !toggle;      Load = 1;    }}//-----------------------Select Constant Current LCD set up--------------------------------void Current(void) {  Mode = ("CC");  lcd.setCursor(0,0);  lcd.print("DC LOAD");    lcd.setCursor(0,2);  lcd.print("                ");  lcd.setCursor(0,2);  lcd.print("Set I = ");  lcd.setCursor(16,2);  lcd.print("    ");  lcd.setCursor(14,2);  lcd.print("A");  lcd.setCursor(0,3);                                   //clear last line of time info  lcd.print("                    ");                    //20 spaces so as to allow for Load ON/OFF to still show  CP = 9;                                               //sets cursor starting position to units.}//----------------------Select Constant Power LCD set up------------------------------------void Power(void) {  Mode = ("CP");  lcd.setCursor(0,0);  lcd.print("DC LOAD");  lcd.setCursor(0,2);  lcd.print("                ");  lcd.setCursor(0,2);  lcd.print("Set W = ");  lcd.setCursor(16,2);  lcd.print("    ");  lcd.setCursor(14,2);  lcd.print("W");  lcd.setCursor(0,3);                                   //clear last line of time info  lcd.print("                    ");                    //20 spaces so as to allow for Load ON/OFF to still show  CP = 10;                                               //sets cursor starting position to units.}//----------------------- Select Constant Resistance LCD set up---------------------------------------void Resistance(void) {  Mode = ("CR");  lcd.setCursor(0,0);  lcd.print("DC LOAD");    lcd.setCursor(0,2);  lcd.print("                ");  lcd.setCursor(0,2);  lcd.print("Set R = ");  lcd.setCursor(16,2);  lcd.print("    ");  lcd.setCursor(14,2);  lcd.print((char)0xF4);  lcd.setCursor(0,3);                                   //clear last line of time info  lcd.print("                    ");                    //20 spaces so as to allow for Load ON/OFF to still show  CP = 10;                                               //sets cursor starting position to units.}//----------------------- Select Battery Capacity Testing LCD set up---------------------------------------void BatteryCapacity(void) {  Mode = ("BC");  lcd.setCursor(0,0);  lcd.print("BATTERY");  lcd.setCursor(0,2);  lcd.print("                ");  lcd.setCursor(0,2);  lcd.print("Set I = ");  lcd.setCursor(14,2);  lcd.print("A");  lcd.setCursor(0,3);                                   //clear last line of time info  lcd.print("                    ");                    //20 spaces so as to allow for Load ON/OFF to still show}//----------------------Battery Type Selection Routine------------------------------------------------void batteryType (void) {  exitMode = 0;                                         //reset EXIT mode  lcd.noCursor();                                       //switch Cursor OFF for this menu                 lcd.clear();  lcd.setCursor(0,0);  lcd.print("Select Battery Type");    lcd.setCursor(0,1);  lcd.print("1=LiPo/Li-Ion 2=LiFe");  lcd.setCursor(0,2);  lcd.print("3=NiCd/NiMH   4=ZiZn");  lcd.setCursor(0,3);                                   //clear last line of time info  lcd.print("5=Set Voltage 6=Exit");                    //20 spaces so as to allow for Load ON/OFF to still show  customKey = customKeypad.waitForKey();                //stop everything till the user press a key.  if (customKey == '1'){  BatteryCutoffVolts = LiPoCutOffVoltage;  BatteryType = ("LiPo");    }  if (customKey == '2'){  BatteryCutoffVolts = LiFeCutOffVoltage;  BatteryType = ("LiFe");    }  if (customKey == '3'){  BatteryCutoffVolts = NiCdCutOffVoltage;  BatteryType = ("NiCd");      }  if (customKey == '4'){  BatteryCutoffVolts = ZiZnCutOffVoltage;  BatteryType = ("ZiZn");     }  if (customKey == '5'){   BatteryType = ("SetV");    }  if (customKey == '6'){                                  //Exit selection screen  exitMode = 1;    }  if (customKey == '7' || customKey == '8' || customKey == '9' || customKey == '0' || customKey == 'A' || customKey == 'B' || customKey == 'C' || customKey == 'D' || customKey == '*' || customKey == '#' || customKey == 'E' || customKey == 'F' || customKey == '<' || customKey == '>' ){  batteryType();                                                      //ignore other keys    }  if(BatteryType == "SetV" && exitMode != 1){  setBatteryCutOff();    }  batteryTypeSelected();                                    //briefly display battery type selected and discharge cut off voltage  lcd.clear();}//--------------------------Set DAC Voltage--------------------------------------------void dacControl (void) {  if (!toggle){  dac.setVoltage(0,false);                                 //set DAC output voltage to 0 if Load Off selected    if(Mode == "BC" && ActualVoltage >= BatteryCutoffVolts && timer.status() == 1){    timer.stop();    }    }else{  //Serial.println("Control Voltage");                    //used for testing only  //Serial.println(controlVoltage);                       //used for testing only  dac.setVoltage(controlVoltage,false);                   //set DAC output voltage for Range selected    if(Mode == "BC" && ActualVoltage >= BatteryCutoffVolts && timer.status() != 1){    timer.start();    }  }}//--------------------------Battery Selected Information--------------------------------------------void batteryTypeSelected (void) {  if (exitMode !=1){                                      //if battery selection was EXIT then skip this routine  lcd.clear();  lcd.setCursor(2,0);  lcd.print("Battery Selected");  lcd.setCursor(8,1);  lcd.print(BatteryType);                                 //display battery type selected  lcd.setCursor(2,2);  lcd.print("Discharge Cutoff");  lcd.setCursor(6,3);  lcd.print(BatteryCutoffVolts);                          //display battery discharge cut off voltage  lcd.print(" volts");                                      delay(3000);  }}//--------------------------Set Battery Cut-Off Voltage--------------------------------------------void setBatteryCutOff (void) {  lcd.clear();  lcd.setCursor(4,0);  lcd.print("Enter Battery");  lcd.setCursor(3,1);  lcd.print("Cut-Off Voltage");  y = 8;  z = 8;  r = 2;  inputValue();  BatteryCutoffVolts = x;  lcd.clear();}//------------------------Key input used for Battery Cut-Off and Transient Mode------------------------void inputValue (void){ while(customKey != 'F'){               //check if enter pressed (was previously #)    customKey = customKeypad.getKey();  if(customKey >= '0' && customKey <= '9'){               //check for keypad number input       numbers[index++] = customKey;       numbers[index] = '\0';       lcd.setCursor(z,r);                                     lcd.print(customKey);                              //show number input on LCD       z = z+1;     }    if(customKey == '>'){                                   //Decimal point      if (decimalPoint != ('>')){                         //test if decimal point entered twice - if so ski      numbers[index++] = '.';      numbers[index] = '\0';      lcd.setCursor(z,r);      lcd.print(".");      z = z+1;      decimalPoint = ('>');                               //used to indicate decimal point has been input        }      } if(customKey == '<'){                                    //clear entry    index = 0;    z = y;    lcd.setCursor(y,r);    lcd.print("     ");    numbers[index] = '\0';                                //    decimalPoint = (' ');                                 //clear decimal point test character reset  } }   if(customKey == 'F') {                                  //enter value     x = atof(numbers);         index = 0;    numbers[index] = '\0';    decimalPoint = (' ');                                 //clear decimal point test character reset  }}//----------------------------------------Transient Mode--------------------------------------------void transientMode (void) {if(Mode != "TL"){  y = 11;  z = 11;    lcd.noCursor();                                       //switch Cursor OFF for this menu                 lcd.clear();  lcd.setCursor(3,0);  lcd.print("Transient Mode");    lcd.setCursor(0,1);  lcd.print("Set Low  I=");  lcd.setCursor(19,1);  lcd.print("A");  r = 1;  inputValue();    if(x >= CurrentCutOff){    LowCurrent = CurrentCutOff;  }else{    LowCurrent = x;  }  lcd.setCursor(11,r);  lcd.print(LowCurrent,3);    customKey = '0';  z = 11;  lcd.setCursor(0,2);  lcd.print("Set High I=");  lcd.setCursor(19,2);  lcd.print("A");  r = 2;  inputValue();  if(x >= CurrentCutOff){    HighCurrent = CurrentCutOff;  }else{    HighCurrent = x;  }  lcd.setCursor(11,r);  lcd.print(HighCurrent,3);  customKey = '0';  if(Mode == "TC" || Mode == "TP"){  z = 11;  lcd.setCursor(0,3);  lcd.print("Set Time  = ");  lcd.setCursor(16,3);  lcd.print("mSec");  r = 3;  inputValue();  transientPeriod = x;  lcd.setCursor(11,r);  lcd.print(transientPeriod);  }else{  lcd.setCursor(0,3);  lcd.print("                    ");  }  lcd.clear();  toggle = false;                                           //switch Load OFF  lcd.setCursor(8,0);  lcd.print("OFF");                                         //print on display OFF    }else{    transientListSetup();  lcd.clear();   toggle = false;                                           //switch Load OFF  lcd.setCursor(8,0);  lcd.print("OFF");                                         //print on display OFF  }}//----------------------------------------Transient Type Selection--------------------------------------------void transientType (void) {  toggle = false;                                         //switch Load OFF  exitMode = 0;                                           //reset EXIT mode  lcd.noCursor();                                         //switch Cursor OFF for this menu                 lcd.clear();  lcd.setCursor(3,0);  lcd.print("Transient Mode");    lcd.setCursor(0,1);  lcd.print("1 = Continuous");  lcd.setCursor(0,2);  lcd.print("2 = Toggle");  lcd.setCursor(11,2);                                    //  lcd.print("3 = Pulse");                                 //  lcd.setCursor(0,3);                                     //  lcd.print("4 = List");                                  //  lcd.setCursor(11,3);                                    //  lcd.print("5 = Exit");                                  //  customKey = customKeypad.waitForKey();                  //stop everything till the user press a key.  if (customKey == '1'){  Mode = ("TC");     }  if (customKey == '2'){   Mode = ("TT");    }  if (customKey == '3'){  Mode = ("TP");      }  if (customKey == '4'){  Mode = ("TL");      }  if (customKey == '5'){                                  //Exit selection screen  exitMode = 1;    }  if (customKey == '6' || customKey == '7' || customKey == '8' || customKey == '9' || customKey == '0' || customKey == 'A' || customKey == 'B' || customKey == 'C' || customKey == 'D' || customKey == '*' || customKey == '#' || customKey == 'E' || customKey == 'F' || customKey == '<' || customKey == '>' ){  transientType();                                                      //ignore other keys      }lcd.clear();if (exitMode == 1){                                       //if NO Transient Mode type selected revert to CC Mode    lcd.setCursor(8,0);    lcd.print("OFF");    Current();                                            //if selected go to Constant Current Selected routine    encoderPosition = 0;                                  //reset encoder reading to zero    customKey = 'A';      }else{    transientMode();      } } //----------------------------------------Transient--------------------------------------------void transient (void) {    if(Mode == "TC" || Mode == "TP" || Mode == "TT" || Mode == "TL"){  lcd.noCursor();                                         //switch Cursor OFF for this menu   lcd.setCursor(0,0);  lcd.print("DC LOAD");    if(Mode != "TL"){  lcd.setCursor(0,2);  lcd.print("Lo=");  lcd.setCursor(3,2);  lcd.print(LowCurrent,3);  lcd.setCursor(8,2);  lcd.print("A");  lcd.setCursor(11,2);  lcd.print("Hi=");  lcd.setCursor(14,2);                                    //  lcd.print(HighCurrent,3);  lcd.setCursor(19,2);  lcd.print("A");  }else{    delay(1);  }  if(Mode == "TC" || Mode == "TP" || Mode == "TL"){  lcd.setCursor(0,3);                                     //  lcd.print("Time = ");  lcd.setCursor(7,3);  lcd.print(transientPeriod);  lcd.setCursor(12,3);                                    //  lcd.print("mSecs");    }else{     lcd.setCursor(0,3);     lcd.print("  ");    }  }delay(1);}//-------------------------------------Transcient List Setup-------------------------------------------void transientListSetup(){  lcd.noCursor();                                                   lcd.clear();  lcd.setCursor(0,0);  lcd.print("Setup Transient List");  lcd.setCursor(0,1);  lcd.print("Enter Number in List");  lcd.setCursor(0,2);  lcd.print("(between 2 to 10 max");   y = 0;  z = 0;  r = 3;  inputValue();  total_instructions = int(x-1);  customKey = '0';  lcd.clear();      for(int i=0; i<=(total_instructions); i++){      lcd.setCursor(0,0);      lcd.print("Set Current ");      lcd.print(i+1);      lcd.setCursor(16,1);      lcd.print("A");      y = 0;      z = 0;      r = 1;      inputValue();            //get the users input value      transientList[i][0] = x; //store the users entered value in the transient list       customKey = '0';      lcd.setCursor(0,2);      lcd.print("Set Time ");      lcd.print(i+1);      lcd.setCursor(16,3);      lcd.print("mSec");      y = 0;      z = 0;      r = 3;      inputValue();            //get the users input value      transientList[i][1] = x; //store the users entered value in the transient list      customKey = '0';      lcd.clear();     }  current_instruction = 0;      //start at first instrution}//-------------------------------------Transcient Load Toggel-------------------------------------------void transientLoadToggle(){  if(Mode == "TC"){  current_time = micros();                              //get the current time in micro seconds()  if (last_time == 0){    last_time = current_time;  } else {    switch (transient_mode_status){      case (false):          // we are in the low current setting          if ((current_time - last_time) >= (transientPeriod * 1000.0)){             transientSwitch(LowCurrent, true);            }        break;      case (true):          // we are in the high current setting           if ((current_time - last_time) >= (transientPeriod * 1000.0)){            transientSwitch(HighCurrent, true);                      }        break;       }      }    }  if(Mode == "TP"){    current_time = micros();                            //get the current time in micro seconds()    if (last_time == 0){        last_time = current_time;        transientSwitch(LowCurrent, true);    }    if (digitalRead(TriggerPulse) == LOW){      // a trigger pluse is received      // set to the high current      transientSwitch(HighCurrent, true);     } else {        if ((current_time - last_time) >= (transientPeriod * 1000.0)){            transientSwitch(LowCurrent, true);                    }      }    } if(Mode == "TT"){          // this function will toggle between high and low current when the trigger pin is taken low  if (digitalRead(TriggerPulse) == LOW){    switch (transient_mode_status){      case (false):        transientSwitch(LowCurrent, true);        delay(200);         //added to prevent key bounce when transient button used (date of addition 31-03-2018)        break;      case (true):        transientSwitch(HighCurrent, true);        delay(200);         //added to prevent key bounce when transient button used (date of addition 31-03-2018)           break;      }    }  }if(Mode == "TL"){  if (Load == 1){                                                 //Only perform Transient List if Load is ON   current_time = micros();                                       //get the current time in micro seconds()    if (last_time == 0){      last_time = current_time;      transientPeriod = transientList[current_instruction][1];    //Time data for LCD display      transientSwitch(transientList[current_instruction][0], false);    }    if((current_time - last_time) >= transientList[current_instruction][1] * 1000){     //move to next list instruction        current_instruction++;        if(current_instruction > total_instructions){          current_instruction = 0;        }      transientPeriod = transientList[current_instruction][1];   //Time data for LCD display      transientSwitch(transientList[current_instruction][0], false);      }    }  }}//-------------------------------------Transcient Switch-------------------------------------------void transientSwitch(float current_setting, boolean toggle_status){  if (toggle_status){  transient_mode_status = !transient_mode_status;  }  setCurrent = current_setting;  //Serial.print("set current = ");                     //used for testing only  //Serial.println(setCurrent);                         //used for testing only  last_time = current_time;}//-------------------------------------User set up for limits-------------------------------------------------void userSetUp (void) {  y = 14;  z = 14;    lcd.noCursor();                                       //switch Cursor OFF for this menu                 lcd.clear();  lcd.setCursor(4,0);  lcd.print("User Set-Up");  lcd.setCursor(0,1);  lcd.print("Current Limit=");  lcd.setCursor(19,1);  lcd.print("A");  r = 1;  inputValue();  CurrentCutOff = x;  EEPROM.write(0x00, CurrentCutOff);  lcd.setCursor(14,r);  lcd.print(CurrentCutOff);    customKey = '0';  z = 14;  lcd.setCursor(0,2);  lcd.print("Power Limit  =");  lcd.setCursor(19,2);  lcd.print("W");  r = 2;  inputValue();  PowerCutOff = x;  EEPROM.write(0x20, PowerCutOff);              //  lcd.setCursor(14,r);  lcd.print(PowerCutOff);  customKey = '0';  z = 14;  lcd.setCursor(0,3);  lcd.print("Temp. Limit  =");  lcd.setCursor(18,3);  lcd.print((char)0xDF);  lcd.print("C");  r = 3;  inputValue();  tempCutOff = x;  EEPROM.write(0x40, tempCutOff);  lcd.setCursor(14,r);  lcd.print(tempCutOff);    lcd.clear();    lcd.setCursor(8,0);    lcd.print("OFF");    Current();                                            //if selected go to Constant Current Selected routine    encoderPosition = 0;                                  //reset encoder reading to zero    customKey = 'A';}//------------------------------------------High Temperature Cut-Off--------------------------------------------------------------void temperatureCutOff (void){  if (temp >= tempCutOff){                                 //if Maximum temperature is exceeded  reading = 0;  encoderPosition = 0;   lcd.setCursor(0,3);  lcd.print("                    ");  lcd.setCursor(0,3);  lcd.print("Over Temperature");  lcd.setCursor(8,0);  lcd.print("OFF");  toggle = false;                                         //switch Load Off  }}//-----------------------------------------Current Read Calibration for LCD Display --------------------------------------------void currentDisplayCal (void){  if(ActualCurrent <= 0){    ActualCurrent = 0;  }else if(Load == 0){    ActualCurrent = 0;  }else{  ActualCurrent = ActualCurrent + displayCurrentCal;  }/*  if (ActualCurrent <= 0.5)    {    ActualCurrent = (ActualCurrent +(displayCurrentCal * 3));    }    else if (ActualCurrent >= 0.5 && ActualCurrent <1.0)    {    ActualCurrent = (ActualCurrent + (displayCurrentCal * 2));    }    else if (ActualCurrent >= 1.0 && ActualCurrent <= 1.4)    {    ActualCurrent = (ActualCurrent + (displayCurrentCal));    }    else     {    ActualCurrent = ActualCurrent;    }*/}  //-----------------------------Show limits Stored Data for Current, Power and Temp-----------------------------  void setupLimits (void){  lcd.clear();  lcd.setCursor(1,0);  lcd.print("Maximum Limits Set");  lcd.setCursor(0,1);  lcd.print("Current Limit=");  lcd.setCursor(17,1);  lcd.print("A");  lcd.setCursor(15,1);  CurrentCutOff = EEPROM.read(0x00);  lcd.print(CurrentCutOff);  lcd.setCursor(0,2);  lcd.print("Power Limit  =");  lcd.setCursor(17,2);  lcd.print("W");  lcd.setCursor(15,2);  PowerCutOff = EEPROM.read(0x20);  lcd.print(PowerCutOff);  lcd.setCursor(0,3);  lcd.print("Temp. Limit  =");  lcd.setCursor(17,3);  lcd.print((char)0xDF);  lcd.print("C");  tempCutOff = EEPROM.read(0x40);  lcd.setCursor(15,3);  lcd.print(tempCutOff);  }    //---------------------------------------------------------------------------------------------------------
 |