
The Millivolt Meter Project

This is a DIY millivolt meter that was originally designed by
Scullcom Hobby Electronics and presented on Youtube.
Definitely take the time to watch the videos (4 parts) as they are very well done and presented in a way that’s easy to follow and learn from.
I was in need of a meter that I could dedicate to low voltage precision readings, and could be done for a reasonable cost. So this project seemed like a perfect fit. As usual after viewing the video I saw many areas that the meter could be modified and improved for my specific use, and quickly began designing my own version of it using Eagle CAD V7.5.
Some of the changes I made were to reduce the noise and add shielding to the input section. I also made the decision to utilize a precision voltage divider network instead of the less expensive individual resistors.
Another major departure from the original design was to use a MCP23017 (16 input/output) I2C port expander board that I had designed several years ago, for connecting the LCD display and push-buttons. This reduced the number of pins used by the Arduino Pro mini, and made it easier to mount the RGB LCD display. As originally designed there were no pins left for future options.
I added guard rings on the top and bottom of the circuit board around the low level input circuitry to the op-amp, and also removed the soldermask around all low level traces.Bottom of board with input section on top right.
Top of board with input section on top left.
Boards were made in USA using the OSHpark board service.
https://oshpark.com/shared_projects/qgv0fpKN
The voltage divider resistor is a Caddock 1776-C6815 of which the 1K, 9K, and 90K sections are used in series to make up a 100K leg of the divider along with the 900K section for the other leg of the divider. The 9M section was not used.
Next to the LTC2400 24bit ADC, a set of solder pad jumpers were added to allow selection of either the 50Hz or 60Hz notch filter.Top of board with solder paste and surface mount devices except C7 mounted.
Board after refow soldering using a Presto Liddle Griddle and manual temperature control utilizing an infrared non-contact thermometer.
An important step is to clean the traces and components in the input section so that no contaminants such as flux, oils, surfactants, or fingerprints remain.
Caddock divider network and Sparkfun 16 MHz 5 volt Arduino Pro Mini added.
Other components and I2C header installed, ready for testing.
After some preliminary testing I found some instability in the output of the ADR4540B 4.096 volt reference IC. I had clearly left out the required output filter capacitor shown in the original schematics.
I was easily able to bodge the required capacitor from the 4.096 test pin to the ground pin of the tantalum capacitor directly below it. The new V 1.51 schematic and board files now include it.
One thing I noticed during testing was that the main board and display used less than 50 mA and the original specified 5 volt TO220 package regulator, along with my added heatsink was much more than is needed. Moving to a smaller footprint 150 mA regulator would free up considerable space. Another addition to the design would be to add a 250 mA PTC resetable fuse on the 9V input.
Checking fit and clearances of copper shield.
Mounting main board with shield and input wires attached. Battery guide and holder also mounted in enclosure.
Battery assembly is comprised of 6 AA 1.5 volt batteries for 9 V nominal input to regulator.
Front panel with display, switches, and jacks mounted.
The front panel was designed using “Front Panel Designer” to fit a standard Hammond 1455N1601 extruded box with metal end plates 6.299″ L x 4.055″ W x 2.087 H. — Link to design file at end of page.
Design file was sent to Front Panel Express in Seattle, WA. USA and was shipped five days later. The panel is made from “Medium bronze” anodized aluminum and is 2 mm in thickness. With a rectangular beveled cutout for the LCD display, D‑holes for the two banana jacks, countersunk holes for box mounting, and standard holes for the three switches.
RGB LCD with I2C port expander board connected to main board.
OpenEVSE display board with a 16 IO I2C port expander and battery backup RTC (Real Time Clock) DS3231. The board also includes current limiting resistors for the three backlight RGB led’s, and a contrast potentiometer. For the I2C bus there are two pullup resistors positions provided along with address select solder pad jumpers for the port expander. Four I/O ports are broken out and can be individually configured as inputs or outputs, along with a ground pin. The board is sized to match the standard footprint for many 2 X 16 LCD displays.
https://oshpark.com/shared_projects/J6RW88kf
The code provided by Scullcom Hobby Electronics was modified to use the I2C interface for the LCD and input push-buttons. RGB backlighting on the LCD changes color depending on which mode the meter is currently in.
During startup the EEPROM stored Cal Level is displayed.
Pressing the Calibrate button starts the calibration mode prompting the user to short the input leads.
After the calibration is completed the Adjust Factor that is written to EPROM is displayed on the LCD briefly before returning to measurement mode.
Measurement mode, currently displaying micro volts.
With all shielding in place and calibrations performed the meter fluctuates
±12 uV maximum with the input leads shorted and typically ± 5 uV.

Link to EagleCAD Millivolt meter V1.51 schematic and board files ZIP
Link to EagleCAD I2C port expander display V4.2 schematic and board files ZIP
Link to Front Panel Designer V1.1 front panel file ZIP
Modified code Version 3.20 December 2018 using Paul Versteeg’s filter, calibration, and many other code enhancements. For I2C RGB display and I/O.
UPDATED Millivolt Meter Version 2.11 blog post
MilliVoltMeter320.zip firmware

This looks like an amazing project; great work, to both you and Scullcom.
Would you consider putting together these parts into a kit (containing a PCB, components, enclosure, panel)? I’d readily buy one or two.
Hi Simon,
Currently with my work commitments, I am unable to put together a kit for sale. I sometimes have an extra bare board that I can sell though. I will try to look into some kits later this Winter.
Thanks,
Barbouri
Hi Barbouri,
I already posted a similar request with one of your other designs with the same topic. Do you have a BOM to share for this project?
Many thanks in advance,
Paul
I also tried to get to a BOM or this project, by extracting the partslist from Eagle.
I noticed a discrepancy between the board layout (that has R5) and the schematic (that does not), although both carry the correct version number. Maybe you can update the schematic?
Here is my attempt to create a BOM, I hope you can either correct on comment on my proposal partnumbers:
Part Value Package Supplier Partnumber
C1 1000uF/25V CPOL-RADIAL-1000UF-25V 5mm UVR1E102MPD
C2 100uF/25V CPOL-RADIAL-100UF-25V lead spacing 2mm? UVK1E101MDD
C3 0.1uF MLCC 594-K104K20X7RH5TL2
C4 3.3uF/25V tantalum “TAP335K010SCS or TAP335K010SRW or T356A335K010AS
”
C5 0.1uF MLCC 594-K104K20X7RH5TL2
C6 0.1uF MLCC 594-K104K20X7RH5TL2
C7 0.1uf 805-CAP SMD 08055G104ZAT2A
C8 3.3uF/25V tantalum “TAP335K010SCS or TAP335K010SRW or T356A335K010AS
”
C9 10uF/25V tantalum TAP106K025SCS
C10 220pF 0402-CAP SMD “C0402X5R1C221K020BC or C0402X7R1A221K020BC
”
C11 0.1uF MLCC 594-K104K20X7RH5TL2
C12 0.1uf MLCC 594-K104K20X7RH5TL2
IC1 7805T (1Amp) TO220H LM7805CT or NCP7805TG
IC1 78L05 (100mA) TO92 can be used alternatively LM78L05ACZ
R3 10K 0603-RES SMD AC0603JR-0710KL
R4 10K 0603-RES SMD AC0603JR-0710KL
R5 10K 0603-RES SMD AC0603JR-0710KL
RN1 1776-C6815 Caddock network resistor 1776-C6815
U$1 ARDUINO_PRO_MINI ARDUINO_PRO_MINI Sparkfun Arduino Pro Mini 328 — 5V/16MHz
U1 LTC2400IS8 SOIC127P600X175-8N DigiKey LTC2400IS8#PBF
U2 AD8628 SOIC127P600X175-8N AD8628ARZ
U3 ADR4540B SOIC127P600X175-8N ADR4540BRZ
Many thanks in advance,
Paul
Hi Paul,
Here is a link to my Digikey shared parts list.
http://www.digikey.com/short/393771
R5 was only used in the V1.5 board as an extra pull-up for the reset pin.
In V1.51 and above it is not used as there is already a pull-up included on the pro-mini board.
Most of the pictures in the post show the V1.50 prototype board, and do not include the important C12 capacitor.
Thanks,
Barbouri
I forgot, sorry.
There is another discrepancy between the circuit diagram, the silkscreen and picture of your stuffed board.
You have installed a 1000uF/25V capacitor for C1, and a 100uF/25V capacitor for C2.
I also added a 78L05 TO-92 (100mA) on the BOM because that can be used instead of the 7805 TO-220 (1A), right?
Do you have a source or information (thickness, layout) for the copper(?) shield you built?
Sincerely,
Paul
Hi Paul,
I used the 1000 / 100 uF capacitors (C1 / C2) because that is what I had in stock, but the designed capacitor values 220 / 10 uF will work great. I had recommended a 150 mA regulator in the post, but the 100 mA one should work as long as an efficient display is used.
Barbouri
Thanks for the answers, everything is clear now.
Unfortunately, the link you provided does not work:
http://www.digikey.com/short/393771
Getting that list would be useful…
Tks,
I worked with the hotline from DigiKey and I now have access to the list. I will work on that and see if I can coordinate what I’m doing with Louis Scully, he is hopefully doing the same thing as he did with the mill-ohm meter. Maybe I can help.
Thanks,
Paul
Hey Greg, I finally finished my built of the voltmeters, I actually built two. I believe that there are some interesting enhancements to the design, so I here is a link to my blog that details it all.
I must say that I am very impressed with the accuracy of the meter, they are much better than I anticipated and a very welcome addition to my toolkit. Thank you again for doing this all!
Paul
http://www.paulvdiyblogs.net/2016/09/building-6-digit-digital-milli-voltmeter.html
Hi Paul, I have been following your blog since your earlier post.
Your meters look great. You might be interested in the calibration code used in the Programmable Voltage Reference project as it might be used as a basis for setting up a multi-point calibration scheme for the Millivolt Meter.
Greg (Barbouri)
Thanks for pointing this out. I saw it earlier when I looked at some of your other projects. As a matter of fact, I had been contemplating doing something similar for the voltmeter as soon as I saw the linearity deviation.
Compensating for linearity deviations and the likes is a technique I read about some time ago, but I have never used it myself. I will need to do some soul and Google searching to get my arms around it, for now I have a few too many other projects to finish. One of which is the milli-ohm meter. The PCB is done and works great, I now need to order some more parts to fit it in the housing I have in mind. When done I’ll report back on that project too.
Thank you for your contributions!
Paul
Greg,
There is a nasty spike introduced to the main 5V and also the 4.096 reference voltage, which is caused by the switching of D10, the LTC_CS signal. I used a 4n7 decoupling capacitor on the Arduino PCB, but you may want to consider adding some room on the main PCB when you do another turn. Look at my blog for more details.
Greg, I think we may have a potential bug in the LTC code that will compromise the averaging of the multiple readings.
I assume that Louis got his original code from Martin Nawrath, (Academy of Media Arts Cologne, Germany) it looks a lot like that, but Martin uses no averaging in his code.
In the main loop, we use a for-next loop that increments the number of samples we get from the Spi_Read function and then average the result by diving the result with the number of samples.
However, in the Spi_Read function, we skip the complete reading of the ADC if it is not ready.
Here:
if (!(PINB & (1 « 4))) {
…
}
This means that the averaging can be off. If I’m right, the Spi_Read function should have a flag to signal a correct reading back to the main loop.
I will put a logic analyzer on the pins to see if this is happening, and how often.
What is your take on this?
Regards,
Paul
I could not find instances of this happening, but I did fix the code. I also made some measurements with my Logic Analyzer and put that on my blog also. (address somewhere in another post)
I have a question.please this is an AC millivolt meter or DC one ? Thanx for sharing this project by the way
Hi Greg, and others,
I have made several significant changes to the software that in my view, will make the meter much more usable and accurate.
Have a look here for details: http://www.paulvdiyblogs.net/2016/09/building-6-digit-digital-milli-voltmeter.html
I am more and more impressed with the hardware, and now also happy with the software. Again many thanks for making this available!
Paul
hi Paul didn’t see my question? I think I get the answer : this is a DC millivolt meter since there is a reference voltage ‚baut I think we can turn this device into AC millivoltmeter and make it more usefull in electronics . do you agree?
Paul,
Ik heb een bordje gekocht op Ebay van een Finse aanbieder.
Daar heb ik na veel experimenteren goede resultaten bereikt.
De ingangsspannings deler is direct op de ingang van de LTC2410 aangesloten.
Er is dus geen bufferversterker aan de ingang toegepast.
Het bordje heeft de volgende specificaties;
(I bought a board on Ebay from a Finnish provider.
I’ve achieved good results after a lot of experimenting.
The input voltage divider is directly connected to the input of the LTC2410.
Thus, there is no buffer amplifier applied to the input.
The board has the following specifications;)GoogleTranslate
24bit LTC2410 ADC Module designed for Arduino or other embedded systems to acheive high resolution analoge to digital conversion. The module use LTC2410 24bit ADC IC and MAX6126 2.048 volt ultra high precision, ultra low noise reference.
A p‑p noise of 3uV can be achieved when measuring the module Voltage reference with a voltage divider using a USB power source.
LTC2410 24bit fully differential Analog to digital converter
2 ppm INL, No missing code
2.5 ppm Full Scale Error
0.1 ppm Offset
0.16 ppm Noise
Maxim MAX6126 ultra high precision, ultra low noise reference.
Reference voltage output 2.048 volt
±0.02% accuracy
Ultra low 3ppm/C max temperature coefficient
Ultra Low 1.3uVp.p noise
High stability Voltage divider for voltages up to MAX 32 volt.
Vishay UXB0207 1Mohm 0.1% 5ppm
Vishay UXB0207 33K2 0.1% 2ppm
Measurement maximum voltage
Voltage V1 – ½ Vref (-1.024 volt) to ½ Vref (+1.024 volt)
Voltage V2 – Maximum 32 volt. (resistor divider,
Na enige aanpassingen aan het bordje (koeling van IC’s) en de software, heb ik de volgende resultaten bereikt.
(After some adjustments to the sign (cooling IC) and software, I have achieved the following results.)GoogleTranslate
Calibratie module Gemeten LTC2410 Deviatie
2,501144 2,501142 ‑2,0 uV
5,00241 5,002386 ‑24,0 uV
7,50235 7,502346 ‑4,0 uV
10,00313 10,003146 16,0 uV
Het enige probleem wat ik nog heb is dat de offset nog wat zwabbert. Ongeveer zo’n 15 uVolt. (Loopt langzaam op en neer — ongeveer een paar minuten).
Bedankt voor je inbreng op het ontwerp van SCULLYCOM.
Ik ga ook dit ontwerp bouwen en heb inmiddels de print en onderdelen besteld.
(The only problem I still have is that the offset some wobbles. Approximately about 15 uVolt. (Runs slowly up and down — about a couple of minutes).
Thanks for your input on the design of SCULLYCOM.
I’m going to build this design and have already ordered the PCB and components.)GoogleTranslate
I’ve been looking over the design for this board, and one thing troubles me a little. The AD8628 input op-amp doesn’t have a negative supply input, it’s only using digital ground. This means the input can’t quite go down to true 0V, because the output of that chip is limited to around 1mV above its V- supply rail. Given the 10:1 divider network on the input, that limits it to an input voltage of 10mV.
Would this be improved if the input op-amp had a negative supply rail too — e.g. a little charge pump chip like the ICL7660 to provide it something below-0V to give it true to-ground headroom?
Paul,
The input of the AD8628 can go down 0V, and even down to ‑0.3 volts. It is the output which is limited, even being a rail to rail op amp. The AD8628 is also tied to the analog ground section, not the digital ground. So the input divider has no limiting effect on the input voltage.
I have done some tests with input voltages in the 1 to 10 mV range with good tracking. I do see a lot more noise at these lower mV ranges, but for me this is acceptable.
The circuit would benefit from a +/- power supply, but would be limited to 6 volts total for the AD8628.
If someone is interested in developing a working prototype using a dual supply, I would be interested in putting together a final circuit board for it.
Greg (Barbouri)
I’m intending to build mine in two halves, with a totally different digital section on a separate board, connected to your board containing the ADC via an isolator chip (I have a spare ADU1402 from a different project). This disconnects the digital (display, buttons, serial port?) half from the measurement frontend. An isolated DC-DC converter will power it, letting the measurement half float independently of bench power — I’m not intending to use batteries in mine.
Because of this, it occurs to me one adaptation I could make is to move the “analog ground”, the level that’s at the end of the resistive divider and the “- IN” terminal, by splitting the supply 5V via an op-amp of some sort. If I did that, then the range is extended into partly negative values, by reducing the positive end of the range. Of course now I’d have to subtract the offset from the ADC’s reading in software, and this would require some fragile calibration. I don’t know how that would drift over time though. It would however, give me a bipolar measurement ability.
Hi Greg,
do you have file of this revison, with TI isolator?
best regards
Hi Vincenzo,
Currently there is not a revision with the TI isolator.
But I was just thinking about updating this project this morning, and one of the updates would be the isolator.
I most likely will not get to this until August, after completion of the DC Load project.
Thanks,
Greg (Barbouri)
i’m confused about the analog ground. how does this work when you only have a single-ended ADC? surely if the ADC is expecting the reference voltage to be referenced to its ground, then the ADC and the voltage reference must be using the same ground? i would think that having a separate analog ground would only make sense if you were using a differential ADC?
Hi Barbouri,
I have built this project and I have a question regarding the enclosure. Would it be possible to post a picture of the mechanics involved in mounting the LCD display to the front panel. I did order the front panel and Hammond enclosure. The circuit is working fine, I just need some assistance in the enclosure mounting. Thanks to you and Louis for all the hard work.
Hi Rob,
In the photo with the caption “Mounting main board with shield and input wires attached” there are four black plastic machine screws with nuts attached to the corners of the display. After centering the display, these can either be epoxied or attached with hot-melt glue to the back of the front panel. After attachment I used the nuts to adjust the clearance of the display for a light contact fit.
Thanks. That helps. I figured that you glued the mounts to the front panel.
Anyone have any spare V1.51 PC boards that I could purchase ? I thought that I’d give it a try before I put in an order to board house. I’m in the US and could Paypal you.
Hi Phil, Did you get your boards? If you are interested—I just ordered 3 boards. Let me know your thots. Jim
you could insert another input with ADC for current reading?
This is a great project, but I don’t like the drift of the last decimals. So I am in the process to rewrite the adc part by integrating the results over time, pretty much the same principle as oversampling. So the last digits get way more stable and the result imho gets closer to my 6.5 digit bench meter.
In the moment I try to develop an algorithm to figure out when to start a new mesurement instead of oversampling the old one.
Once I am happy with the result I will share my code if you are interested.
Cheers
Rubi
Hi Borbouri,
Nice project, i’m looking to build one or an offshoot depending on what I want to add on/improve. I noticed Paul Versteeg had issues with the CS pin causing noise on the voltage rail. Is this perhaps caused by no limiting resistors on D10, D12 and D13?
Hello again Borbouri,
Looking at making an offshoot of the V1.5 Board with 3x 300K and 1x 100K 0.05% resistors in place of the Caddock. I’m also thinking of pushing it to 100x100mm and adding additional connectors for more buttons or rotary encoder and the banana jack connections right to the PCB. Any thoughts?
https://www.digikey.com/product-detail/en/susumu/RG2012N-304-W-T1/RG20N300KWCT-ND/600935
https://www.digikey.com/product-detail/en/susumu/RG2012N-104-W-T1/RG20N100KWCT-ND/600924
Chopper shield is connected to AGND?
Hi Vincenzo,
Yes, indirectly. The copper shield is connected to the Digital Ground plane. The Digital Ground plane is connected to the Analog Ground thru a single point.
Greg (Barbouri)
Mano thanks Greg, What is the thickness of copper shield?
It is difficult to find it in My country
Hi Vincenzo,
I used .5 mm / 24 awg hobby copper sheet for the shield, but anything in that range will work.
Some have even used copper clad circuit board material, soldered together to form a box.
Greg (Barbouri)
Did anyone have a thought on A/C application?
Hi Bstrag,
As it is designed the Millivolt meter is a DC only system. The update rate of the LTC2400 ADC is less than 10 Hz so you would need to add something like an AD8436ARQZ true RMS-to-DC converter to measure an AC signal.
Greg (Barbouri)
Can anyone tell me the difference between V1.5 and V1.51 boards?
Addition of C12 on the output of the 4.096 vref.
Greg (Barbouri)
Lovely, and inspiring, I am also in phase of making one for measuring low ac voltages. thankyou for sharing this
Hi Barbouri, I can see you are shorting the leds to calibrate the meter. Scullcom hobby use a 5v reference to calibrate it. Have you changed the code, or can it be calibrated both ways? (:
Rasmus.
Hi Rasmus,
I am only shorting the leads for 0 volt calibration and have one variable that was programmed in software that gives me the best linearity for the entire range.
The software variable will be different for each meter depending on ADC, voltage divider resistors, and voltage reference.
Greg (Barbouri)
Kudos on the board Greg. I bought six through OSHPark, and have built up four … very close to testing (maybe tonight). One small issue that I’ve noted is that pins connected to the ground plane are very difficult to solder. I know Kicad does thermal relief for flood fills. Is this available in Eagle? Regards
Hi Mike,
Eagle does have the option of using “thermal isolation” and the OSH Park boards Ver. 1.51 do include thermal isolation for the ground planes.
But because of the large ground planes on both sides of the board, even with thermal isolation a lot of heat does get pulled from the ground connections.
I get the best results when using a flat-blade solder tip, adding extra flux, and increasing the temperature by 20 deg C when soldering the ground connections.
Currently I am working on an updated Millivolt Meter board that uses a Coto 9002–05-11 relay for automatically selecting the lowest range (0 — 4.6 volts) with the same max range of 46 volts as the original V 1.51 board.
Looking for 1 to 10 uV usable resolution. Hopefully I will complete the design later this summer.
Thanks Greg. I look forward to seeing your new design.
On a side note, I have been reading through the LT2400 data sheet, ad note that the input conversion range is ‑12.5% Vref to 112.5% Vref, and that there is an output sign bit. I have yet to think through the implications for the input stage, but maybe you have already done so! How far ‑ve do you think the design can safely go? I’m thinking there might be some scope to use the millivoltmeter not only for absolute DC measurement of a precision voltage reference, but also as a null meter. (I should also mention that I am in the early stages of laying out Conrad Hoffman’s MML null meter design in KiCAD.) Regards
So, I have just had a quick look at the AD8628 datasheet (front end operational amplifier) used in the millivoltmeter. The input specification is strictly zero to 5V, although there is some wriggle room given the absolute rating is ‑0.3V (diode clamp). Who knows what happens for a negative input 🙂 !
I’ve also had a cursory look at the three versions of software that seem to be commonly available. Louis’s V33 from his site, an earlier simpler version V7 from github, and PaulV’s version from his website (http://www.paulvdiyblogs.net/2016/09/building-6-digit-digital-milli-voltmeter.html). None recognize the LTC2400 negative bit. I think V7 calibrates at 0V, V33 at 5V, and PaulV’s at zero *and* a number of reference voltages.
With a grounded input, any noise entering the system after the AD8626 will make a positive contribution (even if negative). There is also a 1uV offset voltage for this device.
In short, the calibration looks like it can be improved. I have my first board up and running, and using V33, I get a 2 millivolt reading with input grounded. No copper shield yet, and the board was cleaned, but before the Caddock was soldered in.
Greg, when you revise the circuit :-), that small negative input range for the LTC2400 (actually ‑0.3V because of the diode clamp) sure looks enticing. If it could be carried through the front end, it would allow a true zero cal.
Hi Mike,
I will look at some options for replacing the AD8628 with an op-amp that supports a small negative input voltage.
Greg (Barbouri)
Greg, you are a absolute champion!
I have been thinking a lot about the millivoltmeter, and the general “hobbyist” through volt-nut application of the LTC2400. I’m sure you’re aware of the various threads on EEVBlog et al. Notably, no-one has yet produced a comparable easy to implement project for the hobbyist with reasonable skills, or made any useful software public.
Although I seem to be developing volt-nut aspirations, I really don’t want to own an Agilant (HP) 3458, nor do I want to be responsible for keeping one in calibration. I would however, like to have a precision low voltage DC measuring tool, and the Scullcom/Barbouri design ticks a lot of boxes for me.
I was already working on the Chinese knock off of the Pro-Mini platform, and especially the low power angle. Using the RocketScream library and removing the regulator and power LED from the board allows the pro-mini to sleep and use only 6uA. Outstanding, given it is an Arduino for a few bucks. It was an *excellent* choice for the millivoltmeter.
The LTC2400 also uses a miserly amount of power when not sampling.
What worries me most about the design (as I’ve mentioned before) is the accuracy of the calibration. The existing software doesn’t impress me.
So, here is where I am proposing to go. (It’s mostly software … you might like to think about it in terms of the hardware design you are contemplating) :
— a 4 cell LiIon battery supply, charged with a balance charger. (Hence low noise.) LT1763‑5 LDO regulator for the 5V supply. For the most part, the instrument is always powered on, with the internal voltage reference getting more stable with age. The pro-mini, LCD, and LTC2400 in power down when not actively taking a measurement.
— calibration with a grounded input, and using an external precision voltage reference to correct for offset, and gain errors (I’m pretty naive in my understanding, which at present comes from the Atmel application note AVR120 “Calibration and Characterization of the ADC on the AVR”). Silly me, I got trapped into thinking I would need a precision 5.0000 reference to calibrate. I was pursuing the AD586LQ which is a pretty fine 5V buried zener reference, but now am thinking that 5V is not relevant at all. I have a friend of a neighbour with an HP34401 in cal, so I can send a portable reference (always powered on) to him, and it comes back saying say 6.950432V. Given the caveats of temperature coefficient, and long term stability, I can take that figure, and hard code it into my Arduino software, then calibrate to that precise reference value. The instrument should then be very stable until the temperature changes, at which point … recal. (I have in mind to soon hack together a program to see if I’m on the right track).
— Don’t lose the negative sign bit from the LTC2400! Reject those values on the zero cal, because they’re not helping. Flag overvoltage on the display, although I don’t propose to get anywhere near 50V.
— put the pro-mini to sleep when the LTC2400 is acquiring a sample, and wake from interrupt on SPI, hence again, reduced noise.
— Here is the most important part. I can’t compete with Agilant/HP/Keithley with Kalman filtering that responds immediately to changes to the input. So I need to change the philosophy around the way that the instrument operates. I tell the instrument when I’m ready to take a measurement by pushing a “go” button, let it go off and take say 100 samples, then 16.5 seconds later, it presents an average voltage reading, AND the standard deviation. Isn’t 3sd a 99% confidence interval, and basically a measurement of the noise figure of the measurement? Again, the 16 x 2 LCD having two lines was a great choice to present the information on. Now, I don’t know how many samples gives 3sd approaching the noise floor of the hardware. Some smartypants probably does, but say it was 1000 samples. Can I wait for 165 seconds? Sure. Also, having I2C allows me to easily connect a real time clock, EEPROM for storing measurements, temperature/humidity sensing etc.
So, lots of work to do. Should be back with some results in six months or so :-).
I hope you don’t mind me putting these thoughts up, tacked onto your board design. (Again, congratulations on the board. It’s taken the whole concept streaks ahead). Say the word, and I’ll stop :-).
Regards
Quick update. I’m wrong. I have tested some code looking for a negative input to the LTC2400. I never see a negative flag, so I guess the op amp has a slight positive offset?
Hi again Greg,
Firstly, let me apologize for making this so message so long. I have attached 200 lines of code below for the Millivoltmeter which I think you will find valuable. May I suggest that you back in your display and button changes from the basic ScullcomV33 version of the code (so that it runs on your hardware) have a play, and then post the code with maybe a review as a new blog entry?
So, as promised, I have implemented offset, and gain correction, with standard deviation calculated for measurements. With my build of the hardware (in an aluminium box, powered via USB, board not properly cleaned, and no copper shield yet) and taking 30 samples I am getting standard deviation of around 40uV. So effectively I think I have a 5 1/2 digit instrument.
From here, apart from working on the power supply and shielding, I’d like to try putting the Arduino into low power sleep while the ADC is converting. I hope that will improve the noise levels.
Regards,
—– cut —–
//
// mvm.ino — Six Digit Millivoltmeter Software
//
// © Mike, 12 July 2018
// Released under GPL2 or later.
//
// Derived from Scullcom V33/Barbouri code
//
// This version uses the “new” SPI library interface, refer https://www.arduino.cc/en/Reference/SPI, accessed June 2018.
// Standard deviation provided by Rob Tillarts “Statistic” library.
//
// Major innovation in this software over previous versions comes from the Atmel application note “AVR120:
// Characterization and Calibration of the ADC on the AVR”. In particular, see Section 2.1 “Fixed-Point Arithmetic for
// Offset and Gain Error Compensation”. Note that we use a scaling factor of 2^24 (16777216).
//
// This version is controlled through three buttons: Left, Centre, and Right.
//
// Shorting the input, then pressing the left button sets the “offset” (zeroes the instrument). Do this first.
// Enter a value for your most accurate reference voltage below using the EXTERNAL_VOLTAGE_REFERENCE define. Connect
// your external reference, and press the right button. This sets the instrument “gain”. Do this second.
// The centre button takes a number of samples (num_samples), and displays the average, and standard deviation. The
// standard deviation should be a noise measure for your setup.
// Offset calibration clears the gain. The calibration order is important.
#include // Serial Peripheral Interface Library used to communicate with LTC2400 (MISO, MOSI and SCK)
#include
#include
#include
//
// Hardware Definition
//
//LTC2400 with CS on pin 10, SDO on pin 12, and SCK on pin 13.
#define LTC2400_CS 10
#define BUTTON_Left 4
#define BUTTON_Center 3
#define BUTTON_Right 2
#define INTERNAL_VOLTAGE_REFERENCE 4.096 // Nominal ADR4540 reference voltage for LTC2400
#define EXTERNAL_VOLTAGE_REFERENCE 4.99870 // External reference voltage used for calibration
// 16 x 2 backlit LCD module with I2C backpack
// Address is generally 0x27, or 0x3F. Change if the LCD does not respond.
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
//
// Software Parameters
//
// To avoid loss of precision in the carefully crafted integer arithmetic, Statistic is only used to keep the
// standard deviation. A side project is to rewrite the package using int64_t.
Statistic stats;
const uint8_t num_samples = 30;
int64_t ADC_offset = 0LL;
uint8_t num_bad_reads = 0;
const int64_t scaled_half_LSB = 0x7FFFFFLL; // 0.5
int64_t scaled_gain_factor = 0xFFFFFFLL; // initially 1.0
int64_t ScaledReadADC(void) {
bool error_flag;
SPI.beginTransaction (SPISettings (1000000, MSBFIRST, SPI_MODE0)); // 1 MHz clock, MSB first, mode 0
digitalWrite(LTC2400_CS, LOW); // LTC2400 chip select pin taken low enables ADC conversion
delayMicroseconds(10); // timing delay but may not be required
while ((PINB & (1 « 4))) { } //check to see if ADC is ready by testing EOC — wait while conversion completed
int64_t sample = 0;
for (int i = 0; i < 4; ++i) { // Read 4 bytes (32 bits) from the ADC
sample <>= 4; // Discard 4 LSBs (noise)
digitalWrite(LTC2400_CS, HIGH); // LTC2400 chip select pin taken high disables ADC output
SPI.endTransaction(); // SPI transaction completed
int64_t scaled_result = sample * scaled_gain_factor + scaled_half_LSB — ADC_offset * scaled_gain_factor;
return(scaled_result);
}
int64_t ScaledReadMultipleADC(void){
stats.clear();
// Throw away first sample to clear ADC.
// Note that on my hardware, when the input voltage is changed from ground to +5V (and the reverse), the first
// sample read seems to be in error. Could be that the 220pF capacitor at the Op Amp input is not
// correctly soldered on the board (it is tiny!). (Or something else?) Check later by comparison
// between hardware builds.
ScaledReadADC();
int64_t sum_samples = 0LL;
for (int i = 0; i > 24; // ADC offset is not scaled
lcd.setCursor(0, 1);
ShowReading(ConvertToVoltage(ADC_offset <> 24);
return(descaled_ADC_sample * 10.0 * INTERNAL_VOLTAGE_REFERENCE / 16777216.0);
}
void ShowReading(float x) {
uint8_t decimal_places = 6;
char prefix = 0;
if (x < 0.001) {
x *= 1000000;
prefix = ‘u’;
decimal_places = 0;
} else if (x < 1) {
x *= 1000;
prefix = ‘m’;
decimal_places = 3;
}
lcd.print(x, decimal_places); // Print voltage as floating number with the right number of decimal places
lcd.print(“ ”); // Add one blank space after voltage reading
if (prefix)
lcd.print(prefix);
lcd.print(“V ”); // Extra spaces to clean up when voltages go from large to small (8 spaces)
}
void DisplayMeasurement(float average, float standard_deviation, uint8_t bad_reads) {
lcd.setCursor(0, 0);
ShowReading(average);
lcd.setCursor(15, 0);
lcd.print(bad_reads);
lcd.setCursor(0, 1);
ShowReading(standard_deviation);
}
// end
Hi Mike,
Thanks for the code. It will be several weeks before I have some time to work with the Millivolt meter.
I am currently travelling, which is the reason for the delay in responding to you.
Thanks again.
Greg
This might help!
//
// mvm.ino -- Six Digit Millivoltmeter Software
//
// (C) Mike, 12 July 2018
// Released under GPL2 or later.
//
// Derived from Scullcom V33/Barbouri code
//
// This version uses the "new" SPI library interface, refer https://www.arduino.cc/en/Reference/SPI, accessed June 2018.
// Standard deviation provided by Rob Tillarts "Statistic" library.
//
// Major innovation in this software over previous versions comes from the Atmel application note "AVR120:
// Characterization and Calibration of the ADC on the AVR". In particular, see Section 2.1 "Fixed-Point Arithmetic for
// Offset and Gain Error Compensation". Note that we use a scaling factor of 2^24 (16777216).
//
// This version is controlled through three buttons: Left, Centre, and Right.
//
// Shorting the input, then pressing the left button sets the "offset" (zeroes the instrument). Do this first.
// Enter a value for your most accurate reference voltage below using the EXTERNAL_VOLTAGE_REFERENCE define. Connect
// your external reference, and press the right button. This sets the instrument "gain". Do this second.
// The centre button takes a number of samples (num_samples), and displays the average, and standard deviation. The
// standard deviation should be a noise measure for your setup.
// Offset calibration clears the gain. The calibration order is important.
#include // Serial Peripheral Interface Library used to communicate with LTC2400 (MISO, MOSI and SCK)
#include
#include
#include
//
// Hardware Definition
//
//LTC2400 with CS on pin 10, SDO on pin 12, and SCK on pin 13.
#define LTC2400_CS 10
#define BUTTON_Left 4
#define BUTTON_Center 3
#define BUTTON_Right 2
#define INTERNAL_VOLTAGE_REFERENCE 4.096 // Nominal ADR4540 reference voltage for LTC2400
#define EXTERNAL_VOLTAGE_REFERENCE 4.99870 // External reference voltage used for calibration
// 16 x 2 backlit LCD module with I2C backpack
// Address is generally 0x27, or 0x3F. Change if the LCD does not respond.
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
//
// Software Parameters
//
// To avoid loss of precision in the carefully crafted integer arithmetic, Statistic is only used to keep the
// standard deviation. A side project is to rewrite the package using int64_t.
Statistic stats;
const uint8_t num_samples = 30;
int64_t ADC_offset = 0LL;
uint8_t num_bad_reads = 0;
const int64_t scaled_half_LSB = 0x7FFFFFLL; // 0.5
int64_t scaled_gain_factor = 0xFFFFFFLL; // initially 1.0
int64_t ScaledReadADC(void) {
bool error_flag;
SPI.beginTransaction (SPISettings (1000000, MSBFIRST, SPI_MODE0)); // 1 MHz clock, MSB first, mode 0
digitalWrite(LTC2400_CS, LOW); // LTC2400 chip select pin taken low enables ADC conversion
delayMicroseconds(10); // timing delay but may not be required
while ((PINB & (1 « 4))) { } //check to see if ADC is ready by testing EOC — wait while conversion completed
int64_t sample = 0;
for (int i = 0; i < 4; ++i) { // Read 4 bytes (32 bits) from the ADC
sample <>= 4; // Discard 4 LSBs (noise)
digitalWrite(LTC2400_CS, HIGH); // LTC2400 chip select pin taken high disables ADC output
SPI.endTransaction(); // SPI transaction completed
int64_t scaled_result = sample * scaled_gain_factor + scaled_half_LSB — ADC_offset * scaled_gain_factor;
return(scaled_result);
}
int64_t ScaledReadMultipleADC(void){
stats.clear();
// Throw away first sample to clear ADC.
// Note that on my hardware, when the input voltage is changed from ground to +5V (and the reverse), the first
// sample read seems to be in error. Could be that the 220pF capacitor at the Op Amp input is not
// correctly soldered on the board (it is tiny!). (Or something else?) Check later by comparison
// between hardware builds.
ScaledReadADC();
int64_t sum_samples = 0LL;
for (int i = 0; i > 24; // ADC offset is not scaled
lcd.setCursor(0, 1);
ShowReading(ConvertToVoltage(ADC_offset <> 24);
return(descaled_ADC_sample * 10.0 * INTERNAL_VOLTAGE_REFERENCE / 16777216.0);
}
void ShowReading(float x) {
uint8_t decimal_places = 6;
char prefix = 0;
if (x < 0.001) {
x *= 1000000;
prefix = ‘u’;
decimal_places = 0;
} else if (x < 1) {
x *= 1000;
prefix = ‘m’;
decimal_places = 3;
}
lcd.print(x, decimal_places); // Print voltage as floating number with the right number of decimal places
lcd.print(“ ”); // Add one blank space after voltage reading
if (prefix)
lcd.print(prefix);
lcd.print(“V ”); // Extra spaces to clean up when voltages go from large to small (8 spaces)
}
void DisplayMeasurement(float average, float standard_deviation, uint8_t bad_reads) {
lcd.setCursor(0, 0);
ShowReading(average);
lcd.setCursor(15, 0);
lcd.print(bad_reads);
lcd.setCursor(0, 1);
ShowReading(standard_deviation);
}
// end
Nope. Sorry. Ok, looks like you do have some serious editing to do.
If you can’t recover the code (at least the read ADC function is missing bits as well as the includes), PM me, and I’ll send you a zipped file.
Hi thought i would give this a go! I can solder!! But do you have a parts list for the two types of board I ordered? with digi key or similar
Christian,
Please the shared parts list:
http://www.digikey.com/short/393771
Thanks
Hi Greg,
How accurate your Millivoltmeter please? I can see on the lower end, for example, measuring 1mV is giving me around 500uV, which is 50% error. I think linearity error is higher closer to a LTC2400 limits. Accuracy improve closer to 10mV (Millivoltmeter is reading 9.5mV). I have proper shielding in place. Also using different voltage standards (I tried 2.5V, 5V and 10V) to calibrate the meter is giving me slightly different results, but as a rule of thumb if I calibrate it at 10V accuracy is the best at 10V. If I calibrate it at 2.5V, around 2.5V accuracy is the best. High resolution is good, but how do I achieve a good accuracy on the 0–10V scale please?
Hi Alex,
My V1.5 Millivolt meter is within 25 uV at 700 mV, and within 50 uV at 100 mV using a 5 volt cal.
Something that I am working on with the new prototypes, is the ability to calibrate at multiple points and have the software use the most appropriate cal for the voltage measured.
I have also implemented a dual input range of 0 — 4V and 0 — 40V on the most recent prototype which should improve the low end resolution and accuracy below 4V.
Greg (Barbouri)
Thanks Greg,
That’s pretty much my problem. Either it is more accurate around 5V using 5V cal or around 10V using 10V cal. Like in Ian Johnston’s vref (or in fact in your variable vref), there has to be a multiple points calibration.
Looking forward to your new prototype.
Regards
Alex
Hi Greg,
This looks like a great build and design. I was planning on building this (and the MiliOhm project too). Looking through the comments I see that a while back you were working on a dual range version with a Coto relay. Was there any further progress in this — I am sure I would be pretty pleased if I can get the same sort of resolution as you have achieved but would be good to try an updated version of you had made further modifications .
Hi John,
Next blog post will be on the Millivolt Meter version 2.11
The blog post is mostly done, just finishing up all the documentation.
Thanks,
Greg (Barbouri)
For those of you that are interested in further developments of the firmware, have a look at my blogpost : http://www.paulvdiyblogs.net/2016/09/building-6-digit-digital-milli-voltmeter.html
On that blog is also a link to my Github where the firmware can be downloaded.
At this moment I’m in the process of adding a logging feature to the meter so you can graph the measured results.
Are ther gerber files available for this PVB?