*/ Title: RS Reflow Oven Controller
* Version: SEv0.01_SAC
* Date: 29-05-2013
* Author : SDS Optoelectronics .
* Brief
* =====
* This is a firmware for RocketScream's Arduino compatible reflow oven controller.
* The reflow curve used in this firmware is meant for lead-free solder profile
* Alloy -----Sn96.5Ag3Cu0.5--------- (acc. to J-STD-020D.01)
* Specially 'tuned'for led IMS/MCPCB reflow soldering .
* -For Arduino shield of v1.60 & above .
* -Sends data ,over serial port ,for real time monitoring of reflow parameters
* to >>>>SimPlot Software <<<<< (Kinda like an Oscilloscope ).Up to 4 channels can be plotted.
* Really good one ,there !!!
* - LCD shows Reflow State (OK=ready PH=Pre-heat SK=Soak RF:reflow & CL=cool ,
* -along with input temperature ,time remaining for soak (rem
& ramp rate for preheat,reflow & cool .
* - Enhanced Serial Data
* -Plenty of 'Buzzin 'n ' flashin ' , indicative signals.
* One bi-color led was used (green-red ) for Case . ( pin 4 +300-1000 Ω res. / ground / 5v + on board res<=Green always on.)
* Light combo chosen is Amber & Green .( pin 4 HIGH -pin 4 LOW ,respectively )
* One red led at pin 13 (+300-1000 Ω res.)
* +++
* Required Libraries
* ==================
* - Arduino PID Library:
* >>
https://github.com/br3ttb/Arduino-PID-Library
* - MAX31855 Library (for board v1.60 & above):
* >>
https://github.com/rocketscream/MAX31855
* +++
* >>>>>>>>>>>>>>>>>>>For details of SimPlot go to>>>>>>>>>>>>>>>>>>>>>>>>
* >>>>>>>>>>>>>>>>>>>>>>>>>>>
www.negtronics.com/simplot <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
***********************************************************************************/
//Initialise
#define USE_MAX31855
// Libraries Included
#include <LiquidCrystal.h>
#include <MAX31855.h>
#include <PID_v1.h>
// Type Definitions
typedef enum REFLOW_STATE
{
REFLOW_STATE_IDLE,
REFLOW_STATE_PREHEAT,
REFLOW_STATE_SOAK,
REFLOW_STATE_REFLOW,
REFLOW_STATE_COOL,
REFLOW_STATE_COMPLETE,
REFLOW_STATE_TOO_HOT,
REFLOW_STATE_ERROR,
}
reflowState_t;
//
typedef enum REFLOW_STATUS
{
REFLOW_STATUS_OFF,
REFLOW_STATUS_ON
}
reflowStatus_t;
//
typedef enum SWITCH
{
SWITCH_NONE,
SWITCH_1,
SWITCH_2
}
switch_t;
//
typedef enum DEBOUNCE_STATE
{
DEBOUNCE_STATE_IDLE,
DEBOUNCE_STATE_CHECK,
DEBOUNCE_STATE_RELEASE
}
debounceState_t;
// Solder Profile SnAgCu
#define TEMPERATURE_ROOM 40 // Oven has to be < 40°C for reflowing to start
#define TEMPERATURE_SOAK_MIN 150 // preheat should be done at ~2°C/sec ramp rate .For ~ 60 sec
#define TEMPERATURE_SOAK_MAX 200 // Soak ramps up to ~180°C at ~0.5 °C /sec slope..For ~100sec
// ( 150 °C => 200 °C ,From ~ 180°C ramp ,increase to ~2°C/sec.
#define TEMPERATURE_REFLOW_MIN 217 // Liquidus Point .Total time above is ~80 sec.
#define TEMPERATURE_REFLOW_MAX 248 // Max =260°C (Pref .Peak =245°C) .20" total within ( 245°C - 5°C )
#define TEMPERATURE_COOL_MIN 150 // Ramp-down rate : -3°C/sec .Max = -6°C/sec
#define SOAK_TEMPERATURE_STEP 5 //====> ~ 0,555 °C/sec %
#define SOAK_MICRO_PERIOD 9000 //%=========>^^^^^^^^^
// PID parameters
// ~ Pre-heat
#define PID_KP_PREHEAT 100 // Suggested Range 75-110
#define PID_KI_PREHEAT 0.025 // Suggested Range 0.020-0.030
#define PID_KD_PREHEAT 20 // Suggested Range 15-25
// ~ Soak
#define PID_KP_SOAK 300 // Suggested Range 180-350
#define PID_KI_SOAK 0.05 // Suggested Range 0.05-0.1
#define PID_KD_SOAK 250 // Suggested Range 75-300
// ~ Reflow
#define PID_KP_REFLOW 300 // Suggested Range 150-350
#define PID_KI_REFLOW 0.05 // Suggested Range 0.05-0.1
#define PID_KD_REFLOW 350 // Suggested Range 75-350
//~Sampling
#define PID_SAMPLE_TIME 250 // Stable:1000
#define SENSOR_SAMPLING_TIME 250 // Stable :1000
// Switch Debounce Limit
#define DEBOUNCE_PERIOD_MIN 50
// Buzzer
// soak-to-reflow buzz
#define BEEPDURATION_REFLOW 250
// reflow-to-cool buzz
#define BEEPDURATION_COOL 500
// cool-to-complete buzz
#define BEEPDURATION_COMPLETE 1500
// LCD Display messages
const char* lcdMessagesReflowStatus[] = {
"OK:",
"PH:",
"SK:",
"RF:",
"CL:",
"OpenOven",
"HOT"
};
//
// ° symbol for LCD display
unsigned char degree[8] =
{
140,146,146,140,128,128,128,128
};
//
// PINS
int ssrPin = 5;
int thermocoupleSOPin = A3;
int thermocoupleCSPin = A2;
int thermocoupleCLKPin = A1;
int lcdRsPin = 7;
int lcdEPin = 8;
int lcdD4Pin = 9;
int lcdD5Pin = 10;
int lcdD6Pin = 11;
int lcdD7Pin = 12;
int ledRedPin = 13; // <= scrape off the 'L' on-board led of Arduino UNOv3 .It draws current from pin 13 .
int ledGRPin = 4;
int buzzerPin = 6;
int switchPin = A0;
// PID Control Variable List
double setpoint;
double input;
double p_input;
double slope;
double output;
double kp = PID_KP_PREHEAT;
double ki = PID_KI_PREHEAT;
double kd = PID_KD_PREHEAT;
int windowSize;
unsigned long windowStartTime;
unsigned long nextCheck;
unsigned long nextRead;
unsigned long timerSoak;
unsigned long SoakStartTime;
unsigned long ReflowStartTime;
unsigned long ReflowDwellTime;
unsigned long buzzerPeriod;
// Reflow oven controller "machine state" variable
reflowState_t reflowState;
// Reflow oven controller "status"
reflowStatus_t reflowStatus;
// Switch debounce "machine state" variable
debounceState_t debounceState;
// Switch debounce timer
long lastDebounceTime;
// Switch press "status"
switch_t switchStatus;
// Seconds timer
int timerSeconds;
// PID control interface
PID reflowOvenPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// LCD interface
LiquidCrystal lcd(lcdRsPin, lcdEPin, lcdD4Pin, lcdD5Pin, lcdD6Pin, lcdD7Pin);
MAX31855 thermocouple(thermocoupleSOPin, thermocoupleCSPin, thermocoupleCLKPin);
// Simplot Data Buffer
int buffer[20];
int data1;
int data2;
int data3;
//Declare plot function
//***********************************************************************************
void plot ( int data1, int data2, int data3 )
{
int pktSize;
buffer[0] = 0xCDAB; //SimPlot packet header. Indicates start of data packet
buffer[1] = 3*sizeof(int); //Size of data in bytes. Does not include the header and size fields
buffer[2] = data1;
buffer[3] = data2;
buffer[4] = data3;
pktSize = 2 + 2 + (3*sizeof(int)); //Header bytes + size field bytes + data
//IMPORTANT: Change to serial port that is connected to PC
Serial.write((uint8_t * )buffer, pktSize);
}
//*******************************************************************************************
void setup()
{
// SSR pin initialization / reflow oven off
digitalWrite(ssrPin, LOW);
pinMode(ssrPin, OUTPUT);
// Buzzer pin initialization / buzzer off
digitalWrite(buzzerPin, LOW);
pinMode(buzzerPin, OUTPUT);
// LEDs pins initialization /turn on (active low)
digitalWrite(ledRedPin, HIGH);
pinMode(ledRedPin,OUTPUT);
delay(1000);
digitalWrite(ledRedPin, LOW);
digitalWrite(ledGRPin, HIGH);
pinMode(ledGRPin,OUTPUT);
delay(1000);
digitalWrite(ledGRPin, LOW);
// LCD Start-up splash
digitalWrite(buzzerPin, HIGH);
lcd.begin(8, 2);
lcd.createChar(0, degree);
lcd.clear();
lcd.print("Profile:");
lcd.setCursor(0, 1);
lcd.print("SnAgCu");
digitalWrite(buzzerPin, LOW);
delay(2500);
lcd.clear();
// Serial communication @ 57600 bps
Serial.begin(57600);
// PWM window size
windowSize = 2000;
// Initialize time variable
nextCheck = millis();
// Initialize thermocouple input variable
nextRead = millis();
}
void loop()
{
// Current time
unsigned long now;
// Time to read thermocouple
if (millis() > nextRead)
{
// Read thermocouple next sampling period
nextRead += SENSOR_SAMPLING_TIME;
// Read current temperature
p_input = input ;
input = thermocouple.readThermocouple(CELSIUS);
slope = (input - p_input);
// If thermocouple problem detected
if((input == FAULT_OPEN) || (input == FAULT_SHORT_GND) || (input == FAULT_SHORT_VCC))
{
// Illegal operation-ERROR!
reflowState = REFLOW_STATE_ERROR;
digitalWrite (ledRedPin,HIGH );
digitalWrite (ledGRPin,HIGH );
reflowStatus = REFLOW_STATUS_OFF;
}
}
if (millis() > nextCheck)
{
// Check input in the next 1"
nextCheck += 1000;
// If reflow process is on going
if (reflowStatus == REFLOW_STATUS_ON)
{
// Toggle LEDS as system's heart beat
digitalWrite(ledRedPin, !(digitalRead(ledRedPin)));
digitalWrite(ledGRPin, !(digitalRead(ledGRPin)));
// Increase seconds timer for reflow curve analysis
timerSeconds++;
Serial.print("T:"); // time
Serial.print(timerSeconds);
Serial.print(" SET: "); // Set point
Serial.print(setpoint);
Serial.print(" IN:"); // input TC °C
Serial.print(input);
Serial.print(" OUT:"); // mS window PWM
Serial.print(output);
Serial.print(" R:"); // Δ Slope -/+ °C /sec
Serial.print(slope);
Serial.print(" RfIni:"); // Reflow Start
Serial.print(ReflowStartTime);
Serial.print(" AbTl:"); // Reflow Total Dwell Above liquidus State 217=>~245=>217 °C
Serial.println(ReflowDwellTime);
// SimPlot Data
data1 =thermocouple.readThermocouple(CELSIUS);
data2= setpoint;
data3= slope;
plot (data1,data2,data3);
delay(10);
}
else
{
// Clear LCD
lcd.clear();
// Print current system state
lcd.print(lcdMessagesReflowStatus[reflowState]);
// Move the cursor to the 4th digit of first line
lcd.setCursor(4, 0);
}
// If currently in error state
if (reflowState == REFLOW_STATE_ERROR)
{
// No thermocouple wire connected
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("TC Error!");
// Turn on green+red(amber) led .Turn on red LED
digitalWrite(ledGRPin,HIGH);
digitalWrite(ledRedPin,HIGH);
}
else
{
unsigned int temp;
unsigned int minutes;
unsigned int seconds ;
// Print current temperature
lcd.print(input);
#if ARDUINO >= 100// Print degree Celsius symbol in 3 digit value
lcd.write((uint8_t)0);
#else
// Print degree Celsius symbol in 2 digit value
lcd.print(0, BYTE);
#endif
lcd.print("C ");
//Soak remain time
if ( reflowState == REFLOW_STATE_SOAK )
{
lcd.setCursor(0, 1);
lcd.print("Rem:");
temp = ( (SOAK_MICRO_PERIOD/1000) - ( timerSeconds - SoakStartTime ) );
minutes = int(temp / 60 );
seconds = int(temp % 60 );
if (minutes < 10) lcd.print(" ") ;
lcd.print(minutes);
lcd.print(":");
if (seconds < 10) lcd.print("0") ;
lcd.print(seconds);
}
//Slope
if ( ( reflowState == REFLOW_STATE_PREHEAT ) || ( reflowState == REFLOW_STATE_REFLOW ) || ( reflowState == REFLOW_STATE_COOL) )
{
lcd.setCursor(0, 1);
lcd.print ("R:");
if ( slope<0 )
{
lcd.print("-") ; // "-" if negative slope
}
else
{
lcd.print("+") ; // "+" if positive slope
}
lcd.print(abs(int(slope))) ;
lcd.print(".") ;
lcd.print(abs( int( ( slope - int( slope ) ) * 10) ) ); // print slope
lcd.write((uint8_t)0);
lcd.print("C ");
}
}
}
// Reflow oven controller state machine
switch (reflowState)
{
case REFLOW_STATE_IDLE:
// If oven temperature is still above room temperature
if (input >= TEMPERATURE_ROOM)
{
reflowState = REFLOW_STATE_TOO_HOT;
// Turn on Amber LED
digitalWrite(ledGRPin,HIGH);
// Red led on
digitalWrite(ledRedPin,HIGH);
}
else
{
// If switch is pressed to start reflow process
if (switchStatus == SWITCH_1)
{
// Send header for CSV file // Processing Software
Serial.println();
// Intialize seconds timer for serial debug information
timerSeconds = 0;
// Initialize PID control window starting time
windowStartTime = millis();
// Ramp up to minimum soaking temperature
setpoint = TEMPERATURE_SOAK_MIN;
// Tell the PID to range between 0 and the full window size
reflowOvenPID.SetOutputLimits(0, windowSize);
reflowOvenPID.SetSampleTime(PID_SAMPLE_TIME);
// Turn the PID on
reflowOvenPID.SetMode(AUTOMATIC);
// Proceed to preheat stage
reflowState = REFLOW_STATE_PREHEAT;
}
}
break;
case REFLOW_STATE_PREHEAT:
reflowStatus = REFLOW_STATUS_ON;
// If minimum soak temperature is achieve
if (input >= TEMPERATURE_SOAK_MIN)
{
// Chop soaking period into smaller sub-period
timerSoak = millis() + SOAK_MICRO_PERIOD;
// Set less agressive PID parameters for soaking ramp
reflowOvenPID.SetTunings(PID_KP_SOAK, PID_KI_SOAK, PID_KD_SOAK);
// Ramp up to first section of soaking temperature
setpoint = TEMPERATURE_SOAK_MIN + SOAK_TEMPERATURE_STEP;
// Proceed to soaking state
reflowState = REFLOW_STATE_SOAK;
SoakStartTime = timerSeconds ;
}
break;
case REFLOW_STATE_SOAK:
// If micro soak temperature is achieved
if (millis() > timerSoak)
{
timerSoak = millis() + SOAK_MICRO_PERIOD;
// Increment micro setpoint
setpoint += SOAK_TEMPERATURE_STEP;
if (setpoint > TEMPERATURE_SOAK_MAX)
{
// Set agressive PID parameters for reflow ramp
reflowOvenPID.SetTunings(PID_KP_REFLOW, PID_KI_REFLOW, PID_KD_REFLOW);
// Ramp up to first section of soaking temperature
setpoint = TEMPERATURE_REFLOW_MAX;
// Proceed to reflowing state
reflowState = REFLOW_STATE_REFLOW;
ReflowStartTime = 0;
buzzerPeriod = millis() + BEEPDURATION_REFLOW ;
digitalWrite(buzzerPin, HIGH);
}
}
break;
case REFLOW_STATE_REFLOW:
if (millis() > buzzerPeriod)
{
digitalWrite(buzzerPin, LOW);
}
// Avoid hovering at peak temperature for too long
// Crude method that works like a charm and safe for the components
if ( (input >= TEMPERATURE_REFLOW_MIN) && (ReflowStartTime == 0 ) )
{
ReflowStartTime = timerSeconds ; // set Reflow Start Time
}
if (input >= (TEMPERATURE_REFLOW_MAX - 5))
{
// Set PID parameters for cooling ramp
reflowOvenPID.SetTunings(PID_KP_REFLOW, PID_KI_REFLOW, PID_KD_REFLOW);
// Ramp down to minimum cooling temperature
setpoint = TEMPERATURE_COOL_MIN;
// Proceed to cooling state
reflowState = REFLOW_STATE_COOL;
buzzerPeriod = millis() + BEEPDURATION_COOL;
digitalWrite(buzzerPin, HIGH);
}
break;
case REFLOW_STATE_COOL:
if (millis() > buzzerPeriod) // turn off buzzer
{
digitalWrite(buzzerPin, LOW);
}
// If minimum cool temperature is achieve
if (input <= TEMPERATURE_COOL_MIN)
{
// Retrieve current time for buzzer usage
buzzerPeriod = millis() + BEEPDURATION_COMPLETE;
// Turn on buzzer +Green led to indicate completion
digitalWrite(buzzerPin, HIGH);
// Turn off reflow process
reflowStatus = REFLOW_STATUS_OFF;
// Proceed to reflow Completion state
reflowState = REFLOW_STATE_COMPLETE;
}
break;
case REFLOW_STATE_COMPLETE:
if (millis() > buzzerPeriod)
{
// Turn off buzzer & green led
digitalWrite(buzzerPin, LOW);
digitalWrite(ledGRPin,LOW);
digitalWrite(ledRedPin,LOW);
// Reflow process ended
reflowState = REFLOW_STATE_IDLE;
}
break;
case REFLOW_STATE_TOO_HOT:
// If oven temperature drops below room temperature
if (input < TEMPERATURE_ROOM)
{
// Ready to reflow
reflowState = REFLOW_STATE_IDLE;
}
break;
case REFLOW_STATE_ERROR:
// If thermocouple problem is still present
if((input == FAULT_OPEN) || (input == FAULT_SHORT_GND) || (input == FAULT_SHORT_VCC))
{
// Wait until thermocouple wire is connected
reflowState = REFLOW_STATE_ERROR;
}
else
{
// Clear to perform reflow process
reflowState = REFLOW_STATE_IDLE;
}
break;
}
// If switch 1 is pressed
if (switchStatus == SWITCH_1)
{
// If currently reflow process is on going
if (reflowStatus == REFLOW_STATUS_ON)
{
// Button PRESS FOR ABORT !
// Turn off reflow process!
reflowStatus = REFLOW_STATUS_OFF;
// Turn on Red LED and beep.then turn led
digitalWrite(ledRedPin,HIGH);
digitalWrite(buzzerPin,HIGH);
delay (1000);
digitalWrite(ledRedPin,LOW);
digitalWrite(buzzerPin,LOW);
// Reinitialize state state idle
reflowState = REFLOW_STATE_IDLE;
}
}
// switch debounce state machine
switch (debounceState)
{
case DEBOUNCE_STATE_IDLE:
// No valid switch press
switchStatus = SWITCH_NONE;
// If switch #1 is pressed
if (analogRead(switchPin) == 0)
{
// Intialize debounce counter
lastDebounceTime = millis();
// Proceed to check validity of button press
debounceState = DEBOUNCE_STATE_CHECK;
}
break;
case DEBOUNCE_STATE_CHECK:
if (analogRead(switchPin) == 0)
{
// If minimum debounce period is completed
if ((millis() - lastDebounceTime) > DEBOUNCE_PERIOD_MIN)
{
// Proceed to wait for button release
debounceState = DEBOUNCE_STATE_RELEASE;
}}
// False trigger
else
{
// Reinitialize button debounce state machine
debounceState = DEBOUNCE_STATE_IDLE;
}
break;
case DEBOUNCE_STATE_RELEASE:
if (analogRead(switchPin) > 0)
{
// Valid switch 1 press
switchStatus = SWITCH_1;
// Reinitialize button debounce state machine
debounceState = DEBOUNCE_STATE_IDLE;
}
break;
}
// PID computation and SSR control
if (reflowStatus == REFLOW_STATUS_ON)
{
now = millis();
reflowOvenPID.Compute();
if((now - windowStartTime) > windowSize)
{
// Time to shift the Relay Window
windowStartTime += windowSize;
}
if(output > (now - windowStartTime)) digitalWrite(ssrPin, HIGH);
else digitalWrite(ssrPin, LOW);
}
// Reflow oven process is off, ensure oven is off
else
{
digitalWrite(ssrPin, LOW);
}
}
// **********end*****************************
// SimPot:
// -Set Conn speed at 57600.
//
//_Input_ , _Setpoint_ & _Slope_ values, are plotted .
//
// -At tab "Plot Setup" At X axis __time set 480 ( 8 min =480 sec )
// ( ~8 min is >max duration< of reflow from >start to finish< ,
// according to J-STD-020D.01 standard. )................................
//
// -At Y Axis__Input/Set Temperature, set min Val @ -10 ( cooling ramp :no more than -6°C /sec. ).
// -Max Y Axis value set ~ 270-280 as "ceiling " of plot .
//
//-Set COM xx port.
// -Press "Connect " .
// &
// Start reflowing...
// Enjoy !!!
// **************************************