Created
August 29, 2019 22:31
-
-
Save dragonlock2/6ffff3bab0b6ffeb1ea236e82348d606 to your computer and use it in GitHub Desktop.
Code for my Reflow oven.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "max6675.h" | |
#include <LiquidCrystal.h> | |
#include <PID_v1.h> | |
//profiles | |
const int NUM_PROFILES = 2; | |
const int NUM_POINTS = 5; | |
String profiles[] = {"Lead (Sn63 Pb37)", | |
"Lead-free SAC305", | |
"Manual Control "}; //16 char long | |
double temps[NUM_PROFILES][NUM_POINTS] = {{150.0, 165.0, 230.0, 230.0, 20.0}, | |
{150.0, 180.0, 250.0, 250.0, 20.0}}; | |
long timeToTemp[NUM_PROFILES][NUM_POINTS] = {{60, 120, 5, 20, 180}, | |
{60, 120, 5, 15, 180}}; //time to stay at temp points in seconds | |
bool waitOrNot[NUM_PROFILES][NUM_POINTS] = {{false, false, true, false, false}, | |
{false, false, true, false, false}}; //if above timeToTemp, wait or not till reach target | |
bool ramp[NUM_PROFILES][NUM_POINTS] = {{true, true, false, false, false}, | |
{true, true, false, false, false}}; //go immediately or increment target over time | |
String pointNames[] = {" Preheat", " Soak", " Reflow", " Reflow", " Cooling"}; //8 char long | |
const String wait = "WAITING"; | |
const double startTemp = 20.0; //assumed starting temp | |
const int rampTime = 1; //time in seconds to change temp when ramping | |
int prof = 0; //current profile highlighted | |
int i = 0; //temp point at | |
long rampPrevTime = 0; //used for ramping | |
long tempPrevTime = 0; //used between temp points | |
int timeLeft = 0; | |
bool tempFlag = false; //used to check if wait or not | |
//screen stuff | |
LiquidCrystal lcd(A5, A4, A3, A2, A1, A0); | |
//thermocouple stuff | |
int thermoDO = 12; | |
int thermoCS = 11; | |
int thermoCLK = 13; | |
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO); | |
double temp = 0.0; //stores temperature readings (pid input) | |
#define loopTime 300 //max6675 limited read speed | |
long prevTime = 0; | |
//switch stuff | |
#define TOP_SW 7 | |
#define TOP_SW_LED 5 | |
#define BOT_SW 8 | |
#define BOT_SW_LED 6 | |
//power stuff | |
#define TOP_HEAT 9 //not sure if correct | |
#define BOT_HEAT 10 //but its fine | |
#define ZERO_CROSS_PIN 2 //interrupt 0 | |
#define RESOLUTION 255 //wave packet ac control | |
volatile int count = 0; //keeps track of wave | |
double duty = 0.0; //duty cycle*255 (pid output) | |
double targetTemp = 20.0; //(pid setpoint) | |
double Kp = 100.0; | |
double Ki = 6.0; | |
double Kd = 450.0; | |
PID myPID(&temp, &duty, &targetTemp, Kp, Ki, Kd, DIRECT); | |
void setup() { | |
pinMode(TOP_SW, INPUT_PULLUP); | |
pinMode(BOT_SW, INPUT_PULLUP); | |
pinMode(TOP_SW_LED, OUTPUT); | |
pinMode(BOT_SW_LED, OUTPUT); | |
pinMode(TOP_HEAT, OUTPUT); | |
pinMode(BOT_HEAT, OUTPUT); | |
digitalWrite(TOP_SW_LED, HIGH); | |
attachInterrupt(digitalPinToInterrupt(ZERO_CROSS_PIN), acPWM, FALLING); | |
myPID.SetMode(AUTOMATIC); | |
//myPID.SetOutputLimits(0, 255); //defaults to 0-255 | |
myPID.SetSampleTime(2*loopTime); //match up with thermocouple | |
lcd.begin(16, 2); | |
lcd.setCursor(0, 0); | |
lcd.print(profiles[prof]); | |
updateTemp(); | |
delay(250); | |
} | |
void loop() { | |
if(!digitalRead(TOP_SW)) { //scroll thru selections | |
prof++; | |
if(prof > NUM_PROFILES) prof = 0; | |
lcd.setCursor(0, 0); | |
lcd.print(profiles[prof]); | |
while(!digitalRead(TOP_SW)) { updateTemp(); } | |
} | |
if(!digitalRead(BOT_SW)) { //run prof | |
if(runProf()) { | |
lcd.setCursor(11, 1); | |
lcd.print("Done!"); | |
} else { | |
lcd.setCursor(11, 1); | |
lcd.print("Fail!"); | |
} | |
targetTemp = startTemp; //safety net just in case | |
lcd.setCursor(0, 0); | |
lcd.print(profiles[prof]); | |
while(!digitalRead(TOP_SW)) { updateTemp(); } //for when canceling | |
while(!digitalRead(BOT_SW)) { updateTemp(); } | |
} | |
updateTemp(); | |
} | |
bool runProf() { | |
if(prof < NUM_PROFILES) { //runs profile | |
for(i = 0; i < NUM_POINTS; i++) { | |
while(!digitalRead(BOT_SW)) { updateTemp(); } | |
tempFlag = false; | |
lcd.setCursor(8, 0); | |
lcd.print(pointNames[i]); | |
tempPrevTime = millis(); | |
updateTimeLeft(); | |
if(ramp[prof][i]) { | |
targetTemp = (i == 0)?startTemp:temps[prof][i-1]; | |
updateTargetTemp(); | |
while(timeLeft > 0) { | |
targetTemp = temps[prof][i] - (temps[prof][i] - ((i == 0)?startTemp:temps[prof][i-1])) / (timeToTemp[prof][i]) * timeLeft; | |
if(millis() - rampPrevTime > 500) { | |
updateTargetTemp(); | |
rampPrevTime = millis(); | |
} | |
updateTemp(); | |
updateTimeLeft(); | |
if(temp >= targetTemp) tempFlag = true; | |
if(!digitalRead(TOP_SW)) return false; //cancel run | |
if(!digitalRead(BOT_SW)) timeLeft = 0; //skip temp point | |
} | |
} else { //if not ramping | |
targetTemp = temps[prof][i]; | |
updateTargetTemp(); | |
while(timeLeft > 0) { | |
updateTemp(); | |
updateTimeLeft(); | |
if(temp >= targetTemp) tempFlag = true; | |
if(!digitalRead(TOP_SW)) return false; //cancel run | |
if(!digitalRead(BOT_SW)) timeLeft = 0; //skip temp point | |
} | |
} | |
if(!tempFlag && waitOrNot[prof][i]) { | |
lcd.setCursor(11, 1); | |
lcd.print("WAIT!"); | |
while(!tempFlag) { | |
updateTemp(); | |
if(temp >= targetTemp) tempFlag = true; | |
if(!digitalRead(TOP_SW)) return false; //cancel run | |
if(!digitalRead(BOT_SW)) tempFlag = true; //skip temp point | |
} | |
} | |
} | |
} else { //manual mode | |
targetTemp = startTemp; | |
lcd.clear(); | |
updateTargetTemp(); | |
lcd.setCursor(0, 1); | |
lcd.print(temp); | |
lcd.print((char)223); | |
lcd.print('C'); | |
if(temp < 100) lcd.print(' '); | |
while(digitalRead(TOP_SW) || digitalRead(BOT_SW)) { | |
if(!digitalRead(TOP_SW)) targetTemp += 1.0; | |
if(!digitalRead(BOT_SW)) targetTemp -= 1.0; | |
updateTemp(); | |
updateTargetTemp(); | |
delay(20); | |
} | |
} | |
return true; | |
} | |
void updateTimeLeft() { | |
timeLeft = timeToTemp[prof][i] - (millis()/1000 - tempPrevTime/1000); | |
lcd.setCursor(11, 1); | |
if(timeLeft < 100) lcd.print(' '); | |
if(timeLeft < 10) lcd.print(' '); | |
lcd.print(timeLeft); | |
lcd.print(' '); | |
lcd.print('S'); | |
} | |
void updateTargetTemp() { | |
lcd.setCursor(0, 0); | |
lcd.print(targetTemp); | |
lcd.print((char)223); | |
lcd.print('C'); | |
if(targetTemp < 100) lcd.print(' '); | |
} | |
void updateTemp() { //keep running always | |
if(millis() - prevTime >= loopTime) { | |
prevTime = millis(); | |
temp = thermocouple.readCelsius(); | |
lcd.setCursor(0, 1); | |
lcd.print(temp); | |
lcd.print((char)223); | |
lcd.print('C'); | |
if(temp < 100) lcd.print(' '); | |
} | |
myPID.Compute(); | |
} | |
void acPWM() { //interrupt code | |
count++; | |
if(count >= RESOLUTION) | |
count = 0; | |
//analogWrite(TOP_SW_LED, count); | |
if(count <= (int)duty && (int)duty > 0) { | |
digitalWrite(BOT_SW_LED, HIGH); //status led | |
digitalWrite(TOP_HEAT, HIGH); | |
digitalWrite(BOT_HEAT, HIGH); | |
} else { | |
digitalWrite(BOT_SW_LED, LOW); | |
digitalWrite(TOP_HEAT, LOW); | |
digitalWrite(BOT_HEAT, LOW); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment