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

Equipment doesn't work? Troubleshooting? If you're handy, members can help.
User avatar
AssafL (original poster)
Posts: 2588
Joined: 14 years ago

#11: Post by AssafL (original poster) »

shawndo wrote:Nice! I always assumed a 3-way solenoid workflow would take up more space than a 2-way assembly, but that looks pretty compact.
Thanks - A 3 way saves you a Tee. Making the assembly wasn't that difficult.

BTW - It was your neat project that got me to buy the Metering Valve. Only then did I realize you had the shot puller :?

Bending those tubes to clear the OPV can, chassis and autofill tubing was a real nightmare. Especially since one needs to keep enough straight tube at the end to support the compression olive...
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar
AssafL (original poster)
Posts: 2588
Joined: 14 years ago

#12: Post by AssafL (original poster) »

The controller box

The controller box houses the following:
  • Arduino Mega
    Prototype shield
    Pololu VNH5019 motor controller breakout
    Switching regulator (to reduce the 24V motor voltage to 10V for powering the electronics; the Arduino card then - using and on board LDO supplies 5V to the rest of the circuit)
    Wirewound pot
    Bicolor LED
    Adafruit display
    8 pin header and a 4 pin header
Wiring is Teflon throughout for longevity and resistance to chemicals. And it is cheap on eBay (US government spent a fortune on it - and we get it for next to nothing! :)).

The header will be replaced by a 15 pin Cinch Jones (or the compatible Molex Beau) connector. I usually dislike Jones' connectors as they are rather ugly with big exposed pins. If there is one advantage to Jones type connectors - is that they care little if there is espresso dust or humidity (as long as the circuit is fairly tolerant of leakage currents, etc. - hence it will be important to separate the power and motor lines from the analog lines).

(At first I considered a beautiful round "push type" NOS Swiss LEMO connectors; but then I considered the humid and rather dirty espresso environment. The idea of cleaning espresso grounds from the latch of a LEMO was - to say the least - not appealing at all. What fits a Leica Camera does not necessarily have to fit an espresso machine.)

The Arduino Mega is the last board in a compact sandwich arrangement:


The two shields are a prototype shield and a display shield. Since the prototype shield I had in my tackle box did not have an ICSP passthrough connector I had to wire the SPI to the Mega Pins 50, 51 and 52. (MOSI, MISO and SCK).



The terminal header:


LED and potentiometer:
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar
AssafL (original poster)
Posts: 2588
Joined: 14 years ago

#13: Post by AssafL (original poster) »

The software:

The software is rather sprawling at 1500 lines of code, but most of it is unwieldy menu systems, mode selection, dashboard text manipulation and graphics. There is no OS so most of the graphing is done by sending pixels to the screen driver. Adafruit does supply a library that does basic line drawings and text so it isn't too much work - but still makes for some repetition (for example, one must clear an area to write over it; but it also causes flicker so you end up with code to eliminate annoying flicker).

Of interest are how motor speed is set. There are 6 pull modes:
  • 3 use PID (Flow, Pressure and Union)
    1 is manual
    1 is fixed PWM
    1 tracks a stored PWM graph (no PID)
The two "maintenance" modes, cleanCycle and flushCycle bot use a single PWM setting which should be at around 9bar when using a blind basket.
//***********************************************************************************
// Calculate and return pump speed per pull mode
//***********************************************************************************

byte setPumpSpeedbyMode(int profileIndex, long pullTimer, byte pumpSpeedByte, float currentPressure, boolean preInfusion, int sumFlowProfile, int unionSkew)
{
	if (g_cleanCycle || g_flushCycle)
		pumpSpeedByte = (float) cleanPWM * 2.55;
	else
	{
		switch(g_pullMode)
		{
			case MANUAL_PULL:
				pumpSpeedByte = (byte)(analogRead(CONTROL_POT) >> 2); //converts int to byte.
				break;
			case AUTO_PWM_PROFILE_PULL:
				pumpSpeedByte = (byte)(g_PWMProfile[profileIndex]+
					(int)((double)(g_PWMProfile[profileIndex+1] - 
					g_PWMProfile[profileIndex])*((double)(pullTimer % 500)/500.0)));
				break;
			case AUTO_PRESSURE_PROFILE_PULL:
				pumpSpeedByte = executePID_P(profileIndex, pumpSpeedByte, currentPressure, pullTimer);
				break;
			case AUTO_FLOW_PROFILE_PULL:
				pumpSpeedByte = executePID_F(profileIndex, pumpSpeedByte, sumFlowProfile, pullTimer);
				break;
			case SLAYER_LIKE_PULL:
				pumpSpeedByte = (float)slayerMainPWM * 255 / 100; // 0-255 range
				break;			
			case AUTO_UNION_PROFILE_PULL:
				//Need to define two setpoints and two Inputs
				if (preInfusion) //if union is in preInfusion mode, it is running Flow Profiling
					pumpSpeedByte = executePID_F(profileIndex, pumpSpeedByte, sumFlowProfile, pullTimer);
				else
					pumpSpeedByte = executePID_P(profileIndex - unionSkew, pumpSpeedByte, currentPressure, pullTimer);
				break;
		}
	}
	
	// Action Time - operate pump & operate FLB solenoid
	md.setM1Speed(constrain(((int)((float)pumpSpeedByte * 400.0 / 255.0)), pumpMinPWM, pumpMaxPWM));
	return pumpSpeedByte;
}

And how bypassing is calculated and executed. For most modes, the preinfusion is calculated per a PWM threshold (FLBThreshold parameter). For Stepped Flow (slayer like) mode - the decision is based on time:
//*******************************************************************************
// Calculate FLB cutoff for each pull mode and activate/deactivate solenoid
//*******************************************************************************

boolean setFlowLimitBypass(byte pumpSpeedByte, int profileIndex, boolean preInfusion)
{
	if (preInfusion)
	{
		switch(g_pullMode)
		{
			case MANUAL_PULL:
			case AUTO_PWM_PROFILE_PULL:
			case AUTO_PRESSURE_PROFILE_PULL:
			case AUTO_FLOW_PROFILE_PULL:
				if ((unsigned)pumpSpeedByte * 100 / 255 > FLBThreshold)
					preInfusion = false;
				break;
			case SLAYER_LIKE_PULL:
				if (profileIndex > slayerPIPeriod << 1)
					preInfusion = false;
				break;			
		}
	}
	if (g_cleanCycle || g_flushCycle)
		preInfusion = false;
	
	if (preInfusion) 
	{
#ifdef INVERT_FLB_OUTPUT 
			digitalWrite(FLOW_LIMIT_BYPASS, HIGH);
#else
			digitalWrite(FLOW_LIMIT_BYPASS, LOW);
#endif
		if (profileIndex % 2 == 0)
			digitalWrite(GREEN_LED, HIGH);
		else
			digitalWrite(GREEN_LED, LOW);
	}
	else
	{
#ifdef INVERT_FLB_OUTPUT  
			digitalWrite(FLOW_LIMIT_BYPASS, LOW);
#else
			digitalWrite(FLOW_LIMIT_BYPASS, HIGH);
#endif
		digitalWrite(GREEN_LED, HIGH);
	}

	return preInfusion;	
}


void initFlowLimitBypass()
{
	pinMode(FLOW_LIMIT_BYPASS, OUTPUT);

#ifdef INVERT_FLB_OUTPUT 
	digitalWrite(FLOW_LIMIT_BYPASS, HIGH); // turn off FLB solenoid
#else
	digitalWrite(FLOW_LIMIT_BYPASS, LOW); // turn off FLB solenoid
#endif

}
PID does some interesting things.
1. We bind the output to a band around the reference profile.
2. We interpolate the Set point to the time of the current loop (as it mostly falls between two profile points).
3. We also do the necessary conversions from the stored bytes (0-255 levels) to the required double (which for the 8-bit AVR is the same as regular float).

Note: The PID library I use was intended for thermal management. It assumes that it is proper, upon shutting down the PID loop, that the states should be kept (mainly the current integral error), and reinitiated upon subsequent activation of the PID loop. This may be okay for thermostats - but really bad for this application. Even worse, it keeps the output state! What it means is upon the next pull the pump would whizz for a few seconds. There are lines in the code that are intended to reset the output, and changes to the library that are intended to reset the integral error upon shutdown of the PID loop. Anyone contemplating doing something similar should watch out for this.
//*************************************************************************************************
// Execute a PID calc (if it is time to...) and display PID parameters (Input, Output and Setpoint) 
//*************************************************************************************************
byte executePID_P(int profileIndex, byte pumpSpeedByte, float currentPressure, unsigned long pullTimer)
{
	double minSetOutputLimit = constrain((double)g_PWMProfile[profileIndex] * (100.0 - PWM_TRACK_LOWER_BOUND) / 100.0, pumpMinPWM, pumpMaxPWM);
	double maxSetOutputLimit = constrain((double)g_PWMProfile[profileIndex] * (100.0 + PWM_TRACK_UPPER_BOUND) / 100.0, pumpMinPWM, pumpMaxPWM);
	pressurePID.SetOutputLimits(minSetOutputLimit, maxSetOutputLimit);
	
	g_PIDInput_P = (double)currentPressure;
	
	// Note: in the profile we store averagePressure as displayed (i.e. multiplied by 100 / 12) so we have to correct the Setpoint by multiplying with reciprocal (12 / 100)
	g_PIDSetpoint_P = (double)g_pressureProfile[profileIndex] * 12.0 / 100.0 + (double)(g_pressureProfile[profileIndex + 1] -
		g_pressureProfile[profileIndex]) * 12.0 / 100.0 * ((double)(pullTimer % 500) /500.0) ; 
		
....

byte executePID_F(int profileIndex, byte pumpSpeedByte, int sumFlowProfile, unsigned long pullTimer)
{
	double minSetOutputLimit = constrain((double)g_PWMProfile[profileIndex] * (100.0 - PWM_TRACK_LOWER_BOUND) / 100.0, pumpMinPWM, pumpMaxPWM);
	double maxSetOutputLimit = constrain((double)g_PWMProfile[profileIndex] * (100.0 + PWM_TRACK_UPPER_BOUND) / 100.0, pumpMinPWM, pumpMaxPWM);
	flowPID.SetOutputLimits(minSetOutputLimit, maxSetOutputLimit);
	g_PIDSetpoint_F = (double)sumFlowProfile + (double)(g_flowProfile[profileIndex + 1] >> 1 -
		g_flowProfile[profileIndex] >> 1) * ((double)(pullTimer % 500) /500.0) ;
	g_PIDInput_F = (double)g_flowPulseCount;
	
....

That concludes the espresso aspects of the software. Of some interest to anyone who wishes to interface with the 3d5 is the following interface code. I used Serial2, but one can use any of the 4 serial ports available (albeit Serial0 is usually reserved for USB).
//************************************************************************************
//
// These functions receive the buttons pressed on the GS/3 so we can assign different  
// pull modes to the machine's buttons.
//
// This works with the Dosatori 3d5 Deluxe 2ptk Bi-Power board I have running 
// 1.16 version firmware. It is not guaranteed to work with another card, or one that 
// uses a different version of firmware.    
//
// Pinout of CN8: 
// Pin 1 GND 
// Pin 2 Rx
// Pin 3 Tx
// Pin 4 16V ----------	BE EXTRA CAREFUL - THE VOLTAGE ON PINS 2 & 3 is 5V TTL LEVEL; 
// 						EXCEEDING 5V MAY DESTROY THE 3D5
// 
// A short is affixed between Pin 2 and Pin 3 otherwise the buttons pushed will be ignored.
// 
// 3d5 Port Settings:
// 1200 baud
// Even Parity
// 8 bit
// 2 stop bits
//
// 3d5 Port sends out the following:
// Pin number on CN2 (1GR)	Button Label	RS232 Hex Code	Pin number of CN4 (2GR)
// 		2					One Shot				11h			1
// 		4					Two Shot				12h			2
// 		6					One Mug					13h			3
// 		8					Two Mug					14h			4
// 		10					Function				19h			5
// 		16					Power/Tea				15h			8
//
//************************************************************************************* 

void serialControl() 
{
	int incomingByte = Serial2.read(); // read the incoming byte
	
	switch(incomingByte)
	{
		case 0x11: 	// This is the 1 shot button
			g_pullMode = SLAYER_LIKE_PULL;
			g_currentMenu = 3;
			g_cleanCycle = false;
			g_modeSwitchIncomplete = true;        // This variable ensures the mode is fully initialized even if the interrupt comes quickly! 
			break;
	    case 0x12:  // This is the 2 shot button
			g_pullMode = AUTO_PRESSURE_PROFILE_PULL;
			g_currentMenu = 1;
			g_cleanCycle = false;
			g_modeSwitchIncomplete = true; 
			break;
		case 0x13: // This is the 1 mug button
			g_pullMode = AUTO_FLOW_PROFILE_PULL;
			g_currentMenu = 2;
			g_cleanCycle = false;
			g_modeSwitchIncomplete = true; 
			break;
		case 0x14: // This is the 2 mug button
			g_pullMode = MANUAL_PULL;
			g_currentMenu = 0;
			g_cleanCycle = false;
			g_modeSwitchIncomplete = true; 
			break;
		case 0x19: // This is the Fn button
			g_modeSwitchIncomplete = false;
			g_cleanCycle = false;
			g_flushCycle = true;
			break;
		default:
			break;
	}		
	
	Serial2.write(incomingByte); //This line authorizes the 3d5 to start the pull; we now wait for group solenoid interrupt....
}
Note: If you remove the short between Tx And Rx, the machine will not work unless the last line in the above "Serial2.write(incomingByte); " gets executed within 30mSec of the 3d5 sending the code to us. That means that the loop watching the serial port as well as the code within the selection handler must be sufficiently fast to catch the button press, execute whatever needs to get executed, and send the code back so that the 3d5 will continue with the pull.

The advantage of intercepting the 3d5 code is for cases the Arduino was too busy to execute the pull. The disadvantage is a failure point (albeit for this mod, that is a moot point since the pull cannot proceed without the Arduino controlling the pump).
Scraping away (slowly) at the tyranny of biases and dogma.

boost
Posts: 450
Joined: 9 years ago

#14: Post by boost »

Wow thank you very much for sharing Assaf, I had to read it a few times just to understand what it is capable of.
I have question about the RS232 interface on the 3D5 board, I have seen this interface in other machines (Linea etc) and always assume they are for flashing or updating the firmware.
You mention that it is used for external terminal/credit card reader. So is it basically just capable of receiving button command in hex and transmitting the button status of the keypad? So sending 11h will be the same as pressing one shot button? And also if you physically press the one shot button then it will transmit 11h? How does it work with external card reader, unless you disable the physical keypad?
I also wonder if there is other data transmitted other than just the button status/request.

User avatar
AssafL (original poster)
Posts: 2588
Joined: 14 years ago

#15: Post by AssafL (original poster) »

Final & Future thoughts:

Pressure & Flow Profiling:
Mimicking or masquerading a state machine (e.g. the Slayer's Stepped Flow profile, or the GS/3's/Gicar 3d5 stepped motor PI) is easy. Masquerading as a lever or an e61 is a bit more involved and quite possible using a GS/3 AV or other volumetric machines.

Obviously, a lever is easier, as it has to mimic a limited flow "filling" (where water enters the piston and wets the puck), after which the lever is released and the machine has to become volumetric. Luckily, the flowmeter is extremely accurate and can easily serve as the basis for the pressure of the pull. One does have to remember that in a lever the spring force, F (=-kx) is related to x, which is relative to the flow and NOT TIME. Time is irrelevant when mimicking a lever as the force is only related to the length of the spring, and since water is incompressible, that length is only relative to the volume of water that traversed the puck.

So one would build a model by where a piston would have volume, and a spring with a constant k that is relative to the volume (pi x r^2 x dx). Easy to do.

An e61 (or any other group that has a PI chamber) is a bit more difficult. One would have the same model of a piston and a spring, with the exception that the piston is in parallel to the puck resistance. Not difficult to do - except that all it really means is one could find an equivalent "flow drop" parameter to the volumetric pump. A parallel installed PI chamber is nearly identical to a timed reduction in pump volumetric flow (it would be identical if the reduction coefficient is deltaPressure related (between PI piston force and spring force).

Again - not an issue for an ATMEGA2560 but perhaps discombobulating for the programmer trying to model the profile.

Controls - leveraging the 3d5 cash register interface make controlling the process easy. There are a few considerations:
1. The 3d5 buttons can be intercepted upon initiating a pull. However, they cannot be intercepted when a pull is ongoing. Hence one cannot use the demitasse 1 shot button to initiate a Slayer shot, and then use the same button to accelerate (or punch through) the shot. One would need to use the potentiometer or a separate button to do that.
2. Currently, the mapping between buttons to modes is Stepped Flow (one shot)/ PP (2 shot button) / FP (one mug button) / Manual (two mug) / Flush at cleaning speed (Fn@ button). So each button maps as if it was a different machine altogether. A bit confusing. The current thought is perhaps a better mapping would be to EBF ratios. So say Stepped flow (or PP or FP) at 1:1.5, 1:2 and 1:2.5 EBF.
3. The box is kind of annoying. it would be nicer to find a way to mount the display somewhere, and add the potentiometer to the machine somewhere (like a paddle or throttle). Personally, I like the mods to be completely reversible (even if lots of work) so drilling the body is out.

EBF Mode: Got a Bluetooth host module. Will work to see if I can get it to work with Acaia or will get the (what seems to be) the superior Decent scale. If only Acaia had a better management team...
Scraping away (slowly) at the tyranny of biases and dogma.

User avatar
AssafL (original poster)
Posts: 2588
Joined: 14 years ago

#16: Post by AssafL (original poster) »

boost wrote:Wow thank you very much for sharing Assaf, I had to read it a few times just to understand what it is capable of.
I have question about the RS232 interface on the 3D5 board, I have seen this interface in other machines (Linea etc) and always assume they are for flashing or updating the firmware.
You mention that it is used for external terminal/credit card reader. So is it basically just capable of receiving button command in hex and transmitting the button status of the keypad? So sending 11h will be the same as pressing one shot button? And also if you physically press the one shot button then it will transmit 11h? How does it work with external card reader, unless you disable the physical keypad?
I also wonder if there is other data transmitted other than just the button status/request.
Thank you for reading.

Take a look at the following cash register interface documents for the 3d5. The first is a spec of the codes sent between the 3d5 and that cash register:
http://wiki.etcworld.be/uploads/pdf/Kof ... is_bis.pdf

And the second shows the connectivity (it shows the Centrale Version - not the Maestro Deluxe - so the component and connector labelling are different).
http://wiki.etcworld.be/uploads/pdf/Kof ... 122012.pdf

As far as I can see the serial port is hardly interactive. No interface, no response and it completely ignores anything I try to send its way. I was thinking of looking at the code and trying to find resources (ascii) that may hint on commands - but have yet to do that (nor do I find that a good use for my time). Maybe when this project will be over (i.e. EBF functional) I will revisit. So I currently do not believe you do anything except interface with a cash register with the RS232. (Note: For software updates the connector labelled OBP - OnBoard Programming - is used with an ISP programming connector.)

The way it works is as follows (for the sake of the example say I want a single dose):
1. Put a card in the cash register.
2. Press demitasse button (one shot). (Note: this button press would be on the Gicar Keypad)
3. 3d5 sends 11h on the Tx pin and starts counting 30mSec.
4. Cash register gets code from 3d5 (or from a relay board that apparently Gicar offers for people that can't program in C++).
5. If the transaction is okay, the cash register send 11h back to the 3d5 (received on the Rx pin).
6. If (and only if) the 3d5 received 11h within 30mSec period does it proceed to pull the shot.

Pushing a button during the shot stops the shot and DOES NOT send a code from the 3d5 (So you don't get charged twice if you stop a pull at a ristretto EBF).

Obviously, if the cash register was not padded with money, it will not send the 11h and the 3d5 will timeout the 30mSec and wait for a new button press.

Last point is - that this is the reason the short is so important. If the short between Tx and Rx is missing, the 3d5 will wait and will always timeout - and the machine will appear broken.

Hence - to answer your question - You CANNOT send a button 11h and get a response - that is not how it works.

However, there seems to be a workaround: the buttons are all open collector. They are wired in parallel to the 3d5 (from each of the 3 keyboards). And their common is scanned by the ATMEGA and its demuxer. However, in the GS3 case, since the GS3 has 1 keyboard only, and seems to be programmed to only respond to the first keyboard, one can pulse its button to ground and get the same result as pushing the button (which would have tied it to the the keyboard scanner virtual ground). It works for all buttons except the power. My assumption is that the power button is tied to an interrupt which does not wake it up if it isn't the virtual ground.

In case of a 2 or a 3 group - one would need to pulse the button pin to the correct (scanned) ground (and not to the general board ground).

NB - The 30mSec is real. When I originally integrated the controller with the 3d5 - some presses would be ignored. As it happened I loaded too much functionality (not good to do in any case) into the Serial Interface function. Seeing all the wait states in the system and the functionality within some of the modes - it exceeded the 30mSec.

So currently I treat the Serial Handlers like you would an interrupt handler - set the variables, and immediately respond to the 3d5. Since the next mSec or so the 3-way would open (which could trigger an interrupt possibly before the mode was fully implemented) the critical part was to set a boolean flag that would tell the system to finish setting the mode correctly (g_modeSwitchIncomplete = true; - yes sadly it is a global variable).

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

DaumierS
Posts: 189
Joined: 7 years ago

#17: Post by DaumierS »

I registered at HB a couple of months ago, and it was of tremendous help. Assaf, I am envious of you, I wish I'd be not a mathematician, but an engineer and could do fantastic things you did!

User avatar
AssafL (original poster)
Posts: 2588
Joined: 14 years ago

#18: Post by AssafL (original poster) replying to DaumierS »

Thanks -

Its all math and logic. Engineering is just about finding ways to apply math and logic.

None of this is extreme engineering or difficult to do - but there are quite a few disciplines involved (like tube forming, electronics and soldering, programming, calibrating, mains wiring, mechanical assembly, even component purchasing etc.).

There are easier ways to get started. As an example, an FG209 motor can be used with a potentiometer and a power supply to do manual profiling. Much easier to assemble, hardly any electronics.

Indeed 2 months ago this very project was a pot and an Arduino (unlike the FG209, the MG line of pumps need a full PWM controller hence the need for an Arduino).

When I found the display in my drawer (I purchased it 2 years earlier on a whim) - that is when scope creep happened in earnest and the project became automated with profiles and graphs and the rest. This was not by design - but by a constant progression of features and feature improvements.
Scraping away (slowly) at the tyranny of biases and dogma.

boost
Posts: 450
Joined: 9 years ago

#19: Post by boost »

There is so much information in this thread that I am still trying to partially digest this bit by bit :idea:

I think I understand how the RS232 interface works with external card terminal now. I did look at the stock 3D5 board from Linea and I can definitely see the jumper between Tx and Rx pin.
It makes sense that if you press a button then a hex command will be sent to the external terminal would "authorize" it and send the the same command back to the 3D5. Hence if you bridge the Tx and Rx pin together then it will always receive the same command back.



Initially I was hoping to tap into this signal for some kind of cheap shot timer for 3D5/Gicar equipped machine without LCD or shot timer such as the Linea or Linea mini. But as you said earlier if you press the button again to stop the shot then it will not send the same hex command since you wouldn't want to get charged twice. So really there is no way to figure out when the shot has stopped.

User avatar
AssafL (original poster)
Posts: 2588
Joined: 14 years ago

#20: Post by AssafL (original poster) »

Indeed you may be able to get the button press (pull initialization) from the serial port - but not the pull end.

A workaround is to get the pull end from the group relay.



The relay is driven by an open collector driver, so the voltage you'll see on it is the power supply unregulated voltage. It may be easier to take the signal directly from the relay driver chip.



The relay signal would come from the second relay from the bottom in the lower picture. The top most solder pad for the relay connected to the anode of the freewheeling dioode. (inside the Yellow Box). I marked the pad with a teal arrow below. Remember the voltage there can be as high as 14-16 volts. Some technologies would need at least a resistor and zener to work reliably with that sort of input...



BTW - I think the driver chip is the ST STPIC6C595 high power 8 bit shift register - unfortunate as getting the signal in a 5V version isn't easy (the output is connected directly to the relays). Gicar make extensive use of shift registers to cut down on the microcontroller pins they need.

An alternative is to use one of these (or a 110V version) in parallel to the 3-way solenoid (it may be easier, however, to connect to terminals on the 3d5, or use Faston splitters appliance makers use). The disadvantage of using the 3-way without the Serial port is in case the Gicar is programmed to use PI. In that case the 3-way may open and close to do PI and you'll have to compensate for that in logic.

http://www.ebay.com/itm/302146399094?
Scraping away (slowly) at the tyranny of biases and dogma.