Project tutorial
Arduino CAP-ESR-FREQ Meter

Arduino CAP-ESR-FREQ Meter © GPL3+

A metering instrument to measure capacitance, ESR value and frequency.

  • 7,179 views
  • 3 comments
  • 10 respects

Components and supplies

Arduino duemilanove
see the circuit diagram for all used components
×1

Apps and online services

About this project

CAP-ESR-FREQ meter with an Arduino Duemilanove.

In this tutorial you can find all neccessary information about a metering instrument based on an Arduino Duemilanove. With this instrument you can measure three things: capacitor values in nanofarads and microfarads, the equivalent serie resistance (ESR value) of a capacitor and last but not least frequencies between 1 Herz an 3 MegaHerz. All three designs are based upon descriptions I found on the Arduino forum and on Hackerstore. After adding some updates I combined them into one instrument, controlled with just one Arduino ino program. The different meters are selected via a three position selector switch S2, connected to pins A1, A2 and A3. ESR zeroing and meter selection reset is done via a single pushbutton S3 on A4. Switch S1 is the power ON/OFF switch, needed for 9 V DC battery power when the meter is not connected to a PC via USB.These pins are used for input:A0: esr value input.A5: capacitor input.D5: frequency input.

The meter uses a Liquid Crystal Display (LCD) based on the Hitachi HD44780 (or a compatible) chipset, which is found on most text-based LCDs. The library works in 4- bit mode (i.e. using 4 data lines in addition to the rs, enable, and rw control lines). I started this project with an lcd with only 2 datalines the (SDA and SCL I2C connections) but unfortunally this conflicted with the other software I used for the meters. First I will explain he three different meters and finally the assembly instructions. With each type of meter you can also download the separate Arduino ino file, if you want to only install that specific type of meter.

Step 1: The Capacitor Meter

The digital capacitor meter is based on a design from Hackerstore. Measuring the value of a capacitor:

Capacitance is a measure of the ability of a capacitor to store electrical charge. The Arduino meter relies on the same basic property of capacitors: the time constant. This time constant is defined as the time it takes for the voltage across the capacitor to reach 63.2% of its voltage when fully charged. An Arduino can measure capacitance because the time a capacitor takes to charge is directly related to its capacitance by the equation TC = R x C. TC is the time constant of the capacitor (in seconds). R is the resistance of the circuit (in Ohms). C is the capacitance of the capacitor (in Farads). The formula to get the capacitance value in Farads is C = TC/R.

In this meter the R value can be set for calibration between 15kOhm and 25 kOhm via potmeter P1. The capacitor is charged via pin D12 and discharged for a next metering via pin D7. The charged voltage value is measured via pin A5. The full analog value on this pin is 1023, so 63.2% is represented by a value of 647. When this value is reached, the program calculates the capacitor value based on the above mentioned formula.

Step 2: The ESR Meter

See for definition of ESR https://en.wikipedia.org/wiki/Equivalent_series_resistance.

See for the original Arduino forum topic https://forum.arduino.cc/index.php?topic=80357.0Thanks to szmeu for the start of this topic and mikanb for his esr50_AutoRange design. I used this design including most of the comments and improvements for my esr meter design.

Equivalent Series Resistance (ESR) is the internal resistance that appears in series with the device's capacitance. It can be used to find faulty capacitors during repair sessions. No capacitor is perfect and the ESR comes from the resistance of the leads, the aluminium foil and the electrolyte. It is often an important parameter in power supply design where the ESR of an output capacitor can affect the stability of the regulator (ie, causing it to oscillate or over react to transients in the load). It is one of the non-ideal characteristics of a capacitor which may cause a variety of performance issues in electronic circuits. A high ESR value degrades the performance due to power losses, noise, and a higher voltage drop.

During the test, a known current is passed through the capacitor for a very short time so the capacitor doesn’t charge completely. The current produces a voltage across the capacitor. This voltage will be the product of the current and the ESR of the capacitor plus a negligible voltage due to the small charge in the capacitor. Since the current is known, the ESR value is calculated by dividing the measured voltage by the current. The results are then displayed on the meter display. The test currents are generated via transistors Q1 and Q2, their values are 5mA (high range setting) and 50mA, (low range setting) via R4 and R6. Discharching is done via transistor Q3. The capacitor voltage is measured via analog input A0.

Step 3: The Frequency Meter

See for the original data the Arduino forum:https://forum.arduino.cc/index.php?topic=324796.0#main_content_section.Thanks to arduinoaleman for his great frequency meter design.

The frequency counter works as follows: The 16bit Timer/Counter1 will add up all clocks coming in from pin D5. Timer/Counter2 will generate an interrupt every millisecond (1000 times per second). If there is an overflow in Timer/Counter1, the overflow_counter will be increased by one. After 1000 interrupts (= exactly one second) the number of overflows will be multiplied by 65536 (this is when the counter flows over). In cycle 1000 the current value of the counter will be added, giving you the total number of clock ticks that came in during the last second. And this is equivalent of the frequency you wanted to measure (frequency = clocks per second). The procedure measurement(1000) will set up the counters and initialise them. After that a WHILE loop will wait until the interrupt servive routine sets measurement_ready to TRUE. This is exactly after 1 second (1000ms or 1000 interrupts). For hobbyists this frequency counter works very well (apart from lower frequencies you can get 4 or 5 digit accuracy). Especially with higher frequencies the counter gets very acurate. I have decided to display only 4 digits. However, you can adjust that in the LCD output section. You must use D5 pin of the Arduino as the frequency input. This is a prerequirement for using the 16bit Timer/Counter1 of the ATmega chip. (please check the Arduino pin for other boards). To measure analog signals or low-voltage signals a preamplifier is added with a pre-amplifier transistor BC547 and a block pulse shaper (Schmitt trigger) with a 74HC14N IC.

Step 4: The Components Assembly

The ESR and CAP circuits are mounted on a piece of perfboard with holes 0.1 inch distance. The FREQ circuit is mounted on a separate perfboard (this circuit was added later). For the wired connections male headers are used. The lcd screen is mounted in the top cover of the box, together with the ON/OFF switch. (And one spare switch for future updates). The layout was made on paper (much easier than using Fritzing or other design programs). This paper layout was later also used to check the real circuit.

Step 5: The Box Assembly

A black plastic box (dimensions WxDxH 120x120x60 mm) was used to mount all components and both circuit boards. The Arduino, the perfboard circuits and the battery holder are mounted on a 6mm wooden mounting plate for easy assembly and soldering. In this way everything can be assembled and when finished it can be placed inside the box.Under the circuit boards and the Arduino nylon spacers were used to prevend the boards from bending.

Step 6: The Final Wiring

Finally all internal wired connections are soldered. When this was completed, I tested the esr switching transistors, via the test connections T1, T2 and T3 in the wiring diagram. I wrote a small test program to change the connected outputs D8, D9 and D10 from HIGH to LOW every second and checked this on the connections T1, T2 and T3 with an oscilloscope.To connect the capacitors under test a pair of short test wires were made with crocodile clip connections.

For frequency metering longer test wires can be used.

Happy testing!

Code

CAPESRFREQ.inoArduino
  /*CAP-ESR-FREQ meter with Arduino Duemilanove.
  Version 1.1 December 2018.
  A metering instrument with three different applications:
  
  AlexP
  
  Capacitor metering for capacitors from 100nF up to 2000 uFarad or more.
  See the digital capacitor meter (C) 2013 Hackerstore.
  https://www.hackerstore.nl/BlogArtikel/4
  Measuring the value of the capacitor:
  Capacitance is a measure of the ability of a capacitor to store electrical charge. 
  The Arduino capacitance meter relies on the same basic property of capacitors: the time constant.
  The time constant of a capacitor is defined as the time it takes for the voltage
  across the capacitor to reach 63.2% of its voltage when fully charged. 
  An Arduino can measure capacitance because the time a capacitor takes to charge is
  directly related to its capacitance by the next equation: TC = R x C.
  TC is the time constant of the capacitor (in seconds).
  R is the resistance of the circuit (in Ohms).
  C is the capacitance of the capacitor (in Farads). 
  The formula to get the capacitance value in Farads is C = TC/R.
  In this meter the R value can be set for calibration between 15kOhm and 25 kOhm via potmeter P1.
  In the formula for the calculation a value of 22 kOhm is used. Adjustment is possible with potmeter P1. 
  the full analog value on the ANALOG_PIN is 1023, so 63.2% is a value of 647.
 
  ESR metering (Equivalent Serie Resistance). 
  See for definition of ESR https://en.wikipedia.org/wiki/Equivalent_series_resistance.
  ESR can be used to find faulty capacitors durung repair sessions.
  See https://forum.arduino.cc/index.php?topic=80357.0
  Thanks to szmeu for the start of this forum topic and mikanb for his esr50_AutoRange design. 
    
  Frequency meter from 1 Hz. to 3 MHz.
  See https://forum.arduino.cc/index.php?topic=324796.0#main_content_section
  Thanks to arduinoaleman.
  
  The different meters are selected via a three position selector switch, connected to pins A1, A2 and A3.
  A0: esr input.
  A1: capacitor meter selection.
  A2: ESR meter selection.
  A3: frequency meter selection.
  A4: the esr zeroing and general reset button.
  A5: capacitor input.
  D5: frequency input.
  
  See https://www.instructables.com/id/Arduino-CAP-ESR-FREQ-Meter for assembly instructions and how to use it. 
  
  LCD Library and settings used by all programs**********************
  */
  #include <LiquidCrystal.h>
  LiquidCrystal lcd(13,11,6,4,3,2);// these setting can be altered when needed.
  // note: an lcd screen with i2C connections could not be used, this setup conflicted with the other software used. 
  
  int wait =2000;
  int rememberSetting=0;   // Used to force a reset after changing the meter type selection.
   
  //capacitor meter initialisation ************************************
  #include <Wire.h>
  #define ANALOG_PIN  A5   // Analog pin for measuring. The value can be between 0 and 1023.
  #define P1_PIN      12   // Pin for charging the capacitor via R9 & P1.
  #define R8_PIN      7    // Pin for discharging the capacitor via R8.
  float MicroFarad =0;
  long tStart =0;
  float nDuur =0;
    
  //esr meter initialisation ******************************************
  //The prescaler is changed for the ADC to make the conversion happen faster.
  //this code section was sugested on the arduino forum.
  #include <avr/eeprom.h>
  #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 DISCHARGE_PIN 8
  #define PULSE_HighRange_PIN 10
  #define PULSE_LowRange_PIN 9
  #define ESR_PIN A0
  #define BUTTON_PIN A4
  unsigned long measureESR(void);    
  //measuring function, increases ADC to 14-bit resolution by oversampling
  unsigned long esrSamples;
  double milliVolts;
  double esrCalib;
  double vRef = 1.1;                
  //voltage on the Vref pin (this sketch uses internal voltage reference 1.1V)
  double milliAmps = 48.48;         
  //(in mA) needed only for "Zeroing". 
  //Proper calibration can be done entering the right value for the milliAmps, (U=I*R).
  // in the ideal situation "milliAmps" is 50 mA, this condition is true only if R10 is 100 Ohm, 
  // and Vcc is exactly 5V and the transistor while fully saturated idealy is at 0 ohm.
  double Rs = 1003.0;               
  //proper calibration can be done entering the right value for Rs. 
  //Enter the value also in the "IF" branch! This is only the initialization of the Upper Range!
  double Vin = 5000;
  double Rm;
  boolean esrRange = false;
  int stabilizer = 0;

  //freq meter initialisation ********************************************************
  /* 
  Pin D5 of Arduino must be used for frequency input.
  Counter1 : 16 bits / used to count the frequency impules
  Counter2 : 8 bits / used to generate a 1000ms or 100ms gate time for measurement
  I use "bitClear(TIMSK0,TOIEO)" instead of "TIMSK0 &=~(1<<TOIE0)"
  I use "bitSet(TIMSK0,TOIEO)"   instead of "TIMSK0 |=~(1<<TOIE0)"
  */
  #include <avr/interrupt.h>
  volatile unsigned long frequency=0;            
  volatile boolean       measurement_ready;
  volatile unsigned char overflow_counter;  // number of overflows within gate_time
  volatile unsigned int  time_so_far;       // number of ISR calls
  volatile unsigned int  gate_time;  

  ISR(TIMER2_COMPA_vect) {
 
  if (time_so_far >= gate_time) {         // end of gate time, measurement is ready
    TCCR1B &= B11111000;                  // stop counter1 by setting CS12, CS11 and CS10 to "0"
    bitClear(TIMSK2,OCIE2A);              // disable counter2 interrupts
    bitSet(TIMSK0,TOIE0);                 // enable Timer0 again // millis and delay
    measurement_ready=true;               // set global flag for end count period
                                          // calculate now frequeny value 
    frequency=0x10000 * overflow_counter; // mult #overflows by 65636 (0x10000)
    frequency += TCNT1;                   // add counter1 contents for final value
    overflow_counter=0;                   // reset overflow counter
  }
  else {
    time_so_far++;                        // count number of interrupt events
    if bitRead(TIFR1,TOV1)  {             // if Timer/Counter 1 overflow flag = "1" then ...
       overflow_counter++;                // increase number of counter1 overflows
       bitSet(TIFR1,TOV1);                // reset counter1 overflow flag
  }
  };  
  }
  
  void setup() {
  //setup used by all meter types***********************************************
  pinMode (A1,INPUT_PULLUP); // select capacitor meter S2.
  pinMode (A2,INPUT_PULLUP); // select esr meter S2.
  pinMode (A3,INPUT_PULLUP); // select frequency meter S2.
  pinMode (A4,INPUT_PULLUP); // select reset/zeroing button S3.
  lcd.begin(16, 2);          // lcd screen with 2 rows with each 16 characters. 
  // LDC test
  for (int row=0; row<=1; row++) {
    for (int column=0; column<=16; column++) { 
       lcd.setCursor(column,row);
       lcd.print("*");
       delay(20);}
    };
  delay(wait);
  
  // three different setups, selected by the switch S2. (A1, A2 and A3).
  //capacitor meter setup*******************************************************
  if(digitalRead(A1)==LOW)
  {rememberSetting=1;
  pinMode(P1_PIN, OUTPUT);   // connection for charging the capacitor.
  digitalWrite(P1_PIN, LOW);  
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Capacitor meter");
  lcd.setCursor(5,1);
  lcd.print("version 1.1");
  delay(wait);
  lcd.clear();
  }

  //esr meter setup*************************************************************
   if(digitalRead(A2)==LOW)
   {rememberSetting=2;
    if (FASTADC)
    {     sbi(ADCSRA,ADPS2); //seting prescaler to 32 for faster adc (500khz)
          cbi(ADCSRA,ADPS1); //at 500khz  results are still looking good (same values as if 250khz ADC clock)
          sbi(ADCSRA,ADPS0); // the shorter the pulse on a small value capacitor it has no time to charge and denaturate the result
    }
 
  pinMode(ESR_PIN, INPUT);                      //reading milliVolts
  pinMode(PULSE_HighRange_PIN, OUTPUT);
  digitalWrite(PULSE_HighRange_PIN,HIGH);       //low enables Q1
  pinMode(PULSE_LowRange_PIN, OUTPUT);
  digitalWrite(PULSE_LowRange_PIN,HIGH);        //low enables Q2
  pinMode(DISCHARGE_PIN, OUTPUT);
  digitalWrite(DISCHARGE_PIN,HIGH);             //high enables Q3
  pinMode(BUTTON_PIN,INPUT);                    //setting up for a button (for zeroing)
  digitalWrite(BUTTON_PIN,HIGH);                //enabling the pull up on the button, when button pressed to the ground zeroes out the cable
  analogReference(INTERNAL);                    //setting vRef to internal reference 1.1V
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("ESR meter");
  lcd.setCursor(5,1);
  lcd.print("version 1.1");
  delay(wait);
  lcd.clear();

  eeprom_read_block((void*)&esrCalib, (void*)0, sizeof(esrCalib));   //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  
  lcd.clear();
  }
  
  //frequency meter setup*******************************************************
  if(digitalRead(A3)==LOW)
  {rememberSetting=3;
  pinMode(5,INPUT);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Frequency meter");
  lcd.setCursor(5,1);
  lcd.print("version 1.1");
  delay(wait);
  lcd.clear();
  }
  }
  
  // three different loops, selected by the switch S2. (A1, A2 and A3).
  void loop() {
  //capacitor meter loop*********************************************************
  if(digitalRead(A1)==LOW)
  {
  if (rememberSetting !=1)   //force a reset.  
  {softReset();}
  
  if(digitalRead(A4)==LOW)
  {softReset();}             // reset via reset button on A4.
  
  Discharge();               // discharge the capacitor until zero voltage.
  lcd.setCursor(0,1);
  lcd.blink();                         // cursor blinks during measuring.
  digitalWrite(P1_PIN, HIGH);          // charge the capacitor. 
  tStart = millis();
  while(analogRead(ANALOG_PIN)<647){}; 
  nDuur = abs(millis()-tStart);
  MicroFarad = (nDuur/22000.0) * 1000; // calculate the value of the capacitor.  
  delay(wait);
  lcd.noBlink();

  // show the calculated value on the lcd screen:
  if(MicroFarad >= 1.0){
  // show the value in microFarad:
  lcd.clear();
  lcd.print("     ");
  lcd.print(MicroFarad,1); 
  lcd.print(" ");
  lcd.print((char)228);               // print the greek character mu.
  lcd.print("F ");
  delay(wait);
  }

  else{ 
  // show if no capacitor is connected or the value is too low to be accurate:
  if (MicroFarad < 0.07) {lcd.clear();
  lcd.print("  no capacitor");}
  // show the value in nanoFarad:
  else{ 
  lcd.clear();
  lcd.print("     ");
  lcd.print(MicroFarad*1000,1); 
  lcd.print(" ");
  lcd.print("nF ");
  delay(wait);
  }
  }
  } 

  //esr meter loop**************************************************************
  if(digitalRead(A2)==LOW)
  {
  if (rememberSetting !=2)                      //force a reset.  
  {softReset();}  
  esrSamples = measureESR(); 
  milliVolts = (esrSamples * vRef) / 16.384;    //Divide by 16.384 due to a 14-bit oversampling
  Rm = Rs / ((Vin / milliVolts) - 1);           //voltage divider (R2=R1(U2/(U1-U2)))
  Rm = Rm - esrCalib;                           //Compensate cable resistance
  if (Rm < 0) Rm = 0;                           //Do not show eventual negative values
    
  if (stabilizer == 3) {                        //Make three measurements before autoranging the ESR. This is hysteresis to avoid oscillation when autoranging.
            if (Rm < 5.5) {
              Rs = 100.2;                       //If needed, adjust this value for low range measurement (Below 6 Ohms)
              esrRange = false;
            }
            else if (Rm > 5.8) {
              Rs = 1003.0;                      //If needed, adjust this value for high range measurement (Above 6 Ohms and below 50 Ohms)
              esrRange = true;
            }
            stabilizer = 0;
            }
  lcd.clear();    
  lcd.setCursor(0,0);
  lcd.print("Vout = ");
  lcd.print(milliVolts);
  lcd.print("mV");
  lcd.setCursor(1,1);
  //If measurement is less than 50 Ohms show the result, otherwise show <OL> (Overload) on the display.      
  if (Rm <= 50){                                
            lcd.print("ESR = ");
            lcd.print(Rm,2);
            lcd.print((char)244);               // print the greek character ohm.
  }
  else {
  lcd.print("ESR = <OL>     ");
  }
    
  //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 posible this influence (short cables, soldering the cables, etc)
    
  if(!digitalRead(BUTTON_PIN)){
  lcd.clear();
  lcd.print("Zeroing...");
  esrCalib = milliVolts/milliAmps;
  lcd.print(" done!");
  lcd.setCursor(0,1);
  eeprom_write_block((const void*)&esrCalib, (void*)0, sizeof(esrCalib));
  lcd.print("saved to EEPROM");
  delay(wait);
  }
  }

  // frequency meter loop******************************************************
  if(digitalRead(A3)==LOW)
  {
  if (rememberSetting !=3)                //force a reset.  
  {softReset();}
  
  if(digitalRead(A4)==LOW)
  {softReset();}                         // reset via reset button on A4.
    
  float floatfrq;  
  int range;
  long frq;
  measurement(1000);                     // 1000ms standard gate time
  while (measurement_ready==false);
  frq=frequency;
  floatfrq=frq;                          // type conversion (required!!)
  if ((frq >= 0)&& (frq < 10))           {range=0;};                           // Hertz
  if ((frq >= 10)&& (frq < 100))         {range=1;};   
  if ((frq >= 100)&& (frq < 1000))       {range=2;};
  if ((frq >= 1000)&& (frq < 10000))     {range=3; floatfrq=floatfrq/1000;};   // KHz
  if ((frq >= 10000)&& (frq < 100000))   {range=4; floatfrq=floatfrq/1000;};
  if ((frq >= 100000)&& (frq < 1000000)) {range=5; floatfrq=floatfrq/1000;};
  if (frq >= 1000000)                    {range=6; floatfrq=floatfrq/1000000;};// MHz 
  
  lcd.setCursor(0, 0);                 
  lcd.print("Freq:           ");        
  switch(range) {             
    case 0:                                          
       if (frq!=0) 
       {lcd.setCursor(6, 0);lcd.print(frq);
       lcd.setCursor(13,0);lcd.print("Hz ");}
       else
       {lcd.clear();lcd.setCursor(0, 0);
       lcd.print("No signal...   ");}
       break;
    case 1:   // 10Hz thru 100Hz
       lcd.setCursor(6, 0);   lcd.print(frq);
       lcd.setCursor(13,0);   lcd.print("Hz ");    
       break;
    case 2:    // 100Hz thru 1KHz
       lcd.setCursor(6, 0);   lcd.print(frq);
       lcd.setCursor(13,0);   lcd.print("Hz ");    
       break;
    case 3:    // 1KHz thru 10KHz
       lcd.setCursor(6, 0);   lcd.print(floatfrq,3);
       lcd.setCursor(13,0);   lcd.print("kHz");    
       break;
    case 4:   // 10KHz thru 100KHz
       lcd.setCursor(6, 0);   lcd.print(floatfrq,2);
       lcd.setCursor(13,0);   lcd.print("kHz");    
       break;
      case 5:   // 100KHz thru 1MHz
       lcd.setCursor(6, 0);   lcd.print(floatfrq,1);
       lcd.setCursor(13,0);   lcd.print("kHz");    
       break;
     case 6:   // above 1MHz
       lcd.setCursor(6, 0);   lcd.print(floatfrq,3);
       lcd.setCursor(13,0);   lcd.print("MHz");    
       break;       
  } 
  } 
  }

  // Subroutines:

  // discharge capacitor subroutine********************************** 
  void Discharge() { 
  digitalWrite(P1_PIN, LOW);            
  pinMode(R8_PIN, OUTPUT);           
  digitalWrite(R8_PIN, LOW);         
  while(analogRead(ANALOG_PIN)>0){} ;
  pinMode(R8_PIN, INPUT);
  delay(wait); 
  }

  // reset subroutine*************************************************
  void softReset(){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("please reset....");
  while(digitalRead(A4)!=LOW){} ;
  asm volatile ("  jmp 0");
  }

  // esr measure subroutine********************************************
  unsigned long measureESR() 
  {
  unsigned long accumulator = 0;
  unsigned int sample = 0;
  int i = 0;
  // oversampling 256 times (for 14 bit is 4^(desiredResolution - ADCresolution) where is 4^(14-10) = 4^4 = 256)
  while ( i++ < 256 )
  {
  digitalWrite(DISCHARGE_PIN, HIGH);           // discharge the capacitors
  delayMicroseconds(600);                      // discharge wait time
  digitalWrite(DISCHARGE_PIN, LOW);            // disable discharging
  if (esrRange == false) {
  digitalWrite(PULSE_LowRange_PIN,LOW);        // start milliAmps pulse
  delayMicroseconds(5);                        // on the scope it appears that after enabling the pulse a small delay is needed for oscillations to fade away
  sample = analogRead(ESR_PIN);                // read ADC value
  digitalWrite(PULSE_LowRange_PIN, HIGH);      // stop milliAmps pulse
  }
  if (esrRange == true)  {
  digitalWrite(PULSE_HighRange_PIN,LOW);       // start milliAmps pulse
  delayMicroseconds(5);                        // on the scope it appears that after enabling the pulse a small delay is needed for oscillations to fade away
  sample = analogRead(ESR_PIN);                // read ADC value
  digitalWrite(PULSE_HighRange_PIN, HIGH);     // stop milliAmps pulse
  }
  accumulator += sample;                       // accumulate sampling values
  }
  esrSamples = accumulator >> 4;               // decimate the accumulated result
  stabilizer ++;
  return esrSamples;
  }

  // frequency measure subroutine********************************************
  void measurement(int ms) {
  bitClear(TIMSK0,TOIE0);     // disable counter0 in order to disable millis() and delay()
                              // this will prevent extra interrupts that disturb the measurement
  delayMicroseconds(66);      // wait for other interrupts to finish
  gate_time=ms;               // usually 1000 (ms)
  
  // setup of counter 1 which will be used for counting the signal impulses

  TCCR1A=0;                   // reset timer/counter1 control register A
  TCCR1B=0;                   // reset timer/counter1 control register B
  TCCR2A=0;                   // reset timer/counter1 control register A
  TCCR2B=0;                   // reset timer/counter2 control register A
    
  // setup of counter2 which will be used to create an interrupt every millisecond (used for gate time)

  TCCR2B |= B00000101;        // set prescale factor of counter2 to 128 (16MHz/128 = 125000Hz)
                              // by setting CS22=1, CS21=0, CS20=1

  bitSet(TCCR2A,WGM21) ;      // set counter2 to CTC mode
                              // WGM22=0, WGM21=1, WGM20=0                   
  OCR2A = 124;                // CTC divider will divide 125Kz by 125 
   
  measurement_ready=0;        // reset
  time_so_far=0;              // reset
  bitSet(GTCCR,PSRASY);       // reset the prescaler 
  TCNT1=0;                    // set frequency counter1 to 0
  TCNT2=0;                    // set gate time counter2 to 0
      
  bitSet(TIMSK2,OCIE2A);      // enable counter2 interrupts
  TCCR1B |= B00000111;        // set CS12, CS11 and CS10 to "1" which starts counting 
                              // on Arduino pin D5
  }
capacitorfinal.inoArduino
// Digital capacitor tester
// (C) 2013 Hackerstore, see https://www.hackerstore.nl/BlogArtikel/4
// update 2018 alexP
// See diagram cap-esr-freq.jpg for hardware connections.

#include <Wire.h>
#include <LiquidCrystal.h>
#define ANALOG_PIN  A5   // Analog pin for measuring. The value can be between 0 and 1023.
#define P1_PIN      12   // Pin for charging the capacitor via R9 & P1.
#define R8_PIN      7    // Pin for discharging the capacitor via R8.

float MicroFarad =0;
long tStart =0;
float nDuur =0;
int wait =2000;

LiquidCrystal lcd(13,11,6,4,3,2);

void setup() {
 pinMode(A4, INPUT_PULLUP); // button S3 connected for reset.
 pinMode(P1_PIN, OUTPUT);   // connection for charging the capacitor.
 digitalWrite(P1_PIN, LOW);  
 lcd.begin(16, 2);          // lcd screen with 2 rows with each 16 characters. 
}

void loop() {
 
 if(digitalRead(A4)== LOW)  // reset the software via button S3.
 {softReset();}
 
 Discharge();               // discharge the capacitor until zero voltage.
 
 /* 
 Measuring the value of the capacitor:
 Capacitance is a measure of the ability of a capacitor to store electrical charge. 
 The Arduino capacitance meter relies on the same basic property of capacitors: the time constant.
 The time constant of a capacitor is defined as the time it takes for the voltage
 across the capacitor to reach 63.2% of its voltage when fully charged. 
 An Arduino can measure capacitance because the time a capacitor takes to charge is
 directly related to its capacitance by the next equation: TC = R x C.
 TC is the time constant of the capacitor (in seconds).
 R is the resistance of the circuit (in Ohms).
 C is the capacitance of the capacitor (in Farads). 
 The formula to get the capacitance value in Farads is C = TC/R.
In this meter the R value can be set for calibration between 15kOhm and 25 kOhm via potmeter P1.
In the formula for the calculation a value of 22 kOhm is used. 
the full analog value on the ANALOG_PIN is 1023, so 63.2% is a value of 647.
*/
 
 lcd.setCursor(0,1);
 lcd.blink();                         // cursor blinks during measuring.
 digitalWrite(P1_PIN, HIGH);          // charge the capacitor. 
 tStart = millis();
 while(analogRead(ANALOG_PIN)<647){}; 
 nDuur = abs(millis()-tStart);
 MicroFarad = (nDuur/22000.0) * 1000; // calculate the value of the capacitor.  
 delay(wait);
 lcd.noBlink();

 // show the calculated value on the lcd screen:
 if(MicroFarad >= 1.0){
 // show the value in microFarad:
 lcd.clear();
 lcd.print("     ");
 lcd.print(MicroFarad,1); 
 lcd.print(" ");
 lcd.print((char)228);               // print the greek character mu.
 lcd.print("F ");
 delay(wait);
 }

 else{ 
 // show if no capacitor is connected or the value is too low to be accurate:
 if (MicroFarad < 0.07) {lcd.clear();
 lcd.print("  no capacitor");}
 // show the value in nanoFarad:
 else{ 
  lcd.clear();
  lcd.print("     ");
  lcd.print(MicroFarad*1000,1); 
  lcd.print(" ");
  lcd.print("nF ");
  delay(wait);
 }
 }
 }
// discharge the capacitor for the next measuring. 
void Discharge() { 
 digitalWrite(P1_PIN, LOW);            
 pinMode(R8_PIN, OUTPUT);           
 digitalWrite(R8_PIN, LOW);         
 while(analogRead(ANALOG_PIN)>0){} ;
 pinMode(R8_PIN, INPUT);
 delay(wait); 
    
}
// reset procedure
void softReset(){
 lcd.clear();
 lcd.setCursor(0,0);
 lcd.print("reset...");
 delay(wait);
 asm volatile ("  jmp 0");
}
ESRfinal.inoArduino
#include <LiquidCrystal.h>
#include <avr/eeprom.h>

//we have to change prescaler for the ADC to make the conversion happen faster this code section was sugested 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 pins used

#define DISCHARGE_PIN 8
#define PULSE_HighRange_PIN 10
#define PULSE_LowRange_PIN 9
#define ESR_PIN A0
#define BUTTON_PIN A4

//function measure
unsigned long measureESR(void);   //measuring function, increases ADC to 14-bit resolution by oversampling

//global variables
unsigned long esrSamples;
double milliVolts;
double esrCalib;
double vRef = 1.1;                //voltage on the Vref pin (this sketch uses internal voltage reference 1.1V)
double milliAmps = 48.48;         // (in mA) needed only for "Zeroing". Proper calibration can be done entering the right value for the milliAmps, (U=I*R).
double Rs = 1003.0;               //proper calibration can be done entering the right value for Rs. Enter the value also in the "IF" branch! This is only the initialization of the Upper Range!
double Vin = 5000;
double Rm;
boolean esrRange = false;
int stabilizer = 0;

//idealy milliAmps is 50 mA, this condition is fufiled only if R10 is 100 Ohm, Vcc is exactly 5V and the transistor while fully saturated idealy is at 0 ohm.

LiquidCrystal lcd(13,11,6,4,3,2); //this is my display setup, I'm using standard 4-bit control.

void setup(void)
{

  if (FASTADC)
    {
          sbi(ADCSRA,ADPS2); //seting prescaler to 32 for faster adc (500khz)
          cbi(ADCSRA,ADPS1); //at 500khz  results are still looking good (same values as if 250khz ADC clock)
          sbi(ADCSRA,ADPS0); // the shorter the pulse on a small value capacitor it has no time to charge and denaturate the result
    }

  pinMode(ESR_PIN, INPUT);                      //reading milliVolts
  pinMode(PULSE_HighRange_PIN, OUTPUT);
  digitalWrite(PULSE_HighRange_PIN,HIGH);       //low enables Q1
  pinMode(PULSE_LowRange_PIN, OUTPUT);
  digitalWrite(PULSE_LowRange_PIN,HIGH);        //low enables Q2
  pinMode(DISCHARGE_PIN, OUTPUT);
  digitalWrite(DISCHARGE_PIN,HIGH);             //high enables Q3
  pinMode(BUTTON_PIN,INPUT);                    //setting up for a button (for zeroing)
  digitalWrite(BUTTON_PIN,HIGH);                //enabling the pull up on the button, when button pressed to the ground zeroes out the cable

  analogReference(INTERNAL);                    //setting vRef to internal reference 1.1V
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print("ESR meter");
  lcd.setCursor(5,1);
  lcd.print("version 1.0");
  delay(500);

  eeprom_read_block((void*)&esrCalib, (void*)0, sizeof(esrCalib));   //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  
  lcd.clear();
}
//**************************************************************************************************************************

void loop(void)
{
      esrSamples = measureESR(); 
      milliVolts = (esrSamples * vRef) / 16.384;    //Divide by 16.384 due to a 14-bit oversampling
      Rm = Rs / ((Vin / milliVolts) - 1);           //voltage divider (R2=R1(U2/(U1-U2)))
      Rm = Rm - esrCalib;                           //Compensate cable resistance
      if (Rm < 0) Rm = 0;                           //Do not show eventual negative values
    
      if (stabilizer == 3) {                        //Make three measurements before autoranging the ESR. This is hysteresis to avoid oscillation when autoranging.
            if (Rm < 5.5) {
              Rs = 100.2;                           //If needed, adjust this value for low range measurement (Below 6 Ohms)
              esrRange = false;
            }
            else if (Rm > 5.8) {
              Rs = 1003.0;                          //If needed, adjust this value for high range measurement (Above 6 Ohms and below 50 Ohms)
              esrRange = true;
            }
            stabilizer = 0;
            }
      lcd.clear();    
      lcd.setCursor(0,0);
      lcd.print("Vout = ");
      lcd.print(milliVolts);
      lcd.print("mV");
      lcd.setCursor(1,1);
        
      if (Rm <= 50){                             //If measurement is less than 50 Ohms show the result, otherwise show <OL> (Overload) on the display
            lcd.print("ESR = ");
            lcd.print(Rm,2);
            lcd.print((char)244);               // print the greek character ohm.
          }
      else {
            lcd.print("ESR = <OL>     ");
      }
    
      //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 posible this influence (short cables, soldering the cables, etc)
    
      if(!digitalRead(BUTTON_PIN)){
            lcd.clear();
            lcd.print("Zeroing...");
            esrCalib = milliVolts/milliAmps;
            lcd.print(" done!");
            lcd.setCursor(0,1);
            eeprom_write_block((const void*)&esrCalib, (void*)0, sizeof(esrCalib));
            lcd.print("saved to EEPROM");
            delay(1000);
      }
    
}

//**************************************************************************************************************************
unsigned long measureESR()
{
  unsigned long accumulator = 0;
  unsigned int sample = 0;
  int i = 0;
  // oversampling 256 times (for 14 bit is 4^(desiredResolution - ADCresolution) where is 4^(14-10) = 4^4 = 256)
  while ( i++ < 256 )
  {
            digitalWrite(DISCHARGE_PIN, HIGH);                 // discharge the capacitors
            delayMicroseconds(600);                            // discharge wait time
            digitalWrite(DISCHARGE_PIN, LOW);                  // disable discharging
            if (esrRange == false) {
                  digitalWrite(PULSE_LowRange_PIN,LOW);        // start milliAmps pulse
                  delayMicroseconds(5);                        // on the scope it appears that after enabling the pulse a small delay is needed for oscillations to fade away
                  sample = analogRead(ESR_PIN);                // read ADC value
                  digitalWrite(PULSE_LowRange_PIN, HIGH);      // stop milliAmps pulse
                }
            if (esrRange == true)  {
                  digitalWrite(PULSE_HighRange_PIN,LOW);       // start milliAmps pulse
                  delayMicroseconds(5);                        // on the scope it appears that after enabling the pulse a small delay is needed for oscillations to fade away
                  sample = analogRead(ESR_PIN);                // read ADC value
                  digitalWrite(PULSE_HighRange_PIN, HIGH);     // stop milliAmps pulse
            }
            accumulator += sample;                             // accumulate sampling values
  }
  esrSamples = accumulator >> 4;                               // decimate the accumulated result
  stabilizer ++;
  return esrSamples;
}
freqfinal.inoArduino
/*  Frequency Counter With LCD Display - by arduinoaleman - May 2015
    updated by alexP December 2018
    Pin D5 of Arduino must be used for frequency input.
    Counter1 : 16 bits / used to count the frequency impules
    Counter2 : 8 bits / used to generate a 1000ms or 100ms gate time for measurement
    I use "bitClear(TIMSK0,TOIEO)" instead of "TIMSK0 &=~(1<<TOIE0)"
    I use "bitSet(TIMSK0,TOIEO)"   instead of "TIMSK0 |=~(1<<TOIE0)"
*/

#include <avr/interrupt.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(13,11,6,4,3,2);
volatile unsigned long frequency=0;            
volatile boolean       measurement_ready;
volatile unsigned char overflow_counter;     // number of overflows within gate_time
volatile unsigned int  time_so_far;              // number of ISR calls
volatile unsigned int  gate_time;                 

void measurement(int ms) {

    bitClear(TIMSK0,TOIE0);     // disable counter0 in order to disable millis() and delay()
                                // this will prevent extra interrupts that disturb the measurement
    delayMicroseconds(66);      // wait for other interrupts to finish
    gate_time=ms;               // usually 1000 (ms)
  
    // setup of counter 1 which will be used for counting the signal impulses

    TCCR1A=0;                   // reset timer/counter1 control register A
    TCCR1B=0;                   // reset timer/counter1 control register B
    TCCR2A=0;                   // reset timer/counter1 control register A
    TCCR2B=0;                   // reset timer/counter2 control register A
    
    // setup of counter2 which will be used to create an interrupt every millisecond (used for gate time)

    TCCR2B |= B00000101;        // set prescale factor of counter2 to 128 (16MHz/128 = 125000Hz)
                                // by setting CS22=1, CS21=0, CS20=1

    bitSet(TCCR2A,WGM21) ;      // set counter2 to CTC mode
                                // WGM22=0, WGM21=1, WGM20=0                   
    OCR2A = 124;                // CTC divider will divide 125Kz by 125 
   
    measurement_ready=0;        // reset
    time_so_far=0;              // reset
    bitSet(GTCCR,PSRASY);       // reset the prescaler 
    TCNT1=0;                    // set frequency counter1 to 0
    TCNT2=0;                    // set gate time counter2 to 0
      
    bitSet(TIMSK2,OCIE2A);      // enable counter2 interrupts
    TCCR1B |= B00000111;        // set CS12, CS11 and CS10 to "1" which starts counting 
                                // on T1 pin (Arduino pin D5)
 }


ISR(TIMER2_COMPA_vect) {
 
  if (time_so_far >= gate_time) {         // end of gate time, measurement is ready
    TCCR1B &= B11111000;                  // stop counter1 by setting CS12, CS11 and CS10 to "0"
    bitClear(TIMSK2,OCIE2A);              // disable counter2 interrupts
    bitSet(TIMSK0,TOIE0);                 // enable Timer0 again // millis and delay
    measurement_ready=true;               // set global flag for end count period
                                          // calculate now frequeny value 
    frequency=0x10000 * overflow_counter; // mult #overflows by 65636 (0x10000)
    frequency += TCNT1;                   // add counter1 contents for final value
    overflow_counter=0;                   // reset overflow counter
  }
  else {
    time_so_far++;                        // count number of interrupt events
    if bitRead(TIFR1,TOV1)  {             // if Timer/Counter 1 overflow flag = "1" then ...
       overflow_counter++;                // increase number of counter1 overflows
       bitSet(TIFR1,TOV1);                // reset counter1 overflow flag
    }
  };
}


void setup() {

  pinMode(A4, INPUT_PULLUP);
  pinMode(5,INPUT);
  lcd.begin(16, 2);                            // defines a LCD with 16 columns and 2 rows
  lcd.clear();
  
  for (int row=0; row<=1; row++) {
    for (int column=0; column<=16; column++) { // LDC test
       lcd.setCursor(column,row);
       lcd.print("*");
       delay(20);
    };
  }
  lcd.clear();
  }


void loop() {
  float floatfrq;  
  int range;
  long frq;

if(digitalRead(A4)== LOW)
{softReset();}

  measurement(1000);                     // 1000ms standard gate time
     
  while (measurement_ready==false);
  frq=frequency;

  floatfrq=frq;                          // type conversion (required!!)
  
  if ((frq >= 0)&& (frq < 10))           {range=0;};                           // Hertz
  if ((frq >= 10)&& (frq < 100))         {range=1;};   
  if ((frq >= 100)&& (frq < 1000))       {range=2;};
  if ((frq >= 1000)&& (frq < 10000))     {range=3; floatfrq=floatfrq/1000;};   // KHz
  if ((frq >= 10000)&& (frq < 100000))   {range=4; floatfrq=floatfrq/1000;};
  if ((frq >= 100000)&& (frq < 1000000)) {range=5; floatfrq=floatfrq/1000;};
  if (frq >= 1000000)                    {range=6; floatfrq=floatfrq/1000000;};// MHz 
  
  lcd.setCursor(0, 0);                 
  lcd.print("Freq:           ");        
   
  switch(range) {             
    case 0:                                          
       if (frq!=0) 
       {lcd.setCursor(6, 0);lcd.print(frq);
       lcd.setCursor(13,0);lcd.print("Hz ");}
       else
       {lcd.clear();lcd.setCursor(0, 0);
       lcd.print("No signal...   ");}
       
       
       break;
    case 1:   // 10Hz thru 100Hz
       lcd.setCursor(6, 0);   lcd.print(frq);
       lcd.setCursor(13,0);   lcd.print("Hz ");    
       break;
    case 2:    // 100Hz thru 1KHz
       lcd.setCursor(6, 0);   lcd.print(frq);
       lcd.setCursor(13,0);   lcd.print("Hz ");    
       break;
    case 3:    // 1KHz thru 10KHz
       lcd.setCursor(6, 0);   lcd.print(floatfrq,3);
       lcd.setCursor(13,0);   lcd.print("kHz");    
       break;
    case 4:   // 10KHz thru 100KHz
       lcd.setCursor(6, 0);   lcd.print(floatfrq,2);
       lcd.setCursor(13,0);   lcd.print("kHz");    
       break;
      case 5:   // 100KHz thru 1MHz
       lcd.setCursor(6, 0);   lcd.print(floatfrq,1);
       lcd.setCursor(13,0);   lcd.print("kHz");    
       break;
     case 6:   // above 1MHz
       lcd.setCursor(6, 0);   lcd.print(floatfrq,3);
       lcd.setCursor(13,0);   lcd.print("MHz");    
       break;       
  } 
}

void softReset(){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("reset...");
delay(3500);
asm volatile ("  jmp 0");
}
  

Schematics

frequency part
hardware circuit of the frequency tester
Arduinometer (25) a1h96ribro
ESR tester
hardware part of the ESR tester
Arduinometer (23) racuczhvva
the hardware inside the box
Arduinometer (6) erk5pfkadz
the final assembled meter
Arduinometer (27) y3yg79nlyn
top view with lcd screen
Arduinometer (14) jn6db0br4k
Wiring Diagram
Arduinometer (12) pakpbzlej1

Comments

Shummi

Shummi

8 months ago

Hi
I like your build.
what is the voltage in ESR testing, would it be below 100mV so it can test in Circuit ?

alexpikkert

alexpikkert

4 months ago

I think you should only test capacitors separated from a circuit. A circuit would influence the result.

har99tono

har99tono

5 months ago

I got another opinion on this ESR measuring principle, I am assuming that the first idea is based on dr. Hutale's schematics, but the software have really different approach.
Both approach are working, but when we mixed with the hardware, it will not working well.
Dr. Hutale's working with high frequency, low amplitude principle (100kHz, 0,3Volt peak?), and this project based on short pulse (microseconds?), to assume that the Cap Under Test has no time to charge.
We use the same hardware for both idea.
The hardware schematic working well for Hutale's, but not for this one.
Imagine that we put in series 2 (or3) Capacitor with totally different capacitance (47uF+100nF) plut C Under Test. When we close the discharging circuit, we cannot discharge these capacitors well.
So, we must short circuit this (47uF+100nF) capacitor, and it will works with our software.

Add projectSign up / Login