Open Vapors – Open Source Toaster Reflow Oven

Last Updated:  07/27/2014
Status:  In Progress (First Completed Prototype)

Home Screen Menu

3rd Prototype

Second Prototype (on the Bench)

First Prototype (in the kitchen)

GiHub:  Open Vapor Menu Code
Related Blog Posts:  N/A
Flickr Set:  http://www.flickr.com/photos/9849051@N04/sets/72157630154132342/with/7390533878/ 

Abstract

My first attempt at making a reflow oven controller.  The project is being released as an open source project, with the Open Vapors.

Electronics

Parts list:

  • Toaster Oven (Goodwill)
  • Low Voltage Electronics:
    • Maxim MAX31855 Thermocouple Amplifier (Adafruit Breakout)
    • K-Type Thermocouple (from Adafruit)
    • Arduino Uno
  • AC Line-Level Electronics:
    • 15-Amp AC Wall Outlet  (Home Depot)  Replaced with IEC Outlet
    • 15-Amp AC Wall Switch  (Home Depot)  Removed.  May be replaced in next version.
    • 14 Gauge Romex  (Home Depot)
    • Donor Cord from 6-Outlet Strip (to plug into wall)  (Home Depot)  Replaced with IEC Inlet
    • Crydom SSR D2425 (Mouser)
      • Heat Sink
      • Thermal Pad
  • To Be Added:
    • A couple of LEDs  Added
    • Buttons  Added
    • Character LCD  Added, 16×2 to be replaced with 20×4 display soon.
    • Custom Enclosure  Workd

Schematic:

Overall Hardware

 

Controller Board

The controller board is based on the Arduino Leonardo’s ATmega32u4.  This eliminates the USB to serial chip and helps to avoid the auto-reset issue presented into the Uno design.  This makes it possible to attach or detach the USB cable for logging without causing the controller to reset (potentially during a reflow cycle.)

EAGLE Project files for R1: Reflow_Control_Board_R1 (zip)
Schematic in PDF:  Reflow Control Board (pdf)

Firmware

For now, I’ve separated the work on the UI and code that runs the oven.

Menu Software

A basic structure for a multi-level menu is in place.  It is possible to edit multi-digit values (like 255) by increasing/decreasing individual digits.  All information is saved to the ATmega32u4′s onboard EEPROM.  The software is on GitHub.

Control Software

The current firmware is borrowing heavily from the Rocket Scream Arduino Reflow Shield.  In fact, this code just changes support to the MAX31855, disables the buzzer, disables LCD, and provided more output to Serial.

I plan to fork this code into a different path, but for now it provides an excellent starting point and I sincerely appreciate the efforts of Rocket Scream.

/*** Modified Reflow Oven Code.
* Original Author: Rocket Scream Electronics, www.rocketscream.com
* Modified Author: James Lewis, james@cmiyc.com, www.cmiyc.com
*
* This firmware owed very much on the works of other talented individuals as
* follows:
* ==========================================
* Brett Beauregard (www.brettbeauregard.com)
* ==========================================
* Author of Arduino PID library. On top of providing industry standard PID
* implementation, he gave a lot of help in making this reflow oven controller
* possible using his awesome library.
*
* ==========================================
* Limor Fried of Adafruit (www.adafruit.com)
* ==========================================
* Author of Arduino MAX6675 library. Adafruit has been the source of tonnes of
* tutorials, examples, and libraries for everyone to learn.
*
* Disclaimer
* ==========
* Dealing with high voltage is a very dangerous act! Please make sure you know
* what you are dealing with and have proper knowledge before hand. Your use of
* any information or materials on this reflow oven controller is entirely at
* your own risk, for which we shall not be liable.
*
* Licences
* ========
* This reflow oven controller hardware and firmware are released under the
* Creative Commons Share Alike v3.0 license
* http://creativecommons.org/licenses/by-sa/3.0/
* You are free to take this piece of code, use it and modify it.
* All we ask is attribution including the supporting libraries used in this
* firmware.
#include "Adafruit_MAX31855.h"
#include

// ***** 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_ERROR
} reflowState_t;

typedef enum REFLOW_STATUS{
REFLOW_STATUS_OFF,
REFLOW_STATUS_ON
} reflowStatus_t;

typedef enum DEBOUNCE_STATE{
DEBOUNCE_STATE_IDLE,
DEBOUNCE_STATE_CHECK,
DEBOUNCE_STATE_RELEASE
} debounceState_t;

// ***** CONSTANTS *****
#define TEMPERATURE_ROOM 50
#define TEMPERATURE_SOAK_MIN 150
#define TEMPERATURE_SOAK_MAX 200
#define TEMPERATURE_REFLOW_MAX 250
#define TEMPERATURE_COOL_MIN 100
#define SENSOR_SAMPLING_TIME 1000
#define SOAK_TEMPERATURE_STEP 5
#define SOAK_MICRO_PERIOD 9000
#define DEBOUNCE_PERIOD_MIN 50
#define THERMOCOUPLE_DISCONNECTED 10000

// ***** PID PARAMETERS *****
// ***** PRE-HEAT STAGE *****
#define PID_KP_PREHEAT 300
#define PID_KI_PREHEAT 0.05
#define PID_KD_PREHEAT 400
// ***** SOAKING STAGE *****
#define PID_KP_SOAK 300
#define PID_KI_SOAK 0.05
#define PID_KD_SOAK 250
// ***** REFLOW STAGE *****
#define PID_KP_REFLOW 300
#define PID_KI_REFLOW 0.05
#define PID_KD_REFLOW 350
#define PID_SAMPLE_TIME 1000

// ***** LCD MESSAGES *****
const char* lcdMessagesReflowStatus[] = {
"Ready",
"Pre-heat",
"Soak",
"Reflow",
"Cool",
"Complete",
"Error"
};

// ***** DEGREE SYMBOL FOR LCD *****
unsigned char degree[8] = {140,146,146,140,128,128,128,128};

// ***** PIN ASSIGNMENT *****
int ssr = 6;
int thermocoupleSO = 8;
int thermocoupleCS = 9;
int thermocoupleCLK = 10;

int ledRed = 3;
int ledGreen = 2;

/* int lcdRs = 2;
int lcdE = 3;
int lcdD4 = 4;
int lcdD5 = 5;
int lcdD6 = 11;
int lcdD7 = 12;

int buzzer = 5;
int button1 = 13;
int button2 = 3; */

// ***** PID CONTROL VARIABLES *****
double setpoint;
double input;
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 buzzerPeriod;
// Reflow oven controller state machine state variable
reflowState_t reflowState;
// Reflow oven controller status
reflowStatus_t reflowStatus;
// Button debounce state machine state variable
debounceState_t debounceState;
// Button debounce timer
long lastDebounceTime;
// Button press status
boolean buttonPressStatus;
// Seconds timer
int timerSeconds;

// Specify PID control interface
PID reflowOvenPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// Specify LCD interface
//LiquidCrystal lcd(lcdRs, lcdE, lcdD4, lcdD5, lcdD6, lcdD7);
// Specify MAX6675 thermocouple interface
//MAX6675 thermocouple(thermocoupleCLK, thermocoupleCS, thermocoupleSO);

/*
int thermocoupleSO = 8;
int thermocoupleCS = 9;
int thermocoupleCLK = 10;*/

Adafruit_MAX31855 thermocouple(thermocoupleCLK, thermocoupleCS, thermocoupleSO);

void setup()
{
// SSR pin initialization to ensure reflow oven is off
Serial.println(F("Init and Turn off SSR"));
digitalWrite(ssr, LOW);
pinMode(ssr, OUTPUT);

// Buzzer pin initialization to ensure annoying buzzer is off
/* digitalWrite(buzzer, LOW);
pinMode(buzzer, OUTPUT);
*/
// LED pins initialization and turn on upon start-up (active low)
digitalWrite(ledRed, LOW);
digitalWrite(ledGreen, LOW);
pinMode(ledRed, OUTPUT);
pinMode(ledGreen, OUTPUT);
/* // Push button pins initialization
pinMode(button1, INPUT);
pinMode(button2, INPUT);
*/
// Start-up splash
/* digitalWrite(buzzer, HIGH);
lcd.begin(8, 2);
lcd.createChar(0, degree);
lcd.clear();
lcd.print("Reflow");
lcd.setCursor(0, 1);
lcd.print("Oven 1.1");
digitalWrite(buzzer, LOW);
delay(2500);
lcd.clear();
*/
Serial.println(F("Reflow Oven 1.1"));
// Serial communication at 57600 bps
Serial.begin(115200);

// Turn off LED (active low)
digitalWrite(ledRed, HIGH);
digitalWrite(ledGreen, HIGH);

// Set window size
windowSize = 2000;
// Initialize time keeping variable
nextCheck = millis();
// Initialize thermocouple reading varible
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
input = thermocouple.readCelsius();

// If thermocouple is not connected
if (input == THERMOCOUPLE_DISCONNECTED)
{
// Illegal operation without thermocouple
reflowState = REFLOW_STATE_ERROR;
reflowStatus = REFLOW_STATUS_OFF;
}
}

if (millis() > nextCheck)
{
// Check input in the next seconds
nextCheck += 1000;
// If reflow process is on going
if (reflowStatus == REFLOW_STATUS_ON)
{
// Toggle red LED as system heart beat
digitalWrite(ledRed, !(digitalRead(ledRed)));
// Increase seconds timer for reflow curve analysis
timerSeconds++;
// Send temperature and time stamp to serial
Serial.print(timerSeconds);
Serial.print(" ");
Serial.print(setpoint);
Serial.print(" ");
Serial.print(input);
Serial.print(" ");
Serial.println(output);
}
else
{
// Turn off red LED
digitalWrite(ledRed, HIGH);
}

// Clear LCD
// lcd.clear();
// Print current system state
// lcd.print(lcdMessagesReflowStatus[reflowState]);
Serial.println(lcdMessagesReflowStatus[reflowState]);
// Move the cursor to the 2 line
// lcd.setCursor(0, 1);

// If currently in error state
if (reflowState == REFLOW_STATE_ERROR)
{
// No thermocouple wire connected
Serial.println("No TC!");
}
else
{
// Print current temperature
/* lcd.print(input);
#if ARDUINO >= 100
lcd.write((uint8_t)0);
#else
// Print degree Celsius symbol
lcd.print(0, BYTE);
#endif
lcd.print("C "); */
}
}

// Reflow oven controller state machine
switch (reflowState)
{
case REFLOW_STATE_IDLE:
// If button is pressed to start reflow process
if (Serial.available()) {
if (Serial.read())
{
// Ensure current temperature is comparable to room temperature
// TO DO: To add indication that temperature is still high for
// reflow process to start
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;
}
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;
}
}
break;

case REFLOW_STATE_REFLOW:
// We need to avoid hovering at peak temperature for too long
// Crude method that works like a charm and safe for the components
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;
}
break;

case REFLOW_STATE_COOL:
// If minimum cool temperature is achieve
if (input buzzerPeriod)
{
// Turn off buzzer and green LED
// digitalWrite(buzzer, LOW);
digitalWrite(ledGreen, HIGH);
// Reflow process ended
reflowState = REFLOW_STATE_IDLE;
}
break;

case REFLOW_STATE_ERROR:
// If thermocouple is still not connected
if (input == THERMOCOUPLE_DISCONNECTED)
{
// Wait until thermocouple wire is connected
reflowState = REFLOW_STATE_ERROR;
}
else
{
// Clear to perform reflow process
reflowState = REFLOW_STATE_IDLE;
}
break;
}

// If button is pressed
if (Serial.available()) {
//if (buttonPressStatus == true)
if (Serial.read())
{
// If currently reflow process is on going
if (reflowStatus == REFLOW_STATUS_ON)
{
// Button press is for cancelling
// Turn off reflow process
reflowStatus = REFLOW_STATUS_OFF;
// Reinitialize state machine
reflowState = REFLOW_STATE_IDLE;
}
}
}
// PID computation and SSR control
if (reflowStatus == REFLOW_STATUS_ON)
{
//unsigned long now;
now = millis();

reflowOvenPID.Compute();

if((now - windowStartTime) > windowSize)
{
// Time to shift the Relay Window
windowStartTime += windowSize;
}
if(output > (now - windowStartTime)) {
//Serial.println("SSR ON");
digitalWrite(ssr, HIGH);
} else {
//Serial.println(F("SSR OFF"));
digitalWrite(ssr, LOW);
}
}
// Reflow oven process is off, ensure oven is off
else
{
digitalWrite(ssr, LOW);
}
}

Demonstration

Temporary video of the oven actually reflowing.

Reflow of a capacitor board

Menu System Working

From Stencil to Reflow

Acknowledgements

1.  Limor Fried for creating the MAX31855 Breakout Board and Arduino Library

2.  Rocket Scream Electronics for providing the code and inspiration from their Arduino Reflow Shield

3.  By extension of #2, Brett Beauregard (www.brettbeauregard.com) for the PID library the code is based on.

4.  My neighbors, for not calling the Fire Department after my first attempt… *grin*

2 Responses to "Open Vapors – Open Source Toaster Reflow Oven"

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

©2014 Baldengineer Productions, Inc.