Wednesday, November 23, 2011

Saeco Aroma PID Makeover

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.

With the firmware appearing to be working smoothly, it was now time to crack open the Saeco Aroma, rip out the existing wiring and thermostat circuitry, and replace it with the new system. This was pretty straightforward, as the wiring for the MCU-based system that I was putting in place was a lot simpler than the wiring for the passive system that had been there before (which I mean I cut more out than I put back in). Here is a shot of the internals after the surgery had been conducted:

The temperature sensor is connected to the red/yellow/orange wires and is pushed against the boiler by some metal tabs that were used to hold the thermostats in place. The two external buttons control the pump and steam/brew setting; the pump switch still switches the pump on and off (for now, anyway), but the steam/brew switch now runs to the Espresso PID board instead of switching between the two different thermostats. The heavier gauge wires exiting the cavity on the right are the 120VAC power used to derive the 5VDC to power the Espresso PID board, and the power for the heating element which runs through the solid-state relay on the Espresso PID board.

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:

The SSR is obscured by the heat sink, and behind that lies the circuitry for generating the 5VDC power supply for the board. The blue cylinder is the piezo buzzer, and between that and the parallel LCD wiring lies the ATMega328 MCU.

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:

When the Saeco Aroma is powered on the Espresso PID system comes to life with LCD lighting up and the greem indicator LED beginning to pulse to indicate that it is warming to the setpoint:

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:

Pushing the brew button turns the pump on and delicious epsresso begins filtering out through the twin spouts on the portafilter:

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!).


  1. Nice work!
    I've been thinking about doing the same with my cheap espresso machine at home.
    I'm a civil engineer with control systems as my speciality.
    I would love to contribute to this project. If you send me a bit more data. Like the control signal as well as temperature output and your PID-parameters I'm sure I will be able to help you improve the control significantly.
    Add me on facebook:

  2. When I build a PID system, not only do I output the target and current temperatures, but also the values of the PID terms. This allows considerable visibility into what the PID loop is "thinking".

    I also do a few other things, such as clearing the I term if the P term is saturated (and I set the P gain (Gp) very high, but not so high that the temperature overshoots with Gi=0, Gd=0), and use a squared error but assign it the same sign as the original error.

    You might consider adding a fixed constant to the PID result when the pump is turned on (you know you're going to need to add heat before you see the temperature change). You would do something similar if you could measure steam pressure (at 120C).

  3. Very nice build !
    Just wondering if routing the pump through a SSR is necessary. All that's required is knowing when the pump is activated. But I can imagine that being able to drive the pump would allow for two things: time the run to deliver the correct amount of liquid in the cup automatically (using a push button instead of a switch) and also delay the start of the pump to give the heating element a head start to counter the cooling of fresh water.

  4. Thanks for the comments!

    mobileme: Sure, once I spend a bit more time working on the tuning I'll send the gain signal as well as the temperature and it would be great to get feedback on further improvements.

    Brett_cgb: Yes, that is a great idea. I currently have a debug mode where I have it turn the display panel LED on whenever it turns the boiler on, so I do get some visual feedback as to what the loop is doing, but it would be nice to plot this along with the temperature, as you say. That is a great idea to clear the I term when P is saturated! I have been struggling with how to get good steady state error and disturbance rejection without overshoot, I will definitely try that. Thanks!! I have considered the idea of a fixed offset added to the controller output when the switch is on (feedforward compensation), the current challenge is that the pump switch is a SPST switch that directly switches 120VAC across the pump, so I don't have a good way of using a low voltage input to sense when the pump is on. I'll work on that though, since it would be a big help for disturbance rejection.

    Yann: Thanks! You are right in that it isn't strictly necessary, the current issue is that the pump switch is a SPST switch that switches 120VAC across the pump, so I don't have a good way of sensing it's position with a low voltage MCU input. Using the SSR to control the pump would be one way to get around that, but unless I was also going to implement closed-loop control for the pump it would make more sense to just retrofit a double pole, single throw switch for the pump and use half of it for sensing and the other half for turning the pump on. Yes, that is a good idea to delay the pump to allow the boiler to turn on. I would also like to add a pressure sensor and then control the pump to maintain the requisite pressure for pulling a shot (usually about 9 bars).

  5. Good!
    Brett_cgb is right about clearing the I part to avoid integrator windup (which is probably the cause of the huge overshoot).
    However I think it's better to look at both P and D parts and see if they saturate together. If they do look at the sign of the I part. If it's the same as P & D together. Clear it.
    Also feedforward is a good idea.
    An indicator that the pump is running is that sudden temperature drop that comes when starting the pump. It's not as instant as knowing when the pump is actually running but it's something, and it should increase performance if used.

    Another issue is that you can heat it upp much faster than it cools down (when the pump is not on that is). This makes the whole system quite non-linear. Feedforward is really useful here.
    Since the only time the temperature is really important is when pulling a shot. Finding the output that keeps the temp at 94 deg when the pump is running and using that as offset when "pump running" is detected will probably give very good results.

    Please post some more plots somewhere when you have implemented new things.

    P.S changed my nick from mobileme... I don't know why it ever was mobileme.

  6. I have a Starbucks branded espresso machine which may very well be a Saeco in disguise! Your project makes me want to do the same thing to make it a bit easier to use.

    Do the buttons on the front fascia still work?

    One think I noticed is that you pull shots before the milk is ready, should not the milk be steamed and ready prior to pulling the shot? In my experience, espresso dies pretty quickly unless consumed or "saved" with a bit of milk, sugar, or water. The flavor (or lack thereof) of a dead espresso shot is outright flat compared to a freshly pulled one.

    1. Yes, the front buttons still work. And yes, I have since realized it is better to steam the milk first, especially on this machine with a small boiler that can be cooled for the shot pretty quickly after steaming.

  7. Here are some more motivations for decoupling or sensing the pump's existing button:

    1. Feedforward compensation, as mentioned.
    2. Timed semi-auto cooling flushes, in case you take LeeSayao's advice and steam milk first. Displace some hot water with cold, let the temperature settle, check again. (If it's good enough for a Mr Coffee pump toy, surely it's good enough for a nicer machine.)
    3. Pump timeout/lockout for pump protection.
    4. If necessary, you could replenish the boiler and improve the pressure during steaming by sending full cycles of ac about twice a second.
    5. Styling. I suspect finding a DPST button to match the existing look will be difficult, and with all that effort you put into the tombstone, it deserves better.

    Since you don't appear to be galvanically isolating your circuit from the mains (and with all that water in there? for shame!) a large resistor, a 3v zener and software "debouncing" will sense the presence of ac well enough. If you do the safe thing and insert a small filament transformer, it's still just a matter of an ac optocoupler like the FODM2705 plus an appropriate resistor and it costs under a buck. A couple of mA through the LED side of the opto causes the open collector side to pull to ground. You'll probably still need a debounce to correct for the zero crossing (but you'll probably want to sync to the line to implement #4 so keep any low-pass filtering light).

    It's not clear whether you are using any PWM/dithering/delta-sigma modulation on the heater output. If not, you should try it as I think it would make the control loop perform much better. It should be slow (period on the order of 2Hz) since the SSRs will only turn off on zero crossings.

    Finally, if you were to test the response of the system to a step input *from the pump* you can probably characterize the boiler better.

    Great build and great writeup!

    1. Good points, I am working on a new version that will have control over the pump for many of the reasons that you mention. It will also be safer, no line voltage sitting out in the open above a water tank :-).

  8. LeeSayoa: Yes, the functionality of the front buttons has been preserved. And you are right, I probably should steam first and then pull the shot second.

    Jonathon: Great ideas! Those are some great motivations for pump control, I designed in a spot on the PCB for that so I will probably throw one on there and implement some of the things you mention. I especially like the idea for cooling the boiler more quickly by running the pump.

    Thanks for the advice on switch sensing in the presence of AC also, those are great ideas. I have no idea what galvanic isolation is, it sounds like I should look it up :-). It is on a GCFI outlet, so at least that provides some modicum of safety.

    Interesting idea for sigma delta modulating the PWM output. My take on that is: sigma delta modulation pushes the noise to a higher frequency so that it can be filtered out by the bandwidth of the system, but in this case the bandwidth of the boiler thermal system is already so far below my PWM frequency that I would get limited additional benefit by using a delta sigma to push the noise even higher. It sure would sound great on my feature list though!

    That is an interesting idea to characterize the step response to the pump, do you suggest that for the purpose of the feedforward control for disturbance rejection?

    Thanks for all of the great suggestions, I'll start working on those and post an update with the improved version.

  9. I love reading about your projects - both in the house and on the espresso machine and out in the mountains. Glad to hear you are staying busy during the cold wet months of winter.

    Happy belated anniversary to you and Roanne! I smile thinking about mt. Baldy exploits....


  10. Thanks Jen! I have been checking your blog out, good to hear that you are liking CO! We'll have to come out for a ski trip this winter. Enjoy the sunshine!

  11. Cool modification, but if you're putting in all this effort you really ought to switch to a non-pressurized portafilter.

  12. Good idea, I just ordered one! I have been thinking of doing that for a while, so thanks for the prompt. I ordered this one from Seattle Coffee Gear:

  13. I hope you enjoy it. I remember being very reluctant to spend that much on a portafilter (plus the cost of a tamper) but it's really a huge improvement over the one that comes with the machine. It'll take some practice to get the grind and tamp right though.

  14. Since the temperature spike when the pump turns on is caused by the sudden rush of cold water in; what do you think about adding a second heater to the reservoir?
    Something like this system. (and you already have the PID! if it has enough IO left)

    It doesn't have to be 80-90°C that the boiler is targeting, anything above 20-25°C will be an improvement.

  15. Thanks AmrithS, that is a great idea! I had actually been thinking about that lately also, since the temperature drop when brewing is due to the inrush of cold water, why not head the water? I think it would be perfect to have a small buffer reservoir that gets heated (larger than a shot), since it would take a while to heat the entire water reservoir. I do have another SSR on the board, so if I decided not to use it for the pump I could use it to heat the water. Lately I have been focused on trying to get the PID constants dialed in, I'll post an update soon where I go through what I have learned from that. Thanks for the comment!

  16. You'd need to repackage the whole thing to add another reservoir. But you have a good point about heating the whole tank just for one espresso.

    Maybe just have a heater (w/o PID) that's located near the inlet, and just make it an uncontrolled preheat before the water gets sucked in to the pump.

  17. nice explanation....,
    most of people don't know about the internal working or it's process.
    thanks for giving us useful information.
    saeco coffee machines perth

  18. Hi again! (I'm G-man/mobileme but changed name... again
    Modded my own machine and now the control is very good.
    I had to do implement special cases to account for the different states (warm-up, "keeping warm -> pulling shot" and pulling shot)
    The transition from keeping warm to pulling shot is handled, like I suggested, by looking at the d-part.
    In the image below x-axis is seconds y-axis is output 0-1024 and input is not in degrees but 590 (warm-up setpoint) is 102.8 degrees and while pulling shot 476 i.e 94.0 degrees.
    These are just the ADC values heavily oversampled to increase precision.

    I've done some unorthodox things to improve performance (open loop at times, qudratic derivatives to quickly detect shot pulling (about 1 sec delay), and pseudo feedforward).
    After warmup it holds at about +/-0.5 degrees from 102.8.
    After drop in heat due to influx of cold water when pulling a shot it holds at about +/- 2.5 degrees from 94.

    Below is a version with the P,I and D parts. It's a bit messy.

    I'm @GusOst on twitter if you want the code or otherwise.
    You've been a huge inspiration and I would have bloged about it if i wasn't so lazy. My entire build including wireless by bluetooth will be about 12$. That would have been my angle. On. The. Cheap.

  19. Hey Gustav,

    Sorry for the super slow reply and thanks for posting that. Nice work, that is really tight control! I would love to see the code for implementing that, I'll try to get in touch on Twitter. I came to similar conclusions with respect to adjusting the gain constants depending on whether in warming or maintaining modes, and I also ended up using a higher setpoint to warm to so that when you pull the shot it settles out at the right temperature. What did you use Bluetooth for? What MCU did you use?


  20. Interesting project(s). I have an Racillio Silva that I for a long time have had a plan to hack. Sure for this machine is there several pid replacement already made that i prebuilt but rolling my own is so much more fun.

    Would love to see you guys share the code (and schematics files), perhaps put the code on github?

  21. I don't know if you still have your Saeco, but I found on Seattle Coffee Gear that you can buy a non pressurized portafilter (bottomless!). This could also improve your coffee. I will look forward to implement a PID eventually to my unit.


  22. Yes, I have since replaced the portafilter with the one you mention, I also put a new frothing wand on it (from the same place) which made a great improvement on the milk steaming performance. I am planning on making an updated version of this system with some improvements, hopefully others can make use of it also once I am finished.

  23. Hi Cameron,
    i am hacking my Saeco right now and i would love to see your code. Is there any chance i can get it?

  24. Very nice work M.Charles! I started reading about your project over a year ago and I've developed my own project (you inspired me, it's very cool, thank you). Can't wait to see the updated version!

    By the way, I don't think somebody pointed out the fact that Saeco has flowmeters on automatic machines. I've added one on my system, it's affordable.

    I'm currently programming the thing and I don't have experience with PID tuning. To help me getting in the ball park, I would appreciate having PID constants and, of course, the corresponding ranges of inputs and outputs from someone who has acceptable results. Any help is greatly appreciated !

    1. Thanks Gabriel, what flowmeter did you use? I have thought about this but never found one that seemed suitable. Let me know if you still want to know my gain constants, on this one I think they were something like 50-0.2-1000 for P-I-D respectively but I can double check. I can also send along the plots if you are interested still.

  25. Very impressive !
    I wish I could do the same.
    Btw, what bothers me is that it is easy to forget to turn the machine off and then it heats up a lot and wastes energy. I wish they had put a thing to automatically turn the machine off after 4-5 minutes and that would allow for immediate turn on after it operated in case you need more time.

  26. Hi,

    I'm currently trying to do pretty much the same thing on a Netduino. Currently, I have some problems when running my relays in pwm mode. I'd like to know if your code is available somewhere or if you're willing to share it?


    1. Sorry for the slow reply, do you still want to see the code? Maybe you fixed it yourself in the intervening 2 years :-).

  27. Is there any chance you would be willing to send me your schematic file or a file I could use for toner transfer. I would love to be able to do this.

    1. Sorry for the slow reply (2 years late!), I am actually working on a new version of this that I should be able to write about soon, and I would be happy to share the design files. Let me know if you are still interested.

  28. Hello Mr Charles,

    I figured out something interesting about the Aroma and I wanted to share.

    On a stock Aroma, the temperature sensor is over a section of the reservoir separated from the heating element by a baffle. The result is that the temperature at that spot is greatly affected by the cold water entering the reservoir. This has been done by Saeco because it's a cheap way start the element when pulling a shot. But what is measured there it's far from being the temperature of the water leaving the reservoir.

    What I've done on mine is that is glued a 2mm x 3mm RTD sensor on the reservoir between the element leads and the vapor port, over the water exit port. I'm using a flowmeter that came with an automatic Saeco machine (bought used in a repair shop) and I compensate for the cold water assuming a temperature of 23°C. I finished the implementation yesterday, I still have to improve the thing a little.

    The result is that the temperature measured stays between 94°C and 92.4°C. This is a great improvement over my previous results which were close to yours.

    Thanks and good luck!

    1. Nice! Thanks for the tip, I am working on a new version of the Saeco PID control, and will look into changing the way the temperature reading is taken, as you suggest.

  29. This comment has been removed by the author.