//SCULLCOM HOBBY ELECTRONICS //ELECTRONIC DC LOAD PROJECT //Software Version 35B (4x4 Matrix Keypad Version) //14th April 2018 #include //include SPI library (Serial Peripheral Interface) #include //include I2C library #include // F Malpartida's NewLiquidCrystal library // https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/NewliquidCrystal_1.3.4.zip #include // #include //Adafruit DAC library https://github.com/adafruit/Adafruit_MCP4725 #include //Steve Marple library avaiable from https://github.com/stevemarple/MCP342x #include //Scullcom Hobby Electronics library http://www.scullcom.com/MCP79410Timer-master.zip #include //include EEPROM library used for storing setup data #include //http://playground.arduino.cc/Code/Keypad const byte ROWS = 4; //four rows const byte COLS = 4; //four columns //define the symbols on the buttons of the keypads char hexaKeys[ROWS][COLS] = { {'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'} }; byte rowPins[ROWS] = {5, 6, 7, 8}; //connect to the row pin outs of the keypad byte colPins[COLS] = {9, 10, 11, 12}; //connect to the column pin outs of the keypad //initialize an instance of class NewKeypad Keypad 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; //constructor uint8_t address = 0x68; //0x68 is the default address for the MCP3426 device MCP342x adc = MCP342x(address); const byte MCP79410_ADDRESS = 0x6f; //0x6f is the default address for the MCP79410 Real Time Clock IC MCP79410_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,D7 LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7); //0x27 is the default address of the LCD with I2C bus module const 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 Encoder const 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/OFF const byte TriggerPulse = 16; //analog pin A2 used as a digital pin for trigger pulse input in transient mode const 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 seconds float BatteryCutoffVolts; //used to set battery discharge cut-off voltage float MaxBatteryCurrent = 1.0; //maximum battery current allowed for Battery Capacity Testing int stopSeconds; //store for seconds when timer stopped int CP = 8; //cursor start position boolean toggle = false; //used for toggle of Load On/Off button unsigned long controlVoltage = 0; //used for DAC to control MOSFET long current = 0; //variable used by ADC for measuring the current long voltage = 0; //variable used by ADC for measuring the voltage float reading = 0; //variable for Rotary Encoder value divided by 1000 float setCurrent = 0; //variable used for the set current of the load float setPower = 0; //variable used for the set power of the load float setResistance = 0; //variable used for the set resistance of the load float setCurrentCalibrationFactor = 0.980; //calibration adjustment - set as required if needed (was 1.000) float displayCurrentCal = 0.040; //calibration correction for LCD current display int Load = 0; //Load On/Off flag float setControlCurrent = 0; //variable used to set the temporary store for control current required int VoltsDecimalPlaces = 3; //number of decimal places used for Voltage display on LCD float ActualVoltage = 0; //variable used for Actual Voltage reading of Load float ActualCurrent = 0; //variable used for Actual Current reading of Load float ActualPower = 0; //variable used for Actual Power reading of Load float ResistorCutOff = 999; //maximum Resistor we want to deal with in software float BatteryCurrent; // float LoadCurrent; // //int CurrentCutOff = eeprom_read_dword(0x00); 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 current float OutputVoltage = 0; // String Mode =" "; //used to identify which mode int modeSelected = 0; //Mode status flag int lastCount = 50; // volatile float encoderPosition = 0; // volatile unsigned long factor= 0; //number of steps to jump volatile 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 battery float LiFeCutOffVoltage = 2.8; //set cutoff voltage for LiFe battery float NiCdCutOffVoltage = 1.0; //set cutoff voltage for NiCd battery float ZiZnCutOffVoltage = 1.0; //set cutoff voltage for ZiZn battery float PbAcCutOffVoltage = 1.75; //set cutoff voltage for PbAc battery String BatteryType =" "; byte exitMode = 0; //used to exit battery selection menu and return to CC Mode char numbers[10]; //keypad number entry - Plenty to store a representation of a float byte index = 0; int z = 1; //was 0 float x = 0; int y = 0; int r = 0; float LowCurrent = 0; //the low current setting for transcient mode float HighCurrent = 0; //the high current setting for transcient mode unsigned long transientPeriod; //used to store pulse time period in transcient pulse mode unsigned long current_time; //used to store the current time in microseconds unsigned long last_time; //used to store the time of the last transient switch in micro seconds boolean 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 data int total_instructions; //used in Transient List Mode int 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. 35B(4x4 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 //Serial.print("customKey = "); //only used for testing keypad //Serial.println(customKey); //only used for testing keypad //} //only used for testing keypad if(customKey == '*' && digitalRead(CursorPos) == LOW){ //check if Set-Up Mode Selected (press * and Cursor Button together) 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 == 'A' && digitalRead(CursorPos) == LOW){ //check if Transient Mode Selected (press A and Cursor Button together) 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 == 'C'){ //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 == 'D'){ //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 == '#') { //check if Load ON/OFF button pressed 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 routine void 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 == '#'){ 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 != '#'){ //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 == 'C'){ //clear entry index = 0; z = y; lcd.setCursor(y,r); lcd.print(" "); numbers[index] = '\0'; // decimalPoint = (' '); //clear decimal point test character reset } } if(customKey == '#') { //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 == '#'){ 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); break; case (true): transientSwitch(HighCurrent, true); 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); } //---------------------------------------------------------------------------------------------------------