//Temperature Control //Version 1.1 //by Robert R. Leiter 2013-14 //Electron Microscopy Group of Materials Science //Ulm University // //Send bugs and comments to robert.leiter@uni-ulm.de //Complaints to gerardo.algara-siller@uni-ulm.de //Feel free to modify! /* Manual: -Press "Select" to start -Use up/down to select target temperature, confirm with select -Repeat with Heating rate (in degrees per minute) and Annealing time (in minutes) ->The temperature process will now start ->Arduino's serial monitor (set to 115200 baud) can be used to obtain a temperature protocol for the process PID Tuning: WARNING: DO NOT MODIFY THE PID VALUES UNLESS ABSOLUTELY NECESSARY! The PID process is executed in the "tempcontrol" function. The constants Kp, Ki and Kd are the proportional, integral and differential coefficients of the PID function, respectively. For a detailed description of those parameters, see for example http://en.wikipedia.org/wiki/PID_controller */ /* Keypad Codes (in left-to-right order): None - 0 Select - 1 Left - 2 Up - 3 Down - 4 Right - 5 */ #include "Adafruit_MAX31855.h" #include #include //MOSFET pin int pwmpin = 3; //Thermocouple pins int thermoCLK = 11; int thermoCS = 13; int thermoDO = 12; Adafruit_MAX31855 thermocouple(thermoCLK, thermoCS, thermoDO); //Pin assignments for the LCD Keypad Shield LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //--------------------------------------------- DFR_Key keypad; int localKey = 0; int oldKey = 0; String keyString = ""; int backlightpin = 10; int backlighton = true; int targettemp = 300; //degrees celsius int heatingrate = 10; //degrees per min int annealingtime = 30; //minutes int coolingrate = 10; //degrees per min int errorcount = 0; //counts the number of thermocouple errors -> interrrupts tempcontrol if exceeeding limit int overloadcount = 0; //counts the number of times the output power reaches its maximum void setup() { pinMode(pwmpin, OUTPUT); // setzt den Pin pwmpin als Output pinMode(backlightpin, OUTPUT); digitalWrite(backlightpin, HIGH); Serial.begin(115200); Serial.println("Settings: Type 'Key Targettemp Heatingrate Annealingtime Coolingrate'"); Serial.println("e.g.: 1 270 5 20 5"); Serial.println("Waiting for settings..."); //Serial.println("Temperature Protocol:"); lcd.begin(16, 2); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Temperature"); lcd.setCursor(0, 1); lcd.print("Control"); delay(2500); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Version:"); lcd.setCursor(0, 1); lcd.print("1.1"); delay(2500); // need at least 500ms for thermocouple chip to stabilize /* OPTIONAL keypad.setRate(x); Sets the sample rate at once every x milliseconds. Default: 10ms */ keypad.setRate(10); } void loop() { int currentKey = pressedkey(); double temperature = gettemperature(0); double internaltemperature = getinternaltemperature(); // check for serial command: if (Serial.available() > 0) { currentKey=Serial.parseInt(); Serial.print("Key "); Serial.println(currentKey); } switch (currentKey) { case 0: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Int. Temp = "); lcd.print(internaltemperature); lcd.setCursor(0, 1); lcd.print("TC = "); lcd.print(temperature); delay(500); break; case 1: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Target Temperature"); lcd.setCursor(0, 1); lcd.print(targettemp); lcd.print(" C"); delay(500); do { if (Serial.available() > 0) { targettemp=Serial.parseInt(); Serial.print("Targettemp: "); Serial.println(targettemp); break; } currentKey = pressedkey(); lcd.setCursor(0, 1); lcd.print(targettemp); if (currentKey==3 && targettemp<600) { targettemp++; } if (currentKey==4 && targettemp>30) { targettemp--; } delay(100); } while (currentKey!=1); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Heating rate"); lcd.setCursor(0, 1); lcd.print(heatingrate); lcd.print(" C/min"); delay(500); do { if (Serial.available() > 0) { heatingrate=Serial.parseInt(); Serial.print("Heatingrate "); Serial.println(heatingrate); break; } currentKey = pressedkey(); lcd.setCursor(0, 1); lcd.print(heatingrate); lcd.print(" "); if (currentKey==3 && heatingrate<60) { heatingrate++; } if (currentKey==4 && heatingrate>0) { heatingrate--; } coolingrate=heatingrate; delay(100); } while (currentKey!=1); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Annealing Time"); lcd.setCursor(0, 1); lcd.print(annealingtime); lcd.print(" min"); delay(500); do { if (Serial.available() > 0) { annealingtime=Serial.parseInt(); Serial.print("Annealingtime: "); Serial.println(annealingtime); break; } currentKey = pressedkey(); lcd.setCursor(0, 1); lcd.print(annealingtime); lcd.print(" "); if (currentKey==3 && annealingtime<60) { annealingtime++; } if (currentKey==4 && annealingtime>0) { annealingtime--; } delay(100); } while (currentKey!=1); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Cooling Rate"); lcd.setCursor(0, 1); lcd.print(coolingrate); lcd.print(" C/min"); delay(500); do { if (Serial.available() > 0) { coolingrate=Serial.parseInt(); Serial.print("Cooling Rate: "); if (coolingrate==0) { Serial.println("off"); }else{ Serial.println(coolingrate); } break; } currentKey = pressedkey(); lcd.setCursor(0, 1); if (coolingrate==0) { lcd.print("off "); }else{ lcd.print(coolingrate); } lcd.print(" "); if (currentKey==3 && coolingrate<60) { coolingrate++; } if (currentKey==4 && coolingrate>0) { coolingrate--; } delay(100); } while (currentKey!=1); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Countdown:"); lcd.setCursor(0, 1); lcd.print(3); delay(1000); lcd.print(2); delay(1000); lcd.print(1); delay(1000); tempcontrol(targettemp, heatingrate, annealingtime, coolingrate); delay(1000); break; case 2: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Left"); delay(1000); break; case 3: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Up"); delay(1000); break; case 4: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Down"); delay(1000); break; case 5: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Right"); delay(1000); break; default: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Error!"); delay(1000); break; } } /* This runs the actual temperature process */ boolean tempcontrol(int target, int rate, int time, int ratedown) { errorcount = 0; //reset thermocouple error count overloadcount = 0; //reset output power overload count double error=0; //PID stuff double integral=0; double derivative=0; double previouserror=0; double output=0; double Kp=10.0; double Ki=0.10; double Kd=2; Serial.print("Kp: "); Serial.println(Kp); Serial.print("Ki: "); Serial.println(Ki); Serial.print("Kd: "); Serial.println(Kd); double curtemp = gettemperature(0); double settemp = curtemp; //starts at current temperature double ratepersecond= ((double) rate)/60.0; double ratedownpersecond= ((double) ratedown)/60.0; Serial.print("ratepersecond: "); Serial.println(ratepersecond); double ramptime = ((double) target - curtemp)*60.0*1000.0/((double) rate); double rampdowntime; if (ratedown==0) //check if cooling is set to "off" { rampdowntime = 0; } else { rampdowntime = ((double) target - curtemp)*60.0*1000.0/((double) ratedown); } Serial.print("ramptime: "); Serial.println(ramptime); unsigned long starttime = millis(); unsigned long endramptime = starttime+ramptime; unsigned long endtime = starttime+((unsigned long) time)*60000+((unsigned long)ramptime); unsigned long endtotaltime = endtime+rampdowntime; Serial.print("starttime: "); Serial.println(starttime); Serial.print("endramptime: "); Serial.println(endramptime); Serial.print("endtime: "); Serial.println(endtime); Serial.print("time: "); Serial.println(time); Serial.print("60*1000*((unsigned long) time): "); Serial.println(((unsigned long) time)*60000); Serial.print("deltatime: "); Serial.println(endtime-starttime); Serial.println("Temperature Protocol:"); Serial.println("Time (ms) Temperature (°C)"); for (int i=0; millis()25) //emergency conditions { emergency(1); } if(overloadcount>120) { emergency(3); } /* Temperature Protocol once every seconds */ curtemp=gettemperature(curtemp); /* lcd.clear(); lcd.setCursor(0, 0); lcd.print(curtemp); lcd.print(" C"); */ lcd.clear(); lcd.setCursor(0, 0); lcd.print("T"); lcd.print(target); lcd.print(","); lcd.print(rate); lcd.print("/m,"); lcd.print(((endtotaltime-millis())/1000)/60); lcd.print("'"); lcd.print(((endtotaltime-millis())/1000)%60); lcd.print("s"); Serial.print(millis()); Serial.print(" "); Serial.print(curtemp); settemp=settemp+ratepersecond; /* lcd.setCursor(0, 1); lcd.print("Set: "); lcd.print(settemp); lcd.print(" C"); */ lcd.setCursor(0, 1); lcd.print("TS"); lcd.print(round(settemp)); lcd.print(","); lcd.print("TC"); lcd.print(curtemp); Serial.print(" "); Serial.println(settemp); curtemp=gettemperature(curtemp); error = settemp - curtemp; integral = integral + error; derivative = error - previouserror; output = Kp*error + Ki*integral + Kd*derivative; if(output>254) { overloadcount++; } output = constrain(output, 0, 255); previouserror = error; analogWrite(pwmpin, output); //analogWrite Values are between 0 and 255 Serial.print("Output "); Serial.print(output); Serial.print(" err "); Serial.print(error); Serial.print(" int "); Serial.print(integral); Serial.print(" der "); Serial.println(derivative); while (millis()25) { emergency(1); } if(overloadcount>120) { emergency(3); } /* Temperature Protocol once every 0.1 seconds */ curtemp=gettemperature(curtemp); /* lcd.clear(); lcd.setCursor(0, 0); lcd.print(curtemp); lcd.print(" C"); */ lcd.clear(); lcd.setCursor(0, 0); lcd.print("T"); lcd.print(target); lcd.print(","); lcd.print(rate); lcd.print("/m,"); lcd.print(((endtotaltime-millis())/1000)/60); lcd.print("'"); lcd.print(((endtotaltime-millis())/1000)%60); lcd.print("s"); Serial.print(millis()); Serial.print(" "); Serial.print(curtemp); /* lcd.setCursor(0, 1); lcd.print("Set: "); lcd.print(settemp); lcd.print(" C"); */ lcd.setCursor(0, 1); lcd.print("TS"); lcd.print(round(settemp)/5*5); lcd.print(","); lcd.print("TC"); lcd.print(curtemp); Serial.print(" "); Serial.println(settemp); curtemp=gettemperature(curtemp); error = settemp - curtemp; integral = integral + error; derivative = error - previouserror; output = Kp*error + Ki*integral + Kd*derivative; if(output>254) { overloadcount++; } output = constrain(output, 0, 255); previouserror = error; analogWrite(pwmpin, output); // analogRead Werte reichen von 0 to 1023, analogWrite Werte von 0 to 255 Serial.print("Output "); Serial.print(output); Serial.print(" err "); Serial.print(error); Serial.print(" int "); Serial.print(integral); Serial.print(" der "); Serial.println(derivative); while (millis()25) { emergency(1); } if(overloadcount>120) { emergency(3); } /* Temperature Protocol once every second */ curtemp=gettemperature(curtemp); /* lcd.clear(); lcd.setCursor(0, 0); lcd.print(curtemp); lcd.print(" C"); */ lcd.clear(); lcd.setCursor(0, 0); lcd.print("T"); lcd.print(target); lcd.print(","); lcd.print(rate); lcd.print("/m,"); lcd.print(((endtotaltime-millis())/1000)/60); lcd.print("'"); lcd.print(((endtotaltime-millis())/1000)%60); lcd.print("s"); Serial.print(millis()); Serial.print(" "); Serial.print(curtemp); settemp=settemp-ratedownpersecond; /* lcd.setCursor(0, 1); lcd.print("Set: "); lcd.print(settemp); lcd.print(" C"); */ lcd.setCursor(0, 1); lcd.print("TS"); lcd.print(round(settemp)/5*5); lcd.print(","); lcd.print("TC"); lcd.print(curtemp); Serial.print(" "); Serial.println(settemp); curtemp=gettemperature(curtemp); error = settemp - curtemp; integral = integral + error; derivative = error - previouserror; output = Kp*error + Ki*integral + Kd*derivative; if(output>254) { overloadcount++; } output = constrain(output, 0, 255); previouserror = error; analogWrite(pwmpin, output); //analogWrite Values are between 0 and 255 Serial.print("Output "); Serial.print(output); Serial.print(" err "); Serial.print(error); Serial.print(" int "); Serial.print(integral); Serial.print(" der "); Serial.println(derivative); while (millis()600) { emergency(2); } return temperature; } double getinternaltemperature() { double temperature = thermocouple.readInternal(); return temperature; } void emergency(int condition) { analogWrite(pwmpin, 0); //cuts output power if (condition==1) { Serial.println("Thermocouple error!"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("ERROR!"); lcd.setCursor(0, 1); lcd.print("THERMOCOUPLE"); } if (condition==2) { Serial.println("System Overheated!"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("ERROR!"); lcd.setCursor(0, 1); lcd.print("OVERHEATED"); } if (condition==3) { Serial.println("System Overloaded!"); lcd.clear(); lcd.setCursor(0, 0); lcd.print("ERROR!"); lcd.setCursor(0, 1); lcd.print("OVERLOAD"); } while (true){} //Intentional infinite loop to force user to acknowledge that an error has occurred. System can be restarted by pressing the "reset" button. }