Skip to content
← All projects

Project / Hardware

Solder Reflow Oven

Role
Solo build
Timeline
Add date
Context
Personal project
Tools
Arduino PID MAX31855 SSR

At a glance

Problem
Hand-soldering fine-pitch SMD components is slow and inconsistent. Commercial reflow ovens are expensive; a converted toaster oven needs a proper temperature controller to follow a solder paste profile.
Approach
Converted a toaster oven with a solid-state relay and thermocouple, then wrote a PID controller on a microcontroller to execute a configurable reflow profile (preheat → soak → reflow → cooling).
Outcome
Successfully reflowed boards with components down to X mm pitch. Used on all subsequent personal PCB projects.
Toaster oven converted into a reflow oven, with thermocouple probe and laptop running serial monitor in background
Fig. 1 — Converted reflow oven with thermocouple probe and live serial monitor.

Overview

Reflowing SMD boards by hand is tedious and inconsistent — a proper oven lets you run a controlled thermal profile and get repeatable results. Rather than buying a commercial unit, I converted a toaster oven by adding two solid-state relays (one for the top element, one for the bottom), a Type K thermocouple via a MAX31855 breakout, and an Arduino to close the control loop.

The firmware executes a five-stage profile — Preheat, Soak, Transition, Reflow, and Cooling — advancing automatically based on elapsed time or temperature, whichever comes first. Temperature and timestamps are streamed over serial in CSV format each loop cycle, which made it easy to log real trials and plot measured vs. target curves.

Design decisions

The biggest firmware challenge was keeping the temperature tracking smooth without overshoot. I used a dual-gain PID strategy: aggressive tuning (Kp=300, Ki=30, Kd=15) when the oven is far from the setpoint, switching to conservative tuning (Kp=150, Ki=10, Kd=5) within 2 °C. The PID output drives a 5-second time-proportional window rather than PWM, since SSRs are rated for limited switching frequency.

The top and bottom elements are controlled independently — the top element runs at 75% of the bottom's output to compensate for the natural heat distribution of a toaster oven, which tends to be bottom-heavy. On any MAX31855 fault (open thermocouple, short to GND or VCC), both relays cut immediately and the loop exits — no silent failures.

Each stage linearly ramps the setpoint from the temperature at stage entry toward the target, which avoids the step-change overshoot you'd get from instantly jumping the setpoint to the stage maximum.

Reflow profile

Stage Duration Target temp Notes
Preheat90 s90 °CLinear ramp from ambient
Soak90 s130 °CFlux activation
Transition30 s138 °CApproach liquidus
Reflow30 s165 °CPeak — above liquidus
Cooling30 s≤ 138 °CBoth relays off; natural cool
Fig. 2 — Measured thermocouple temperature across 5 trial runs. Dashed grey lines mark stage target temperatures (90 / 130 / 138 / 165 °C).

Trial progression

Trial 1 was an early heating test with no stage gating — the oven climbed continuously past every target temperature, reaching nearly 200 °C before being stopped manually. It confirmed the SSR and thermocouple were working, but exposed that the PID alone, without stage boundaries enforcing a setpoint ramp, would happily overshoot indefinitely.

Trial 2 aborted at 59 °C over a 241 s window. The oven sat at ambient for the first 30 s then heated very slowly, suggesting an SSR connection issue or a cold-start condition that prevented the relays from driving at full authority in the early stages.

Trial 3 was the first run with the full five-stage profile and dual-gain PID enabled. The controller tracked the preheat and soak stages reasonably well but stalled around 133 °C — the initial aggressive Kp was too low to push the oven past the soak plateau and into the reflow zone before the stage timer expired. The oven ran the full 270 s cycle but never reached liquidus.

Raising the aggressive gains to Kp=300, Ki=30, Kd=15 gave the controller enough authority to push through. Trials 4 and 5 both tracked the datasheet profile closely — hitting 90 °C near the 90 s preheat mark, clearing 130 °C just after the 180 s soak boundary, and peaking at roughly 170 °C within 10 s of the 240 s reflow target. The slight overshoot above 165 °C is a known artefact of the 5 s proportional window: thermal energy already committed in the last window cycle continues heating the oven slightly after the controller backs off.

Reflowed PCB with SMD components held up in front of the reflow oven
Fig. 3 — Reflowed PCB with SMD components, fresh out of the oven.
Technical details
Parameter Value Notes
MCU Arduino (SPI) Pins 3/4/5 software SPI, 8/9 relay outputs
Thermocouple Type K Adafruit MAX31855 breakout
Heating elements Top + Bottom SSR Top runs at 75% of bottom output
Control method Time-proportional PID 5 s window; dual aggressive/conservative gains
PID — aggressive Kp=300, Ki=30, Kd=15 Gap > 2 °C from setpoint
PID — conservative Kp=150, Ki=10, Kd=5 Gap ≤ 2 °C from setpoint
Peak temperature 165 °C Reflow stage target
Total cycle time ~270 s 90 + 90 + 30 + 30 + 30 s
Data logging Serial CSV Temperature, timestamp per loop cycle
Solder paste ChipQuick NC191LT10 No-clean, low-temp Bi-Sn; liquidus ~138 °C
Fault protection MAX31855 fault flags Open, short-to-GND, short-to-VCC → both relays cut
Firmware — key snippets

Dual-gain PID switch

double gap = abs(setpoint - currentTemp);
if (gap < 2) {
  // close to target — conservative tuning
  bottomPID.SetTunings(consKp, consKi, consKd);
} else {
  // far from target — aggressive tuning
  bottomPID.SetTunings(aggKp, aggKi, aggKd);
}

Time-proportional window + dual SSR

// windowSize = 5000 ms
if (now - windowStartTime > windowSize) windowStartTime += windowSize;

// bottom element
if (bottomOutput > now - windowStartTime)  digitalWrite(BOTTOM_RELAY, HIGH);
else                                         digitalWrite(BOTTOM_RELAY, LOW);

// top element runs at 75% of bottom
double topOutput = bottomOutput * 0.75;
if (topOutput > now - windowStartTime)     digitalWrite(TOP_RELAY, HIGH);
else                                         digitalWrite(TOP_RELAY, LOW);

MAX31855 fault detection

uint8_t fault = thermocouple.readError();
if (fault) {
  digitalWrite(TOP_RELAY, LOW);
  digitalWrite(BOTTOM_RELAY, LOW);
  if (fault & MAX31855_FAULT_OPEN)   Serial.println("Fault: open circuit");
  if (fault & MAX31855_FAULT_SHORT_GND) Serial.println("Fault: short to GND");
  if (fault & MAX31855_FAULT_SHORT_VCC) Serial.println("Fault: short to VCC");
  return; // abort current cycle
}

Stage profile & linear setpoint ramp

// Stage definitions: {duration_ms, target_temp}
ReflowStage stages[] = {
  {90000, 90},   // Preheat
  {90000, 130},  // Soak
  {30000, 138},  // Transition
  {30000, 165},  // Reflow
  {30000, 138},  // Cooling
};

// Linear ramp: interpolate between stageStartTemp and stage target
double progress = (double)(now - stageStartTime) / stages[currentStage].duration;
setpoint = stageStartTemp + progress * (stages[currentStage].target - stageStartTemp);