Skip to content

Instantly share code, notes, and snippets.

@dragonlock2
Created August 29, 2019 22:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dragonlock2/6ffff3bab0b6ffeb1ea236e82348d606 to your computer and use it in GitHub Desktop.
Save dragonlock2/6ffff3bab0b6ffeb1ea236e82348d606 to your computer and use it in GitHub Desktop.
Code for my Reflow oven.
#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