How to Build a 4.5 Digit Digital Multimeter

How I Built, and You Can Build Your Own, Custom 4.5 digit DMMs for ~$30 in parts*

*If you built ten. Unit cost is higher for 1x.

Current Version, June ‘25

Now with a GitHub Link, here: https://github.com/oihdesigns/Micro-DMM/tree/main

The above includes my design files and the Arduino sketch.

Here are a couple videos of taking measurements:

Voltage Measurements

Resistance Measurements

Last Updated: Mid-June ‘25:

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

I am always making improvements. These are the current design:

Above and below: The most recent versions, which use a XIAO RA4M1 board. This is the same main MCU as the Arduino Uno R4.


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

    • Power

    • 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 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 ~$30 for an overall very useful meter, so I can have multiple around.

  • Very Fast Auto-Ranging.

    • The limiting factor for the circuits to auto-range and provide a reading is in practice only limited by eye-comfort. I have it set to refresh at 1Hz normally and 5Hz when it detects a big change.

  • 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 and 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.

(I have tested the most recent design in a controlled and safe matter with a MegaOhmeter, hitting the the voltage circuit with 1000V. It survived, but doesn’t provide a reading that high.

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 current version, with the top panel slide off to access the buttons.

(Top panel On)

Chassis:

This part is pretty straight-forward. I 3D-printed a box. The battery sits on the bottom and the main PCB slides in and out. At this point all the switches/ buttons/ connectors hook mount directly to the PCB.

Barrel vs Banana Jack. One is a lot bigger than the other. The board mount ones are even smaller.

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.


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 LM4040 5vdc reference.

(In the schematic I used a 5.6Vdc Zener, but in the current version of the my design I use a 5vdc LM4040 voltage reference. Functionally the same, but more stable and precise.)

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.003 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’s 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). There’s now also a fuse between the low side and ground plane for the PCB, because otherwise  if you accidentally measured significant voltage with the resistance circuit and if you had the probes backwards where the hot side was on the ground plane and if you were plugged into a computer, and if there was no other isolation, you could seriously damage your computer (maybe, it’s hard to figure out what a dead short will take out first). In any case, a small fuse on the low side would prevent this.

Another note about this circuit: When on, it’s horribly power inefficient. It draws a constant 60mA or so. This is more than the rest of the circuitry combined (the whole thing pulls ~100mA as of the early May ‘25 version). But I’ve fixed this in the current (mid-June 2025) version. I can now turn the voltage to the LM317 on or off from the MCU, so I have it normally at a 0.4% (1/254) PWM duty cycle, which is enough to see when there’s a resistance below ~1M ohm is present. Then the circuit turns on and stays on while you measure resistance, then turns off again when the leads are open for a few seconds. This drops the overall unit current consumption down to about 50mA.

Voltage Accuracy Test Result:

I’ve checked the voltage against some lab grade units. I get better than 0.1% agreement at from 0.5 to 36vdc, which increases to a ~2% deviation at 30mV. Pretty happy with this.

At 10vdc off a lab-grade source, the minimum step is less than ~0.0014mV, so I can read 9.9982vdc and 9.9996vdc, but not in between. I think that means it’s a “4.5 Digit Meter.”


Voltage Circuit: Conditioning The Signal

*(2025-07-22) My newest versions have a modified voltmeter circuit which can tell me when the leads are open or not, which effectively gives me continuity testing in voltmeter mode. It’s cool and eventually I’ll update the below, but for now that circuit has it’s own page here. I also have a paper about the circuit, here.

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.

Up-Bias Voltage Divider. V2 is the measured voltage.

Polarity 

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.

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

*For reasons I have not yet dived into, there is a discrepancy between LTSpice’s simulation and the plain math on “given resistors X-Y-X, what is the voltage drop across Y?” The math is accurate, this is not. The result is I have a 2-3x margin on the functional voltage range.

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 noisy - 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. My design below is safer. Not going to try or test this. (Keep reading)

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

I use an LM4040 with a steady 2.5vdc sink off my 5vdc rail for a reference (I previously used an LM317, it also worked, but the LM4040 setup it smaller). The reference rail goes to one ADC input directly. The two test points for measuring voltage are connected via 500K resistors to either side of a 22K resistor, the far side of the 22K resistor also connects to the ADC. This means that when I’m not making a measurement the ADC reads 2.5VDC 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 22K resistor. -50VDC in pushes the 2.5 volts down to about 0.5vdc, while +50VDC brings it to about 4.5vdc. 

I need to run the reference to the ADS1115 (the ADC I’m using) instead of just program in the 2.5vdc constant, because it has a differential mode, where I can measure the difference in two lines in small ranges. Therefore I can measure with full resolution in the +/-0.256v range about the 2.5vdc offset.

There is also a big safety advantage compared with simply dividing the voltage. If I were to divide the voltage down to an arbitrarily small ratio, but then I measured a 50vdc rail with the negative probe on the hot rail, the ground would have 50vdc on it. If the DMM was also plugged into a computer when I did this my understanding is it would be… bad. Even at lower voltages it could be bad. My design is extremely current limited and isolates both measurement rails from any computer connection (or other circuitry). 

Speaking of accidentally measuring too much voltage, my current design has a pair of Schottky diodes to protect the ADC from overvoltage. They work. I’ve hit the input with a 1000v mega-ohmmeter without a problem (it doesn’t read that high though). 

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.

An Aside: Battling Ground Loop Noise

I ran into a problem while designing 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.

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.

Current Circuits: Converting Amps to Volts

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 R4 / XAIO RA4M1 (same chip - 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.5vdc. 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.5vdc. 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 (when using an Arduino R4, the XAIO RA4M1 has different battery contacts I connect to).


Processor / Code

Brain

The brain of it all is the Renesas RA4M1 MCU, which is both the main chip on the  Arduino Uno R4 and on the Seeed Studio XIAO RA4M1.

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.

Front End

I’ve tried to make the unit as automatic as possible.

Mode Switching:

By default when it turns on you start in ohmmeter mode. If it detects voltage on the voltmeter circuit on then it switches to voltage. It switches back to Ohmmeter mode when it detects a low resistance. It automatically displays ammeter data when it detects an ammeter attached.

Auto Zero:

When the unit is first turned on, if the resistance on the ohmmeter circuit is less than 10 then it uses that value for the null zero point.


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.

Note that on the small screen version I can capture a log and read it over the serial (just as long as the unit isn’t turned off).

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 V4 shield.

Here’s the component list. Resistors with a decimal need to be high quality (thin film, low temp coefficient). If you want email me and I’ll send the spreadsheet. Note this doesn’t include an MCU, screen, or battery.

The current (June 2025) version

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:

A very beautiful photo of a now out of date one. Update pending…

  • 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.

I’ve had units manufactured by both PCBWay and JLC now. I’m happy with both of them. They’re both based in China and all versions I had printed came to about $5-8/piece including shipping, and arrived at my door in downstate Illinois less than a week after I uploaded the order.

I’m possibly going to have another manufacturer make a batch and then populate them. I got a quote that comes to about $18/unit (not including the screen, MCU, or battery). This is cheaper than I can buy the parts myself, but that quote was for 50 units.

These images are out of date… But I like how they came out so they can stay.

Some other notes on the design:

I keep finding smaller ways to get MOSFET minimal Rds voltage. This is a generic DC-DC boost, an Adafruit 12vdc output board, and a MAX1683 charge pump on a breakout.

  • 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.

    • The two above points are out of date. The most recent board uses a MAX1683 voltage doubler charge pump instead of the Adafruit board. It works and is very small (the only supporting components required are 2x capacitors).


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!

      • March update: 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.

      • Early May Update: Most recent versions also use this same board. Works well. One of the few design notes is the board doesn’t provide a 5vdc rail when powered off the battery. This is mildly problematic for the circuitry otherwise. Right now I’m using a DC-DC boost board off the battery to provide a 5vdc rail to the PCB circuitry that needs it. A future version of the PCB will probably use a charge pump IC to provide a 5vdc rail on the board in minimal space.

      • Mid June Update: I’ve fully implemented these boards now.


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.