GS/3 Mod EP "Chimera" - a fully configurable profiling mod for La Marzocco GS/3 AV

Equipment doesn't work? Troubleshooting? If you're handy, members can help.
User avatar

#1: Post by AssafL »

Okay; perhaps a little bit of bait-n-switch. Perhaps this mod is not that simple :)

Obviously - this project was mainly reading and picking through the various other projects written by other H-B'ers. So the work of Peppersass, Jacob, Shawndo, Shadowfax and many others.

Read Dan's warning in the sticky section; this modification modifies both the hydraulic and pneumatic circuits, adds a pump and a mains voltage solenoid. It can shock you if you touch the wrong part. Performing adjustments can scald you and wiring incorrectly can ruin the GS/3 "brain" - the $500-600 3d5 card (and thus ruin your day).

Unlike some of my other projects which were fully annotated (and a few here adopted for their own use) - this project is not well documented for a variety of reasons. It is a bit complex, many of the components are eBay (and tough luck finding the same), and there are probably many adjustments the need to be done. Hence, the annotations below are more descriptive than instructive. While I like to help (and offer my help to anyone who tries this) - please note that there is only so much I can do to help. As I stated - it is for experienced modders. Alternatives such as Ace Sureshot are available that can do some of what this project can do and are probably much easier to install.

What is the GS/3 MOD EP "CHIMERA" about?
I have had the GS/3 for 7-8 years. It is a great machine when maintained well. It is in daily use multiple times daily. As the years progress, other machines pop up. Slayer, GS/3 MP w/Strada mod, Mini Strada, Synesso, many levers like the Londinium, improved and beautiful KvdW, etc.

So in an attempt to bring my GS/3 to modern day "state of the espressive art" standards - here is a spec of a mod I installed over the last month or two.
1. Software:
  • a. Profiling modes:
    i. Manual profiling - rotate potentiometer to increase/decrease pump PWM voltage - a "poor man's paddle" profiling (One could manually profile pressure or flow - I decided on PWM). A manual profile can be saved.
    ii. Pressure profiling - follows the saved pressure reference profile.
    iii. Flow profiling - aka H-B member's Jacob's mod - follows the flowmeter ticks of the reference profile.
    iv. Union profiling - hybrid profile ala "rule of thirds" - PI using flow profiling, rest using pressure profile. (this mode is buggy doesn't work well at all).
    v. Stepped flow ("Slayer like") profile - constant RPM, closed FLB for PI, open FLB for rest of shot.
    b. A manual profile may be saved and recalled from on board EEPROM. The profile serves as a reference for the different profile modes and consists of 200 measurement points each of PWM, pressure and flowmeter ticks.
    c. System parameters can be adjusted using menus and potentiometer and are stored and retrieved from the onboard EEPROM.
    d. Optional 3d5 button support using the Maestro Cash register interface (a serial port onboard the 3d5). Enables one to use the GS/3 buttons instead of manipulating the box.
2. Gear pump and 20kHz PWM based pump control (using VNH5019 high power DC motor driver)
3. Stepped flow profile (Slayer style) using a medium flow (Cv) metering valve for pre-infusion and a bypass controlled by an FLB (flow limit bypass) solenoid. FLB thresholds and/or timing is adjustable from GUI and stored in EEPROM.
4. Color display & touch screen - "poor man's" espresso digits.
  • a. Dashboard shows main pull parameters (PWM, motor current, pressure, flow volume in ml and flow rate in ml/min).
    b. Graph shows the reference profiles (PWM, pressure and flow) and overlays the current pull graph.
    c. Timer.
    d. Menu system to select modes and presets.
5. ATMEGA2560 based (Arduino Mega or compatible).
6. 1gr Strada like use of the original rotary vane pump for the steam boiler; so hydraulics are now completely separate from the pneumatic circuit.
7. If 3d5 serial is integrated, the software can automatically detect cleaning cycle (and has a set PWM for cleaning).
8. Calibration mode for the pressure transducer
9. Separate PID loops control for flow tracking and pressure tracking. PID parameters (like the sliding SetPoint) are shown (for debug) on display near the graph.
10. Increased flow resolution by timing the Gicar flowmeter pulses (currently getting 10% accuracy on the flow rate while in flow limiting).

Future plans are to add EBF (and the ability to shut a flow) control.

The main controller is a small box, with a potentiometer, a status 2 color LED and a touchscreen display (an Arduino shield). Currently 11 wires connect via a makeshift umbilical to the GS3. Cables are Teflon to withstand water steam and heat.

Software screenshots:
The graphs show the PWM voltage (in red), the pressure (in cyan) and the volume (in green).

The extraction below is a pressure profile pull. The profile was originally created in manual profile mode using the potentiometer, and saved to the EEPROM (non volatile storage). The system can then be set to replicate the same profile automatically. The system can follow either the flow (flowmeter ticks) or the pressure. PID controls the pump motor to try to replicated the reference profile. An additional "safety" against runaways (such as puck fracturing") is limiting the pump PWM to a set band around the reference profiles PWM. The white horizontal line shows at what PWM the preinfusion solenoid is activated.

The graphs show very clearly the aging of the coffee. The green line shows the volume. The volume line sets steeper as the coffee ages. Updosing or grinding finer can help offset the age of the coffee. The sensitivity of age on coffee pull dynamics is pretty remarkable: effect of coffee aging is noticeable on a daily basis.

The following screen shows a different profile - a steeped flow profile. It mimics the Slayer style profile, in that PWM is constant and the user sets the time at which the solenoid valve bypasses the preinfusion metering valve.

Parameters can be adjusted using the potentiometer onscreen. They are saved in EEPROM.

Cleaning cycle

Source code:
The source code is free to download at I am not a C++ developer so I apologize for the sloppy code... Obviously additional libraries are needed. See the include section on the main sketch (PID, Adafruit GFX and drivers, Pololu VNH5019 etc.).

Warning - it is still work in progress. At least 1 profile mode does not yet work as it should (the Auto-UP mode). So there will be changes uploaded as time permits.

NB - Obviously, given the flowmeter, pressure sensor and volumetric pump, one can build all sorts of pull modes. One can accurately track a -kx pressure decay of a lever, create the preinfusion piston response of an e61 and replicate almost any other sort of machine. In that - I name this mode the Chimera. But Chimeras are usually ominous. In this case ominous because like having many espresso machines to choose from can make life more complicated.

So for those of us that will savor the ability to play around once in a while - but won't be overly concerned with the possibility that one mode is better than another - this concept is a winner. But those prone to tweaking the knobs with the assumption that out of two options one must be better (a natural by product of linguistics in spite of its logical fallacy) this type of modification may contribute more to confusion than be of assistance.
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar

#2: Post by AssafL »

1. MEGA2560 Arduino board
2. Perfboard Arduino shield (I used an Uno shape shield I had lying around ... e-hardware) - Note I do not recommend this shield for this purpose - just use a perfboard shield.
3. Potentiometer - any normal mid-range value would be good. I used a salvaged wirewound "precision" potentiometer (I think it was 4.7k Ohm).
4. Dual color LED for status indication
5. For 3d5 Serial port inverting logic:
  • a. 2x 2N2222 transistors (or any other standard NPN device).
    b. 2x 7-10k resistors.
    c. 2x 220 Ohm load resistors.
6. Adafruit 2.8" display with resistive touch panel
7. Pump and pump motor control
  • a. Polulu VNH5019 breakout board
    b. Pump: F-O-T MGCF11S & MG204 motor & pump head combination ... ES-DRIVES/(due to availability in country the specimen I received has the stronger magnets and the bypass safety use valve)
    c. Various fittings, adapters and copper tube pieces (I used 1/4" copper tube throughout, albeit the stock GS/3 is the metric similar (but not identical) 6mm tubes.
8. FLB:
  • a. Parker 4A(v) NML-V-SS-K medium orifice metering valve (any medium orifice metering valve will do ... -MV%20.pdf
    b. ACL 306 Universal 3-way valve
    c. Various fittings, copper tube pieces and adapters
    d. 5V SSR module to operate the FLB solenoid
9. Terminal headers, wires, shrink tube, etc.
10. Pressure transducer: used (from ebay) Dynisco model PT130-2.5C 250PSI 10VDC
11. INA333 Low-Power, Zero-Drift, Precision Instrumentation Amplifier Module (since the Dynisco has a bridge output, we need a balanced to single ended module).
12. Isolation devices (opto couplers) for the flowmeter and the group solenoid valve.

Regarding the choice of pump: I chose the MG204 which is a 4mm gear with PTFE seals. Alternatives are the 9mm gear versions (MG209) and Oring seals (MG304 or MG309). Of particular interest is the FG209 & FG309 which has the added advantage that it seems to be limited in pressure to about 12 bar. This should make ensuring the system is safe at all pump speeds a bit easier.

Regarding metering valve:

Using a medium Cv metering valve at 3 bar inlet pressure (no pump) I got the pressure up and conducted some measurements:

Turn ml time period ml/sec
0 turn N/A (can't measure, machine flashes, some drip)
½ turn 10ml 20sec ½ ml/sec
1 turn 19ml 20sec 0.95 ml/sec
1.5 turn 27ml 20sec 1.35 ml/sec
2 turns 36ml 20sec 1.8 ml/sec

Inlet line pressure is about 2.8bar. Pump is off. Orifice is 0.06" 1.5mm (medium valve - NM series). So let's say you want 14ml into 14 grams as preinfusion - and you want it at 10 seconds. Hence you'd set it at 1.4ml/sec so about 1.5-2 turns (from almost closed).

2 turns is not bad for precision.

Looking at the graphs for Cv, it seems a small orifice metering valve (NS series vs NM series) will nearly double the turns for the same flow. Orifice would be 0.03" 0.76mm (medium valve - NM series).

The same range (Cv=0.017) would be at 3 full turns for the 4A-NSL or 4A-NSA versions (same compression fittings, fine vs. medium flow).

Of course it would be 4A-NSL-V-SS-K (for Viton, Stainless and Knurled nut). If I were to do it again I'd opt for the wider range adjustment. Not that it matters.

In any case, do not be tempted to get a get the NL series - large orifice series: the same Cv of 0.017 would be a 1/5 of a turn! - so no precision - might as well be getting a ball valve). Orifice for the NL series is 0.13" or a whopping 3.3mm.

Pump and pressure transducer installed in the GS3:


Pump connection to flowmeter:

Metering valve and bypass assembly:

FLB assembly connections to HX mixer and boiler Tee:

Note: the knurled knob is easily accessible under the GS/3 near the hot water mixer knob. The Flow rate (to 10% accuracy) is displayed on screen making adjustment and repeatability easy.
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar

#3: Post by AssafL »

Serial connection to 3d5:
To make use of the GS/3 keypad, we will "cash in" (pun intended) on the GS/3's 3d5 card's cash register interface. It is there for when the card is used in vending machines, or office machines where an employee swipes a card and gets a drink.

Note: The serial interface is not compatible with RS232 voltages (12V) and should never be connected to a computers serial port (if you happen to have an old computer). It is 5V TTL level and connection to a computer should be through an RS232 driver card that supports 5V UART signals (such as the MAX3232 interface chips).

The serial port is on CN8 whose pinout is as follows:
Pin 1 GND
Pin 2 Rx
Pin 3 Tx
Pin 4 16V

(Note: Be careful if you use the power pin on C8. It is the unregulated relay power (probably used to power the relays on the cash register interface board. If you must - use a regulator to drop to 5V).

A short is affixed between Pin 2 and Pin 3 otherwise the buttons pushed will be ignored. The port's configuration is not the standard 8N1 but as follows:
1200 baud
Even Parity
8 bit
2 stop bits
Hence to program the UART one should use SERIAL_8E2.

On CN2 which a 16 pin box header the pinouts for the buttons is as follows:


Also, if the short between the Tx & Rx isn't connected, the relays won't click.

Obviously - I tried feeding the codes in manually - but the relays did not click as well. I'll set it up as a null modem to see what happens. I am pretty certain the 30mS timeout is in there.

To get the board to work on a bench (and not time out with boiler levels):
CN3: Must short Pin 1 (GND) to Pin 4 (Steam boiler level) - otherwise 3d5 is busy filling up boiler... Futile, and it times out. CN1 & CN5: Short - so 3d5 thinks the boilers are hot. In fact very hot....

CN10: Good source for 5V for the MAX3232 - Pin 14 is 5VDC.

It looks like RS232 cannot help us operate the machine. But it can tell us what mode the machine will be in.
Note: It seems it doesn't send the signal if the push is to turn off a pull. We only get codes that turn on a pull - not cut one mid pour...

Note: The one line CN4 (labelled 2GR) is wired identically to the even row (button row) of the Keypad connector CN2. Since the CN2 (1GR), CN4 (2GR) and CN7 (3GR) are wired in parallel - I think they are connected in a scan - so that the ground is selected to the each connector in succession by the microcontroller. Since there is only one group - it matters not if we hook it to
ground. It seems to work well. []\CN4 is good for the buttons, but we still need one line from CN2 (the common line); they do not share the same common. I do believe tying it to ground is enough (as the buttons are pulled high anyway). I verified it works well, the only exception being that using GND instead of Button's Common does not reliably wake up the 3d5 from sleep.

What that means is that instead of breaking out the wires to the keypad, just pulse short one of the button pins to ground and you pushed that button. So if we get a code - we can end the shot by pulsing the function button. We will know the machine ended before us via the 3-way Optocoupler....

Null modem: So I shorted the Tx & Rx pins of CN8 - and connected only the Tx line to the RS232 level converted. Works fine, I get the codes, and the relays click. This alleviates the need to build a null modem that reacts in 30mSec in software.

Not all button presses are sent over serial:
1. When a pull is in progress, the 3d5 will not send the button code if it is pressed to turn off a pull. This makes sense - wouldn't want the credit card to be charged twice for a Ristretto pull cut short J. Obviously we'll know the pull is done via the 3-way signal...
2. Fn button. All the buttons send an update upon being pressed (except 1 above). Fn however, is different. It sends the code only when the button is released. This means:
a. Entry to programming menus is not sent through RS232
b. Operating the cleaning cycle does not get sent over RS232. Obviously, this mean we now have a way to set a cleaning mode (Group Solenoid active but no RS232 code - that is a cleaning cycle!).

Since the serial signal is NOT an inverted TTL level signal (and UARTS use inverted TTL signals), there is a need for level conversion with regular AVR Arduinos (5V ones - not 3.3V models).


So we will need two wires between the 3d5 and the Arduino (GND and Tx). Also will probably want a third line going to CN4 to the function pin that will be a high impedance output that strobes low when we want to end a pull.

Test code (verified working):

int incomingByte = 0;//for incoming serial data

void setup() 
	Serial2.begin(1200, SERIAL_8E2);	// opens serial port, sets data rate to 1200 bps, 8 bits even parity, 2 stop bits

void loop() 
	// send data only when you receive data:
	while (Serial2.available())
		// read the incoming byte:
		incomingByte =;
		// say what you got:
		Serial.print("I received: ");
		Serial.println(incomingByte, HEX);

Test setup. Insulate everything and be careful of the exposed mains (110V or 220V) on the 3d5 PCB, relays and connectors.
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar

#4: Post by AssafL »

Strada EP modification to allow the 3d5 to operate the rotary vane pump during autofill (but not during a pull - that is done by the Arduino and VNH5019 during a pull).

Two 1N4148 diodes activate the main pump relay whenever either the Tea or Autofill solenoids are on. A cut track ensures that when the 3-way is engaged - the rotary pump remains off and only the gear pump is activated.

Note: this modification is reversible - except that a bridge has to be soldered where the track was cut. The track was cut between a solder joint and a test point making such a bridge easy to apply.



Connection diagram to the Gicar flowmeter:


FP - Why track flowmeter pulses and not flow rate? the answer is reduction in the accumulated error.


Interpolation for each Arduino loop:

Flow rate accuracy:

When using the Metering valve to load the flowmeter the accuracy increases considerably. I now have a pretty stable and rather precise reading of flow roughly calibrated in ml/min.

It works as follows:
1. An interrupt pin receives the pulse, increments the flowPulseCount (for the shot volume) and captures the pulse time:
void flowPulseReceived() // receives flow pulses from the Gicar flow sensor
                currentFlowPulseMillis = millis(); 
Noise will make this signal wander. I used a 470 Ohm resistor and a 270nF capacitor to buffer this signal.

2. A small routine in the main loop checks if there is a new measurement and pushes it it into the Average.h FIFO stack of measurements.
                if (currentFlowPulseMillis > lastFlowPulseMillis)
                                averageF.push(currentFlowPulseMillis - lastFlowPulseMillis);
                                lastFlowPulseMillis = currentFlowPulseMillis;
3. To display:
tft.print((float)mlPerFlowMeterPulse * 60.0 * 1000.0 / (float)averageF.mean(), 0);
Calibration of the reading is via constant mlPerFlowMeterPulse:
Const Float mlPerFlowMeterPulse = 0.4;

The average is set up for 10 measurement points:
Average<unsigned long> averageF(10);
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar

#5: Post by AssafL »

EEPROM Storage is arranged as follows:
Parameter data structure
Address	Data				Length
 0		FLBThreshold		byte
1		debounceCount		byte
2		pumpMinPWM		byte
3		pumpMaxPWM		byte
4		mlPerFlowMeterPulse
8		unionThreshold
12		Kpp				double (4 bytes)
16		Kpi				double (4 bytes)
20		Kpd				double (4 bytes)
24		Kfp				double (4 bytes)
28		Kfi				double (4 bytes)
32		Kfd				double (4 bytes)
36		layerPIFlowRate		byte
37		slayerMainPWM		byte
38		slayerPIPeriod		byte
39       slayerMaxPWM		byte

Profile data structure
97		Size of PWMprofile	byte
98		Size of Pressure p.	byte
99		Size of Flow prof.	byte
100-301	PWM Profile		bytes (2 per second)
302-503	Pressure Profile 	bytes (2 per second)
504-705	Flow Profile		bytes (2 per second)

A special function enables the EEPROM content to be downloaded using the serial port on the Arduino. Obviously one can create any sort of profile and store them in the EEPROM but no interface has been created for such editing yet.
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar

#6: Post by AssafL »

Calibration & Adjustment parameters:

There are a few parameters that need calibration.
1. Pressure sensor:
Setting the CALIBRATE_PRESSURE directive within the main sketch displays the 10-bit reading on the analog pressure input pin. Assuming the pressure sensor is sufficiently linear, we will need two points to calibrate the readings (to pressure in bars):
A low range pressure input and its associated reading, and a high pressure source and its associated reading.

Use manual pull mode and a blind basket to:
1. Set a constant 3 bar, and write down the displayed A/D value (between 0 to 1023)
2. Do the same for 9 bar and write down the displayed value.

1. Espresso machine manometers are not very accurate. A Scace device or a Fluke pressure gauge are usually more accurate. See below for an example setup.
2. Wait for the pressure to stabilize. if pressure exceeds the intended pressure it may not be possible to reduce the pressure so if that happens run a new pull.
3. It is recommended to run the test a few times to ensure consistency of the results.
4. Pressure sensors (like all resistive bridge based transducers) have a high tempco. Some, but not all, are accurately compensated for temperature. Recommended to wait for the machine to heat up and for the sensor temperature to stabilize before calibration.

// Write the low end calibration figure below
#define LOW_CALIBRATION_PRESSURE_READING 432 // 10-bit AD reading when low (3.0 bar) pressure is applied (between 0-1023)

// Write the high end Calibration figure below
#define HIGH_CALIBRATION_PRESSURE_READING 581 // 10-bit AD reading when high (9.0 bar) pressure is applied (between 0-1023)

// If your calibration point are different than 3.0 and 9.0 bar - set the relevant pressures below
// Note: This setting is provided for sensors that are linear within a narrower pressure range - so calibration
// can be performed within the linear range of the transducer; or if your reference transducer supports different 
// reading points (e.g. a maximum of 8 bar).
#define LOW_CALIBRATION_PRESSURE 30 // x10 in bar - so for 3.0 bar write 30
#define HIGH_CALIBRATION_PRESSURE 90 // x10 in bar - so for 9.0 bar write 90
Calibrating pressure with an external Fluke sensor:

2. Flowmeter calibration:
  • Set the ml/Pulse setting to 1ml/pulse.
    Use an accurate sub-gram scale to measure the water weight over 20 seconds.
    Read the mL value on the dashboard. This is the number of pulses generated by the Gicar flow meter.
    Divide the glass content by the number of pulses received.
    For my flowmeter, the number was 0.42. Set that number in the parameter adjustment screen.
3. EEPROM Content download. For debugging, this sends the entire EEPROM to the serial monitor

4. Inverted FLB output. If the output relay inverts (like some modules do) uncomment the next line

5. Serial port control: Allows control of the unit via CN8 on the 3d5

6. byte FLBThreshold = 15

7. Pump Max and Min PWM. Pump Max PWM is especially important and MUST be calibrated.
  • Use a blind portafilter to choke the machine.
    Set manual profile mode and gradually increase PWM (using the potentiometer until the manometer reads 11.5-12bar.
    Read the PWM value displayed on the screen.
    Multiply that number by 2.55 and round it to the nearest integer (the value is a byte, so is a value between 0-255).
    Configure using the parameter modification screen.
Default is pumpMaxPWM = 210, pumpMinPWM = 0,

8. To eliminate false triggers, this setting requires a minimum number of cycles before a shot is concluded. Default is debounceCount = 3,

9. This is a graphic representation of the Slayer PI level - it should not be changed. Default is slayerPIFlowRate = 5,

10. Slayer pull parameters. The defaults are - slayerMainPWM = 28, slayerPIPeriod = 15, slayerMaxPWM = 45; These can be set on the "Salyer" like configuration screen using the potentiometer.

11. Union Threshold: In bar - at this point the system will switch from FP to PP. The default is unionThreshold = 3.0d;

12, PID settings. There are two sets of PID settings. One os for Flow PID, and one for Pressure PID loop.
Proportional error, integral error and derivative error. The defaults are: Kpp = 5.0d, Kpi = 5.0d, Kpd = 1.0d, Kfp = 5.0d, Kfi = 5, Kfd = 1.0d;
Scraping away (slowly) at the tyranny of biases and dogma.


#7: Post by Stanic »

Hats off to you Sir!

User avatar

#8: Post by AssafL »

Thank you.
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar

#9: Post by AssafL »

Arduino pin allocations
May be modified within the main sketch - with perhaps the exception of the interrupts.

#define CS1 A0 // A0 - VNH5019 Motor driver current default
#define CONTROL_POT A1 // A1 - Control potentiometer is on analog pin 1
#define PRESSURE_SENSOR_INPUT A2 // A2 - 0-5V Pressure Input
// D0 and D1 are serial port
#define FLOW_COUNT_INPUT 2 // D2 - INT0 Flow counter interrupt pin
#define GROUP_SOLENOID 3 // D3 - INT1 Group solenoid 220v detector is on pin 3 (an interrupt pin)
#define INB1 4 // D4 - VNH5019 Motor driver default
#define RED_LED 5 // D5 - LED lights RED during standby
#define EN1DIAG1 6 // D6 - VNH5019 Motor driver default
#define GREEN_LED 7 // D7 - LED lights GREEN during a pull
#define STMPE_CS 8 // D8 - Adafruit TS CS
#define TFT_DC 9 // D9 - Adafruit TFT DC
#define TFT_CS 10 // D10 - Adafruit TFT CS
// _PWM1 = 11 // D11 - VNH5019 Motor driver default set in VNH5019MotorShieldMega.h file
#define INA1 12 // VNH5019 Motor driver - Move INA1 from D2 to D12 to allow pin 2 (INT0) use as an interrupt for the flow meter
#define FLOW_LIMIT_BYPASS 13 // D13 - Digital output to flow control bypass solenoid (0V - flow limited; 5V - bypass enabled)

Serial pins for the 3d5 are Serial2 pins. 17(Rx) and 16(Tx).

Water connections:
Water inlet from the softener/filter goes to a Tee. The Steam circuit get routed directly to the inlet of the rotary vane pump.
The coffee circuit gets regulated to about 3 bar by a Watts regulator and from there to the inlet of the gear pump.
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar
Supporter ◈

#10: Post by shawndo »

Nice! I always assumed a 3-way solenoid workflow would take up more space than a 2-way assembly, but that looks pretty compact.
Darmok and Jalad at Tanagra