ome time ago, I embarked on a project to control my Gaggia Classic Espresso machine with a Raspberry Pi. Obviously, you can buy a fully automatic “bean to cup” machine… but where’s the fun in that!
This project started out with the simple idea of improving the temperature stability, and grew into a more elaborate controller with an LCD screen, water level sensing and pump control. This is an attempt to document the project (partly for my own notes), which I hope to update as I continue to tweak the design.
The project described here is for a Raspberry Pi, although some of the information could certainly be applied to other controllers. If you are interested in an Arduino project, take a look at this great project here also.
In this setup, the machine currently boots up automatically when powered on, regulates coffee temperature, displays current temperature, pressure and water level on an LCD and logs time, temperature, pressure and flow data to CSV files, which are accessible over the wireless network via Samba.
Currently, there are two push buttons on the front panel. The top one automatically dispenses a measured shot, using a flow sensor and automatically switching the pump on/off. This can be interrupted by pushing the button again. The lower button has two functions: a short push will toggle the boiler power off or on, whereas a long push will cause it to safely shut down the system.
The system has a “shot timer” showing elapsed time during a shot. In future there are lots of other possibilities such as showing the volume of coffee dispensed, temperature and pressure graphs, and an interface to adjust settings.
Other ideas not fully thought through include pre-infusion, pump modulation and possibly running the pump intermittently during steaming to top up the boiler. I’ve recently built some new hardware for pump pressure modulation and the next step is to implement the user interface to control that…
Needless to say, this project involves using high voltage mains wiring at high current, as well as the high temperature and high pressure components inside the machine, and the combination of electrical components with water which can obviously be very hazardous. Please don’t attempt to copy any of the circuit diagrams or descriptions here unless you fully understand these risks, and are confident you know exactly what you are doing.
The boiler in the Gaggia Classic has two heating elements embedded in the sides of the boiler. These are normally controlled by a pair of thermostats – one for coffee temperature, and one for steam temperature. Again, these thermostats are wired in series with the element, and the steam button simply bypasses (effectively shorts out) the coffee thermostat. When the steam button is turned on, then only the steam thermostat can interrupt power to the boiler.
The circuit diagram below is for the original Gaggia Classic (the 240V model).
To control the boiler with a Raspberry Pi, we need to switch mains power at about 1300W. We will also want PWM (Pulse Width Modulation) to allow proportional control of boiler power. The best solution is to use a Solid State Relay (SSR), which allows us to efficiently switch power to the boiler. Unlike a mechanical relay, it switches very quickly (essential for PWM) and has no mechanical parts to wear out. Better still, we can select one with zero-cross switching (i.e. the SSR only switches when the mains voltage crosses zero, avoiding spikes), built in opto-isolation (important for safety) and compatibility with logic outputs on the Raspberry Pi.
The SSR will replace the coffee thermostat, so that the boiler can be proportionally controlled, based on temperature measurements taken by the Pi. The model used in this project is the Fotek SSR-25 DA (pictured below). It’s rated for 25A load and operating voltage of 24~380V AC.
The SSR is screwed directly to the stainless steel body of the Gaggia, to provide a heat-sink. The mains cables need to be cable tied into the main loom, to keep them well away from the high temperature components. Note that insulating covers are available for the SSR terminals (not pictured below). The only place the SSR will really fit is at the back left hand side of the unit, near the pump (the water filler spout occupies a lot of space on the right hand side).
The Raspberry Pi has one hardware PWM pin (GPIO18), which we will use to control the SSR. The SSR-25 DA can be directly driven from a 3-32V input, and therefore works with the 3.3V logic level GPIO pin on the Pi. The negative input terminal of the SSR is connected to GND, and the positive input to the GPIO pin. The SSR even has an indicator LED to show when it’s triggered.
Since the SSR will only switch where the AC signal crosses zero, this means we need to use a fairly long PWM cycle time (low frequency) in order to get any kind of useful control of the boiler. With 50Hz AC, we have 50 cycles of the mains per second, and 100 points where it crosses zero. Effectively, the boiler can be turned on for whole units of 10ms. Therefore, if we have a 1 second PWM cycle time, we would be able to vary the duty cycle with about 100 discrete levels, or 1% steps.
However, the lowest rate for the Raspberry Pi hardware PWM seems to be about 1.14Hz (with PWM clock of 4095 and range of 4096). Thinking it would be better to use a sensible multiple of the mains frequency, I’m using a clock divisor of 2400 and range of 4000 for a PWM frequency of 2Hz. This means we have 50 “half waves” for each PWM cycle, which means we get a lower resolution of 50 discrete levels (still pretty good).
To control the boiler I use a PID (Proportional Integral Derivative) control loop. At each time-step, this takes a current temperature measurement and, based on the desired target temperature and PID control parameters, calculates the boiler output (the duty cycle for the PWM).
The control parameters of the PID are then tuned to give the best performance.
The pilot light (neon) on the front panel will still flash on and off as the SSR switches the boiler, so we can see when the boiler is operating. The light will be off whenever the boiler is on. As it reaches the target temperature, it will be on almost constantly.
To measure the temperature of the boiler, there are many different options available. We could use a thermocouple or a temperature sensor with linear output, but this would require ADC and, potentially, needs calibration.
In this project, I originally used the Dallas DS18B20 Digital Thermometer, which has a number of advantages. First, it is already calibrated and accurate to +/-0.5°C. It also uses a serial digital interface that can be easily interfaced. Finally, it uses a 1-wire bus interface that means we can potentially add several temperature sensors to a single bus. This reduces the amount of wiring needed and, more importantly, the number of GPIO pins needed.
Naturally, there are some disadvantages also. The sensor has an operating range from -55°C to +125°C, and we could potentially exceed +125°C in steam mode. Also, it has a fairly slow conversion rate which can be as high as 750ms with 12-bit resolution.
Originally, I spread the DS18B20 with thermal paste and cable tied it flush to the aluminium body of the boiler, in the vicinity of the existing thermostat. This performed well, and I ran the machine like this for several months.
When I first interfaced the sensor to the Gaggia, I wrote my own code in C++ to talk to the DS18B20, and set the resolution to 10-bit, which gave a reasonable conversion time of 187.5ms and resolution of 0.25°C. I later found that there was an existing kernel module called w1-therm which directly supported the DS18B20, and decided it would be preferable to use this instead. The major disadvantage of the w1-therm module is that it hard-codes the resolution to 12-bit, with a very slow 750ms conversion rate.
Temperature Sensor Upgrade: the TSIC306
Eventually I upgraded to the TSIC 306 sensor which offers a higher temperature range and faster update rate compared to the DS18B20.
Like the DS18B20, the TSIC 306 is a factory calibrated digital temperature sensor which communicates over a simple 1-wire bus. It has an accuracy of +/-0.3K, resolution of 0.1K, measurement range from -50°C to +150°C and sampling rate of 10Hz. It is available in a TO92 package with three wires (VCC, GND and DATA) and can be directly interfaced to a single GPIO pin.
Of course nothing is perfect and one disadvantage of the TSIC 306 is that it requires a separate GPIO for each sensor (where you could have 100 separate DS 18B20 on a single wire bus!) However, for this application, I can live with that limitation.
The new temperature range of 150°C is much more suitable, given the high boiler temperatures that we could reach during steaming.
I’ve encapsulated the sensor in a hex spacer (more details), and insulated with heat-shrink as pictured below (bottom) next to the original thermostat (top). This screws straight into the side of the boiler, replacing the existing thermostat.
Reading the sensor from user mode worked but was a little unreliable due to unpredictable OS interruptions. To improve reliability, I wrote a kernel driver to talk to the sensor, which I used for about 8 months. Later still, I re-implemented it again using the PIGPIO library, which is the current version used. The new sensor is now installed and running on the machine, and seems to perform very well.
The pump in the Gaggia Classic is an Ulka EP5 vibratory pump, which develops 15bar pressure and is rated at 48W. It has an integrated diode, and is powered directly by 240V mains.
To switch the pump on and off with the Pi, I initially used a relay. This was switched from the Pi with an NPN transistor, resistor and diode across the relay coil. The normally open contacts of the relay were wired in parallel with the existing manual pump switch, so that manual control was still possible. Therefore, the pump could be turned on/off manually, and on/off from software by the Pi.
This has now been replaced (as will be described shortly), but here are the details for the record: the relay was a Rayex Elec. LEG-6 cube relay (coil resistance 100 ohms, nominal coil voltage 6V and current 60mA). The contacts were rated for 10A resistive load and 5A inductive load at 240VAC. The transistor was a 2N4401 NPN. This was the circuit originally used:
More recently, I replaced the pump controller circuit with an upgraded design using an SSR to switch the pump power on/off, and an IGBT to allow the pump pressure to be modulated using PWM. This is documented separately on these posts (which describe development from first prototype to custom PCB manufacture):
The design still allows for the pump to be switched manually using the front panel switch, but it can also be switched in software using an SSR. When the pump is on, an IGBT can be used to chop the supply rapidly (1kHz – 2kHz) to regulate pressure.
Analogue Pressure Sensor
To measure pressure, I’ve added a Danfoss AKS32R analogue pressure sensor with 0-300psi range (20.68 bar).
I’ve also installed a front panel pressure gauge (manometer) which is useful for calibration It also looks really cool on the machine
To connect the pressure sensor and gauge, I cut the high pressure output hose from the pump and inserted a push-fit T piece. Then I ran some 6mm PTFE tubing from the new output to another T piece, which is connected to both the pressure sensor and front panel gauge.
The logic level inputs and outputs from the Pi enter the machine on the ribbon cable pictured below. The first version used an ABS box (Hammond 100 x 50 x 25mm) which acts as a junction box for all the other components (flow sensor, digital thermometer, SSR, ultrasonic ranger and the pump relay). The ABS box only just clears the conical water filler spout, which enters the water tank through the large black hole visible in the centre right. I made a short aluminium bracket to suspend the box about 45mm below the ventilation slots.
Since then, I’ve added another ribbon cable to carry more signals (required for the pump modulation control), and some screened cable for the pressure sensor analogue output. I also needed a larger ABS box to accommodate all the new components. Frankly, it’s getting very cramped inside the machine at this stage!
The internal edges of the casing have sharp edges, and have been taped to protect the ribbon cable.
This is an old photo, and doesn’t show the pressure sensor and new wiring. I’ll upload a new picture soon, but suffice to say it is becoming a challenge to fit everything inside the machine.
To dispense a shot, we need to be able to dispense a measured amount of water. Running the pump for a fixed length of time wouldn’t work, because the flow rate can vary depending on the quantity of coffee as well as the grind and tamp. We need a way to measure the volume of water that passes through the machine.
The obvious way to do this is to add a flow sensor. Since an espresso shot is a relatively small volume (30ml to 60ml), we need a sensor with fairly high resolution. We can’t put it on the output side of the pump because of the high pressure (~15 bar) and the vibratory pump could give erratic readings, and perhaps damage the sensor. We also don’t want it in contact with the boiler. Ideally, we also want something non-toxic and “food safe” since it will be plumbed into the machine!
When I first started looking, I could only find professional flow sensors ranging from £100 to £350 (!), and also some very cheap sensors which I was dubious about. After some searching, it occurred to me that I might be able to find a suitable spare part from an automatic machine (and this would have the advantage of being food safe). The sensor I ended up using was the Digmesa FHKSC 932-9521/B. This is a common spare part used on a wide range of machines (Krups, AEG, Siemens, Bosch, Nespresso, Jura etc.) It’s obviously ideal for this application and, as a spare, cost less than £10.
I didn’t think to take a photo before burying it deep inside my coffee machine, so here’s a drawing instead…
The data-sheet has full details of interfacing, performance etc. The sensor has a 7mm diameter nozzle with 1.2mm diameter bore, and generates 1925 pulses/litre. Of course this is a nominal value, and will vary in a real application (the manufacturer recommends calibration of the complete installation).
The sensor needs a minimum voltage of 3.8V, so it needs to be powered from the 5V rail. It has an open-collector output, but a pull-up to 3.3V doesn’t work, so I used the following level conversion to interface to the Pi:
The maximum output voltage from the potential divider above should be 5V x 8.2 / (4.7 + 8.2) = 3.18V. The FLOW_IN signal above goes directly to a GPIO input on the Pi.
To measure the flow through the sensor, we simply need to monitor the input and count pulses. With a suitable calibration, these can be used to measure the volume of water dispensed.
The best place to locate the flow sensor is on the input side of the pump, so we can measure how much water is drawn from the water tank. The problem is that an OPV (Over Pressure Valve) lies between the output of the pump and the boiler. This valve regulates the 15bar pressure from the pump down to about 9-10bar, by opening at a set pressure. Normally, the output (waste water) from the OPV goes through some silicone tube back into the water tank.
This leads to a problem: if we measure the input to the pump, we can’t measure how much is lost through the OPV, so it won’t give us a true measure of how much water has been dispensed from the group head. To solve this problem, I simply added a T-connector to link the output of the OPV back to the input of the pump. This means that there will only be one tube hanging in the water tank, compared to the normal two.
The T-junction and flow sensor are just visible below (the flow sensor is the circular white unit in the centre right. The OPV is visible immediately above the flow sensor. The red rubber cover is where water enters the OPV from the pump, and the tube exiting the top of the OPV joins the T-piece which combines with the output of the flow sensor, and is connected to the pump inlet.
For more detail: Project Coffee: Es(pi)resso Machine