/* CellTester for Jonescg of AEVA Forums
* Written by Neil Gibbs.
* Anyone is free to use this code in any way they see fit.
* There is no waranty bla bla bla etc on this code.
*
* Description.
* It uses a State Machine to keep track of the current phase in the cycle.
* See comments for the State Machine below.
* The constants defined below allow for:
* Calibrating the A2D convertor.
* Compensation for an external voltage divider.
* Set the number of cycles the tests will run for.
* Settting the Max voltage to terninate a charge cycle.
* Settting the Min voltage to terminate a discharge cycle.
* How often the sampling is performed.
* The time allowed between the phases of each cycle.
* A maximum time for either phase after which the process will stop.
*
* NOTES: If the current monitor is implemented it will only measure Charge
* current (A1 is +ve wrt ground). For safety sake place a 1-10k resistor in
* series with A1 incase there is a short somewhere during discharge.
* This will prevent A2 from going too far -ve wrt ground and damage the
* chip due to excessive input current.
*
* Depending on which display board is used A0 may be used for the keyboard.
* It might be wise to use A1 and A2 instead.
* I have used the AdaFruit display libraries and they work well so you
* should have little trouble if you use their display boards.
*
* Debugging:
* Serial.print() statements send their output to the USB interface and this can
* be used with a terminal program such as Tera Term VT (I have tested this)
* and it can also log to a file if needed.
*
* Please note that at the end of the test the cell(s) will be in a discharged state.
*/
// Define the hardware ports
#define DISCHARGE 13 // Discharge relay
#define RECHARGE 12 // Recharge relay
#define VOLTS A0 // Cell voltage
#define CURRENT A1 // CHARGE current (not discharge)
// Define the system constants
#define VCCVOLTS 4800L // 5 volt rail actual value LEAVE 'L' SUFFIX as this needs to be a long int value
#define EXTERNDIV_NUM 2 // ratio for external voltage divider eg 2:1
#define EXTERNDIV_DENOM 1 // ratio for external voltage divider eg 2:1
#define NUMBEROFCYCLES 20 // Number of cycles to run
#define V_MIN 6000 // Discharge min voltage (in mV)
#define V_MAX 8400 // Charge max voltage (in mV)
#define SAMPLE_DELAY_MILLIS 1000 // Number of mS between voltage checks
#define RECOVERYTIME 10 // Number of Seconds between charge/discharge & discharge/charge Max value is 32767
#define MAXSTATETIME 6000 // Number of Seconds allowed to be in each state (ie charging or discharging) Max value is 32767
#define ON 1 // Turn a relay On
#define OFF 0 // Turn a relay Off
// State machine
#define STATE_START 0 // Setup for and start the charge cycle
#define STATE_CHARGING 1 // Monitor the charge cycle
#define STATE_DISWAIT 2 // Delay before and setup for discharging
#define STATE_DISCHARGING 3 // Monitor the discharging
#define STATE_CHGWAIT 4 // Delay before repeating the cycle
#define STATE_DONE 5 // All done
// Define the system variables
int cellVolts;
int chargeCurrent;
int numberCycles;
int currentState;
int delayTimer;
int stateTimer;
unsigned long sampleDelay;
unsigned long cycleTime;
// readVoltage()
// Read the cell voltage and return the average of 4 readings.
// The result is scaled by the calibration value and also by the external divider.
// Return value is in milliVolts. ie 0 - 5000
int readVoltage()
{
int temp = 0;
for (int i = 0; i < 4; i++)
{
//Scale the A2D reading. The A2D returns 0 to 1023 for a voltage
// range of 0 to the Arduino supply voltage (normally 5v).
temp += (int)(((long)analogRead(VOLTS) * VCCVOLTS) / 1024); // Scale the A2D result
delay(20); // A short delay between readings
}
return (temp*EXTERNDIV_NUM)/(EXTERNDIV_DENOM*4); // Compensate for the external voltage divider
}
// This function sets up the ports for use and initialises some variables.
void setup() {
// Setup for debugging/logging
Serial.begin(38400);
Serial.println("setup()"); // Appends a CR - use Serial.print() for no CR
numberCycles = 1;
currentState = STATE_START;
pinMode(DISCHARGE, OUTPUT); // sets the discharge pin as an output
digitalWrite(DISCHARGE, OFF); // Turn discharge relay off
pinMode(RECHARGE, OUTPUT); // sets the recharge pin as an output
digitalWrite(RECHARGE, OFF); // Turn recharge relay off
cellVolts = readVoltage(); // Do first read to initialise the port
// TODO - initialise your display
}
// This function is repeatedly called by the OS so we need to know where in the
// charge discharge cycle we are, so we use a State Machine.
void loop()
{
unsigned long now = millis(); // Get current timer value in mS
if (now - sampleDelay > SAMPLE_DELAY_MILLIS)
{
sampleDelay = now; // Reset the sampling base time
switch (currentState) // Go to the current state
{
case STATE_START:
stateTimer = MAXSTATETIME; // Setup state timeout value
currentState = STATE_CHARGING; // Next state is charging
cycleTime = now; // State start time
digitalWrite(RECHARGE, ON); // Turn charge relay on
Serial.print("Cycle #"); // Logging
Serial.println(numberCycles);
// TODO update display
break;
case STATE_CHARGING:
cellVolts = readVoltage(); // Do a read
if (cellVolts > V_MAX)
{
// Voltage exceeds set threshold
digitalWrite(RECHARGE, OFF); // Turn charge relay off
currentState = STATE_DISWAIT; // Next state is wait before discharge
delayTimer = RECOVERYTIME; // Inter state delay time
Serial.print("Charging Time: (mS) "); // Logging
Serial.println(now - cycleTime);
// TODO update display
}
if (--stateTimer <= 0)
{
// Taken too long
digitalWrite(RECHARGE, OFF); // Turn relay off
Serial.println("CHARGING TAKEN TOO LONG"); // Logging
currentState = STATE_DONE; // Next state is done
// TODO update display
}
break;
case STATE_DISWAIT:
if (--delayTimer <= 0)
{
digitalWrite(DISCHARGE, ON); // Turn discharge relay on
currentState = STATE_DISCHARGING; // Next state is discharging
stateTimer = MAXSTATETIME; // Setup state timeout value
cycleTime = now; // State start time
}
// TODO update display
break;
case STATE_DISCHARGING:
cellVolts = readVoltage(); // Do a read
if (cellVolts < V_MIN)
{
digitalWrite(DISCHARGE, OFF); // Turn discharge relay off
currentState = STATE_CHGWAIT; // Next state is wait before charging
delayTimer = RECOVERYTIME; // Inter state delay time
stateTimer = MAXSTATETIME; // Setup state timeout value
Serial.print("Discharging Time: (mS) "); // Logging
Serial.println(now - cycleTime);
}
if (--stateTimer <= 0)
{
digitalWrite(DISCHARGE, OFF); // Turn discharge relay off
Serial.println("DISCHARGING TAKEN TOO LONG"); // Logging
currentState = STATE_DONE; // Next state is done
}
// TODO update display
break;
case STATE_CHGWAIT:
if (--delayTimer <= 0)
{
if (++numberCycles <= NUMBEROFCYCLES)
{
currentState = STATE_START; // Next state is setup to charge
}
else
{
currentState = STATE_DONE; // Next state is done
digitalWrite(DISCHARGE, OFF); // Turn discharge relay off
digitalWrite(RECHARGE, OFF); // Turn charge relay off
Serial.println("Cycles finished"); // Logging
}
}
// TODO update display
break;
case STATE_DONE:
// TODO update display
break;
}
}
}