Reading Multiple Switches with one Analogue Input

The traditional way to connect a button to a GPIO pin is to have a pull up resistor to Vcc, and have the switch pull the GPIO to ground when it’s pushed. The catch is that you need one pin for every button. An alternative is to use a matrix: for example, using 4 outputs and four inputs to scan a 16 button keypad. To reduce the number of GPIOs still further, we can use a resistor ladder, something like this example below.

3 Switch Resistor Ladder

The diagram above is an attempt to use the minimum number of resistors, and a more optimal design would be possible with additional resistors.

When all the switches are open, we will see 3V3 at the analogue input AIN. When a switch is closed, it connects one of the resistors to ground, forming a potential divider and reducing the output voltage. For example, if we press S2 then the output voltage will be half the supply voltage.

When multiple switches are closed, the resistors are connected in parallel, so we will get a different value for each combination of switches. Provided the values are sufficiently different, we can detect all possible switch combinations.

It’s fairly easy to create a spreadsheet to calculate the output voltage for a given set of resistors, and switch states. The table below shows the predicted output voltage, and the actual measured voltage for every switch combination:

Voltages with 3 switch ladder

The differences between the actual and predicted voltages are due to resistor and supply voltage tolerances. These show fairly good agreement if they are normalised to supply voltage.

This is what the results look like when graphed. Note that the results are not linear, but the values are sufficiently different to be able to reliably detect different key combinations.

Voltage graph for 3 switch ladder

Having taken the analogue reading from the sensor, it can be compared to the possible values shown above. This can either be achieved with a look up table, or by fitting a function to the data. Having obtained the integer code 0 to 7, the state of an individual button can be detected by testing individual bits of that code.

Analogue Input Board for the Pressure Sensor (Espiresso)

Having added a pressure sensor to my Espiresso Machine, I needed an analogue input to be able to read the output voltage from the sensor. Unfortunately, the Raspberry Pi doesn’t have analogue inputs, so an external ADC is required. I settled on the Adafruit ADS1015 which has 4 inputs, 12-bit resolution, includes a programmable gain amplifier, and also uses an I2C interface:

Adafruit ADS1015

There’s some useful information on how to set up I2C on the Pi on the Adafruit site. Having done that, there is sample code available to read the sensor from Python or C. With this, it was pretty easy to get readings from the sensor, and it seems to perform well.

The pressure sensor uses a 5V supply, and has a ratiometric output which ranges from 0.5V to 4.5V, proportional to input pressure. It has a 0-300 psi range (20.68 bar). The absolute maximum analogue input for the ADS1015 is VDD+0.3V. If we are running the ADS1015 from 3.3V for compatibility with the Pi I2C voltage levels, this limits the analogue input voltage 3.6V maximum.

Theoretically, the Ulka EP5 pump can develop up to 15 bar pressure, although this is limited by the OPV in the machine to 9~10 bar. If we assume the maximum pressure is 10 bar, and the response is linear, this would result in a maximum output voltage of:

4V x (10/20.68) + 0.5V = 2.4V

So it seems that for normal use, the output voltage should be below the maximum analogue input voltage limit of 3.6V. Even if we assume peak pressure of 15 bar is possible, that would be a maximum output voltage of:

4V x (15/20.68) + 0.5V = 3.4V

Based on these calculations the current plan is to interface the pressure sensor directly to the ADS1015.

The next step was to interface the board to the Pi in the Espiresso. Unfortunately, the SCL and SDA pins on my Pi are already used as inputs to read the two front panel buttons on the machine (using one GPIO per button). Also, almost all the other GPIOs are in use, so there was no free GPIO available to relocate the buttons.

To make the SCL and SDA lines available for the ADC, I decided to use a resistor ladder to read the buttons. This will make it possible to use a single analogue input to read all the buttons.

I’m also planning to use an opto-coupler to signal the Pi when the brew (pump) switch is switched on. Since that has an open collector NPN output, I plan to connect that to the resistor ladder also, so that it can be treated as another button input.

The pressure sensor and button input will only use 2 of the 4 analogue inputs, so there will be some inputs spare for future use.

Ulka EP5 Pump PWM Pressure Modulation

Recently I’ve been experimenting with using PWM to control the pump pressure in my Gaggia Classic. People have used various methods to control the pump pressure, ranging from triac/thyristor dimmers to using series resistance. A few have used PWM (notably Jonr on CG forums), and this looked by far the best solution, so I decided to use high frequency PWM with an IGBT. This would need to be opto-isolated so it could be driven safely from the Raspberry Pi.

I started off looking at the HCPL-3120 optocoupler with gate drive outputs. There are hundreds of suitable IGBTs. However, as I started sketching out the design, including 15V supply etc. it seemed a lot of components were needed for this solution. Instead, I decided to look at using an IGBT with logic level gate drive to simplify the design, preferably one with a TO-220 package. It turns out that there are very few which meet those requirements, so I’ll list them here in case it helps someone else:

  • IRGB14C40LPBF (430V / 20A) from IRF
  • ISL9V3040P3 (21A) and ISL9V5036P3_F085 (46A) from Fairchild
  • STGP18N40LZ (30A) from ST

From that list, the only one I could find at reasonable cost without importing was the IRGB14C40LPBF, so I settled with that one.

To safely interface with the Raspberry Pi, I use a 4N25 opto-isolator with a 270R series resistor, which is driven directly from a 3V3 GPIO pin (tested at a little under 8mA). Note that the series resistor would need to be increased for 5V input.

The NPN output of the opto-isolator is inverted by a BC556 PNP transistor to switch the IGBT input through a 1K series resistor.

The power supply is the VTX-214-003-105. This is a compact module which provides 5V at 600mA from 90V-240V AC mains input. Caution: in this circuit, note that the DC output GND is connected directly to mains NEUTRAL, meaning that the 5V and GND lines in this circuit are not isolated from the mains. This means that they should be insulated and boxed up.

On the output side, the IGBT is wired in series with the pump. There’s a series diode to prevent AC passing, and a flyback diode in parallel with the pump for protection.

Here’s the current circuit diagram for this design (to be used strictly at your own risk):

EP5 Pump PWM with IGBT

To test it, I built a small prototype on strip-board. Note that the IGBT is separate, as I plan to mount the board inside an enclosure, and mount the IGBT externally. Ultimately I would like to make a custom PCB for this. After extended testing, the IGBT is absolutely stone cold so the heat-sink appears redundant.

Pump PWM Prototype

Since I’m using the Raspberry Pi’s one and only hardware PWM pin to drive the boiler, I used the excellent ServoBlaster which provides high resolution software PWM on any GPIO pin. Initially I set this up for 1kHz PWM, and varied the duty from 0% to 100%. For example, to set up 1kHz PWM on GPIO pin P1-22 and using the PCM hardware, you would use:

sudo ./servod --pcm --cycle-time=1000 --min=0% --max=100% --p1pins="22"

To set up 50% duty cycle on that pin, you would use:

echo P1-22=50% > /dev/servoblaster

Before testing this with the pump, I tested with a 60W incandescent light bulb and series diode. Adjusting the duty cycle allowed the lamp to be smoothly and precisely dimmed. The average voltage drop across the IGBT was around 0.33V and (after fully disconnecting mains power) it was cold to the touch.

I then moved onto testing this with the EP5 pump. For easier testing, the machine was temporarily rewired so that the brew switch supplied power to the entire circuit above, so that I could kill power if needed, without relying on the IGBT alone.

Using a Portafilter pressure gauge, the pump was tested at various duty cycles from 0% to 100%. I found that the pump only seems to start operating at about 30%, and then pressure rises to about 1 bar. Increasing the duty cycle allows the pressure to be controlled quite easily between 1 to 9 bar (or wherever your OPV is set).

So far I’m really pleased with performance. The next step is to box it up and wire it in more permanently. I’m also thinking of adding a small SSR to switch the solenoid. Once I’ve done some further testing, I’ll upload graphs of duty cycle versus pressure.

Update: check out the slightly amended design here which replaces the BC556 with the TC426 driver.

NVIDIA 3D Vision Pro and VESA Stereo Sync

We ran into problems recently getting the NVIDIA 3D Vision Pro glasses to work on a dual-pipe stereo projector (clone mode stereo). The projector takes two DVI inputs, one for the left and one for the right eye at 1920×1080 and 60Hz. These are then displayed frame sequentially at 120Hz. The projector has the standard BNC connector which outputs the stereo sync signal for the glasses (Sync and Ground).

The NVIDIA RF base station (pyramid) has a 2.5mm stereo jack socket which accepts external stereo sync, so we tried feeding the projector sync into that input. Unfortunately, the pyramid seemed to ignore the external sync and wouldn’t activate the glasses…

We had the NVIDIA pyramid working well on another system in active stereo, using the 3-pin VESA sync output from the graphics card, so I decided to test that. This is what I found:

  • When running at 120Hz active stereo, the Sync signal is a simple 5V square wave with 50% duty cycle of around 8.33ms high and 8.33ms low, exactly as you would expect.
  • The LED on the back of the pyramid lights up blue when it’s using external sync, green otherwise.
  • When using active stereo, the Sync light only illuminates when stereo is activated (i.e. when running a stereo application)
  • 5V power is always present on the 3-pin VESA stereo connector.
  • If the 5V power is removed, it ignores the external SYNC input. Using only SYNC and GND is not enough to get it working.
  • Once it has switched on and started accepting external Sync, I tried disconnecting the 5V power: the blue LED stayed lit. This suggests it only checks for presence of 5V at start up.

This is what the Sync LED looks like when the unit is persuaded to accept external sync:

NVIDIA 3D Vision Pro Pyramid

The solution was to make a cable which combines the Sync output from the projector with 5V (taken from USB) and Ground onto a standard 3-pin mini DIN VESA stereo connector (pinout shown below). The picture above shows the cable (white Y-shaped cable) taking Sync via a BNC connector (the T-piece is used to connect another IR emitter in parallel) and the connectors on the right are the 3-pin mini DIN stereo cable, which goes to the 2.5mm jack sync input on the NVIDIA pyramid.

This is the pinout of the VESA Stereo 3-pin mini-DIN socket:

 VESA Stereo 3-pin DIN

 And here’s the corresponding pinout for the NVIDA 3D sync jack:

NVIDIA 3D Sync Jack Plug

Caution: the jack plug isn’t a great choice of connector for something carrying GND and 5V, as there is obvious potential for a temporary short when the plug slides in and out of the socket. Avoid connecting/disconnecting the jack when the system is powered up. It’s safer to disconnect the USB and mini-DIN plugs.