Ever since I carried out the temperature profiling this past spring on my Saeco Aroma espresso machine and saw how inadequate the temperature control was, I had been meaning to hijack control of the boiler heating element and put it under temperature control to improve the regulation.
For context, here is the way that the temperature of the boiler is regulated with the stock Aroma: the power to the heating element is connected by a thermostat switch comprised of two metals with different coefficients of thermal expansion. At low temperatures the switch is closed and the heater is powered, raising the temperature of the boiler. As the boiler heats up so does the switch, and the "throw" of the switch begins to bend up as the metals expand with heat. Eventually the bend is sharp enough that the switch opens, removing power to the heater. As the boiler then cools, so does the switch metal and eventually it will close again, returning power to the heating element. So, the boiler temperature will bounce back and forth between these two extremes, with the swing exacerbated by the hysteresis of the thermal switch. In switching from brew mode to steam mode, the power to the heater is directed through a different thermostat switch with different construction so that the switch opens at a higher temperature (to allow the higher boiler temperatures needed for frothing milk). This form of temperature control is about as crude as it gets, and in my temperature profiling I saw swings of 30 degrees celsius over the course of pulling a shot.
So, I'm no controls expert but I figured that this was a pretty low bar from which to try and improve the temperature control. My plan was to use a similar temperature sensor to the one I used for profiling, hook it up to an Arduino-based platform to poll the temperature, and then use a PID (Proportional-Integral-Derivative) control algorithm to drive the heating element through a relay. And of course, I would need to add a display, LEDs, and buzzers to somehow justify all those otherwise-unused GPIO on the microcontroller.
With this in mind, I set about designing a board based on the Atmel ATMega328 microcontroller. I put on connectors for the various I/O devices (among other things, I would need to read the state of the "Steam" button to adjust the temperature setpoint), a solid state relay for controlling the heater (I would be using a PWM-based approach to adjust the gain of the system and having a mechanical relay switching a few times a second would get pretty noisy), and I also added some extra features for future expansion (such as a second solid state relay for taking control of the pump, it would be pretty cool to also use a PID algorithm to regulate the water pressure when pulling a shot!). I also added a 120VAC to 5VDC conversion circuit to the board as I wanted the Espresso PID board to be able to scavenge power directly from the Saeco Aroma so that it wouldn't need to be plugged in seperately or require batteries.
After a few weeknight evenings and a solid weekend day spent on it, the board was ready to go and I sent it off to Dorkbot PDX to be fabricated. Anyone who likes making their own PCBs should check out this service, it is unbelievably cheap with great service. Here is the final design, starting with the schematic:
And the layout:
A couple of weeks later I found some shiny purple boards in the mail box:
In the intervening time I had thought a bit about how to incorporate the display into the Saeco Aroma, and had decided on using a faceplace that would extend up at the back of the device. I designed the 2D layout for this in Inkscape, and sent the files off to be fabricated in mirrored plexiglass by the good folks at Ponoko (another amazing, unbelievably cheap fabrication service). The faceplate arrived in the mail around the same time that the PCBs did, so naturally the first thing to do was to build up the board and use it to power the LCD mounted in the shiny faceplate:
Sexy! Now I just had to make it work for the intended purpose of regulating temperature. I had burned the Arduino bootloader onto the board to allow for easy programming over the serial port and to allow use of the extensive Arduino libraries. Two libraries that helped a lot were the LiquidCrystal library for controlling the parallel LCD, and the PID library for the main PID functionality. The PID library is really well done and has excellent supporting documentation, I highly recommend it for anything like this.
After writing my first version of the firmware I needed a test bed to make sure everything was working, so I taped the temperature sensor to an incandescent light bulb (which also functions remarkably well as a heater) and used this to check the temperature regulation functions and step through the different states for heating and cooling to different setpoints. This provided a good visual display of the PWM gain function: initially the light bulb would be solidly on as it heated toward the setpoint, then it would start to flash as it approached the setpoint, finally settling into a flashing pattern with the appropriate duty cycle to maintain the bulb temperature at the desired setpoint.
Hopefully at some point the board itself will reside in a cavity between the one visible in the photo above and water tank, but for now while I am still adjusting things I just put it out of sight behind the faceplate:
Naturally there were a few hijinks in getting things up and running (think exploding portafilters, cooked temperature sensors, glitching LCDs, etc.), but in the interest of not losing readers to boredom I'll skip over those incidents and stick to the current operation. Here is the finished unit standing in all of it's glory beside my grinder and artfully arranged coffee cannisters:
The LCD indicates what state the machine is in, and updates every second with the current boiler temperature:
Once the boiler temperature reaches the requisite 94 degrees Celsius it is ready to pull a shot:
After stopping the pump we then press the steam button (which is now routed to the MCU and has the effect of changing the desired temperature setpoint in the PID algorithm) and the LCD indicates that it is now warming to steaming temperature, with the indicator LED pulsing accordingly:
Once the boiler temperature hits 120 degrees Celsius we are now ready to steam:
Everything is working as desired now, with the final remaining task being to tune the gain constants for the PID algorithm. For this, I began by characterizing the thermal response of the boiler to a step input and then calculated starting points for the three gain values using the Ziegler-Nichols approach. This gave reasonable performance, but I am in the process of tweaking these to try and reduce overshoot and improve the disturbance rejection when the pump comes on or the steam valve is opened up. For this purpose I have the Espresso PID output the current boiler temperature on the serial port after every read, so whenever I make coffee I can capture the temperature profile on my laptop and then plot it so that I can see what the impact as of the most recent tweaks to the gain constants. Here is a plot from one such recording session:
The first flat part is after the brew temperature has been reached, then the disturbance is when the pump comes on. The second rise to a flat part is for the steaming, and the disturbance after that is when the steam valve has been opened. This one has about 20-25% overshoot so I definitely need to do some tweaking still, but it is already a big improvement over the previous temperature control; during a shot the temperature in the previous system would drop as low as 65 degrees C, in this temperature profile it only drops to about 80 degrees C. A further improvement that could come about through routing the pump switch to the MCU and putting the pump on another SSR would be that the MCU would know when the pump was being switched on, and could instantly boost the signal to the heating element to improve disturbance rejection without having to wait for the PID control to respond (for which the delay is set by the relatively long time constant of the thermal system).
Anyway, that is about all to report for now but stay posted in case I get around to implementing the long list of possible improvements that I have been compiling (or more likely, in case I move on to another fun project and write about that!).