ESR meter with Arduino

For those who are new to this topic and wish to build this simple esr-meter I warmly recommend to read all the four pages because from the first post that I made, there were some significant changes that will improve the esr-meter, these changes were contributed and ironed out by the users of the forum, also reading the pages you will find a lot of answers. Thanks goes to the users who improved this simple solution and fixed bugs!

A short resume of what you will find on reading more than just the first page:

  • a clear understanding off how this esr meter works and many questions answered;
  • the correct formula for calculating the esr!; (the formula on the first sketch is not the correct one, you will find the correct one just a bit deeper in the topic)
  • a better stand alone layout and code contributed by users;
  • ...

ESR meter for capacitors with Arduino

Hi,
This is my first for a “project” on a forum and playing with electronics is a hobby so please take it as is because this is just amateur work, everything is perfectible.
I was searching for an easy to build ESR meter on the net, found some great projects which helped me understand the basics, than I thought this would be an excellent idea for an Arduino project, so here I am, but before I present this solution I’d like to write two-three more lines a bit off-topic:

Skip if you find this kind of boring.
First I’d like to thank to dr. Le Hung here (http://members.upc.hu/lethanh.hung/LCFESRmero/en/index.htm) for the idea of this simple ESR schematic, he sells a kit for the ERS-meter but also features capacitor and inductance measurement.
Second: I’d like to thank the user tinhead on this forum (http://www.eevblog.com/forum/index.php?topic=1571.0) for making it easy for me to choose which scope to buy for tinkering with the current project (thanks for his work on Hantek DSO50xxx series).
Third thanks for all those people who post and share useful info and projects on the net!
Skip end.

The ESR project:
For those who are wondering what is ESR -> Wikipedia (http://en.wikipedia.org/wiki/Equivalent_series_resistance).
The current project is intended to help measure resistors with low value (under 1 Ohm) and capacitor ESR for those times when we want to test capacitors in a SMPS or other similar situations. Another interesting thing about this project is that it shows that over sampling is useful and can give very good results and can be employed successfully in other projects without the need to use external hardware; I had repeatable results in measuring low ESR capacitors and low value resistor (0.05 to 1 Ohm can be accurately measured). For information on using oversampling I highly recommend reading (http://atmel.com/dyn/resources/prod_documents/doc8003.pdf)

The idea behind this ESR is this: Using the well known formula U=I*R, we will pulse very shortly with a known current the cap or resistor, measuring the voltage on the element we have the necessary information to calculate the resistance. There would be a lot to write about this method and why this is functioning but probably more interesting is hand on experience.

The code: I tried to comment it at every step, it is written only for the 50mA current but very easily can be modified to add more upper range to the measurement (the hardware part is ready for this). Code could be rewritten for better performance using interrupts, etc, here is just a basic idea that works.

The hardware: Schematic is provided below, I designed a shield to arduino, probably not the best layout, be aware that the pcb was not tested yet and I’m not 100% is correct, did not use auto route and tried to make it single side, the schematic is good for sure and tested.

The schematic and pcb are attached (you have to sign in to download the atachment)

The DUT should be connected to CN1 between GND (pin3,pin4) and pulse and AIN0 (pin1,pin2) using a setup with four wires.

Here comes the rudimentary code:

#include <LiquidCrystal595.h>
#include <avr/eeprom.h>

//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))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

//define the input and output pin we will use
#define DISCHARGE_PIN 7
#define ESR_PIN A0
#define PULSE_SMALL 8 //this is not used in the sketch, implement it as needed (for 5mA current needed for smaller cap value measurement)
#define PULSE_PIN 9
#define BUTTON_PIN 0

//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
LiquidCrystal595 lcd(6,4,2);

void setup(void)
{
  lcd.begin(20,2);
  lcd.setLED2Pin(HIGH);
  lcd.setCursor(0,0);
  lcd.print("ESR meter");
  lcd.setCursor(5,1);
  lcd.print("version 0.1");
  delay(1000);

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

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

  pinMode(ESR_PIN, INPUT);//reading miliVolt
  pinMode(PULSE_PIN, OUTPUT);
  digitalWrite(PULSE_PIN,HIGH);//low enables T1
  pinMode(DISCHARGE_PIN, OUTPUT);
  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
  delay(1000);
  lcd.clear();
  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) {
    sbi(ADCSRA,ADPS2);
    cbi(ADCSRA,ADPS1);
    sbi(ADCSRA,ADPS0);
  }

  //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)
{
  lcd.setLED1Pin(HIGH);
  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.535;//calculating voltage on AIN0 pin
  esrVal = (miliVolt)/current - esrCal;//calculate ESR in miliOhm (pls read forum for correct formula)

  lcd.clear();
  lcd.print("  V:");
  lcd.print(miliVolt,4);
  lcd.setCursor(13,0);  
  lcd.print("mV");
  lcd.setCursor(0,1);
  lcd.print("ESR:");
  lcd.print(esrVal,4);
  lcd.setCursor(13,1);
  lcd.print("m");
  lcd.print((char)244);

  //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)
  if(!digitalRead(BUTTON_PIN)){
    lcd.clear();
    lcd.print("Zeroing...");
    esrCal = (miliVolt)/current;
    lcd.print(" done!");
    lcd.setCursor(0,1);
    //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");
    delay(400);
  }
}

//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
    i++;
  }
  //we have samples, let's go and compute value
  samples = samples >> 6;//decimating value
  return samples;//all done returning sampled value
}

esr_meter.zip (131 KB)

Nicely Done.

Very nice work.
May I ask what software you used to draw the schmetic that supports the arduino IC

Thank you all for your appreciation, for the schematic I used the Eagle Cad with freeware licence and the adafruit library (you can find it here b00m! Adafruit Eagle library, an Arduino package! « Adafruit Industries – Makers, hackers, artists, designers and engineers!).

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 (Electronics 2000 | Reactance Calculator), 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.

I2C 16x2(1602) LCD Display Module for Arduino - DFRobot

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.

#include <avr/eeprom.h>

//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))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

//define the input and output pin we will use
#define DISCHARGE_PIN 3
#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
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

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


void setup(void)
{
  lcd.begin(20,2);
  //lcd.setLED2Pin(HIGH);
  lcd.setCursor(0,0);
  lcd.print("ESR meter");
  lcd.setCursor(5,1);
  lcd.print("version 0.1");
  delay(1000);

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

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

  pinMode(ESR_PIN, INPUT);//reading miliVolt
  pinMode(PULSE_PIN, OUTPUT);
  digitalWrite(PULSE_PIN,HIGH);//low enables T1
  pinMode(DISCHARGE_PIN, OUTPUT);
  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
  delay(1000);
  lcd.clear();
  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) {
    sbi(ADCSRA,ADPS2);
    cbi(ADCSRA,ADPS1);
    sbi(ADCSRA,ADPS0);
  }

  //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)
{
  //lcd.setLED1Pin(HIGH);
  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.clear();
  lcd.print("  V:");
  lcd.print(miliVolt,4);
  lcd.setCursor(13,0);  
  lcd.print("mV");
  lcd.setCursor(0,1);
  lcd.print("ESR:");
  lcd.print(esrVal,4);
  lcd.setCursor(13,1);
  lcd.print("m");
  lcd.print((char)244);

  //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)
  if(!digitalRead(BUTTON_PIN)){
    lcd.clear();
    lcd.print("Zeroing...");
    esrCal = (miliVolt)/current;
    lcd.print(" done!");
    lcd.setCursor(0,1);
    //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");
    delay(400);
  }
}

//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
    i++;
  }
  //we have samples, let's go and compute value
  samples = samples >> 6;//decimating value
  return samples;//all done returning sampled value
}

@grant1842

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:

lcd.begin(16,2);

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

//lcd.setLED2Pin(HIGH);
//lcd.setLED1Pin(HIGH);

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

@grant1842

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

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.

@grant1842

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:

if(!digitalRead(BUTTON_PIN)){
    lcd.clear();
    lcd.print("Zeroing...");
    esrCal = (miliVolt)/current;
    lcd.print(" done!");
    lcd.setCursor(0,1);
    //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");
    delay(400);
  }

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: Arduino Playground - LCD

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));

and

 if(!digitalRead(BUTTON_PIN)){
    lcd.clear();
    lcd.print("Zeroing...");
    esrCal = (miliVolt)/current;
    lcd.print(" done!");
    lcd.setCursor(0,1);
    //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");
    delay(400);
  }

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 :slight_smile:

@nurbit

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 :slight_smile:

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.