Reflow Oven

Product: https://www.tindie.com/products/williambailes/plex-controller/

For years, I have been making PCBs. Have been through etching homemade PCBs to using a manufacturer and through-hole to SMT. the next step is from hand soldering to reflowing. I have been using hot air and has been good but I wanted to try to use an oven for reflowing.
At first, I just got a cheap toaster oven, set it to about 220 degrees and wait to see the results. Some times it would work great, sometimes it would not and sometimes looked great but didn’t work. after doing a lot of research online, I found out the problem could be many different problems. Reflow temperature profile, uneven heat within oven and solder paste topped the list of problem that I will try to fix.
I used the PLEX controller (see here) to control the heat profile of the oven and installed aluminum plates to try and even out the heat. I will install a fan soon to rotate the heat throughout the oven. I also changed the solder paste which helped.
During testing, the PCB would get too hot and burn a little bit (browning of the white solder mask). This was due to the temperature sensor having a metal cover/cap it. After removing the cover/cap the oven was at the correct temperature for reflowing without burning the PCB.

Code for Project

GNU General Public License v3.0

#include <EEPROM.h>
#include <Plex.h>
#include <max6675.h>

#define UpPin 14
#define DownPin 15
#define SelectPin 16

#define replayPin A3
#define buzzerPin A4

#define NumberOfMenuItems 7

#define ktcCLK 0
#define ktcCS 1
#define ktcSO 3

Plex PLEX;
MAX6675 ktc(ktcCLK, ktcCS, ktcSO);

int menuLocation = 0;

long SoakRamp=3;
long SoakTemp=150;
long SoakTime=100;
long ReflowRamp=3;
long ReflowTemp=220;
long ReflowTime=60;

typedef void (*CallbackFunction) ();
void Run();
void SetSoakRamp();
void SetSoakTemp();
void SetSoakTime();
void SetReflowRamp();
void SetReflowTemp();
void SetReflowTime();

struct Menu {
    String title;
    CallbackFunction Callback; 
} MainMenu[NumberOfMenuItems] = {
    {"Run",Run},
    {"Set Soak Ramp",SetSoakRamp},
    {"Set Soak Temp",SetSoakTemp},
    {"Set Soak Time",SetSoakTime},
    {"Set Reflow Ramp",SetReflowRamp},
    {"Set Reflow Temp",SetReflowTemp},
    {"Set Reflow Time",SetReflowTime},
};


void setup() 
{
    pinMode(UpPin, INPUT);
    pinMode(DownPin, INPUT);
    pinMode(SelectPin, INPUT);
    pinMode(replayPin,OUTPUT);
    digitalWrite(replayPin,LOW);
    pinMode(buzzerPin,OUTPUT);
    digitalWrite(buzzerPin,LOW);
    
    PLEX.lcd.clear();
    PLEX.lcd.print("  Reflow Oven  ");
    PLEX.lcd.setCursor(0, 1);
    PLEX.lcd.print("Tinkers Projects");
    
    Load();

    delay(3000);
}

void loop() 
{  
    if(digitalRead(UpPin)==HIGH)
    {
        menuLocation--;// menu will go up but the count will go down
        delay(200);
        while(digitalRead(UpPin)==HIGH){}
    }

    if(digitalRead(DownPin)==HIGH)
    {
        menuLocation++;// menu will go dpwn but the count will go up
        delay(200);
        while(digitalRead(DownPin)==HIGH){}
    }
    
        if(menuLocation >= NumberOfMenuItems)
          menuLocation=0;
          
        if(menuLocation < 0)
          menuLocation=NumberOfMenuItems-1;
          
    PrintMenu();
    

    if(digitalRead(SelectPin)==HIGH)
    {
        delay(200);
        while(digitalRead(SelectPin)==HIGH);
        MainMenu[menuLocation].Callback();
    }
}

void PrintMenu()
{
    PLEX.lcd.setCursor(0, 0);
    PLEX.lcd.print(">");
    PLEX.lcd.print(MainMenu[menuLocation].title);
    PLEX.lcd.print("                ");
    PLEX.lcd.setCursor(0, 1);
    if(menuLocation+1 < NumberOfMenuItems)
    {
      PLEX.lcd.print(MainMenu[menuLocation+1].title);
    }
    PLEX.lcd.print("                ");
}

void PrintSetMenu(String name, String unit, long &Value, long min, long max, long Defult)
{
    if(0>Value)
      Value=Defult;
      
    PLEX.lcd.clear();
    while(digitalRead(SelectPin)==LOW)
    {
      if(digitalRead(UpPin)==HIGH)
      {
          Value++;
          delay(200);
      }
  
      if(digitalRead(DownPin)==HIGH)
      {
          Value--;
          delay(200);
      }
      if(min>Value)
      Value=min;
      if(max<Value)
      Value=max;
      
      PLEX.lcd.setCursor(0, 0);
      PLEX.lcd.print(name);
      PLEX.lcd.setCursor(0, 1);
      PLEX.lcd.print(Value);
      PLEX.lcd.print(" ");
      PLEX.lcd.print(unit);
      PLEX.lcd.print("        ");
    }
    delay(200);
    while(digitalRead(SelectPin)==HIGH){}
    Save();
    delay(200);
}


void Run()
{
  PLEX.lcd.clear();
  PLEX.lcd.print("Ramp to Soak");
  rampOven(SoakRamp, SoakTemp);
  
  PLEX.lcd.clear();
  PLEX.lcd.print("Getting to Soak");
  ToTemp(SoakTemp);
  
  PLEX.lcd.clear();
  PLEX.lcd.print("Soaking");
  stepOven(SoakTime, SoakTemp); 
  
  PLEX.lcd.clear();
  PLEX.lcd.print("Ramp to Reflow");
  rampOven(ReflowRamp, ReflowTemp);
  
  PLEX.lcd.clear();
  PLEX.lcd.print("Getting to Reflow");
  ToTemp(ReflowTemp);
  
  PLEX.lcd.clear();
  PLEX.lcd.print("Reflowing");
  stepOven(ReflowTime, ReflowTemp);
  
  PLEX.lcd.clear();
  PLEX.lcd.print("Finished");
  PLEX.lcd.setCursor(0, 1);
  PLEX.lcd.print("Open Door");
  Finished(); 
}

void rampOven(long time, long temp)
{
  unsigned long FinishTime = millis()+time*1000;
  unsigned long startTime = millis();
  long startTemp = ktc.readCelsius();
  
  while(FinishTime>millis())
  {
    long G = (temp - startTemp)/(time*1000);
    long currentRampTemp = G*millis()+startTemp;
    unsigned long timeLeft = (FinishTime - millis())/1000;
    onOven(currentRampTemp);
    
    PLEX.lcd.setCursor(0, 1);
    PLEX.lcd.print((int)currentRampTemp);
    PLEX.lcd.print("C   ");
    
    PLEX.lcd.setCursor(5, 1);
    PLEX.lcd.print((int)ktc.readCelsius());
    PLEX.lcd.print("C   ");
    
    PLEX.lcd.setCursor(10, 1);
    PLEX.lcd.print(timeLeft);
    PLEX.lcd.print("Sec    ");
  }
}

void stepOven(long time, long temp)
{
  unsigned long FinishTime = millis()+time*1000;

  while(FinishTime>millis())
  {
    unsigned long timeLeft = (FinishTime - millis())/1000;
    onOven(temp);
    
    PLEX.lcd.setCursor(0, 1);
    PLEX.lcd.print((int)temp);
    PLEX.lcd.print("C   ");
    
    PLEX.lcd.setCursor(5, 1);
    PLEX.lcd.print((int)ktc.readCelsius());
    PLEX.lcd.print("C   ");
    
    PLEX.lcd.setCursor(10, 1);
    PLEX.lcd.print(timeLeft);
    PLEX.lcd.print("Sec    ");
  }
}

unsigned long starttotemp;

void ToTemp(long temp)
{
  starttotemp = millis()+300000;
  while(ktc.readCelsius()<temp-10 && starttotemp>millis)
  {
    onOven(temp);
    PLEX.lcd.setCursor(0, 1);
    PLEX.lcd.print((int)temp);
    PLEX.lcd.print("C   ");
    
    PLEX.lcd.setCursor(5, 1);
    PLEX.lcd.print((int)ktc.readCelsius());
    PLEX.lcd.print("C   ");
  }
}

void onOven(long temp)
{
  long currentTemp = ktc.readCelsius();
  if(currentTemp<temp-10)
  {
    digitalWrite(replayPin,HIGH);
  }
  if(currentTemp>temp+4)
  {
    digitalWrite(replayPin,LOW);
  }
}

void Finished()
{
  digitalWrite(replayPin,LOW);
  digitalWrite(buzzerPin,HIGH);
  delay(2000);
  digitalWrite(buzzerPin,LOW);
}

void SetSoakRamp()
{
  PrintSetMenu("Set Soak Ramp","Sec", SoakRamp,1,20,3);
}

void SetSoakTemp()
{
  PrintSetMenu("Set Soak Temp","C", SoakTemp,100,300,150);
}

void SetSoakTime()
{
  PrintSetMenu("Set Soak Time","Sec", SoakTime,10,200,100);
}

void SetReflowRamp()
{
  PrintSetMenu("Set Reflow Ramp","Sec", ReflowRamp,1,20,3);
}

void SetReflowTemp()
{
  PrintSetMenu("Set Reflow Temp","C", ReflowTemp,100,300,220);
}

void SetReflowTime()
{
  PrintSetMenu("Set Reflow Time","Sec", SoakTime,20,200,60);
}










void Save()
{
  PLEX.lcd.clear();
  PLEX.lcd.print("     Saving     ");

  EEPROMWritelong(0,SoakRamp);
  EEPROMWritelong(4,SoakTemp);
  EEPROMWritelong(8,SoakTime);
  EEPROMWritelong(12,ReflowRamp);
  EEPROMWritelong(16,ReflowTemp);
  EEPROMWritelong(20,ReflowTime);
}

void Load()
{
  if(EEPROMReadlong(0)>0)
  SoakRamp = EEPROMReadlong(0);
  if(EEPROMReadlong(4)>0)
  SoakTemp = EEPROMReadlong(4);
  if(EEPROMReadlong(8)>0)
  SoakTime = EEPROMReadlong(8);
  if(EEPROMReadlong(12)>0)
  ReflowRamp = EEPROMReadlong(12);
  if(EEPROMReadlong(16)>0)
  ReflowTemp = EEPROMReadlong(16);
  if(EEPROMReadlong(20)>0)
  ReflowTime = EEPROMReadlong(20);
}

void EEPROMWritelong(int address, long value)
{
  byte four = (value & 0xFF);
  byte three = ((value >> 8) & 0xFF);
  byte two = ((value >> 16) & 0xFF);
  byte one = ((value >> 24) & 0xFF);

  EEPROM.write(address, four);
  EEPROM.write(address + 1, three);
  EEPROM.write(address + 2, two);
  EEPROM.write(address + 3, one);
}

long EEPROMReadlong(long address)
{
  long four = EEPROM.read(address);
  long three = EEPROM.read(address + 1);
  long two = EEPROM.read(address + 2);
  long one = EEPROM.read(address + 3);

  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

Latest Projects

Popular Projects