ESR meter with Arduino

I am curious will this get the ESR of a low cap value (pF, nF); Thanks for your comments.

Good question, the smallest capacitor I played with was 4,7uF value, this was not a problem to measure, the code presented here uses only the 50mA branch of the schematic (through R8 and the transistor), using the 5mA branch (through R4, the code must be adapted a bit) lower value capacitors can be measured (the smallest cap value that can be measured accurately I don't know). I don't have an reference instrument to compare with, but I guess if one needs to compare two capacitors and sort out the one with the smallest ESR this can be done easily with the current schematic, eventually the schematic can be modified for a lower current (increasing R4's value) so that when the current is applied to the DUT will not charge the small capacitor to quickly and distort the reading (increasing frequency should be necessary to), maybe another schematic with a true sine 100khz constant current generator or even at higher frequency as this is important for the small value capacitors can give better results. Sorry I don't have now this on my breadboard and can't test it. Probably the best way to know if this schematic is good for such small capacitance (pF, nF range) is necessary to study a bit the theory behind ESR measurement and make some calculation to know at least the theoretical limits of this device.

I just took a peak on the net and got to this page where is a calculator for reactance (, as it turns out we must use a frequency where reactance will be negligible versus ESR, as this schematic uses aprox. something around 60khz (more or less) reactance becomes an issue beginning with the nF range, for pF we need something with several Mhz oscillations.

Sadly the answer is no measuring the ESR for (pF) range with this schematic, only the upper range of nF (even here reactance should be taken in account).

One question, how do you seperate ESR from ESL? Some capacitors, especially those designed for low frequency use, can have very high inductive reactance. I don't think your device can distinguish this. You may think you have a cap that has very high ESR, but, in fact works very well at 120Hz, not so good at 10KHz.

Good point, probably one way would be to test the capacitors at two different freq. high an low plus measuring the capacitance (computing the ideal values from capacitance and freq to get the ideal reactance and inductance) and than analyze the outcome comparing all the values and decide upon this result for a proper verdict of what the cap is good for and where it can be used, would be nice to have some capacitors with the same value and different ESR and ESL to test for this variable, the sketch could be modified to use different freq. This gives me an idea of a sketch where we could analyze the capacitor's response on a frequency range and plot the value and compare this to the ideal capacitor.

I am new to Arduino. I have a I2C 1602 LCD module.

It works.

I am trying to use it with your code. I had to comment out 2 lines to get it to compile. If you could look at it and tell me if I need to modify this code for it to work. Thanks so much for your time.


//we have to change prescaler for the ADC to make the conversion happen faster
//this code section was suggested on the arduino forum
#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

//define the input and output pin we will use
#define ESR_PIN A0
#define PULSE_SMALL 3
#define PULSE_PIN 5
#define BUTTON_PIN 7

//function prototype
unsigned long measureESR(void);//measuring function, increases ADC to 16bit resolution through oversampling

//global variables
unsigned long esrSamples;
double miliVolt;
double esrVal;
double esrCal;
double vRef = 1.093;//voltage on the Vref pin (this sketch uses internal voltage reference 1.1V)
double current = 0.046200;//proper calibration can be done entering the right value for the current (U=I*R)
//idealy this is 0.05 A, this condition is fulfilled only if R10 is 100 Ohm, Vcc is exactly 5V and the transistor
//while fully saturated idealy is at 0 ohm.

//this is my display setup, I'm using Stephen Hobley's 3 wire setup and class
//exchange to what is good for you

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

void setup(void)
  lcd.print("ESR meter");
  lcd.print("version 0.1");

  lcd.print("Seting up...");

  analogReference(INTERNAL);//setting vRef to internal reference 1.1V

  pinMode(ESR_PIN, INPUT);//reading miliVolt
  digitalWrite(PULSE_PIN,HIGH);//low enables T1
  digitalWrite(PULSE_PIN,HIGH);//low disables T2
  pinMode(BUTTON_PIN,INPUT);//setting up for a button (will use this for zeroing)
  digitalWrite(BUTTON_PIN,HIGH);//enabling the pull up on the button, when button pressed to the ground zeroes out the cable
  lcd.print("Please Wait...");

  //seting prescaller to 32 for faster adc (500khz)
  //at 500khz  results are still looking good (same values as if 250khz ADC clock)
  // the shorter the pulse on a small value capacitor it has no time to charge and denaturate de result
  if (FASTADC) {

  //reading calibration value, it will be ok if already calibrated, else it might be bogus depends on the content of EEPROM
  //but will be ok after first calibration
  eeprom_read_block((void*)&esrCal, (void*)0, sizeof(esrCal));

void loop(void)
  esrSamples = measureESR();//this function takes a while,)
  // so we don't need other delay for the lcd (this functions time gives the refresh rate for display
  miliVolt = (esrSamples * vRef) / 65.536;//calculating voltage on AIN0 pin
  esrVal = (miliVolt)/current - esrCal;//calculate ESR in miliOhm

  lcd.print("  V:");

  //for zeroing the cables, this can be quite a big resistance compared to the values we intend to measure
  //so it is a good idea to try to reduce in any way possible this influence (short cables, soldering the cables, etc)
    esrCal = (miliVolt)/current;
    lcd.print(" done!");
    //writing calibration value into EEPROM so we don't have to calibrate on restart
    eeprom_write_block((const void*)&esrCal, (void*)0, sizeof(esrCal));
    lcd.print("saved to EEPROM");

//this is where the magic happens, it really works and gives some
//incredibly good results! if you need sub milivolt accuracy is a good way to go
//noise is good ;) if in doubt must read oversampling on ADC from AVR docs
unsigned long measureESR()
  unsigned long samples = 0;
  unsigned int acumulator = 0;
  int i = 0;
  //oversampling 4096 times (for 16 bit is 4^(desiredResolution - ADCresolution))
  while(i < 4096) {
    digitalWrite(DISCHARGE_PIN,LOW);//disable discharging
    digitalWrite(PULSE_PIN,LOW);//making a miliVolt pulse of 50mA
    delayMicroseconds(1);//on the scope it looks that after enabling the pulse a litle delay is
    //recomended so the oscillations fade away
    acumulator = analogRead(ESR_PIN);//reading value on AIN0
    digitalWrite(PULSE_PIN,HIGH);//stopping pulse
    digitalWrite(DISCHARGE_PIN,HIGH);//discharging the capacitors
    delayMicroseconds(600);//waiting a bit longer to fully discharge before another pulse
    samples += acumulator;//acumulating the readings
  //we have samples, let's go and compute value
  samples = samples >> 6;//decimating value
  return samples;//all done returning sampled value


Hi, using your display setup is ok, looking at the code I saw that I made a mistake when specifying the columns and rows of my display (16x2) so this code: lcd.begin(20,2) should really be lcd.begin(16,2), as I saw from your lcd object initialization yours to is 16x2 display, for this reason the code should be:


The two lines that I suppose to which you refer to and commented out are:


they have no important role in the code, they just control the back light on my display and another LED. If the code compiled, everything should be all right, you can test your setup using an 1 ohm resistor (or other small values resistors). If the cables are zero-ed your reading should be accurate (you can further calibrate the meter modifying the value for the double current = 0.046200; variable.) For any questions I can answer I will help gladly.

Respect, szmeu

Thanks for your help,. I do not have any bc337 npn trans in my parts bench.

I do have some 2N4401 . Will this be a suggestible replacement ? Thanks again


2N4401 as I see from datasheet should do the job. :)

Zero Meter.

The lcd I am using is only useing a 2 wire (A4, A5) to show info.

I am confused on the schematic as it uses several connections to the lcd .

I am worndering how to wire up the push button ZERO with my 2 wire LCD display.

Thanks for your help.


The button for the zeroing has nothing to do with the LCD, it is just a button tied to a digital pin (in this case D0) which has the pull up resistor enabled, when you push the button that digital pin is put to the ground and this condition is tested in this section of code:

    esrCal = (miliVolt)/current;
    lcd.print(" done!");
    //writing calibration value into EEPROM so we don't have to calibrate on restart
    eeprom_write_block((const void*)&esrCal, (void*)0, sizeof(esrCal));
    lcd.print("saved to EEPROM");

I suppose you have some kind of serial interface LCD so you must set up your LCD to use a proper library, the schematic here shows HD44780 type LCD wiring using 4bit mode, more on LCD you can find here:

Thanks for all your comments.

I have bread boarded this project and the displays reads(Zeroing Done saved to EEPROM) , it is like it is stuck in a loop .
I thought I may be doing something wrong so I used Proteus to do a simulation and I get the same reslut.
Can you look at the schematic and see if I am doing something wrong.

@grant1842 Hi, I looked at the protesus schematic drawn by you, as I see some things are not right, here are my observations: - Q2 should be a PNP transistor (BC327) (In your schematic I see Q2 as NPN BC337) not to mention that is not wired up properly, the correct transistors used in the schematic are complementary one is BC327 the other BC337, see original schematic. - the part that leads signal to the AIN0 pin is not correctly wired either, look at the the original schematic carefully, the diodes and the 10k resistor's (R7 your schematic) one side should be tied to the ground not in parallel with the 470R resistor (R6 your schematic). - please check carefully with the original schematic to make the proper corrections.

As for the code part you can comment out this part (after you corrected the hardware part):

eeprom_read_block((void*)&esrCal, (void*)0, sizeof(esrCal));


    esrCal = (miliVolt)/current;
    lcd.print(" done!");
    //writing calibration value into EEPROM so we don't have to calibrate on restart
    eeprom_write_block((const void*)&esrCal, (void*)0, sizeof(esrCal));
    lcd.print("saved to EEPROM");

After commenting see if you can produce some readings (should be resistance value of wires + resistance of DUT, zeroing is needed only to eliminate the resistance of the cables used, you will uncomment the part of code when you have some meaningful readings and need a proper zeroing.) For when the hardware is corrected you can test if it is working like this: when a DUT is not conected you should read on the AIN0 pin the max voltage permited by the anti-parallel diodes (around 700 mV, depends on diodes) and when the leads to the DUT are tied together this value should drop to a very small value.

Hello szmeu

Firstly, I'd like to thank you for sharing your design with everyone. I also intend to make this meter but have a question...

I have lots of transistors in stock but unfortunately, dont have the same ones that you've used. If I post a list of the transistors I have, would you be able to suggest a couple that might be suitable?

Thanks again, I look forward to building my new toy :)


The transistors are not critical, you need a pair of complementary transistors that have similar characteristic to the BC, something like general purpose low-power amplifying or switching applications transistor, a good candidate would be 2N2222 and 2N2907, I think the 2N3904 and 2N3906 pair can be used also.

Thanks for that, I'll have a look through my collection and see what I can find :)

I also have a question about the 47uf bipolar capcitor...

Does it have to be an electrolytic or would a ceramic cap work? Also if I get to electrolytics and use them pole to pole, would it be ok to have a larger value or does it have to be 47uf?

I'm still learning myself so it will help me to understand why those particular parts were used. I ok at building a circuit from a schematic but I don't really know why certain parts are chosen over others

Thanks again

Thanks for your time and help.
I am getting some reading now doing the simulation with Proteus .
My next step is to bread board and see what I get.

@nurbit I currently use a 47uF, 35V non-polarized electrolytic capacitor, a polarized electrolytic would not be really good (there is the AC component), but a non-polarized ceramic capacitor with the proper C*V values might work, definitely you should give it a try, an interesting experiment would be obtaining a pseudo "non-polarized" cap from two electrolytic wired in series, experiment and see what results you can get. The ESR readings should not be taken as an exact measurement, as ERS really varies with frequency, temperature, capacitance, one will rather use this device to compare two capacitors with same characteristics to see which has a lower ESR, or test if the selected capacitor has a low ESR value, don't get me wrong, the device produces some good readings but I had no possibility to compare it to a reference, the sub ohm (miliohm) measurement precision for pure resistive load is pretty good once calibrated :)

@grant1842 I'm glad you can see some results there, keep trying and the device will work, it's not a very complicated schematic but is very rewarding once you get it going. (at least it was for me as I learned a lot building it). If there are questions and I have the answers I'll gladly help. Keep us posted with the progress.

My diodes have arrived in the post today :)

I was wondering about the 1% resistors though. Did you choose them because you had them laying around or is it vital they should be 1%? Will 5% resistors work but with less accurate results?

I'm going to put it together with parts I have in stock and then replace certain parts if I'm not getting the correct results.

I'll let you know how it goes :)

Sorry, I have another question regarding the transistors

I have some 2222A s but I'm struggling for the PNP transistor. I do have some A1015 s which seems to match the voltage but the collector current is only -150 instead of -800

Will this suffice or should I try to find something better matching?



If you have time and will follow the link at the start of the topic to dr. Le Hung's page from where I got the original schematic you can find some good information on how this ESR meter works.

Until then I will try to explain in a few words some basic things that will help you understand why some components are used. To calculate the ESR we use ohm's law for which the formula is V = I * R, for us the ESR is represented by the R from this equation, thus R = V / I, now we have to get these two values from somewhere so we can compute a result.

For this we setup a circuit which will connect the DUT to a [u]known current[/u] that in turn will produce a [u]voltage drop[/u] on the DUT which we can measure through the analog pin of the arduino, depending on the resistance (esr) we need to measure we might need different current values, the sketch implements just the 50 mA branch, a 5mA branch is available on the schematic and can be easily used switching to the proper pin for higher ESR measurement (in case of lower value capacitors, let's say 10uF and smaller) thus it can be implemented as an auto-range function.

How we obtain the constant current: For a 50 mA current we have: (I = V / R) = 5 / 100 = 0.05 A. Values are from: for Vcc = 5, this is supplied by the arduino's on board voltage regulator and R = 100 (obtained from R8 + the resistance of the transistor at saturation which should be very close to 0 ohm, thus 100 ohm). [u]So the reason to use low tolerance resistor in the current circuit is to ensure the value of the current is exactly as expected and calculated.[/u]

The 1 k ohm 1% resistor (R4) branch can give us a current of 5 mA (I = V / R) = 5 / 1000 = 0.005 A. I have to mention that the 5 mA current is not implemented in the sketch, so you can leave it out for now, this is necessary if you want to measure higher ESR or higher value resistors.

Some more things about the way we measure: we need to pulse the DUT with a very short pulse so that the capacitor we are measuring (especially if is a lower value) will not have time to charge and mess up the reading, plus charging and discharging fast is needed so that we measure the capacitor with a frequency as high as possible so capacitive reactance will be negligible (this is the reason I tried to speed up the analog read by changing the prescaller).

Conclusion: you can use 5% resistor (you can try to calibrate the device from the variables in the sketch), I guess the A1015 should be good too for a 50mA current, give it a try and see what are the results :)