Introduction
Some time ago I started a project of reusing an old, crappy toaster oven as a reflow solder oven. I did some necessary improvements on the oven, like adding a glass wool isolation. I also examined dynamic characteristics of the oven by looking at its transient response to a step input power. Everything was done as in the textbook, now I just needed to make a controller…
Designing a controller
To control the oven, I decided to go for a classic PID controller. It is easy to implement and I could use it to refresh my memory on automation theory. But how to make one? Here’s the idea:

Put the power in the heaters of the oven and measure the time transient of the temperature in the oven. Measure the time constants and all other coefficients and you have your transfer function of the oven. From parameters of transfer function you can easily calculate parameters for your controller, using, for instance Ziegler-Nichols method. Nice and easy, like that 2nd year course in Automation Control explained.
Except it isn’t.
For my oven, where and
, Ziegler-Nichols method gives very high derivative term (couple of hundreds) and very low integral term (<< 1). This didn’t make any sense – the oven heating is a slow process, you can’t expect fast changing inputs! Also, I-term should be much larger as the output temperature must be proportional to the integral error. A simple Matlab program shows that result is highly unstable with such coefficients. Any other PID tuning methods, like Cohen-Coon, resulted in similar behavior, so I decided to reject them all.
Why couldn’t I use these well-established methods? I don’t know for sure, but I assume that it’s because my oven process is nonlinear. Temperature in the oven will rise if I put more power in the heaters, but it won’t fall at the same way if I reduce the power. In case of warming up, I have thermal transfer by means of convection and radiation, while in the case of cooling, there is only convection. This means that, while cooling down my process is not anymore the transfer function I have measured, but rather something else. Z-N and the others rely highly on linearity of the process and this is probably the reason why they are unusable here.
What to do now? How to get my PID coefficients?
I decided to try it manually. Matlab has some really nice features like pidtuner that enable me to observe the step response of the process with adjustable PID parameters. With the simple manual tuning method, I came to the most satisfying coefficient values: and
.
Implementing a controller
Up till now, everything’s fine only in theory. Math works, I got some nice graphs, but it’s a long way from making it work in a real life.
How to make a PID controller? How to bring all those transfer function in life? To implement a PID controller a single 8-bit microcontroller is all that it takes. Many people have done it already, and there is even an Arduino support for PID contorllers, and I’ve decided to go for a design by Henrik Forsten which I’ve found on his blog. He uses an AVR family microcontroller ATmega8u2 with an embedded USB stack. In his circuit temperature is measured using K-type thermocouple and MAX31855 amplifier that sends data over SPI bus to the microcontroller. Oven input power is controlled by the PWM modulation of the solid-state relay on the mains from the timer in ATmega chip. A code exerpction of his PID implementation is here:
uint16_t pid(uint16_t target, uint16_t temp) { int32_t error = (int32_t)target - (int32_t)temp; if (target == 0) { integral = 0; last_error = error; return 0; } else { int32_t p_term = profile.pid_p * error; int32_t i_term = integral * profile.pid_i; int32_t d_term = (last_error - error) * profile.pid_d; int16_t new_integral = integral + error; /* Clamp integral to a reasonable value */ new_integral = CLAMP(new_integral,-4*100,4*100); last_error = error; int32_t result = approx_pwm(target) + p_term + i_term + d_term; /* Avoid integral buildup */ if ((result &gt;= _ICR1 &amp;&amp; new_integral &lt; integral) || (result &lt; 0 &amp;&amp; new_integral &gt; integral) || (result &lt;= _ICR1 &amp;&amp; result &gt;= 0)) { integral = new_integral; } /* Clamp the output value */ return (uint16_t)(CLAMP(result,0,_ICR1)); } }
The function pid(uint16_t target, uint16_t temp)
is called once every second and it generates a PWM signal that controls the oven based on the target and measured temperatures. Now, let’s disect the code. We start with the error signal, which is basically just the difference between target temperature and actual temperature:
int32_t error = (int32_t)target - (int32_t)temp;
Then comes the part
if (target == 0) { integral = 0; last_error = error; return 0;
that just makes sure that PID is off when target temperature is 0. That means the reflow process is over and we have to turn off the heaters and cool the oven.
Second part:
int32_t p_term = profile.pid_p * error; int32_t i_term = integral * profile.pid_i; int32_t d_term = (last_error - error) * profile.pid_d;
is what calculates the PID terms. Sum of p_term, i_term and d_term
is control signal, i.e. input of the oven. This signal later needs to be adequatly converted to PWM signal.
Now some help variables that need an update in each iteration.
int16_t new_integral = integral + error; /* Clamp integral to a reasonable value */ new_integral = CLAMP(new_integral,-4*100,4*100); last_error = error;
Help function CLAMP
makes sure that integral term doesn’t diverge to unreasonably high values that could lead to PID unstability due to the overflow. Finally P, I and D terms are summed:
int32_t result = approx_pwm(target) + p_term + i_term + d_term;
and the following if
part serves to avoid integral build-up again. The result is constrained to 16-bit size and returned to main function
return (uint16_t)(CLAMP(result,0,_ICR1));
. In the main
function of the code, the result is stored in an OCR1A register which sets the PWM duty cycle in Timer 1 of the ATmega8u2:
OCR1A = pid(target, temp);
Testing the PID controller
To test the math of the controller I’ve put a constant target temperature of 100°C and measured the output. I used a Python script to communicate with the virtual serial port that is connected to the USB of the ATmega and store the data in the text file. The temperature in the oven controlled by the PID is shown on the image below.

I was really happy with the outcome. Rising time decreased to a less than a minute and there was no overshooting. Noisy steady state is attributed to the weird physics of thermal convection and unstable local temperature at the thermocouple position.
I couldn’t wait to see it in real action where the target temperature varies in time to make a given solder profile. In example, warm up rate should be 4°C/s, soaking from 100°C to 110°C in 100 seconds, peak temperature 220°C and time to peak 2 minutes. Here’s the result:

Some photos of reflowed PCBs
Here are some photos of the reflow session. The results are surprisingly good, actually.
One thought on “PID Controller for a DIY Reflow Solder Oven”