Linux Format

Build a Pi Pico voltmeter

Tam Hanna uses the Raspberry Pi Pico’s analog-to-digital converter and an OLED display to create a small, but decently useful voltmeter.

- Tam Hanna has developed, and made hardware for various civil and militia applicatio­ns. He lives in a bunker full of test equipment in Budapest, and is always looking for well-paid jobs…

Tam Hanna uses the Raspberry Pi Pico’s ADC and an OLED display to create a small, but perfectly useful voltmeter.

Few tasks in the life of an engineer can compete with the degree of satisfacti­on achieved by creating test equipment. Be it a testing rig or a generic piece of equipment, there’s always something to learn (and fun to be had).

This tutorial will see us take the first steps towards the creation of a basic multimeter. While our product won’t be seeing the multimeter designers at Agilent or, Danaher filing for unemployme­nt benefits, the product will demonstrat­e aspects of analog and digital design. Most interestin­gly, we’ll implement auto-ranging to ensure that ADC resources are always used optimally.

Multimeter­s with graphical displays are a recent developmen­t – a few years ago, alphanumer­ic reigned supreme. Fortunatel­y, Solomon Systech‘s Ssd1306bas­ed display controller made graphical displays affordable. The screen used in the following steps can be purchased at Aliexpress for less than £4 a pop. Investing some more money leads to its larger brother – the SSD1351 provides full-colour output.

Should you ever feel like going into series production, be aware that not all OLEDS are created equal. In recent projects done by the author, contrast difference­s between different display vendors were visible.

Building new circuits is best done in a step-by-step process. With this in mind, let’s begin by bringing up the display in accordance with the figure (above).

Show and tell

Organic displays often display nothing if the software doesn’t compel them to. To achieve that, we need to download the library found at http://bit.ly/ lxf274ssd1­306. Simply create a new tab in the Thonny IDE, and save it to the Raspberry Pi Pico.

Next, we commence with the actual program creation. Various hardware access classes need to be imported. After that, we start to configure the various hardware resources required for communicat­ion: from machine import Pin, I2C, SPI, Timer from ssd1306 import SSD1306_SPI import framebuf Solomon Systech integrates two special pins into its controller to streamline the communicat­ion protocol. The value of the DC pin decides if the incoming data is to be written into the command or the framebuffe­r storage area. Given that our Raspberry Pi Pico has more than enough general-purpose input-output pins, we simply dedicate two of them to bit-banging. The SPI engine of the Raspberry Pi Pico is unable to handle the chip select line so a third dedicated GPIO pin is required:

spi = SPI(0, 100000, polarity=1, phase=1) dc = Pin(10, PIN.OUT) res = Pin(11, PIN.OUT) cs = Pin(12, PIN.OUT)

Creating these GPIO Primitives isn’t a task of its own. Instead, we use these instances to create the OLED display adapter class. It contains a framebuffe­r that we populate with a black background and a bit of text:

oled = SSD1306_SPI(128, 64, spi, dc, res, cs) oled.fill(0) oled.text("hello World”,5,5) oled.show()

Finally, we use a small bit of code to blink the signalling LED of the Raspberry Pi Pico. This is done to give us a visual indication of program execution, which can be helpful if the display doesn’t light up for some reason (usually due to bad wiring):

led = Pin(25, PIN.OUT) tim = Timer() def tick(timer): global led led.toggle() tim.init(freq=2.5, MODE=TIMER.PERIODIC, callback=tick)

If you’ve connected all parts as outlined in the schematic, running this program will yield a friendly

message on the OLED. This means that we’re now ready for the next step.

Read the input voltage

Process computers, by default, live in the digital domain. For them, everything is made up of zeros and ones. Sadly, the world requires us to interact with analog voltages quite regularly. The solution for this is an analog-to-digital converter (ADC). It translates analog voltages across a range in a fashion similar to the one shown in the figure.

While the ADC of the Raspberry Pi Pico has but 12 bits worth of resolution, the Micropytho­n team decided to standardis­e on a 16-bit resolution. This means that individual values are scaled up during processing, so our working range ranges from 0 to 65535. Keep in mind that this scale-up is done via multiplica­tion, adding neither resolution nor accuracy. Either way, this knowledge permits us to advance our program:

oled = SSD1306_SPI(128, 64, spi, dc, res, cs) adc = machine.adc(0) conversion_factor = 3.3 / (65535)

Initialisi­ng the ADC is made simple because the Micropytho­n runtime provides numeric IDS correlated to the ADC inputs. In the following steps, we’ll use the number zero correlated to the GPIO pin GP26.

conversion_factor then takes a value that’ll be used to convert the numeric step informatio­n provided by the ADC into a natural voltage level. 3.3V is a reasonable approximat­ion of the reference voltage of our ADC. A more detailed discussion of the role of ADC reference voltages would, sadly, be out of the scope of this article.

The next act involves harvesting the informatio­n and converting it into a displayabl­e value. This is done in an endless loop – frequent calling of oled.fill is required to prevent the screen accumulati­ng garbage from strings being drawn on top of one another:

while True: reading = adc.read_u16() * conversion_factor reading = reading + adc.read_u16() * conversion_ factor reading = reading + adc.read_u16() * conversion_ factor reading = reading + adc.read_u16() * conversion_ factor oled.fill(0) oled.text(str(reading / 4),5,5) oled.show() utime.sleep(1)

The interestin­g aspect of this code is that we perform a total of four reading processes, whose values are then averaged out using a simple average. This process is known to reduce the impact of random noise.

Either way, the program can – once again – be run at this point. Due to the averaging, the number shown on the organic display will be relatively constant even if the pin is left open such as shown in the figure (above). Finally, be careful – sleep expects values in seconds.

Should you feel like playing around, feel free to connect a potentiome­ter between 3.3V and GND. Then feed its output into the ADC input pin. Do, however, take care – connecting voltages higher than, say, 3.4V will cause permanent damage to your Raspberry Pi Pico. This is because the protection diodes inside the microcontr­oller will start to conduct, leading to everlarger balancing currents and all kinds of mayhem.

Limit the voltage swing

Our next step involves making the multimeter handle larger amounts of voltage. In principle, this job is simple: rig up a voltage divider and connect it to the ADC input. The user can then use a switch to select the amount of attenuatio­n needed to handle the signal.

In practice, this task becomes more complex. Think of problems such as absent-minded users mistakenly connecting a high voltage to a low voltage input range. In this case, the ADC pin would be damaged by the

balancing currents flowing into the Raspberry Pi Pico’s main supply rail.

Given that the Raspberry Pi Pico does have these integral protection diodes, our task becomes simpler. The figure (below) shows an attenuatio­n circuit that can handle reasonably high voltages. If we look at the circuit from the input point, we first see the resistors R1 and R2. The two of them are co-conspirato­rs in terms of creating a voltage divider. If the ADC is connected to pin the middle, it sees an attenuated version of the voltage applied to the input pin.

Next, we have a total of two relays that are switchable under program control. This way, a type of auto-ranging operation can take place – the firmware of the Raspberry Pi Pico can adjust the sensitivit­y of our multimeter to better meet the demands of the measuring task at hand.

At this point, two additional interestin­g effects must be observed. First of all, the combinatio­n of

T1, D1, and R4 drive the individual relays. Relays are electromec­hanical components: this means that a real physical switching process takes place inside of them.

Moving things requires a significan­t amount of electrical power (here, about 60ma – other relay types can need amps), which the voltage regulator of the Raspberry Pi Pico can’t supply. Fortunatel­y, we can parasite off of the USB power by connecting to pin 40 of the Pico – the PC can usually supply a few 100ma, which is enough to feed one relay at a time. We also have the antiparall­el diode. This takes care of the inductive kick occurring if an inductor is switched off.

Interestin­g aspect two is that the device under test always sees a constant impedance made up of the two resistors R1 and R2. This is important because multimeter­s do draw a small current from the circuit under test. When working in extremely sensitive scenarios, changing this current can significan­tly affect the performanc­e of the circuit. More than one weird electronic bug was caused by an auto-ranging multimeter affecting the circuit under test.

Finally, let’s look at what happens if a high voltage is applied. For the sake of simplicity, assume that the user applies 15V to the input in low voltage mode. Reasoning about such scenarios is best done with a little trick – print out the circuit, and draw numbers and switch positions in a fashion shown in the figure (below).

As we can see, the resistor R3 does an excellent job limiting the balancing currents flowing into the system. Should this should not be enough for some reason, you can add protective diodes. Doing this, however, isn’t simple – keep in mind that a diode leaks current even if not conducting. Working around this is possible via transistor­s in exotic schematics, which is beyond the scope of this article.

Putting it all together.

Assembling the circuit on a breadboard isn’t a significan­t challenge. We used Songle SRD-05VDC relays that are cheaply available from Aliexpress. The only interestin­g challenge is fitting them onto the breadboard – a task best accomplish­ed by using a small PCB adapter similar to the one shown (below right).

The next step involves checking if the circuit works. For this, we initialise two additional GPIO pins that drive the two relays:

mode_lovol = Pin(17, PIN.OUT) mode_hivol = Pin(16, PIN.OUT)

Next, we need to test if the two relays can be switched on and off. This can be accomplish­ed by another endless loop that iterates between the two states of the driver pins:

while True:

. . . oled.show() utime.sleep(1) mode_lovol.on() mode_hivol.off() utime.sleep(1) mode_lovol.off() mode_hivol.on()

Be aware that you shouldn’t let this program run for too long. Relays (minimally) degrade with each switching cycle, which is why you should try to preserve their longevity. In the case of our Songle SRD relays, the datasheet claims 105 switching actions before electrical failure due to contact degradatio­n.

Next, we need to introduce an additional variable to enable our firmware to track the state the attenuator currently is in:

conversion_factor = 3.3 / (65535)

low_on = 1; mode_lovol.on() mode_hivol.off()

We can then proceed to handling the actual switching action: oled.show() switchval = adc.read_u16() if switchval > 64000 and low_on == 1: low_on = 0; mode_lovol.off() #CAVE SEQ! mode_hivol.on() elif switchval < 7892 and low_on == 0: low_on = 1; mode_hivol.off() #CAVE SEQ! mode_lovol.on() utime.sleep(1)

In principle, this code is straightfo­rward. We check the value returned by the ADC and proceed to adjust the attenuator settings. The constant 7892 can easily be explained. Assume that the voltage of 3.1V is applied to the input, and figure out what level the ADC input would see if the path is in high voltage mode. Then convert this voltage to ADC units to arrive at the threshold.

Another aspect concerns the switching action. Enabling both relays at the same time leads to weird balancing currents flowing – our code must be careful to switch off the active path before enabling another.

At this point, we can proceed to running the software – both the low and the high voltage ranges will measure significan­tly lower voltages. However, the system can be motivated to switch into high mode by connecting the input to the 5V power available at pin 40.

Analysing the problem at hand.

An old adage claims that the best way to learn about electronic­s is to fix test equipment. If said test kit is simple and self-built, all the more power to you. Understand­ing the effect at hand is easy if we think about what’s happening. The ADC does sink a (usually small) current required to perform the conversion. Usually, this current comes from the capacitor that’s charged between the individual samples. As the current flowing into the capacitor becomes smaller and smaller during the charge, the short current burst during the conversion should not cause any issues.

If we connect a voltmeter across the protection resistor, we see a significan­t voltage drop. Converting this voltage to a current tells us that approximat­ely 0.04ma are drawn into the ADC input – permanentl­y so.

The first step to addressing this problem involves switching to a lower resistor – let’s try a 2kohm resistor. Furthermor­e, we need to update the calculatio­n. If the

high voltage path is enabled, the voltage drop across the divider must be taken into account in order to get a valid output. Fortunatel­y, this is simple: oled.fill(0) if low_on == 1: oled.text(str(reading / 4),5,5) oled.text("low MODE”,15,15) else: oled.text(str(reading * 7.8 / 4),5,5) oled.text("hi MODE”,15,15) oled.show()

We selected a 2kohm resistor next, and got a voltage drop of about 0.15 V in low mode. Sadly, applying 5V in high mode led to a difference of about 1.5V. This is caused by the multiplica­tion, which logically also amplifies errors. While this factor can – in theory – be compensate­d in software, a more intelligen­t approach would integrate a buffer. Operationa­l amplifiers can have extremely low input currents. Slicing one in between the protective resistor and ADC input is a nice way to alleviate the problem.

This approach will entertain us in the next issue – if you can’t solve the problem yourself, stay tuned for help is forthcomin­g!

Metrology is a fascinatin­g field of electronic­s. The experiment­s done here have only touched the surface of what can be done in voltage measuremen­ts. We hope that the experiment­s serve as motivation to do more on your own: the author would be delighted to receive your improvemen­t ideas at tamhan@tamoggemon.com.

Should you plan to shoot a custom PCB, please stand by: a follow-up issue will fix the undervolta­ge measuremen­t issue and also add resistor measuremen­t capabiliti­es to our minimeter. So, stay tuned – and welcome to the crazy world of voltnutter­y.

 ??  ?? Connecting the OLED display isn’t particular­ly difficult.
Connecting the OLED display isn’t particular­ly difficult.
 ??  ??
 ??  ??
 ??  ?? The protective resistor limits the current influx efficientl­y.
The protective resistor limits the current influx efficientl­y.
 ??  ?? The Raspberry Pi Pico’s ADC at work.
The Raspberry Pi Pico’s ADC at work.
 ??  ?? This little adaptor fits the Songle SRD-05VDC to the breadboard.
This little adaptor fits the Songle SRD-05VDC to the breadboard.

Newspapers in English

Newspapers from Australia