The Mohmmeter

Figure 1
: The finished Mohmmeter.Capabilities and Features:
- Resistance measurement: ≈ 0.1–1.0% or better accuracy across ≈ 10 Ω to ≈ 5 MΩ
- Voltage measurement: ≈ 0.1–1.0% or better accuracy across ≈ 50 mV to 450 V (though not recommended for HV use)
- Power: 2⨯AA batteries (internal) or DC port (2.25–5.95 V, comes with 5 V adapter)
Concept and Motivation

Figure 2
: Midland 23-104 multimeter which, although vintage, is not really 'steampunk'. (Image modified from eBay listing.)While I was shopping for an oscilloscope at a certain second-hand store, my mom, who came along, bought an old analog multimeter (mostly, an ohmmeter with some voltmeter features). It was (or was very similar to) a Midland 23-104 multimeter (manual here) (Figure 2). It didn't quite work, so I fixed it. Anyway, she was quite taken with what she called its "retro-steampunk aesthetic".
Frankly, I didn't see it.
Steampunk is about a romanticized, retrofuturistic vision of Victorian-age technology (and of course especially steam power). The aesthetic is characterized by rich woods, brass fittings, analogue gauges, and gears[1]. Whereas, the Midland multimeter is made predominantly out of a cheap, degrading, primitive black plastic, and screams 1970s aesthetic a lot more than 1870s. In point of fact, it is aesthetically unappealing. Besides which, it doesn't auto-range, and the circuit, though simple, is (to my sensibilities) basic, uninspired.
I figured I could make a multimeter that, like the 23-104, would be mostly an ohmmeter, but that would also better meet the demands of the steampunk aesthetic. It would be an ohmmeter for mom—a 'Mohmmeter', if you will.
Electronics
The basic architecture is a bunch of electronic components, an analogue gauge for output, and a microcontroller to run it all—my one true love, the Raspberry Pi Pico.
I knew mom likes the clicky nature of relays (seriously, who doesn't?), and so I built no fewer than five into the circuit to connect up the components. When doing a measurement, you can hear the relays clicking as the meter autoranges, searching among its circuits (e.g. listen in Figure 5). Each relay has a flyback diode to prevent arcing and improve life, as well as an N-channel MOSFET for activation, since the microcontroller pins cannot source enough power to run them directly[2].

Figure 3
: Detail of the front panel of the Mohmmeter (click to embiggen).The analogue gauge (a voltmeter[3], controlled via 100 kHz PWM[5]) has a custom dial face, which I designed and had professionally printed[6]. The dial face has two decades on a logarithmic scale. The pointer sits just off the scale on the left when there is no detectable measurement, ensuring a null result is not confused with a small one.
There are also five lights in the top plate below the dial, which indicate whether the result should be multiplied by 10-3, 10-1, 10+1, 10+3, or 10+5. There is overlap built into the ranges so that values falling near the edges can sit comfortably on one or the other range, without flipping back and forth (any variation must exceed a certain amount of hysteresis to change ranges). The upper ranges end up mostly used for resistances, while the lower ranges are mostly for voltages.
There are also lights to indicate ohm mode, volt mode, and (on the side) DC power input. All lights (these and the ranging lights) are made to look like incandescent lamps, which are steampunk-coded. However, in practice, incandescent indicator lights are surprisingly hard to come by, annoying to make circuits for, have a much more limited lifespan, and are a bit unsafe. Therefore, I actually used white LEDs[7]. They are painted red with a paint marker and placed under yellow lenses, though, which makes them look incandescent[8]. The covers stick up on the front plate, but the one on the side for DC power is fully recessed to protect it. I included spares of the LEDs and covers inside the enclosure.
Power Circuit
Internal power comes from 2⨯AA batteries, replaceable by removing the back cover. There is also a DC port, which takes 2.25–5.95 V (nominal 5 V) power. These are combined through Schottky diodes and dumped (through the switch) into the Pico's "VSYS" pin. The Pico converts whatever it gets there into a stable 3.3 V rail, which then powers everything. There is also a 3.0 V high-precision reference source, used for the R-series sense circuit (see below) and the ADC itself, to improve performance.
The Pico can also be powered directly, over USB, which is the case when developing the firmware. In this configuration, the USB powers "VSYS"[9]. Originally, the 'off' position of the switch connected "VSYS" to ground, which is fine under normal use, but when debugging, if the circuit is 'off', this shorts USB power to ground through only a diode. I discovered this the hard way: I was messing around with the switch and after clicking it over, the USB port died. Indeed, all USB ports on my computer died! Fortunately, the overcurrent had been detected and the ports had simply disabled themselves; after a reboot, they were back to normal[10]. I corrected the issue by removing the connection to ground when off.
Sensing Circuits

Figure 4
: Sensing circuits for the "R-series" path (see text).There are four sensing modes, which I call 'R-series', 'R-tiny', 'V-series', and 'V-tiny'. The 'R-' circuits measure resistance and the 'V-' circuits measure voltage. The '-series' circuit use one of a series of four resistors, while the '-tiny' circuits measure via op-amps:
- The R-series mode is shown in Figure 4. 3.0 V goes through one of the four resistors (selected by relays), and then through the unknown resistor to ground. This forms a voltage divider whose middle node is measured by the microcontroller at ADC0, allowing the unknown resistance to be worked out.
- The R-tiny mode pushes 100 μA from a (thermally compensated!) constant-current source through the unknown resistor. The voltage required to create this current is measured by four op-amps, which are multiplexed by a relay to ADC1 and ADC2.
- In the V-series mode, the unknown voltage passes through one of the four resistors (in the opposite direction, so that the voltage drop happens before any digital circuitry, allowing higher voltages to be measured), and then through a 10 kΩ resistor to ground. This again creates a voltage divider, measured by ADC0.
- Finally, the V-tiny mode is like the V-series mode, except the nodal voltage is amplified by one of the four op-amps and read by ADC1 or ADC2 instead.
The choice of which circuit to use was very carefully optimized. For example, the coarsest R-series mode can measure a small resistor, but the large ratio in the voltage divider would result in a high error (large variance for each LSB of error). Therefore, it ought to be measured by a finer R-series mode. However, too fine a mode and the ratio becomes very small, again leading to a loss of precision.
I optimized the ranges each circuit was designed to cover using a nonlinear optimization program I wrote, so as to minimize the expected maximum error across the whole range. The theoretical performance of the resistance circuits is < 0.11% error across the whole range to 10 MΩ. The practical performance is of course much lower due to noise and such: not including error from reading the dial, it's about 1%, which is pretty good for DIY and THT, IMNSHO.
There is also protection circuitry. If a negative voltage is applied to the pins, it passes through some diodes instead of killing the circuitry (hopefully). Additionally, the circuitry is protected by a voltage divider, which is used to check the conditions at the input. If too high a voltage is applied in voltage-measurement mode, or if any voltage is detected in ohm-measurement mode, then the input pins remain disconnected. Only if it is safe to do so does a relay set to connect the input to the main circuit[11].
In the original design, the voltage reference supplied the op-amps' power rail, but this caused the reference to drop to 2.57 V, because of the op-amps' power consumption. I had expected this to be negligible, but after I finally figured out what was happening, I corrected it simply enough by supplying the op-amps with 3.3 V from the supply rail instead.
The two highest-gain op-amps seem pegged to always output maximum. I had designed for the possibility that none of the op-amps would work, and even had expected the high-gain ones to fail for noise reasons. The nature of the fail-high suggests a short from the rail, though. I decided not to try to debug the issue, and these are simply unused in the firmware.
Figure 5
: Demo of the Mohmmeter measuring a resistor. The central (101) light indicates and the dial reads slightly above 10, indicating a resistance a little above 100 Ω. Notice that it detects the megaohm-class resistance through my body when I touched both pins for a moment, and of course the delightful relay clicking.Firmware and Calibration
The result of the sense circuits goes into one of three pins on the Pico, which themselves are multiplexed to a single ADC[12] on the actual RP2040 microcontroller. The Pico controls which sense circuit is used and outputs the result onto the analogue gauge.
I elected to use MicroPython for simplicity and rapid prototyping. Even so, building an appropriate state machine was more annoying than I thought it would be.
Although initially I planned to figure calibration coefficients for an idealized model of the circuit and ADC, I soon realized that this would result in less accuracy, even if I could do it perfectly. Instead, I decided to take a fully empirical approach.
For resistance, I made a test resistance board with almost every type of resistor I have (which is a lot): the test board covers seven decades (100–106, inclusive), with the first six having eight entries (nominal coefficients 1.00, 1.10, 1.20, 1.30, 1.50, 2.00, 4.70, and 7.50) and the final having six (1.00, 1.10, 1.20, 1.50, 2.00, and 4.70). Any of the resulting 54 resistors can be connected across the leads via a dedicated DIP switch.
I measured every resistance with a high precision (4–5 sigfigs) calibrated professional instrument to get a 'ground truth'. Then, for each resistor, I measured (over several averaged samples) the ADC value produced from the appropriate circuit(s[13]) in the Mohmmeter. After this procedure, for an unknown resistance, the measured ADC value can be compared to the calibration table and the correct resistance interpolated out. The interpolation uses the (nonlinear) theoretical model of the circuit between the sample points. This results in excellent accuracy—enough to measure the deviation of a 1% resistor from its nominal value, for example[14].
For voltage, I built a simple voltage reference. This is just a 10 kΩ variable resistor across two AAs, resulting in a smoothly variable voltage between 0 V and (with fresh batteries) about 3.3 V, which happens to be the logic level. I did a similar procedure, recording the empirical response across several voltages and interpolating between them. Higher voltages were not calibrated and so are not recommended for use[15].
The dial is both physical and analogue, and so also needs to be calibrated. For each of many different dial readings, I empirically found the PWM duty cycle that results in it, by tuning the duty cycle interactively. To display a reading, we then work backward, interpolating the readings to find the required duty cycle. The interpolation happens in log-space because it is a logarithmic dial. Unfortunately, I found that the dial calibration goes out of true almost immediately (it seems to vary by environmental conditions; temperature is likely the most significant factor), and this is by far the largest source of error in the entire system. At least the dial has a physical tuning screw on the front to correct the calibration on the fly.
Physical Build
Having limited skill with woodworking myself, I looked around for a ready-made wooden box, but was unable to find one that was satisfactory[16]. Steampunk aesthetic calls for a heavy, solid wood, but the stuff you can find online is mostly not.
One of my mom's good friends, Brent VanFossen, happens to be an accomplished hobbyist woodworker, and he quickly agreed to help. After some back-and-forth email, over coffee, we hashed out a final design for the enclosure and took measurements of the circuit, which had by that time been almost entirely built. The finished enclosure was ready soon after.

Figure 6
: Wooden panels for the Mohmmeter. Notice the cut grooves.
Figure 7
: Finishing the wooden panels.For the sides, Brent used[19] walnut and maple woods. They were made as tall as the entire closed box, and then cut apart, so that the grain matches well across the opening. The corners are 1/4" finger joints cut with a table saw and a custom jig, glued together with Titebond wood glue.
Sapele wood is used for the outer-top, inner-main/front, and outer-back panels. The top and main panels are fitted into grooves cut into the sides, leaving a 1/16" gap (hidden, internal) to allow for expansion due to humidity. This groove results in cuts into the fingers of the joints, which were fitted with maple cubes, then cut flush and sanded smooth (Figure 10).
The finish (see Figure 7) is a custom mix following a recipe developed by famed chair maker Sam Maloof: 1 part boiled linseed oil, 1 part tung oil, and 1 part semi-gloss polyurethane varnish. It is a wipe-on finish that brings out the natural color of the wood, and is easily repaired if damaged.

Figure 8
: Materials for making brass labels (and the "volts" and "ohms" labels partially completed).
Figure 9
: Finished brass labels on the front panel layout.
Figure 10
: Test fitting finger joints and maple cubes.Figure 11
: Inserting the LED covers into the precisely made main panel made a delightful popping sound.The enclosure is fastened with a leather strap, which was punched and stained a reddish-brown. Snap closures were swaged onto the strap and screwed onto the box.
The manufacturing tolerances are excellent. For example, the LED lenses are not even 5 millimeters in their largest diameter. Not only did Brent make a panel with precision holes to accept these, the lenses have this little clip-in locking thing—a matter of 0.28 millimeters on a side—and he carved out little grooves inside each hole so that they would snap in perfectly with a satisfying click (Figure 11).

Figure 12
: Me installing the brass labels (photo by Brent)[20].The final assembly consisted in majority of cramming my circuit into the enclosure. The circuit itself had been made on a prototype board[21], and I set it in on top of small wooden supports. Despite many checks, I had somehow wired the rotary switch the wrong way, and I had to swap the wires on a bunch of terminals. The wrong measurement also got used for a small alignment peg on the switch itself; I had to carefully lengthen the matching groove, cut into the underside of the main panel, myself[22].
I had a difficult time aligning the dial because it is difficult to align vertically, horizontally, and rotationally, when gluing from the backside where you cannot see. I also discovered I had made an error in the dial face, and had to get it reprinted.
I had originally intended to paint (possibly with stencils) labels onto the wood itself. However, brass labels are much more steampunk. We were unable to acquire any directly, but Brent filled in and made these himself too (see Figure 8 and Figure 9). The labels are 0.016" thick, 1/2" wide strips of brass, drilled with 5/64" holes to accommodate 1/16" brass screws.
The labels were hand-stamped, and so have a charming, slightly imprecise, handcrafted quality to them—exactly the sort of thing you would see on a vintage scientific instrument! We met up at the local library to do the final installation (Figure 12). Pilot holes were drilled partly through the wood to ensure the thin screws remained strong. The screws themselves are vintage slot head, and are consistently 'clocked'[23].
Overall, the physical build went very smoothly—better than I'd hoped. And, I hope you'll agree the result is stunning.
. . .
Happy birthday, mom! ❤️