Project Coffee: Es(pi)resso Machine

Some 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…

Obligatory Warning

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.

Boiler Control

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

Gaggia Classic Circuit Diagram

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.

Caution: I’ve since read that some of these Fotek branded SSRs are fake, and may not meet the rated current. Take care in selecting a suitable SSR for your mains voltage and load current.


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

SSR Location

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.

SSR Interface

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

Zero Cross Switching

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.

Digital Thermometer

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.

Pump Control

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:

Pump Interface

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):

Ulka EP5 Pump PWM Pressure Modulation

High voltage output stage circuit diagram

PCB Design for the IGBT Driver Board

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


There are more details on the sensor in this post. Since the Pi doesn’t have any analogue inputs, I’m using the Adafruit ADS1015 board to interface this to the Pi.

I’ve also installed a front panel pressure gauge (manometer) which is useful for calibration It also looks really cool on the machine 🙂

Pressure Gauge

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.

Logic Wiring

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.

Internal Junction Box

Flow Sensor

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:

Flow Sensor Interface

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.

Gaggia Plumbing

Note that the water filler spout enters the water tank through the large hole visible in the bottom centre of the image.

Measuring Water Level

To measure the water level in the tank we could use some kind of float sensor (e.g. with a potentiometer), or an optical system (e.g. optical distance sensor to a floating ball in a tube). However, these would both involve having material in direct contact with the drinking water, and could also be difficult to mount and clean. Instead, I decided to try using an ultrasonic range finder, which is non contact and will give a distance measurement that we can convert directly into an estimate of water level.

The HC-SRC04 (shown below) is very simple to interface, and provides a fairly accurate range measurement (claimed 3mm). It is also very low cost (typically less than £3).

HC-SRC04 Ranger

The module has connections for 5V power, GND as well as a TRIG input and ECHO output. To trigger the module, you give it a high pulse lasting at least 10us. It will then transmit 8 pulses of 40kHz ultrasound, and wait for the reflected echo. The ECHO pin will go high for a time period proportional to the distance. Using the speed of sound, we can calculate the distance.

The ranger is designed for 5V power and logic, so we need to use level conversion to interface to the 3.3V GPIO levels used by the Pi. In my case, I found that the 3.3V output from the Pi was sufficient to trigger the unit when directly connected to the TRIG input. However, level conversion is needed for the ECHO output as shown below.

Ranger Interface

To measure the distance accurately, we need to be able to time the ECHO duration quite precisely. Originally, I implemented this in C++ from user mode, using edge-triggered GPIO interrupts, and recorded a time-stamp at the start and end of pulse. Subtracting the two gives a measure of echo time.

However, due to unpredictable OS activity, the time measurements  were not all that accurate, so it was necessary to use multiple sample averaging or filtering to obtain good results.

Eventually, I gave up on user mode  and implemented a kernel driver which can achieve more accurate timing. This worked much more reliably (I would estimate about 2-3mm resolution), and gives a nice steady level indication on the LCD. More recently, I re-implemented the range finder with the PIGPIO library which makes the software much simpler to install and use.


The display is the HY28A which is a  2.8″ Colour Touch Screen TFT LCD with an SPI interface for both the LCD and the touch sensor. It has since been replaced by a newer model HY28B.

This display has 320×240 resolution with 16-bit colour, and the SPI interface means that it only needs a small number of GPIO pins to interface. Amazingly, it cost under £10.

Some very slight negatives are that it uses 2mm pitch connectors instead of the more common 2.54mm (0.1″) and has no mounting holes.

When I bought this, I fully expected to have to write the code to drive it, but was surprised to find that  Linux Framebuffer drivers are already available for this display, and many others. There are some useful instructions here.

I have this configured so that it displays a console on the LCD as soon the Pi boots, so that any boot messages can be seen. Rather than running under X Windows, I decided that the controller would run under the console to reduce overhead and use SDL to draw on the LCD.


The first version was installed in a standard Raspberry Pi case, and simply screwed to the outside of the case. I wanted a more permanent installation that would have space for a display, and something a bit neater looking.

Note that the factory protective film is still stuck to the LCD screen in these photographs, which is a bit dirty/scratched. I’m planning to buy some new screen protection material to replace it when I’ve finished tinkering with the hardware.

Control Panel Position

The current enclosure has two anodised, brushed aluminium panels (175 x 75 x 1.5mm), screwed to an oak frame. The frame is made from 50 x 15mm oak, with two glued mitre joints to make a frame 180mm wide and with two more 45 degree mitre cuts which mount flush to the rear of the machine. The frame is screwed to the machine through the ventilation slots, using two steel L-brackets with machine screws.

Side View of Control Panel

The front control panel is attached by four stainless steel M4 socket headed button screws. There are four holes drilled through the oak frame, and I used M4 inter screws (using only the female part of the original screw which appears to be BZP, and replaced the male screw with longer stainless steel button screws).

There’s a hidden plastic panel so that the electronics are completely enclosed, and to prevent hot humid air from the ventilation slots getting to the electronics. The existing ventilation slots are still open to the air at the rear of the machine.

The Raspberry Pi (Model A) is mounted directly to the rear plate, using a Pi support bar and two machine screws. There is very restricted space inside the unit, and I had to make a cutout in the frame to clear the (redundant) video phono connector. Also, the Pi is powered through the GPIO header, rather than the mini USB power connector.

Control Panel Internals

The LCD touch-screen is (gently!) clamped to the front panel, for a flush fit.

Control Buttons

Although the display has a touch-screen, I decided that wasn’t ideal for daily use in the kitchen, so I’ve added two stainless steel buttons. In the first version, these used 4K7 pull up resistors to 3.3V and were active low, pulling the GPIO pin to ground when pushed. As I added more equipment to the machine, I began to run out of GPIO lines. The latest version uses a single analogue input with a resistor ladder to read all the buttons as described here.

Currently, one of the buttons is used to pour a shot, and one to turn off the boiler and halt the Pi so that it can shut down cleanly before the mains power is switched off. They can obviously be programmed in software for any function.

Complete Circuit Diagram

Here’s an (OUT OF DATE) circuit diagram for the old version. There were three 0.1″ (2.54mm) pin header connectors, P1 and P2 connected the Pi to the LCD touch-screen, and P3 connected the Pi to the machine (sensors and boiler/pump control).

This diagram is very out of date, because it’s missing the new wiring for the TSIC 306 which is currently wired on GPIO 24, analogue button matrix, pressure sensor, high voltage output side for controlling the pump etc. There are circuit diagrams for some of these elsewhere on the blog, and at some stage I will update this page properly.

Note that signals are named from the point of view of Pi, so for example RANGER_TRIGGER_OUT means an output signal from the Pi to trigger the ultrasonic range finder.


The P1, P2 and P3 headers are soldered to a Humble Pi board, along with the various pull-up resistors and potential dividers. All the wiring between the headers and the GPIO connector is underneath the Humble Pi board. The P3 connector is connected by a ribbon cable to an ABS junction box inside the main enclosure.

I’m using the Raspberry Pi model A because I don’t need the Ethernet and uses quite a bit less power. I’m using medium overclocking in Raspbian at the moment. With the Humble Pi mounted over the Pi and the enclosed space, I was a little concerned that it might overheat, but I’ve monitored the CPU temperature and it runs pretty cool so it seems this fear was unfounded.

Complete Source Code

The complete C++ source code for the project is available on Github here:

Espiresso Source Code (Github)

This is my own personal development version, so the code will obviously update as I make any changes to the software or hardware.

The software used to need kernel modules for the HC-SR04 and TSIC 306 sensors. However, these are no longer required since these libraries were rewritten to use the PIGPIO library, which makes it possible to obtain precision timing from user mode.

At time of writing, this software has been working well for over a year, and I’ve gradually added features and improved it to suit my own needs.

The temperature regulator runs on a separate thread, as does the display controller. Most of the sensors (such as the flow sensor and ranger) also use threads. In places, the code makes use of edge triggered interrupts (for the flow sensor pulse counting, range finding etc), to avoid busy waiting.

As far as possible, the majority of GPIO is now using the PIGPIO library, except for the hardware PWM used to regulate boiler power, which goes direct to the registers (in order to achieve a very low PWM frequency). Earlier versions used the sysfs GPIO interface, as can be seen in older revisions.

Performance of PID versus Thermostat

This graph is a comparison of the performance of the stock thermostat (blue) against the PID controller (red). The original thermostat oscillates wildly, compared to the more stable temperature control achieved with the PID. The small dip in the PID curve at around 780 is the temperature drop when pulling a shot.

Thermostat versus PID

In the graph above, the PID target temperature was set to 95C. Since then, I’ve changed the set point to 93C and tuned the PID parameters to optimise the performance further.

Undocumented Things

This site is a work in progress, and I hope to update the content in future to add more explanation of how it all works. In the mean-time, feel free to ask questions in the comments!

123 thoughts on “Project Coffee: Es(pi)resso Machine”

    1. Thanks Csabi! The ZPM Nocturn looks awesome (and very reasonably priced when you consider the development that went into it). Looking at their front panel, I see what you mean 🙂

      1. James, slightly off topic PID stuff way over my head🤗.

        Have you considered plumbing in the water feed therefore excluding the tank. Is it feasible given the components and design of the Classic pee 2015.


        1. Hi Nigel

          I haven’t considered plumbing it in (our water is very hard, and I normally use filtered or bottled water). However, I’m sure it would be possible (anything’s possible!)

          My first thoughts are that you would need a one way check valve at the water mains (as a courtesy to ensure you don’t pump overflow from the boiler back into the public water mains!) Then you would need a solenoid valve, to shut off the mains water supply when the pump isn’t running. You could wire this to the pump, so that the valve opens only when the pump is active.

          The input pipework on the pump is a low pressure push fitting (barbed connector), so you would need to secure this more firmly to cope with mains water pressure. It looks like UK mains water pressure is statutory minimum of 0.7 bar (, so I guess the incoming pressure is unlikely to be much more than 1 bar, and could be a lot lower.

          Obviously, the pump normally pulls water from the tank, rather than having its input under pressure, so I’m not sure how this would work with the EP5 pump (Ulka may have some datasheets or application notes on this).

          There is also the OPV (over pressure valve) to consider. This is used to limit pressure to around 9 bar (depends on how you set it). It normally vents its output back to the tank. I’ve successfully looped it back to the input of the pump in my own setup. If the OPV output was connected with a T piece to the pump input and mains input, it would obviously be fighting against mains water pressure. However, the 9 bar pressure from the OPV should overcome this, and the OPV should act as a check valve to prevent mains water seeping back through the OPV. The OPV setting might need tweaking, as you have both the spring and mains water pressure acting to keep it shut.

          In short, I think it may be possible to make this work. Obviously this is not something I’ve ever tried, and it would need a bit of experimentation to see if it works.

          Best regards

  1. Have you done any further work on this? I have a Rancilio Silvia I am modding, and may end up using some of your source. Would need to re-work the temperature input to use an ADC, as I already have an internal type T TC, that I would like to continue using.

    1. Hi Danny
      I still work on this sometimes, when I want to add a feature (I still use the machine on a daily basis, and it works well). The latest changes are on Google code here:

      Modifying the temperature input to use ADC should be very easy. Just modify the Temperature::getDegrees function to return the value from your sensor in degrees, and that should be the only change needed I reckon.

      Drop me a line if I can be of any help! Regards, James.

  2. Hi,
    First of all Many thanks for this very Good Blog post. I have also a Gaggia Classic and I have installed now also a microcontroller (arduino mega) with same Flowmeter like you. Everything works almost perfect. I have two Buttons 30ml and 60ml. My bigger Problem is, When I Start each Programme without the Portafilter I get exact 30 or 60 ml in the Cup. But with grinder coffee and portafilter I Miss 20ml on the 60ml Cup? Do You know why that happened? And Where is the water? I’ve all connect the Tube with t-connector like you. Maybe I’ve forget something.

    It will very Helpfull When you can may help me.

    Thanks im Advanced

    1. Hi Khalid. If it gives a correct volume without the Portafilter, but a reduced volume when using coffee, I can think of four possible explanations; 1) water passed by the OPV and returning to the tank not being counted (guess we can rule this out, as you’re using a T-piece like me) or 2) water being forced back through the flow-meter to the tank (due to pressure), or 3) some water being absorbed and retained by the coffee grounds, and not reaching the cup, or 4) tank not fully primed and having some air in the system.
      I’m guessing it could most probably be due to (2) and (3) above.
      Other things I would suggest to check are that the flow sensor is mounted in the recommended orientation and consider the layout and length of the pipes used, which may have an influence on how it behaves. On mine, I’ve connected the T-piece so that there is “straight flow” from OPV to pump input and the “upright” leg of the T-piece is the intake from the flow-meter. There is also a fairly large loop of pipe between the flow-meter and T-piece in mine. These are just my suggestions of some possible things to look at – hope it helps?

      1. I have Gaggia Classic too and AFAIK there is vent that allows some water to run trough bypass tube to drip tray. It usually happens when coffee is ground too fine (or if dummy pod for cleaning is attached).

        1. The bypass tube is connected to a 3-way solenoid valve. When the pump is switched off, this opens a path from the group head to the bypass tube, relieving any remaining pressure at the puck (to prevent it spraying you with coffee when the portafilter is removed). There’s a good explanation and diagrams of the valve operation here:

    2. There are some 1.2mpa flow sensors on amazon uk. If correct that’s 12bar so could be put after the pump. Some are on prime. 0.15L/min and others.

      I have a Sage machine where the meter is on the feed from the water tank. The opv output messes it up if it opens. Could try adding the T. Assume that goes between the meter and the pump but scratch my head a little on how well that will work.

      Thanks for the ideas.

      1. Hi John
        1.2 MPa is 12 bar so pressure wise it should be OK. I haven’t tried putting a flow meter on the output side, but I guess it could work.
        I put mine on the input to the pump with a T-piece, and it works really well, seems to give very accurate volumes. I’ve noticed that some of the Gaggia automatic machines uses the same setup (flow meter on the input side, with a T-piece to include the OPV output).

    1. Another option you might want to consider (found on commercial and hobbyist machines) is to have a “learn mode” so that you can manually pull a shot and the CPU measures the volume dispensed, and stores that for use next time you push the brew button.

      1. Hi James,
        many thanks for your fast reply and help! I think also that he problem is 2) or 3). Also the idea with the learn mode is great. But before I try to check the poin 2 and 3 😉

  3. Awesome project you did great, both on technical choices and quality of craftmanship!
    I am currently working on a similar project (with some fancy features) on my espresso machine, i gathered much inspiration from your work: thank you!
    For the temperature, i am using thermocouple K (i have a heat exchanger machine and i plan to put it inside boiler) but it is not giving stable readings.
    How often do you read temperature? what is your timeslot?
    I’m not sure i read it right, in regulator.cpp i see m_timeStep = 1, seems you try to have timeslots of 1 seconds (1×10^3ms).
    Am i correct?


      1. Hi James,
        Thanks for your reply.
        I ended up with a custom temperature probe (using K type) inside my boiler (i have Rocket espresso Hx) and using Perl script with 0,5s update interval because i’m curious of temperature profile, works pretty fine and very accurate to temperature fluctuations.
        I now need to integrate the SSD control with PID software to control the boiler, a major step but with help of your blog it really makes it easier, thanks again!


        1. Hi Jacques
          It sounds like you are making great progress! Thanks for the update, I was curious to know how you were getting on. The responsiveness must be really good with the probe in the boiler.

          1. Hi James,
            Good news: I managed to integrate your PID code and regulator (SSR) code to my python project (using an ugly C++ wrapper, shame on me) it works flawlessly without any modification to the algorithm, hurray \o/
            The PID regulates quite well the temperature, it is overall very reactive and i have almost no temperature “overshot” (+0,5°C max to the target temp).

            Thanks again for your blog and your really well documented code, i don’t think i would have been able to achieve this otherwise.
            Next step for me is the pressure sensor to integrate, let’s see
            Have a good day!

  4. #James
    Awsome Job my friend and great presentation.
    I have a gaggia baby that Im thinking to mod following your example, but I have never before worked with rasberry nor arduino projects at all.

    Do you think that its good idea to proceed ? Maybe you could help a little during the hole project?


    1. Thanks John
      Arduino would be the easier option if you want a simple temperature controller. The Raspberry Pi takes a bit more work to set up, but is more versatile if you want it to be network accessible etc. If you have never used either, it will take some time to learn of course, but for me that is half the appeal of doing a project like this (I’ve certainly learnt a lot in the process of building this machine!)
      I’ll drop you a separate e-mail, and will be happy to help with questions.

      1. That would be great. Thanks.
        The main purpose would be as a temperature control for correcting the temperature and in addition a water flow meter in order to stop the water after a certain water amount passes (30 or 60ml) the basket.

        Pressure will be measured by an individual manometer at the base of the gaggia.
        Again thank for your time


  5. Thank you for describing your project in detail. I like it very much and would like to adapt parts of it for my Rancilio Silvia.

    You described that you replaced the DS18B20 with the TSIC 306 temperature sensor.
    In the data sheet I read that a capacitor (100nF) should be placed between V+ and GND? (“Recommended as close to TSic V+ and GND-Pins as possible”) .

    I would like to know if you use a capacitor and where you did place it.

    Best regards

    1. Hi Sylvia
      I didn’t use the 100nF decoupling capacitor, and it works reliably without the capacitor for me. It is recommended by the manufacturer, so probably better to include if possible, though certainly not essential.
      Hope this helps?
      Best Regards

  6. Fantastic project! Really well done. I am currently using your project as a guide for my Silvia mods.

    WRT the TSIC 306: I took your C++ code and converted to Python. However I noticed I don’t get any readings above 125 degrees. I think this is because the end of the first packet ends high (parity bit is 1) and the start of the second packet also starts high – this fails check right at the bottom of your alert function. I changed this as a test and fell into problems with the ‘ 5 MSBs not high check’ in TSICdecode which I changed to 4 MSB not high check (xf8 to xf0)

    I’m not particularly au fait with this kind of thing so I’d appreciate your confirmation and a fix if you have one.

    If interested, I’ll do a write up on my Silvia mods once I’m happy with it.

    1. Hi Graham

      I had a quick look at this, and the code seems OK at first glance. The maximum value would be 2047 which only needs 11 bits to represent (since 2^11=2048), so that would be the 8 LSBs from packet1, and the 3 LSBs from packet0. The packet0 value is masked with 0xF8 which removes the top 5 bits, leaving only the 3 LSBs. Those are combined together to give an 11 bit value.
      Have you tried replacing the ‘raw’ value with 2047 in the code to see if it reports the maximum temperature?

      I’ll drop you an e-mail – if you send me your Python code, I could perhaps take a look and see if I can spot anything there?

      Re: the write up of the Silvia mods, I would love to see it!

      Kind regards

      1. Hi Guys! (Sorry my previous comment went in the wrong post.)

        Congratulation for all this work!
        I came up on this blog while looking for a way to interface the TSIC 306 with a raspberry. The sensor will be integrated in an electric motor that drives a light electric vehicle (
        As my main system is written in python (interface with the vehicle serial bus and other measurements) I am very interested in Graham post.

        Did you find a solution for the python implementation?

        I was thinking first to use your tsic.cpp and integrate it in my system via socket communication. However would be more happy with a full python code.

        Would you mind sharing it or giving me some advices on it?

        Thanks a lot!

  7. Hi,

    I just wanted to say this is a fantastic project and your documentation is very helpful.

    I’m planning a nano-brewery using my Raspberry PI and a lot of your espresso machine mods are quite similar to what I need to do, though probably not with as precise meausurements as I’ll be dealing with 5-10l of water instead of 30-60ml.

    I’m quite confident with the software side of things as I work as a software developer, but I’m a bit of a newbie with electronics so I’ll be taking it slow there.

    Thanks for pointing me in the right direction 🙂


    1. Hi Dale
      Thanks for the kind words, I’m pleased you found it useful 🙂
      I’m more experienced at drinking beer than brewing it, but I can appreciate that there will be similarities in the software / hardware needed. Are you planning to write up/blog your project? Sounds really interesting!

  8. Hi there,

    First of all thank you for sharing your ideas and methods! This has been really good to read while doing some modifications myself.

    So I am in the process of adding flow meter control to my NS Oscar with a simple Arduino board, and have a few observations that you may have made your self. Any way I would like to hear your comments.

    So the first thing I noticed was that the pump sounded very tired and supplied a lower flow than normally without the flow meter(with the comparison made without portafilter mounted). I suspect this is caused by the 1.2mm orifice of the digmesa sensor compared to the 2-3mm orifice of the ULKA pump, which will make the pump work harder with the flow sensor in place. Is this something that you have experienced?

    To begin with I was a bit worried by this, but as far as Google will tell me the 1.2mm digmesa and ULKA E* series pumps are a very common match. So I have decided to enjoy the strangled(/muffled) sounds of the ULKA and carry on.

    Have you tried to measure the signal from the flow meter? In the flow meter there are six impeller blades, but only two magnets. This should mean that the time with a low signal is longer that the time with a high signal, and hence it should be a bit more precise to only measure the rising or falling signal, not both. Is this something that you have considered?

    As Kahlid in the comments above, I too have got the correct volume without the portafilter mounted, and when I put in coffee I see the same drop in volume out of the machine. This makes sense… luckily. However, what are you doing to mitigate this? I can see in the code in github that you also dispense a fixed volume based on a calibration. I assume this calibration not has been performed with the filter and coffee inserted since ~230g of water would make a really bad espresso:-).

    My workaround to this issue is to measure the frequency of the incoming ticks from the flow meter, and then only to start counting ticks after the frequency has dropped below a certain limit (when the puck has been saturated and everything starts to be in some sort of steady state). This will require a calibration (with portafilter and coffee) of the number ticks needed to get the desired volume/weight of espresso. It will also require consistent dosing/grinding of the coffee to work, but it actually works quite nicely when that is fulfilled.

    I look forward to hear your comments to the above!:-)

    Cheers !

    1. The best solution I’ve seen to deal with this is what Breville does in their Dual Boiler (they don’t do this on their any of their other lower end machines). They have two flow meters: one mounted in the standard location before the pump, and another mounted on the low pressure output side of the OPV. That way, they’re able to account for water flowing out of the OPV, and subtract that from the input flow to get the actual flow through the grouphead.

      1. Hi Ryan

        Interesting to know that Breville use two flow meters! It’s obviously a good technical solution, though perhaps surprising in a consumer product where they usually try to minimise part cost!

        My solution was to include a T piece which creates a closed loop, so that the output of the OPV is combined with the output of the flow sensor which then returns to the input of the pump. This is the same trick used on a few automatic machines, for example Gaggia Automatica:
        It seems counter-intuitive that this should work (by creating such a closed circuit, you might expect the pressure to build up, or even flow backwards through the flow meter)… but it seems to work absolutely fine in practice.

        Ultimately, I was thinking that with an analogue pressure transducer and PWM modulation of the pump under closed loop control based on the pressure data, the mechanical OPV could potentially be eliminated completely (or perhaps set back to a high value, purely as an emergency pressure relief).

        1. The newer Breville machines (Infuser, DB, and Oracle) are fascinating to me because they are all clearly well designed and laid out, and they pay a lot of attention to the user experience. Unfortunately, despite generally positive reviews, it seems like they don’t get as much love from the community (or get taken seriously) owing to Breville being a general kitchen appliance manufacturer instead of a more niche brand associated with coffee machines.

          I did read about your solution to T the OPV output back to between the flow meter and pump input, but it is indeed counter intuitive to me that something like that would work; I had just incorrectly assumed that while better than no T, it would still be off somehow. Interesting to know that it works absolutely fine in practice – thanks!

          I agree that with closed loop control and a pressure transducer that the OPV can be used instead as a safety valve, but it seems like for that to produce consistent results, you’d also want to take flow into account as well. For example, one of the issues I’ve read about the La Marzocco Strada EP is that if your puck is channeling (or your grind is off etc) and putting up a lot less resistance than expected, the machine (being closed loop) would then ramp up the pump in order to achieve the set pressure, resulting in a fast gusher. Similarly, it seems like an OPV exists in designs based around the Ulka pump because when the pump is running at 9 bars, the flow rate is actually much higher than what would be desirable for espresso, so the OPV acts as an outlet for that extra flow to go. Having not done any testing myself, I wonder to what extent PWM has on the pump’s pressure:flow characteristics. I have a machine that uses a Fluid-o-Tech vibe pump with a different pressure to flow curve (closer to ideal espresso flow rates), and does away with the OPV entirely and instead simply has a flow restrictor in place to limit pressure.

          That is all just a long winded way of saying I have no idea how any of this all works together, so I have flow meters, a pressure transducer, and a needle valve on the way so I can hopefully figure all of this out and share my findings. Thanks to the work you’ve done laying out your process, I’m anticipating much less trouble getting all of the different components to work (but then again, that’s what I thought about PWM pump control…)

          P.S. Rather humourously, I made the exact same mistake you made when I attempted to test the effects of the flyback diode – I, too neglected the series diode. Everything is fine aside from the shorted diode, and surprisingly my surge protector does not include any sort of fuse, so it blew out a PCB trace in the surge protection circuit instead. Will probably take another stab at it today.

          1. Hi Ryan

            I agree, the Breville / Sage machines do look well designed. I would enjoy taking one apart to see the internals 😉

            You make an interesting point about flow rates above. Any kind of closed loop control I guess can be confused by certain conditions as you refer to on the Strada EP. One of the frustrating things with an off the shelf appliance is that you can’t tinker with their software to fix these issues! We have a bean to cup machine at work which is really dumb and would benefit from a few simple code changes to allow more control 🙂

            Sorry to hear about your blown diode – at least they are inexpensive, and you escaped unharmed. This reminds me, that I might build in a small fuse into the supply for the pump/solenoid/SSR branch, for future insurance against any mishaps.

    2. Hi Max

      Good to hear from you!

      I’ve not noticed a big difference in the pump performance due to the restriction by the flow meter orifice, but what you say makes complete sense. This type of flow meter is very widely used in automatic and semi-automatic machines with the same Ulka EP pumps, and it seems to perform well. I notice that there’s a version of the flow meter with 2.0mm nozzle size available also, and the manufacturer gives the maximum flow rate (l/min) and pressure loss (bar) for each model.

      You make an interesting point about the flow meter pulse counting. My current software counts both rising/falling edge. Calibration was done by dispensing clean water (without coffee puck!) into a jug on a digital scale, and weighed the water volume dispensed.

      Your idea of only counting pulses when the frequency drops is an interesting approach. There is a lot of potential to analyse the flow and pressure sensor data to detect the point where the puck is fully saturated and it starts to dispense coffee and, at the other end of the scale, to prevent over extraction.

      I’ve only scratched the surface with what can be done in software so far. Most of my efforts have been on the hardware side to date, but I do plan to do some further work on the software when I can find some spare time!

      Curiously, despite having added the capability to dispense automatically, I still tend to use the manual switch at the moment, so I can keep an eye on the extraction!

      My next modification might be to add a load cell to my grinder, so I can get a more consistent dose (irrespective of the varying weight of beans in the hopper)…

  9. Hi, I have what, after reading the above article, may be a very basic question. I am trying to replace the power button on the back of my baby gaggia. The bottom two connections pulled off before I could see what colors they were in the proper place. Would you happen to know how the four wires connect to the new switch? Thanks, K

    1. I don’t have a Baby Gaggia, and I know there have been a few different versions of the Baby over the years. To be safe, I think you might be better off asking this question on the Gaggia User Group forum. Someone on there would likely have the same machine, and some direct experience:
      It would be helpful to indicate the particular version you have, and provide some photos of the machine – this would help people identify which machine you have. There are some wiring diagrams and drawings for most of the Gaggia machines in the library on the Gaggia User Group, so it would be worth taking a look there also.
      There are some really knowledgeable people on there, so it’s a great resource. If you are still stuck, do let me know of course and I’ll try and help.
      Kind regards, J

  10. I am wondering how you physically implemented the ultrasonic range finder. Was mounted in the boiler? Was it pointed towards the water from the top? Any info on how this was done would be great!

    Awesome project by the way.



    1. Hi Daniel
      I used an HC-SR04 range finder, which I mounted in a very shallow plastic box with two circular holes drilled for the two transducers. It’s used the measure the water level in the reservoir (not the boiler). The range finder is mounted directly above the plastic water reservoir, flat against the steel panel, and pointing straight down at the surface of the water in the reservoir.
      Hope that helps? Let me know if you still have questions.

  11. Hi James,

    I am planning on doing a similar project with my Rancilio Silvia in the next couple months. I’m planning on utilizing the Raspberry Pi 7-inch touch screen with the Raspberry Pi B+.

    I originally bought the 40 amp Fotek SSR on ebay to run the heating element, but decided against it after finding out they are counterfeit. Apparently they can only handle about half of their rated amperage before sticking on / melting. I ended up buying a used 25 amp SSR by Opto 22, which is supposed to be a better brand. For everything else (such as the pump, 3-way solenoid valve, etc), I am using a Sainsmart 4-channel SSR board that uses 4x 2-amp Omron solid state relays.

    I am definitely going to be using your blog post as a reference for my build since you go into so much detail and my project doesn’t vary too much from yours hardware-wise.

    Do you happen to have a blog post about how you go about tuning your PID settings? I’ve read a couple things online about different ways to tune a PID algorithm, but I’m curious to hear about what you did since I still don’t feel like I have a rock solid understanding of what I need to do.

    I do plan on documenting my project using a blog and/or Youtube videos, so I am hoping it’ll just be a few weeks to a month before I get that going (I’m waiting on a bunch of parts to come in for the build and some are back-ordered to February, so there is not much I can do, yet).

    1. Hi Brandon

      Great – should be really nice with the 7″ touch-screen! Is that the official one? I’ve got one on order, but they are still out of stock..

      I’ve been using a Fotek SSR-25 DA for over a year now, they seem reliable. Of course, at 240V UK mains, the current needed for the heating element will be lower (under 6A), so 25A is comfortably overrated.

      I didn’t document the PID tuning process, but the following link may be useful as it has a really good explanation of the process (and the description on P12 is basically what I followed):

      The main problem I’ve had is with Raspberry Pi SD cards being corrupted.. I’ve just had to reinstall my machine over Christmas. It is basically working again, but there are a couple of issues to resolve. I’ll be updating my Blog with the details of how to get it all working on Raspbian Jessie!

      Very much looking forward to seeing your project!


      1. Hi James,

        Yes, it is the official one. Mine is still back-ordered as well. I’m hoping to get it late January or early February.

        Everything I’ve heard about the counterfeit Fotek SSRs is that they work absolutely fine as long as you don’t use more than half of their rated capability. For example, I’ve heard that the triac inside of the 25 amp one is rated for 12 amps max.

        Any ideas on what is causing your SD card corruption? Is the number of writes just wearing out the card? I’m planning on tuning everything to minimize read/write IO on the card. The other thing I may end up doing is buying a bigger SD card since it utilizes wear leveling to prevent early failure. Of course, good backups are a must in the event of a complete failure (git will be useful for this).

        I’m looking forward to reading about your updated version using Raspbian Jessie!

        1. Hi Brandon

          I’m pleased to say that it’s all up and running well on Jessie now! I’ve updated the post here with the details, to avoid having out of date information there:

          There are a few changes committed on Github also, to fix issues that appeared in the upgrade to Jessie (and to add some files I forgot to include originally).

          The SD card corruption I think is either the number of writes and/or occasionally due to switching it off “too early”. The Pi is completely enclosed and I can’t see the activity LEDs on the PCB. When I shut it down, I wait for the LCD to go blank, then give it a few seconds before switching off the mains. Not ideal, so I might add a front panel activity LED, or use this:

          In the recent reinstall, I used a larger SD card, disabled swap, enabled noatime and nodiratime. However, it’s still writing temperature/pressure logs to the card every second, so quite a bit of SD card write activity.

          Another option I’ve considered is to make the SD card read only, and use a USB stick for the log files. I’d need to replace the Model A with the B+ as the A only has one USB port (currently used for WiFi).

          Anyway, to put it in perspective, it’s been about 1 year between serious failures. This was using a SanDisk 8GB class 4 card:
          * ~29th December 2014 – reinstalled
          * ~28th October 2015 – card corrupted, but I was able to fix by running fsck from a PC running Ubuntu
          * ~24th December 2015 – reinstalled with Jessie and a 16GB SD card

          When it got corrupted, fortunately the contents of the SD card were still readable after running fsck from Ubuntu, so the files could be salvaged. I relied on my own notes from this Blog to reinstall it!

          Of course, there have been many changes to Raspbian over since I first set it up (e.g. device-tree overlays), so there were a few corresponding changes needed to get it running on Jessie. I’ve committed everything needed to Github so it’s all up to date again.

          Best regards

    1. Hi Nate, yes – this looks like it would be a really good candidate for the job! Page 3 says it can even tolerate 160C for “a short period” without physical damage 🙂 It would be quite easy to interface also. Good find!

  12. These details are great, and your build is amazing. I’ve had a pi 2 lying around for ages and just restored a Gaggia Classic to working order. My coding and electrical skills are very basic so I’m looking to start by implementing a PID only build and working on from that to add in flow control etc.

    What parts would I need to begin beyond the Fotek SSR and the temperature sensor?

    Do you have a blog post from when you were at the initial stages? Apologies if I’ve just done a terrible job of looking.

    1. Hi Ben
      For basic PID control, all you really need are a suitable SSR and temperature sensor. I would recommend getting the temperature sensor working first (which you can obviously do outside the machine). Take care using the SSR, as mains voltages can kill. All the information I’ve written up is on this site, and there’s a circuit diagram / schematic showing how the SSR and temperature sensor and connected up. There are a few separate posts, so you could try the search function with “espresso” and that will probably pull them all up. If you have questions, drop me a line and I’ll try to help.
      Best regards

  13. Hi James,

    Superb job! Thanks to your blog I’ve decided to do the Pi mod instead of just settling on a usual PID one..
    Back a few years ago when Pi was about to start I was still coding in assembly for Microchip PIC’s… this all looks like a next level but I think I should be ok.

    I’m just making a list of things to get started and would appreciate some help.
    Obviously need a Pi (B+? RS 811-1284), bits to make the thermostat replacement, display, what else?
    Were you buying majority of things from ebay?



    1. Hi, thanks for the kind words – it was a fun project!
      Funnily enough, I also used to work with PIC assembler – I’m sure you’ll have no trouble. If you’re unfamiliar with Linux, there is a learning curve to get the Pi set up and working well. Most of the issues I faced are documented on this Blog (in part so that I can use it as a reference myself…)
      Any recent Pi model would be fine – this project doesn’t need a lot of computing power, but it uses a lot of GPIO pins. There are some really nice “off the shelf” display options for the Pi now like the Adafruit PiTFT.
      I think the temperature sensor is well documented on here, but let me know if you have questions. The SSR came from eBay, but RS, Farnell and CPC also carry suitable SSRs (more expensive, but likely better quality than cheap imports on eBay!)
      The other things I used were lots of insulated bullet and spade connectors, and also for the mains wiring I used silicone wire with a high temperature rating and suitable current rating.
      Most of the low voltage wiring is ribbon cable and 0.1″ crimp header connections.
      As always, be safe and take care with the mains voltages!

      1. Thanks!
        I’m probably going to try to do it on a smallest possible budget so SSR from ebay will have to do.. I’ve read about counterfeit Fotek ones and haven’t found even one yet that would look like a genuine one lol. But generally they are overrated for what they have inside, so will just buy a 40Amp one hoping it’s going to be good up to 20Amp.
        For TFT I’m going for ebay again… hopefully integration will not be too bad. Anything to watch for there? Some decent looking kits with a case etc go for ~£10!
        I’ve been trying to find the flow meter you mentioned – there is plenty of similar ones out there. Does it make any difference to what I’d get? Tom (who commented below) mentioned in his post about having finer control with smaller nozzles but even 1.2mm seemed ok for a double which is what I always make.
        How much did your pressure sensor cost? These are normally pretty expensive and over £30 easily.

        Thanks again!

        1. I suspect my Fotek SSR 25A is a counterfeit, but it has proved reliable (been running every day for a good few years now).
          For the TFT if you send a link I’ll be happy to take a look.
          Looking back, I paid £24 for the pressure sensor, they are pretty expensive unfortunately. If you are plumbing in components, you also need to consider toxicity/corrosion, so ideally stainless steel & high temperature plastics. The pressure sensor isn’t essential of course & the main benefit of modifying your machine is to have more precise and stable temperature regulation.
          The flow sensor came from and cost £9 including P&P back in 2013, it was a coffee machine spare part. The 1.2mm bore should be fine. I think the exact model number I used is on this blog somewhere (let me know otherwise), and has worked reliably over the last 3 years. Let me know if you need more help locating one.

          1. Those look like amazing value for money 🙂 Not sure if they are supported by Notro FBTFT, would be wary of using the provided OS image or drivers. The following link might help:
            They seem to use the XPT2046 chipset, so it would be worth searching to see what experience others have had with the display. You could also look for reviews of the same display on Amazon to see how people rate them. For example, searching for XPT2046 on Amazon turns up some similar looking displays. They may be more expensive on Amazon than eBay, but the reviews are often helpful, and you can usually tell from the photos if it’s the same model 🙂

  14. I discovered your page whilst documenting my own Arduino-based project which I been working on for years. Nice to know there are others out there with the same passion for hardware, software and Gaggia coffee.

    I was particularly interested in your temp probe (nice trick to use a hex spacer). My own experimentation and calculations suggest that the built-in thermostat mounting points would have quite a high latency in responding to temperature changes (over 10 seconds). Do you find this?

    I also liked the IGBT idea for pump control. I use a triac and phase control (implemented my own SSRs with optocouples and triac which saves space and costs a lot less). I find the pump’s response is laggy and very non-linear, so controlling it required a lot of experimentation. Mind you, I control for flow, not pressure. You have me wondering whether it might be interesting to try PWM with an IGBT for comparison.

    My efforts are documented here:

    1. Hi Tom
      Pleased to meet a fellow enthusiast!
      I had a quick look over your Arduino based controller from my ‘phone, and it looks like a really nice project – I’ll enjoy reading all the details later!
      Regarding latency in temperature measurements, I’m sure the temperature of the boiler wall is a poor reflection of water temperature, but it works well enough and immersing a sensor inside the boiler (as some people have done) was a step too far for me 🙂
      Regarding pump control, I think flow control is perhaps the most useful application as you can regulate the shot time to compensate for variations in grind and tamp. There is a still a lot of work I would like to / plan to do on the pump control side, when i get around to it. The pump behaviour is very non-linear as you have found, and designing a good snubber for PWM is non trivial, but it’s great fun to play with…
      All the best

      1. Yes, temp probe inside the boiler is a large step too far for me too.

        One thing I must document is my temperature and flow control algorithms. PID (for temperature) takes far too long to stabilise because of the latency in temperature measurement. Inspired by model predictive control, my PID controller controls a model of the temperature which, over time, is updated by the actual read temperature. That way, the latency effect is pretty much eliminated.

        I live for the day when I can get the machine up to steady temperature throughout in less than two minutes.

        PID was useless for flow control, but I hit on another cunning trick there. I’ll have to go back and look at the source code to remind myself what it was!

        1. Hi Tom
          Sounds like an interesting approach you’ve used, I would be interested to know the details if you write it up. I’m sure further optimisation is possible. In my case, the boot up time of the Raspberry Pi extends the start up, as it has to boot Linux before it can even start the PID controller. That’s one delay you won’t have on Arduino of course.
          I’ve not done much work on my machine lately (but I do use it to make coffee every morning!)
          Anyway, your posts are inspiring me to tinker with my machine again so… when this early summer sunshine is replaced by the usual weekend rain, I will no doubt pick up my adjustable spanner and soldering iron again!
          Kind regards

  15. Fantastic tutorial! Thanks for the time spent on documenting the whole process! Would you have any rough estimations of the total cost of this project? I have never done this type of modding before so am a pretty skeptical as whether I’d be able to achieve or not but really fancy trying it out.

    1. I must admit I’ve never added up all the costs, but it doesn’t have to be all that expensive as you can add features incrementally. You can use a Raspberry Pi (£30) or for a much simpler version could even use an Arduino clone (e.g. Arduino Nano clone £3). LCD displays can be as low as £10-£20 now. Temperature sensor £6 or less (depends on type). Ultrasonic ranger (for water level) about £2. Pressure sensors are expensive though…

  16. Hello James,

    Thanks for a superb project. Have you looked at Radpberry Pi Zero? It is much smaller and has got 40 pins GPIO.


    1. Thanks Alex.
      You’re right – the Raspberry Pi Zero would be a really good choice for this project, and probably what I would use if starting from scratch. When the Zero first came out, I tried ordering one to play with, but they were sold out for weeks! Perhaps I’ll use one for my next project, now they’re easier to come by!
      Best regards

  17. Hi James,

    Thanks so much for this invaluable resource.

    I have a digimesa flowmeter identical to the one in the picture above.

    For testing purposes I have just isolated the pump and am going from tank -> digimesa -> pump -> out to measuring jug.

    I am finding that flow is measuring near perfect except for when I switch the pump off…then the last reading with positive flow gives on occasion (not every time) a completely erratic high reading e.g. average pulse count per second is 20 say and the last reading will be 80.

    Have you seen anything like this?

    1. Hi Matt
      I can think of two possible reasons: 1) the impeller is still moving due to back flow, or trapped air or something, or 2) it could be the back-EMF spike from the solenoid causing electrical interference (e.g. electrically inducing noise in the cable from your sensor, causing false triggering).
      To eliminate (2) I would suggest using good quality foil screened cable from the sensor, keeping the wiring short, and running it well away from the pump and other wiring (e.g. adjacent to the metal case). You could also try adding a snubber circuit to the pump.
      It could also be due to water draining back through the impeller I guess, which might be solved by changes to pipe routing and orientation. Is your flow meter mounted horizontally? Mine is mounted as shown in the black and white picture above (with the inlet/outlet pipes horizontal, and the rotation axis of the impeller vertical.
      Hope this is of some help!
      Best regards

  18. James,

    Thanks so much for getting back so quick. Yep ok that is really useful and gives me some pointers.

    At the moment I am running without the solenoid, boiler, grouphead etc literally just the pump and the flow meter….nothing else.

    I have it vertically (mainly because it won’t seem to stay horizontal!) so I think backflow/trapped air is the most likely cause, maybe made worse by position. It seems hard to get it to stay horizontal with the inlet outlet coming from the same side (I have the zero degree version as pictured)….but that’s what i’ll play with next (maybe it’ll help if I use the brass t-joint and more piping) and see if I can get it to behave.

    Thanks again, really appreciated.

    1. No problem, happy to help!

      I’m pretty sure that it will work better in a horizontal orientation. To secure mine, I drilled a 4mm hole in a plastic bracket (part of a curtain rail!), pushed the mounting stud of the sensor into the hole, and screwed the other end of the bracket to a hole in the chassis (using the hole left after removing the return pipe to the water tank).

      If you have access to a 3D printer, I just made a mounting bracket for the sensor which may help secure it:
      (I’ve printed it, but not yet installed/tested..)

      Incidentally, when I mentioned the possibility of it being back-EMF spikes from the solenoid, I actually meant the pump (i.e. I was thinking of the solenoid inside the pump, not the solenoid on the boiler). However, in your case it’s more likely due to the sensor being vertical.

      I also noticed some of the links in my article to the flow sensor datasheet were dead, so I’ve updated those above in case they help.

      Hope you get it working!

      1. Just to mention, with hindsight, I think using a plastic T piece may be preferable to the brass one (mindful of corrosion, lead content in brass etc.)

        1. Hi James,
          I have read your blog with great interest and must say it is very well writen.
          I have been working on and designing espressso machines for the last 15 years from a mechanical point of view and was wondering if I could contact you directly about a current design project I am working on, with a view to getting some assistance or advice ?
          Best regards

          1. Hi Martin
            Thanks for your kind words.
            Certainly, I’ll e-mail you directly so we can discuss offline.
            Kind regards

  19. Hi James,

    Do you have a complete parts list for the most current build of your PID controller for the Gaggia Classic? It makes ordering a lot easier rather than having to miss out a few items to then have to make a return trip or pay for additional delivery not to mention saving time. Or have I missed it in your blog?

    1. Hi Rich
      I’ve never put together a complete part-list, but will have a go at putting one together this weekend…
      Best regards

  20. Hi,

    I was wondering how the power for the Pi is configured. I noticed you said its powered from the GPIO but couldn’t see where in the wiring diagrams. Have you posted this configuration anywhere on the blog?


    1. The best way to power the Pi is via the micro USB port. In my case, I was very limited for space, so I wired direct to the 5V and GND GPIO pins. I just did a quick Google and found a good explanation of the different options here (which includes photographs).

      Hope this helps,

      1. Ah, I read it as the Pi is powered via the same power supply as the espresso machine but it still has its own separate supply just via GPIO instead of micro USB?

        Thanks for the help.

        1. Yes, that’s right – I got a very small 5V mains power supply, installed it inside the machine, and wired it directly to the incoming mains Live and Neutral (after the Gaggia power switch). As soon as you power on the machine, it starts booting up. To shut it down cleanly, I press and hold “button 2” (the lower button in the picture at top of page) and it initiates a shutdown. Then I wait for it to shut down, before flicking the mains power switch to the off position.
          It would be nice to have some battery backup to enable the Pi to do this shutdown automatically!

  21. Hi!

    Just bought myself a gaggia classic and my top priority is recreating this project 🙂
    I was wondering what the reason was you didn’t include pwm control of the boiler steam temperature?

    1. I made a list of all mods I could possibly think about, including all of James’s awesome work:

      Let me know if you can think of anything else or had other ideas in mind.

      For the steam temperature I can’t see why you can’t use the same temperature sensor and just add some code to also support steam PWM? I’m planning on adding another temperature sensor to replace the steam one anyway but I am not sure it’s necessary.

  22. Hi!
    If you have any questions, drop me a line!
    There are a few reasons why I didn’t bother with PWM of steam temperature: 1) I only drink Espresso and very seldom make Cappuccino, 2) I assume temperature control is less critical for steaming, 3) my digital temperature sensor is at the limits of its range when steaming (this could be solved by a different sensor or additional thermocouple of course), 4) I wanted to leave the steam thermostat in place for safety (rather than relying only on the thermal fuse).
    Accurate temperature control is absolutely critical for brewing good coffee of course!

  23. WOW! Top shelf design job and excellent explanation. Thank you – I’m in the middle of making a twin boiler from two old single boiler machines at present – plumbing and wiring – adding a pid would be cool after – like say – you can buy one but…

    1. Thanks Kevin, glad you found it useful. Your twin boiler project sounds intriguing – which donor machines are you using?

  24. James

    I’m in the middle of a similar project using an Arduino and this has been invaluable!

    I’m curious as to whether you ever made much use of the pressure transducer? Also, where did you mount it? If, like me, you installed it on PTFE pump outlet line I would imagine it is very noisy and this would preclude using it in any feedback circuit. I have a plan to re-site mine to read boiler pressure by swapping the standard Classic steam valve for a Gaggia Evolution steam valve. (, stripping the valve out and using this connection for the pressure gauge(s).



    1. Hi Francis
      I’m only using it to display and log pressure, not currently for closed loop feedback control of pressure (although that was my original plan, I never got around to it). To mount the transducer, I split the PTFE pump outlet line with a push-fit equal tee, then connected the pressure transducer (Danfoss AKS 32R) there, as well as a 40mm analogue pressure gauge (I drilled through the front panel of the machine and mounted the gauge on the front panel). I fitted the pressure transducer with a “gauge snubber” to dampen the pulsations from the pump, and I’m also low pass filtering the measurements in software (simple moving average type filter).
      Will your pressure transducer be able to cope with the higher temperatures seen on the boiler?

  25. Love this project and hope to recreate it myself. I had a question though.

    I am control engineer so really want to experiment with getting the best performance out of the controllers and trying different types of control. So I was wondering why you choose to not implement the fast pwm control you created for the pump in the temperature control as well? It seems to me that temperature control is more important.

    1. Hi Jorrit
      The boiler heats and cools relatively slowly, due to its very large thermal mass, so I didn’t think there was any advantage to having fast PWM switching of the boiler elements. For example, when heating at full power, the rate of temperature rise is about 1.8°C/s. If the boiler is brought up to brew temperature and switched off, the temperature falls at a rate of about 0.1°C/s. As you might expect, there is a big time delay also; when the heating elements are switched on, it takes about 4s before there is any measurable change in temperature (at the standard boiler sensor location). There’s some data here which may be of interest: (
      I’m sure there is a lot more that can be done to improve the performance of the controller on the software side, and would be interested to compare notes!
      Kind regards

  26. Hi James,

    Have you done any updates on this software to translate its use to a Raspberry Pi 3 B? I am okay with programming, but taking some of these older projects and implementing on the newer boards has been overwhelming for me as it seems there are a lot of places for things to go wrong.

    Any help would be greatly appreciated!


    1. Hi Dave
      As far as I can see, it should “just work” – I can’t see that any changes would be needed. The Pi 3 is faster, with more USB ports, more memory and built in WiFi, but otherwise very similar. The PIGPIO library works on the Pi 3. In short, I don’t see any issue with it.
      If you should run into any problems, let me know and I will be happy to help.

  27. I’ve googled around as to where to buy a Danfoss AKS 32R pressure sensor and the only places that have it available list it for well over $100. Where and how much did you buy yours for? I went ahead and bought the silver looking one off of ebay for $12… Hopefully it’s accurate.

    1. Hi Dmitry
      Mine was purchased second hand from eBay (it was listed as a spare part that had never been used), therefore much cheaper than list price. I can’t remember how much I paid for it, but I think less than $20. It was back in 2013/14 and my memory is hazy.
      If I had to buy one new at full price, I’d probably do the same as you and get one of the cheaper clones off eBay. Is yours stainless steel?

  28. Hey James!

    Loving your work! Though I have an issue you might be able to help me with.

    I am running a system inspired by yours using a HY28B screen and Pi Zero with temperature sensor etc. The issue is that I am experiencing some graphical artifacts of varying degree on the screen when starting (and sometimes while running) the pump. Initially I thought it might be due to electrical interference through some of the wires from the machine, but for debugging I decoupled the pi totally from the machine by only having the screen attached, powered from a USB charger. When I move the pi and screen close to (on top of) the machine and start the pump I get flickering – moving it away I get none. It seems obvious that it is due to the magnetic field from the pump, so I guess some shielding might mitigate this? I have actually installed the rig inside an aluminium hobby-box, only with holes for cables and the screen, but seems not to be enough. Do you have any ideas?

    1. Thanks! Always good to meet a fellow coffee machine modding enthusiast 😉

      If it tends to happen when you switch off the pump, then it could be back EMF – in which case an RC suppressor or snubber may help. I have one on my own machine for the same reason. Otherwise, screening all the electronics and wiring, and grounding the enclosure may help.

      1. I actually did add a RC suppressor for the pump without luck in this regard, but still nice to have. It occurs when starting, stopping and sometimes while running. I’ll try to ground the box and shield some wires and see if that helps!

  29. Hi James

    Great job on the Classic! Looking for something like this for ages! I was wondering, did you consider building a VFD (variable frequency drive) for the pump instead of PWM? I’ve read various posts now that PWM for a vibratory pump doesn’t necessarily achieve the low flow rates that you might want for pre-pre infusion (basket filling)…



    1. Hi Kannan
      I’ve not considered/tried VFD, although it’s an interesting idea. To be honest, I ultimately found the most critical element for the perfect brew was the grinding. Now I’ve got that dialed in, I’m getting great results without any pump modulation.
      That said, I came to think that PWM may be overkill, and simply chopping the mains for part of the cycle (leading or trailing edge) may be just as effective.
      One thing that hampered further experimentation was the need for my daily morning espresso (hesitant to put my machine out of action). I keep thinking of building a test rig in my garage, with a spare pump and pressure guage… maybe with this virus lockdown I could find the time to pick up this old project again.
      All the best

      1. Lol yes I know that feeling, hence I got a “for parts” machine that I fixed and am now experimenting with! Also ordered a Decent (in my lockdown madness!!)… Love the way they use a manifold to mix water and release pressure back to the tank rather than modulate the pumps themselves (it seems)… But I love to tinker anyway, so I am toying with all sorts of ideas (rotary pump, VFD, clipped cycles etc)… I like the idea of a quiet pump that can be controlled nicely but they are expensive and complicated.

        I’m also playing with grind (flat vs conical burr, dosage, baskets etc)… I am actually liking the more muted taste of flat burr although not sure at this early stage if it’s just my technique (probably!)… Loads and loads to learn, now have a little more time to mess around too – in danger of getting mildly obsessed…

        I did also like the UI of doubleshot’s mod which I think you might have commented on in some forum or other? But he seems to have lost interest so I’ll see if I can revive that at some stage too…

        Hope all happy and well under the circumstances!

  30. James,

    I am so grateful for the information you have compiled here. I am using your data and research in putting together my own Raspberry Pi PID.

    My question for you is this: I ordered some of the TSIC 306 temperature sensors. According to the data sheet, they are calibrated for operation between 4.5v and 5.5v but accept as low as 3v. I see by your diagram, you are giving it 3.3v.
    Have you considered that the output is less precise at this voltage input?
    Are you overcoming this offset in software?
    What would your thoughts be on giving it 5v to operate and then adding resisters to lower the voltage on the data pin?

    Much thanks from Kansas City, Missouri.

    1. Hi Dakota
      Thanks! I’m glad you found it useful, and would be interested to see how your project turns out.
      You are right, the TSIC 306 is factory calibrated to give the best accuracy between 4.5-5V, but 3.3V is still within the allowed supply voltage range, albeit with slightly lower accuracy.
      As you propose below, you could run the device at 5V supply and use carefully chosen resistors to convert the output to 3.3V levels for the Pi. If I can help with that, let me know.
      It would be interesting to see the difference in accuracy.

  31. I’m trying to implement your project with my Breville Barista Express it has 3 way + 2 way solenoid valve and I’m trying to use the raspberry PI for both Brew and Steam control do you have any tips to where to start editing from on your code ??
    Any help will be awesome, thank You

    1. Hi Andrew
      Sounds like a fun project!
      I’m not familar with the Breville machine, but the principles should be the same. Assuming you only want to control brew and steam temperature, the first step I guess would be to disable the parts of the code you aren’t using.
      If you have any specific questions, let me know and I’ll try to help.
      Kind regards

  32. Hi James, big thanks for documenting this! there’s not much info on projects like this, truely a godsend. all the best and stay safe.

  33. Wow! I am just before a same project. I have a Sage machine which includes a grinder next to the espresso. The user still needs to tamper the coffe, but that’s the only human action. So I thought to control the grinder as well and get collect all the sensor datas in a web aplication to have a statistic about the best expreiences of a specific coffee.
    Hovenstly the machine is dissasambled and I am in ordering the sensors and now I just found waht you did, and thanks, that you well documented.

    Please give me one answer, does it realy matter that you have control over the flow rate and the pressure ? (I know the temp is the most important.) Did you found a coffee speciality which recommended to increase/decrease the pressure?

    1. Hi there
      That should be an interesting little project!
      Speaking from my own personal experience over the years, I no longer feel that pressure control is particularly useful. Although it’s an interesting area for experimentation, it isn’t really necessary to get a good cup of Espresso.
      The brew temperature is absolutely critical, as is the grind setting, the weight of coffee used, and consistency of tamping. The importance of using good quality and nicely roasted coffee beans goes without saying of course! Once you have all of those “dialed in” you can get consistently good coffee.
      Hope your project goes well, and please feel free to drop me a line if you have questions.
      Kind regards

  34. Good day James,

    Nice project you did here. I just have a question. I have a same project to control my gaggia baby. The difference is I control my pump using lower frequency using arduino and optocoupler and triac that is zero crossing. i can control it at 6hz(we have 60hz ac frequency). my problem is if i raised the frequency lets say to 10hz to 30hz, the pump has unexpected fast pulsing. I mean the pump will pulse normally then suddenly it will pulse faster for about 3 seconds(but not consistent 3 seconds) then will get back to normal pulsing. but if Im using 6hz frequency the fast pulsing is not present.


  35. Hi Joseph

    Thanks for the kind words.

    From your description, I assume the triac is being switched with 50% duty cycle (e.g. turned on for half the time). Is that right? With 6Hz, that presumably means that for 60Hz AC mains, the pump will be turned on for 5 cycles of the mains, then off for 5 cycles.
    For 10Hz it will be on for 3 cycles, then off for 3 cycles. For 30Hz it will theoretically be on for 1 cycle, then off for 1 cycle.
    However, in practice, the Arduino clock will not be synchronised with the mains frequency. The two frequencies (e.g. 6Hz and 60Hz) will be out of phase with each other, and they will probably also be slowly drifting in and out of phase with each other because they are not derived from a common clock (e.g. one might be 60.0000123Hz and the other might be 6.0032123Hz).
    This means you will probably get strange cycling behaviour as the clocks drift relative to each other, with the pump being turned on for variable numbers of cycles of the mains, or sometimes being turned on very briefly for a fraction of a cycle. This would probably be cyclic, like a beat frequency between the two, so you could get all sorts of strange effects I think.

    Instead, I think you would want to turn on the pump for every single 60Hz mains cycle, but for a variable proportion of that cycle. This is the basic principle of a lighting dimmer. Triac dimmers normally work by turning on the load (i.e. light bulb) part way through the mains cycle then the Triac remains on for the remainder of that cycle, turning off at the next zero cross.
    So basically, the concept would be to detect the start of every new AC mains cycle (detect the zero cross – e.g. on an interrupt), delay for a short time (between 0 and 8.33ms, depending on the desired pump power), then turn on the Triac. The Triac will then shut off at the next zero cross, and you start the process again when the next zero cross is detected.

    Does this explanation make sense, and have I understood your setup correctly?

    Kind regards

Leave a Reply

Your email address will not be published. Required fields are marked *