/******************************************************
Program hbpeltier-MAKE.c

Program written for the HandyBoard development platform
for a Do-It-Yourself Thermal Cycler unit, as constructed
for MAKE magazine

(Jon Nakane, Keddie Brown, UBC Engineering Physics, June 2006)
******************************************************/




/******************************************************
GLOBAL VARIABLE DECLARATION
******************************************************/

/* calibration temperatures and corresponding 8-bit values
generated at the analog input to the HandyBoard */
int TempCalib[5] = {25, 60, 70, 95, 105} ;
int NumCalib[5] = {0, 107, 137, 214, 256} ;


/* thermal cycling profile /*

int numtemps = 3 ; /* number of different temperatures in cycle */
int TempProfile[3] = {
95, 58, 72} ; /* temperature, in degrees celsius */
int TimeProfile[3] = {
60, 60, 60} ; /* time to hold each temperature, in seconds */
int numcycles =
45 ; /* total number of cycles to run */


int Kp = 10 ; /* gain values for tuning (see runPWM function)
int Kd = 10 ;

int error = 0 ; /* difference between current and target temperature */
int preverror = 0 ; /* previous value of difference */
int motorout = 0 ; /* value sent to H-bridge; ranges from -100 to +100 */


int curCycle = 0 ; /* for monitoring current and target values */
int curTempProfile = 0 ;
int targetTemp, targetTime ;
int targetNum, currentNum;
int startTimer, finishedTemp ;
int currentTemp ;




/******************************************************
NumToTemp - converts a temperature value (in degrees celsius)
to an 8-bit number (from 0 to 255), as expected
on the analog input to the HandyBoard from the temperature
circuit
******************************************************/

int NumToTemp (int Num)
{
int value = 0 ;
int index = 0 ;
while (NumCalib[index + 1] < Num)
index = index + 1 ;

value = TempCalib[index] +
(int) ((float) (TempCalib[index+1]-TempCalib[index])*
(float) (Num - NumCalib[index]) /
(float) (NumCalib[index+1]-NumCalib[index]) ) ;
return value ;
}



/******************************************************
TempToNum - converts a temperature value (in degrees celsius)
to an 8-bit number (from 0 to 255), as expected
on the analog input to the HandyBoard from the temperature
circuit
******************************************************/

int TempToNum(int Temp)
{
int value = 0 ;
int index = 0 ;
float tempfloat ;
while ((TempCalib[index + 1] < Temp) && (index < 4))
index = index + 1 ;

tempfloat = ((float) (NumCalib[index+1]-NumCalib[index])*
(float) (Temp - TempCalib[index]) /
(float) (TempCalib[index+1]-TempCalib[index])) ;
value = NumCalib[index] + (int)(tempfloat) ;
return value ;
}



/******************************************************
runPWM - sends a pulse-width modulated signal
to the H-bridge circuit to either cool or
heat the peltier device based on the value in
"error". The value of "signal" is based on a
Proportional-Derivative control loop; more info
about the control loop can be found online, such
as at:
http://www.embedded.com/2000/0010/0010feat3.htm
******************************************************/
int runPWM()
{
int P, D, signal ;

P = Kp * error ;
D = Kd * (error - preverror) ;
signal = P + D ;

if (signal > 100) /* sets the upper and lower threshold of the motor at +/-100 */
signal = 100 ;
if (signal < -100)
signal = -100 ;

motor(0, signal) ;
preverror = error ;
msleep(200L) ;
return signal ;
}


/******************************************************
Main Program
******************************************************/

void main ()
{
for (curCycle = 0 ; curCycle < numcycles; curCycle = curCycle + 1)
for (curTempProfile = 0 ; curTempProfile < numtemps ; curTempProfile = curTempProfile + 1)
{
startTimer = 0 ;
finishedTemp = 0 ;

targetTemp = TempProfile[curTempProfile] ;
targetNum = TempToNum(targetTemp) ;
targetTime = TimeProfile[curTempProfile] ;

while (!finishedTemp)
{
currentNum = analog(1) ; /* get current temp (value from 0-255)
from sensor plugged into analog channel 1*/
error = targetNum - currentNum;
motorout = runPWM() ; /* generate signal to h-bridge based on currentNum;
if error > 0 the H-bridge receives a signal to
to heat the peltier device;
if error < 0 the H-bridge recieves a signal to
send current in the reverse direction to cool device*/

printf("cyc%d, tgt%d, act%d, mot%d\n", /* output status to screen */
curCycle, targetTemp, currentTemp, motorout) ;


if ((error < 2) && (error > -2) && (startTimer == 0)) /* if the temperature is within
+/-2 units (~0.6 degC) start timer */
{
startTimer = 1 ;
reset_system_time() ;
}
if ((startTimer == 1) && ((int)seconds() > targetTime)) /* when the timer value is reached
go to the next temperature */
finishedTemp = 1 ;
}
}

printf("Thermal cycling complete! \n") ;
}