Arduino Multimeter Guide

How I Built, and You Can Build Your Own, Arduino DMM

The second and third most recent versions (little screen and big screen), with their PCB.

Last Updated: Late March ‘25:

This page is not-quite-done, but pretty close, and replaces an earlier version.

Above and right: The most recent version, which uses a XIAO RA4M1 board. More at the bottom.


Guide Organization:

Block Diagram of the Arduino DMM Functions.

This guide is broken in these parts:

  • Preamble

    • Why?

    • Safety Note

    • Scope of Guide

  • Physical Design

    • The Chassis and Related Parts

  • Analog Circuitry

    • Turning the physical electron phenomenon into a readable signal.

  • Inputs

    • Turning signal and human input into digital data

  • Processor / Code

    • Responding to inputs and generating outputs.

  • Outputs

    • Delivering data to the outside world.

  • Putting it all Together

    • Designing the PCB

    • Other Notes

  • Failed Ideas and Future Plans


Why build your own Arduino DMM? Why Not? More specifically, these are my reasons:

Added perk: Depending on how colorful and fun it is, it can also entertain your kids.

  • Type Data Directly into Excel via Keyboard Emulation.

    • This is the main problem I aimed to solve when I started. I can save a lot of time and reduce potential keystroke errors by using the Arduino’s keyboard emulation feature to type values directly into Excel (or a different program). With an added right arrow key press I can input many readings by only moving a probe and pressing a button. No more “apply probe - use my eyes to read the value - awkwardly type the value the in with one hand so I don’t have to set the probe down - move eyes back to whatever I’m measuring.”

  • Log Data to an SD Card with Custom-Defined Triggers.

    • I can measure current and voltage near-simultaneously, and use either to trigger data logging of both. This is useful for understanding how different parts of an overall circuit are interacting.

  • Very Sensitive Low Resistance Readings (Accurate to better than 0.01 Ohms).

    • Sometimes I am testing the integrity of relays, where I need to know the different between 0.1, 0.2, and 0.3 ohms. Most non-expensive meters are not set up for this.

  • Understanding Instrumentation. 

    • Every electronics device that makes any measurement has a volt and/or ammeter at its heart. Building my own is a good way to learn more about how those devices work.

    • Not the point, but this project also took me from virtually 0 Arduino coding ability (only knowing enough to change constants from examples I found online) to reasonably proficient most of the time.

  • Generally Inexpensive

    • All the parts come to $50-$100 for an overall very useful meter, so I can have multiple around.

  • Very Fast Auto-Ranging.

    • The limiting factor for the resistance circuit to auto-range and provide a reading is in practice only limited by the screen refresh rate, which is in turn limited to eye-comfort (I have it set to 1Hz when the value is stable and 5Hz when the value changes dramatically). Voltage tends to settle and provide a stable reading anywhere in the range closer to 1 second.

  • Display Readings on a Computer via a GUI. 

    • Sometimes this is useful and nice.

    • I was also told that designing a meter that shows the readings on a screen would be nice for people who e.g. record tutorial videos.

      • I accomplished this, but unless either your computer or whatever you’re measuring is battery powered (/otherwise isolated) you have use a USB isolator to prevent ground loop noise.

  • Small Physical Size.

    • Much smaller than a standard full-featured meter.

  • Highly Customizable

    • I can customize the code or physical design however I want to change pretty much whatever I want.


How to Styling Preamble

The circuitry in breadboard state. Because this section didn’t have enough photos.

The style of this how-to guide is one of “here’s how I did it, so if you do it you don’t have to reinvent the wheel.” I assume anyone using this as a guide has a working knowledge of electronics and how to design, test, and experiment with circuits in both LTSpice (or similar) and a breadboard, as well as a working knowledge of using Arduinos (or similar). If you don’t, building your own DMM is actually a great idea because you’ll learn it all. In that case, if you have questions please feel free to reach out to me and/or your favorite LLM.


Safety Concerns and Rating

I want to note here at the top that while many DMMs are designed for safe usage at voltages of up to 1000VDC, that is not the design intention of my unit and I am not attempting to build an instrument to handle potentially unsafe voltages. I have a proper, very nice, DMM for when I am measuring potentially unsafe voltages.

My meter is designed to measure (and I have tested it to) a maximum of voltage of +/- 50VDC. This value is intentionally chosen as the rough cutoff for where voltage becomes dangerous to people.

If you want to build a meter that can measure potentially unsafe voltages I am not qualified to provide any guidance on that.


Link (non-)Disclosure: the links in this page are plain links. I am not tracking you, and will not know nor make any money if you purchase anything using any of the links on this page.


Physical Design

The two current versions again.

Chassis:

This part is pretty straight-forward. I have two panels, one for the front and one for the back. The back panel has holes to match the Arduino’s mounting holes and holes for standoffs. The front panel has cutouts for the screen, buttons, probe ports, and standoffs. I made the panels out of acrylic using a laser cutter because I wanted them to be clear, but they could be 3D printed instead. The stand-offs are 3D printed.

Barrel vs Banana Jack. One is a lot bigger than the other.

Barrel Jacks:

My biggest innovation here is using barrel jacks for the input probes instead of banana jacks. These dramatically reduce the required size. Barrel jacks are much smaller, so I can fit everything in tighter together. (Especially given that you need two banana jacks.) It also means I don’t have to worry about the banana jacks being spaced the standard 3/4” apart (which also takes up a lot of space).

Instead I just have a barrel-to-banana jack adapter (I also made a couple test cables directly connected to a barrel jack).

The barrel jack is rated for 50vdc, which coincides nicely with my voltage range (that said, I accidentally, briefly, measured a ~300vdc rail recently and the meter wasn’t damaged).

A nice perk of this system is also that I can plug in / unplug my leads at the same time.

Other Components:

My other components here are all straight-forward. On my most compact meter I have flush-mount buttons (SPST momentary) and a slide switch for battery power.

Power is provided by a rechargeable 9V battery. Battery life isn’t great (some hours, depending on if I’m using the big screen version or not), but it’s enough for general bench-top usage where I can regularly recharge it and then untether myself when I want to move around. For extended runtime I need an external battery pack going into the Arduino’s USB port.


Analog Circuitry

Resistance Circuit: Turning Ohms into Volts

Here I designed a circuit that can be automatically switched (via a pin simulated by V4 and the MOSFET M1 in the schematic) between constant current mode (with the unknown resistor in parallel with about 330 ohm resistance to ground, and a constant voltage mode (with a 22k resistance added in series before the unknown resistor). The overall voltage is clamped via a Zener diode. In the schematic I used a 5.6Vdc Zener, but in the current version of the my design I use an ~4.3Vdc Zener because I need to keep the signal below the ADS1115’s supply voltage of 4.5-5VDC.

The voltage / current source I1 is an LM317 regulator set for 20mA constant current

Constant Current (Low Resistance) Mode:

Measuring a 1 Ohm Resistor. The high level (20mV) is the useable signal.

Constant Current Resistance Calculation as a Function of the Voltage Reading

Constant current mode is for very sensitive low value resistance measurement. The DMM is very sensitive in the constant current mode, but the range is limited to about 500 Ohms. The minimum detectable resistance is less than 0.001 Ohms (which measures ~20µV, still several steps above the the minimum ADC level of 7.8125µV). In reality even 0.005 Ohms is hard to see due to noise and carefully controlling lead resistance, but I can easily see 0.01 Ohms, and that meets my needs.

The equation in my code for the constant current circuit is:

resultRx = Ohms_v_output / (constantI - (Ohms_v_output / constantR)) - minR 
where:
  • Ohms_v_output equal (Ohms_ADS_Int * gain / 1000). 
    • Ohms_ADS_Int is the ADC’s output integer (0-32768).
    • gain is a coefficient based on the ADS range I’m in. 
  • minR is a variable to subtract out the lead resistance.

Constant Voltage (Normal) Mode:

Measuring a 10K resistor. The low (~1.5Vdc) level is the usable signal.

Constant Voltage Resistance Calculation as a Function of the Voltage Reading. With the Zener at 4.3Vdc the value asymptotes there instead of 5.7Vdc.

Constant voltage is the “normal” mode, which has a functional range of 20 to 5M+ Ohms. The 22k resistor means the voltage is always at the Zener clamping level, so the circuit is effectively just a voltage divider. It’s not particularly sensitive at the low end (1 ohm equals about 0.00026vdc, which is too low to reliably measure over noise), but above 20 Ohms or so it begins to be reliable and accurate.

You can see on the voltage vs resistance graph that ~0-1k is fairly compressed, I spread the range of 1K to 100K out in a fairly linear fashion, and then above 100k becomes compressed again.

Here is the equation for the constant voltage circuit:

resultRx = dividerR * (Ohms_v_output / (ZenerMax - Ohms_v_output))- minR 

Where:

  • dividerR is the 22K R7
  • Ohms_v_output is the same as the constant current mode.

This puts the effective range up to about 5M Ohms. Higher would be nice, but I run up against another problem, that the input impedance of the ADS1115 is between 5 and 10M ohms, and when I approach those values I have to start worrying about the effect to the ADC on the measurement. I could do that, but for now I’ve decided that an upper range of 5M is an acceptable limit.

Overall, this means I can measure from 0.001 to 5M ohms with only two different ranges.

By default my program auto-ranges, so I don’t have to think about what range I’m in. It doesn’t noticeably slow down the reading and works very well.

I want to note somewhere that while this circuit works pretty well, there’s a tradeoff that it’d not designed to or good at withstanding voltage on the probe contacts. I haven’t thought of a good way to protecting it (I’ve had a few ideas, but they’re all fairly complicated and/or would sacrifice the low end sensitivity). If you don’t accidentally measure voltage with the probes on the resistance contacts then it’s fine, and I’ve only accidentally damaged one of my boards making this mistake (took out the switching MOSFET). 


Voltage Circuit: Conditioning The Signal

Deciding the Range

The first question with the voltage signal is “What range of values do I want to read?” I settled on +/- 50 VDC. This value is convenient as it’s the general cutoff for when voltage becomes harmful to people. Deciding I’m not going to use my meter to measure higher amplitudes saves a lot of followup safety questions. It’s also convenient because the math with the ADC works out that I can measure down to ~1mV on the same analog circuit with this maximum.

Polarity

Up-Bias Voltage Divider. V2 is the measured voltage. Note R4 has been dropped to 2.4K and V1 is now 2.08Vdc to make sure the peak signal is below the ADC’s Vcc.

The circuit would be fairly straightforward if not for measuring negative voltages. The ADC I’m using has an input range of -0.256 to Vcc (~5vdc in my case, but a max of 6.144vdc). If I was just trying to read a 0 to 50vdc signal I would use a simple voltage divider to drop the signal by a factor of 10, and that would be that. However, I want to measure down to -50vdc, so things are more complicated.

A voltage divider to make a 100vdc swing fit into +/- 0.256vdc (a factor of ~200) would probably be noisy at the low end, although might work. The math indicates a theoretical resolution of 2mV. (I assume it would be too noisey - as I wrote this section I realized  I’ve never tested this. It’s on my to-do list because it might be okay and simplify the design.

The 50VAC out of V2 about becomes 0-5VAC at ADC2

My design to accommodate positive and negative voltages is to up-bias the voltage and then read it deferentially.

I used an LM317 set to a constant 2.08VDC as the circuit base voltage, with the output connected directly to one of the ADC’s inputs and then connected to another of the ADC’s inputs across a 2.4K resistor. (Two ADC inputs allow me to measure the voltage differential, which lets me set the gain to max for small signals.) The two test points for measuring voltage are then connected across 75K resistors to either side of the 2.4K resistor. This means that when I’m not making a measurement the ADC reads 2.08VDC at each of its two inputs, and when I apply a voltage to read it either adds a little bit or drains a little bit from across the 2.4K resistor. -50VDC in pushes the 2.08 volts down to about 0.3vdc, while +50VDC brings it to about 3.6vdc. 

This seems to work fairly well. The fact I am not fully pushing the circuit down to -0.256 or up to Vcc (~5vdc) gives me some extra safety range if I accidentally measure too much voltage. 

Speaking of accidentally measuring too much voltage, I have at this point accidentally measured both 120VAC and ~300VDC with this circuit. In both cases I removed the probes as soon as I saw a three digit voltage appear (I didn’t see what three digit number with the 120VAC, but what turned out to be 300VDC read as about 100vdc). These measurements exceed the ADC’s specs dramatically, but in all cases the meter was unharmed. I am not particularly inclined to find out how many seconds the device can widthstand the voltage, but it does seem to be more than zero.

An Aside: Battling Ground Loop Noise

4 seconds or so of reading a 1VDC source with my laptop plugged in (left), and running off battery (right). This is with the averaging enabled.

I ran into a problem while using this DMM where my voltage measurements weren’t stable, and instead I had clear and persistent oscillations. I attempted to solve this with coding the reading so I got a rolling average, but any average that was accurate was slow to the point of uselessness (several seconds).

Eventually I discovered the source of the noise wasn’t inherent in my circuit or the Arduino, but rather the power source. I was powering that version off a dedicated 9vdc supply (from the wall), and it was also plugged into my laptop (almost always plugged in). The sources I measure tend to also be plugged in.

Prototype Version 2, where the analog rail is provided by a USB-C adapter (the black module), and the Arduino must also be plugged into a computer directly for comms. Very bad idea. Don’t do this.

All these different connections meant I was getting something apparently called ground loop noise. I built a new version powered solely off either a single USB connection (with a boost board for the analog circuitry) or a battery. It is still noisy if a ground loop exists, but if I run off the battery (or I’m measuring a circuit powered off a battery) I get a  nice clean reading.

Getting the nice clean reading when the meter isn’t plugged into anything is good, but means I can’t do data logging or otherwise have a live connection. There are several ways to deal with this problem. One is use an SD card for data logging (which I have done and it works mostly great). Another is use an USB isolator board (Adafruit has one that I’ve tested, it also works but I still need a battery because it can’t deliver the DMM’s power needs).

Other ways to deal with this are wireless communication. I’ve attempted to use RF transmitters / receivers but it appears the Arduino lacks the power to transmit with the other circuitry in place (the RF modules work without other circuitry in place). I could solve this by reinforcing the available power via batteries in one way or another, but that will significantly increase bulk.

The Arduino I’m using (Uno R4) also has both WiFi and Bluetooth abilities, and I’d like to get that working. I’ve experimented a little with it, but haven’t gotten it working yet. For now logging to an SD card works pretty well.

Current Circuits: Converting Amps to Volts

My Current Measurement add-ons. Standalone ACS712 module on the left, and then a PCB with both an ACS712 and resistors plus op-amp on the right.

Lastly we have the current circuits. This was something of an afterthought for the meter because I rarely need to measure current (beyond wanting to know the draw on a power supply, and my troubleshooting supplies display current), but I had a situation at work come up where I wanted to know the draw for different rails on a piece of equipment causing intermittent current surges. Because current was an afterthought I implemented it as removable modules I can plug in and just brought the wires out from the PCB.

Higher Range (0.1 to 5Amps)

There are different ways to measure current based on the amplitude. For medium to higher amplitudes (~0.5A to 20+) the best way is to measure the amps is via the magnetic field created as the current flows using a Hall sensor. There’s an IC, the ACS712, that makes this easy by turning a +/-5, 10, or 20Amp into a 0 to Vcc (up to 5vdc) signal. You can buy off off-the-shelf ACS712 modules, which is what I did (using the 5Amp version). This is a good write up about how it works: https://www.makerguides.com/acs712-current-sensor-and-arduino-a-complete-guide/

This module works well down to about 0.1A, where the reading starts to become unreliable, and it does a rather poor job of reporting currents below 50mA.

The IC requires three wires (Vcc, Signal, and Gnd), which means I can’t use a barrel jack (annoyingly), so the wires are just run off the PCB. This works pretty well because the modules are cheap so I have several, and I can wire one into a circuit, connect the meter to it when I want to the current, but then unplug the meter if I want to read something else without having to take the ammeter module back out of the circuit.

Measuring 4 amps with the Hall effect sensor: Pretty close.

Not so great at the lower range (83.6mA read as ~130mA).

Lower Ranges (<100mA)

Op-amp circuit.

For the low range I made a little PCB with a 1-ohm resistor to put in series with my test subject, I then have an op-amp read and amplify the voltage drop, and calculate current from that. The functional range is ~1.5-600mA.

This arrangement also uses three wires from my DMM, so I can plug the same wires into it as the Hall sensor IC.

I’ve also programmed my meter to detect if / which current board is attached. The zero amp point for the Hall sensor IC is Vcc/2 (or about 2.5vdc in this case), and when the op-amp board is attached the ADC input is pulled very close to zero (instead of floating at some hundreds of millivolts). Because of this when the meter boots up it reads the ADC input for current and configures itself appropriately.

The lowest reading I can get is about 1.5mA.

Up to 500mA.

1mA (a 1mV drop across the resistor) is too low for the op amp to pick up.

I have a second, higher range op-amp (the op-amp IC has two separate amps) where I measure and amplify the drop across a 10 Ohm resistor instead of a 1 Ohm one. It will let me read currents down to something like 0.2mA, but I haven’t done all the testing and math to determine the exact range or scaler to convert the readings to their actual values.


Inputs

Analog to Digital Converter (ADS1115)

The ADS1115 in the middle there. Improving the appearance of my SMD soldering is later on the to-do list.

The heart of the DMM (or any analog measurement device) is the analog to digital converter (ADC). It takes a voltage on a pin and turns it into an integer readable by the processor. The maximum resolution is measured in bits. You need 3.3 bits for each (base 10) digit of measurement you want, so 10 bits is enough for a 3 digit measurement, whereas 4 digits requires 13.3. The Low Level Measurement Handbook is a great resource for reading more about theoretical measurement limits.

Arduinos have built-in ADCs with varying resolutions. The Uno R3 and Micro Pro (what I was using originally for this project) have 10-bit ADCs, whereas the Uno 4R (what I now use) has a 14 bit ADC. Because I started this project with the lower resolution boards I used an external ADC (an ADS1115) for my measurements because it has 16 bits and I wanted the extra resolution.

Now that I’m using an Arduino with more resolution I’ve thought about switching to the internal ADC but haven’t because of additional helpful features the ADS1115 has. Those extra features are digitally adjustable gain and differential measurements. These are both very useful, especially when combined. 

Digitally adjustable gain on the ADS1115 means I can adjust the range from +/-0.256 to +6.144vdc (or Vcc, whichever is less). This means that if my analog circuit is designed to use the full range the minimum detectable voltage change in that full range is 0.1875mV, whereas at the highest gain the minimal detectable signal is 0.0078125mV (~8µV). This is what lets me get stable and reliable resistance readings from less than 0.01 to >5M Ohms with only two analog range circuits. Changing the gain is virtually instant and measuring a value above the current gain range doesn’t hurt the ADS1115 (as long as you’re below Vcc).

The other useful feature is the differential measurements. For my voltage circuit I’m up-biasing the voltage to 2.08vdc. That would mean I can’t use the most sensitive gains of the ADC to measure small voltages because the value would be above range (a value of 2vdc measures as over range in the +/- 1.024 and more sensitive ranges. However, using the differential mode (taking the different of two pins) I can bump the gain up fully again, so I can use the most sensitive +/- 0.256vdc range to measure the difference from 2.08vdc. This is why I can measure - to + 50vdc down to a maximum resolution of close to 1mV (for small voltage ranges since I’m still limited to ~4 digits of resolution) with only one analog voltage circuit.

User Input (Buttons / Serial / IR Remote)

By and large the meter operates completely automatically (resistance zeros when there’s less than 10 Ohms on boot, the display switches to voltage when more than ABS 1V is detected and switches back to resistance with less than 10 Ohms detected, etc). The only user input that’s really needed is: 

  • An ability to trigger typing values over the keyboard emulation.

  • Manual logging data to an SD card.

  • Resetting the min/max values. 

Buttons

The buttons are straightforward. I have two momentary buttons (buttons in one version, and a DPDT switch in another) for several user interface controls, such as logging data to the SD card or typing it into a computer via keyboard emulation. Not a lot to say about this.

Serial

The serial is only slightly more complicated. I’ve set up the code where most modes and settings can be changed via a single letter sent over the serial port. These are mostly debugging-specific settings like printing timestamps as the code runs or showing ADC integer values. I’ll go into more into these in the programming / outputs section.

IR Remote

Similar to the above, I have an IR remote as well as an additional way to trigger keyboard typing or resetting the min/max. It works, but I don’t use it very often.

A note about using the IR remote: Infrared remotes are less standardized than I would like in terms of what codes they send. This is a bit of a pain. The remote I’m using came in a kit long ago with various other parts, and I only have one of them. I bought some other remotes and attempted to record the hex values they sent but it appears I need a different library. I haven’t gotten further in the troubleshooting, and it’s more an annoyance than anything, but it’s something to watch out for if you’re implementing an IR remote.

Other Inputs (Battery)

Battery

I have the battery terminals going to one of the built-in ADCs (by way of a voltage divider, and in parallel with the voltage input) so I can monitor the voltage level.


Processor / Code

The brain of it all is the Arduino Uno R4.

I don’t think I’m doing anything particularly clever or novel in the code.

I recently had OpenAI’s Research Assistant do a significant cleanup job on my code to make it more understandable. Somewhat amazingly I gave it ~1500 lines of code that was… disorganized at best, and it gave me back ~1200 lines that works just as well, and has a lot of comments about what’s going on (although it also commented that it was confused a couple of times, and it didn’t infer everything correctly, but pretty close). This is for the small screen version. If you are interested in seeing that code, it is here: https://www.dropbox.com/scl/fi/bzct98pxz9p14jg4mh48r/ClearMeter_pcb2_3_ReWrite.ino?rlkey=psu94fzadv6smo9ljjmol6xow&dl=0

I plan to do the same cleanup on the big screen version, but if you’re reading this I haven’t yet feel free to reach out.


Outputs

LED

The DMM has one LED on it for indicating information. This information is:

  • Continuity for resistance

    • Here rather than a continuous light I have the LED blink. When continuity (I have it set to resistance less than 20 Ohms) is first detected the LED goes very bright momentarily and then pluses on at lower brightness for 100mS every second. This actually works really well. The bright flash is enough I can see it in my periphery vision, and then the continued blinks are visible but don’t interfere with seeing the display.

  • Voltage where the ABS is equal to or greater than logic level (~3.2Vdc)

    • This is very similar to the resistance setting, except I get two blinks per second after the initial bright flash. Having a logic-level voltage alarm is very nice because it lets me see out of the corner of my eye if I’m getting a voltage reading without looking up from my probe.

  • When a new high voltage is detected when Min/Max display is enabled.

    • New high? Bright flash. Briefly.

  • When data is being manually logged.

    • The screen is blocked from updating during the log for speed reasons, so this is also useful to know it properly triggered.

Display

I have two versions with two different screens. One is a smaller B&W I2C display, while the other is a larger full color SPI display.

Black and White Screen:

This is a small (128x64) screen that uses a 4-wire SDA/SCL/Vcc/Gnd interface with the Adafruit_SSD1306.h library and works pretty great. It’s cheap (there’s an Adafruit version that I’m sure is very nice, but 3rd parties also sell it for 5 for $15) and it’s fast.

Only downside is that 126x64 is not a lot of room. But it actually works pretty well. Future builds will probably only use this one (with a separate SD card module).

Due to the screen size I have the min/max off by default, but long pressing one the buttons displays it.


Full Color Screen:

This is an Adafruit 2.2” (240x320) TFT with the Adafruit_ILI9341.h library. It’s nice image quality (full color) and there’s enough real estate I can put a lot of text there, but there’s a major tradeoff with program speed (also, battery life which is whatever).

Slowing down the code wasn’t something I realized would be a problem. Because it’s a lot of pixels and full color, this screen takes a lot longer to write data to than the small black and white one. Half-filling the screen with text can take hundreds of milliseconds, which dramatically slows things down. Because of this I had to break up the screen code to pre-populate static text and then only update the dynamic text as the program runs (so instead of printing “Min: ### / Max: ###”, the “Min” and “Max” are already there and I only update the values). This wasn’t hard, but was tedious. I’ve also put code in to bypass the screen update when any data gets logged, lest I miss the next data point because the screen was updating.

Even with the only updating the dynamic text, the code takes 80 to 120ms (depending on if I show min/max info) to complete a loop when updating the screen (as opposed to 5 to 13ms normally).

On a fundamental level this does severely limit the screen’s usefulness. I wanted to do fun things like display voltage readings as number of Pikachus, or resistance on a scale from Gyarados to Graveler, but the images would have to be very small not to nerf the usability.

By the way, if you are using an Adafruit 2.2” TFT with the Adafruit_ILI9341.h library and you are also using the Adafruit_ADS1X15.h and/or the SPI.h library, there is a very frustrating bug where the screen will work during setup but then not update again if the libraries are initiallized in the wrong order. You must initialize the ADS1115, and then initialize the display. This was very frustrating to learn, but hopefully this paragraph has the right keyword that other people will find it.

But, Data Logging: The screen has a built-in micro SD card holder, and this is a big functional improvement. More under the SD Card section below.

SD Card

Data Logging Example: Logging an AC current (Blue) rectified with a single diode through an LED and Voltage (Red, either side of the LED) over one second, adding a capacitor across the LED leads for the second half. This is just for fun

On my largely screen model I can now log data automatically based on different conditions. I have it setup right now to log current, voltage, and time whenever one of those hits a new high, a new low, or every 100mA interval on the way down after a new high. (It also logs when I press the red button.)

The main reason I want to log data is to get current and voltage data on failing components, such as when there’s a current surge, was there a voltage spike beforehand? Or is voltage falling so a power supply is trying to pull more current get the voltage back up? This will hopefully help me track down the nature and specific cause of failures.

As I have it currently set up I can get a reading logged about every 5-6ms. Because writing to the card takes time (10ms or so) I got it this fast by having the values logged to an array, and then when the log triggering condition is no longer true I write the array to the card in the form of a CSV. This works pretty well.

The usefulness is something of corner-case (normally if I care about current it’s only the device’s overall current, which I read at the power supply), but for that corner case it’s nice to see what’s going on.

Serial (/GUI)

GUI Screenshot

I have a simple Python based GUI for Serial based interaction. It works and functions, but I mostly only use it for debugging due to ground-loop issues (see the section on voltage measurement).

If you really wanted to use it regularly (i.e. You want to build a multimeter that displays its readings on your screen as you record an electronics video, which I’ve been told is an untapped market, but which I don’t think I can offer a cost-effective product for), then you would need the USB isolator and an external battery I referenced in the ground loop discussion.

 

Keyboard

One of the reasons I built this in the first place was to have a DMM that types values into Excel for me. I can save a lot of time and reduce potential keystroke errors by using the Arduino’s keyboard emulation feature to type values directly into Excel (or a different program). With an added right arrow key press I can input readings by only moving a probe and pressing a button. No more “apply probe - use my eyes to read the value - awkwardly type the value the in with one hand so I don’t have to set the probe down - move eyes back to whatever I’m measuring.”

I had a project at my job that involved finding broken traces on old (but fancy and complicated) mainframe cards. I was able to save a vast amount of time by reading and logging the resistances/impedance between hundreds of SIPP mounting locations and Vcc / Gnd to see where there was a difference between a problem unit and a known-good unit. Make the spreadsheet, color code results, and boom, the results stood out like a sore thumb (lots of places had multiple paths for current to travel, but if the known good board had 2K of impedance and the same contact pair on a different board was at 20k that was suspicious).


Implementing the Circuitry / Designing the PCB

Schematic for my V2 (current as of writing) shield.

My PCB.

The first few versions I made used solderable breadboards with through-hole components. From there I decided to design my own breadboard using SMD components. I did this for several reasons:

  • Compactness

    • Both fitting more components in a given area and reducing the vertical height.

  • Slightly better noise performance.

  • I wanted to assess the feasibility of designing and manufacturing the PCB in case I ever wanted to sell these. (I have no plans to sell assembled units, that said, if someone emailed me and wanted to purchase this PCB either blank or with the components installed I have spares.)

  • I’d never designed either an SMD PCB or a PCB this complicated. This is a great way to learn.

  • Great practice for soldering SMD components, which I normally only do in the context of repair and don’t get to start with a blank slate.

I designed the shield in KiCAD and am pretty happy with how it came out. There are three small errors (Q1, IR, and the I2C  screen connection all have the wrong pin order), which is a minor annoyance and something I’ll fix if/when I make more.

They were manufactured by PCBWay, and I’m very happy with them. They’re based in China and both versions I had printed (first 5x of PB-free HASL and the second 10x of ENIG) came to about $5-6/piece including shipping, and arrived at my door in downstate Illinois less than a week after I uploaded the order.

I’ve thought about paying them to install the components as well. It looks like their fee for assembly is minimal, but you have to pay for parts (of course), and they require you to buy an extra 5-100 of each different component for the machines to work correctly. For a large batch I imagine that cost becomes fairly trivial, but for only a few units that dramatically increases the cost (the ADS1115 and regulators would add up). I also wanted to follow best (or in this case, not stupid) practice and assembly the first ones myself so I could test and troubleshoot them for proper function, which of course I had the errors mentioned above.

Some other notes on the design:

  • For the resistance low range circuit to function correctly and accurately I need more than 4.5VDC on the range switching MOSFET. The Arduino outputs ~4.5VDC at its DIO pins when USB powered. At 4.5VDC the MOSFET doesn’t completely open, and adds extra resistance depending on the Arduino’s power source, making the measurements inaccurate. The MOSFET fully opens somewhere around 5.5vdc, so I need to apply more than that on it. I implemented a “voltage follower” circuit with a NPN BJT transistor that puts 12vdc from a very useful little $3 Adafruit 3.3/5 to 12vdc step-up board to the MOSFET. It’s switched with an active low Arduino DIO output. The BJT is Q2 and the MOSFET is both Q1 and Q3 (it’s both because I wasn’t sure if I would be able to use a surface mount SI2302 MOSFET and wanted the option to instead use a IRLZ44N in the larger through-hole TO-220 package). 

  • The very useful Adafruit board (linked just above) also provides the sufficient voltage to the regulators to operate. Those are LM317s and require a supply ~3VDC over output for that output to be steady. That boost board is very useful for this as well. I used a couple larger ones before discovering this one.

  • For the ADS1115 you can buy either the plain tiny SMD IC or breakout modules that bring the pins to header pins. I wasn’t sure about my ability or desire to solder the SMD down (but doing saves saves significant vertical space, a little bit of cost, and saves the question about authenticity of the component), so I included both on the PCB. Future versions will likely only have the SMD pads.


Failed Ideas / Future Plans

Just for fun: The tower of past meters I’ve made in the now past 6 months with the then-current version in my hand (I changed the physical design since then).

  • Wireless Communication

    • It would be very useful when I need to both log data and avoid ground loops if I had a form of wireless communication to send the data to a computer. I’ve tried getting the WiFi to work properly, getting the Bluetooth to work properly, and attempting to use a pair of RF RX/TX modules.

      • The WiFi doesn’t work, I don’t know why, and I haven’t had a chance to extensively troubleshoot it.

      • For Bluetooth, there seem to be very limited libraries for the Arduino R4 Uno and I haven’t had time to troubleshoot.

      • For RF communication, it seems they use too much power and the DMM doesn’t have it after the other features. This is a solvable problem, but adds components and bulk. I did get communication out of an otherwise empty board, which was cool.

  • Android Interface

    • My normal phone is an iPhone, which apparently can’t (easily?) communicate over serial with attached devices *grumble* (The iPhone is of the USB-C type and it can power the DMM though, which is cool). However, I have an older Samsung Android phone gathering dust that also has USB-C (a c. 2019 Note 9). It can both power and communicate over Serial with the DMM. I want to now build an app that’s a GUI to control the DMM and pull data to log more easily, and then save the data to the internet in some form (or I could email it to myself off the phone). 

    • I’ve spent a little bit of time on this, and have determined that “Tell the AI to make you an Android app” is not a solved problem yet, although it seems we’re maybe close. This answers my main question of “can I do this easily?” and I’m now assessing if it’s what I want to put my troubleshooting  time and effort into vs the other things on this list.

  • Adding a function generator feature.

    • The Uno R4 has a DAC, and I’ve tried to implement a simple function generator output, but it hasn’t worked (instead it makes the DMM crash). It could be as simple (and weird) as what order I initialize libraries, but I haven’t hard time to troubleshoot it.

    • I got the DAC working by changing the order I call libraries (or maybe something else in my code, I’ve changed other things since I resumed troubleshooting this). Now that it’s working I plan to add in some reference voltages /frequencies, and maybe a way to change them.

  • A meter based on the Seeed Studio XIAO RA4M1.

    • This is  the same brain as the Uno R4, but costs only $5, and the meter should work only connected to it (I only need a handful of pins anyway). The only circuitry I need to add is a regulator that converts the battery input to 5vdc, but that’s not hard. Overall it would dramatically reduce the size and cost of the meter. 

    • I’ve order a few of these and if it does work I’ll probably redesign the PCB specifically for this MCU.

    • Update: They’ve arrived. My DMM program compiles and starts to run, but then doesn’t exit the setup phase. So far I’ve determined all the libraries / core functions work (I can read the ADS1115, display to the screen, emulate a keyboard, etc), but when all put together it doesn’t work. There will be more troubleshooting when I have a chance.

      • It works now. The problem library appears to be IRremote.h. When I comment out the function to check IR input the code runs. Yay!

      • I’ve added some photos below. Because the board has battery charging circuitry I can now have an integrated battery that charges off the UCB-C port. To make it work I just needed an additional DC boost board to bring the battery up to 5vdc. I also added a switch that lets me turn off current to the analog circuitry because the battery only trickle charges if the meter is running.


An Arduino Giga with a program that reads 8 of the analog inputs and prints the results (completely changing the screen in the process), and tells you how long it all takes (17ms).

  • A meter based on an Arduino Giga with its internal 16-bit ADC, for several reasons:

    •  Option for multiple voltage channels for simultaneous voltage measurements of different places.

    • Built in function generator using the DAC.

    • The screen is nice and big, and runs fast (~17ms update time for the whole screen). This far faster than the color SPI screen I’ve been using, and fast enough to have some fun like displaying resistance as different Pokemon.