Introducing Salamibot

Recently, I’ve been splitting my morning coffee at various brew ratios, measuring total dissolved solids (TDS) in the first part, and using this information to explore extraction dynamics using various equipment configurations. This method has a few limitations:

  • Splitting a couple of coffees each morning, it takes a week or so to gather data for a single configuration.
  • The measurements are made using several shots, so we’re assuming those shots are reasonably similar.
  • Measuring this way, we have no idea what time each sample was taken—only what brew ratio.

To solve these problems, I have developed an automated test tube carousel which I call Salamibot.


I’ve uploaded the 3D printed parts for this design to Thingiverse. In addition to those parts, Salamibot uses the following components:

The stepper motor I’ve used is a Mercury SM-42BYG011-25, which isn’t easy to find now. This could be substituted with any NEMA 17 stepper motor. The body of the motor I’m using is 34 mm high, and the shaft extends 24 mm above the body. If you use a taller motor, keep in mind that you’ll need to reduce the height of the 3D printed pedestal to compensate.

You will also need an assortment of hookup wires. There is one wire I manufactured, which goes from a single pin on the Arduino header to the two enable pins on the stepper motor driver.

Finally, I’ve uploaded firmware for the Arduino to GitHub.

Design and construction

Broadly speaking, Salamibot is made up of a pedestal to which the stepper motor is mounted, and a carousel which is attached to the stepper motor’s shaft.

In addition, there is a “funnel”—actually 20 funnels merged into one part—which ensures that the coffee goes only into the test tubes. There is also a “nozzle” which fits the stock Gaggia Classic Pro portafilter. The spouts on this portafilter can be unscrewed, revealing a G 3/8-19 thread onto which the nozzle can be installed.

The pedestal and carousel are made up of several parts. I’ve stuck these together using methylene chloride, which you should be able to find at paint and plastics stores, but many adhesives will work. The parts are “keyed”, so that they will only fit together one way.

The pedestal includes features for routing the four stepper motor wires to the side of the unit.


Salamibot is controlled via a serial interface with the Arduino. To use the interface, connect the Arduino to your computer and open any terminal program (e.g., Putty).

Salamibot can be operated manually, by entering ‘m’ at the prompt. In this mode, whenever you hit the space bar, Salamibot will advance the carousel and output a pair of values–the times when the carousel started and finished moving. This allows you to accurately determine when the nozzle moved from one test tube to the next. To finish measuring, hit Escape.

Salamibot can also be operated in automatic mode. Currently, the only automatic mode is “exponential” mode, which samples an exponentially decreasing measurement at equal intervals, as shown in this plot:

Exponential mode takes a single parameter, which is the time constant of the exponential function in milliseconds. You can think of this as the time for the function to drop to about half it’s original value.

In this mode, you’ll need to hit the space bar once to start the timer (e.g., when the pump is started) and a second time when flow starts. After this, you can hit the space bar to switch to manual mode (e.g., if the test tubes are filling up too quickly) or hit Escape to finish measuring. The output of Salamibot in this mode is shown below:


For the first run of Salamibot, I ground 18.07 g of Rosso Decaf coffee at a grind setting of 3.00 on my Eureka Mignon Specialita. I placed a Whatman 5 filter on the bottom of the portafilter basket, and wetted and dried the filter to help it adhere to the bottom of the basket.

I installed the portafilter into my Gaggia Classic Pro, which has the 6.5 bar OPV modification from Shades of Coffee. Then I removed the drip tray and installed Salamibot under the portafilter nozzle.

For this test, I ran Salamibot in exponential mode with a time constant of 15 seconds. I started Salamibot’s timer when I started the pump on the Gaggia, then hit the space bar to indicate when the first drops came out of the nozzle. Toward the end of the shot, I was worried the test tubes might overflow, so I changed to manual mode for the last couple of tubes.

When the shot was complete, I removed the carousel from Salamibot and set it into a round container with a bit of room temperature water, so that the bottom centimeter or so of each test tube was submerged.

For each test tube, I measured its weight, then subtracted the dry weight measured before the experiment. I also measured total dissolved solids (TDS) using a traditional handheld refractometer. For samples early in the shot, I also diluted the sample using an approximately equal weight of water, and re-measured TDS for the diluted sample.

The first plot I did was TDS vs. time:

Then I plotted cumulative beverage weight vs. time:

The shot starts off slow, but eventually reaches a fairly constant flow rate of about 2.4 g/s.

From these two plots, we can calculate cumulative TDS vs. brew ratio:

Error analysis

We can compare these results with data obtained previously by splitting several different shots:

These two sets of data use the same bag of coffee, grind setting, and equipment configuration. The split shot measurements were made over five days, while the Salamibot measurements were made in a single afternoon.

The two data sets appear to be quite similar. However, some differences become more apparent when we look at the plot of extraction yield (EY) vs. brew ratio:

In particular, the split shot data seems to approach a maximum EY quickly and then level off, whereas the Salamibot data “smooths out” the corner, and even at a brew ratio of 3.5, we still see EY climbing steadily. This smoothing behaviour suggests to me that there might be liquid accumulating between the portafilter basket and the nozzle, acting as a sort of buffer to smooth out sudden changes in TDS of the liquid exiting the basket.

It seems to me that the most likely explanation for this behaviour is that the diameter of the portafilter nozzle (6 mm) is too small. This is supported, I think, by the fact that flow rate quickly reaches a value of about 2.4 g/s, then levels off sharply and remains steady for the rest of the shot:

We could test this hypothesis by repeating the experiment with a larger diameter portafilter nozzle, then checking if the Salamibot data matches the split shot data any better.

There is one other error source that I want to mention here. Before and after the experiment, I weighed the funnel that sits on top of the test tubes. The results were 17.30 g and 18.11 g respectively. So, during the course of the experiment, approximately 0.81 g of liquid was absorbed by the funnel. With a total beverage weight of 65.40 g, this represents an error of about 1.2%.

We can perform a first-order correction for this missing weight by adding 1/19 of this weight to the total mass in each test tube. This gives the following result:

We can see this improves the fit slightly, but the Salamibot data still seems to cut the corner in the plot of EY vs. brew ratio:

Future development

My next step will be to perform tests with a larger diameter portafilter nozzle, to see if this improves the observed averaging behaviour.

If the larger nozzle solves the problem, I’d like to go back and repeat several of my split shot experiments using Salamibot.

In the longer term, I would like to make improvements to the control interface. At the moment, Salamibot is controlled via a serial interface with an Arduino. Changing to a Bluetooth Low Energy interface would make it easier to combine Salamibot’s features with those of an external sensor like a smart scale.

Salamibot is a work in progress. This is the first electromechanical open source project I’ve created, so if there is some way I can make it more accessible to other developers, or if you have feedback on the design itself, I hope you’ll let me know.


Leave a comment

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