6

I'm measuring capacitance of a system using a multimeter, and found that the value is very small (fluctuate from 50pF to 100 pF). The system is dynamic so the capacitance changes over time.

Now I need to log this value to some text file (csv for example). How could I do this with Arduino? If it's resistance, I could have built a simple voltage divider and sense the voltage using ADC. Also C is very small, I haven't been able to construct some measuring circuit for C.

2
  • How much precision is required? Do you need to have a precise measure of the capacitance, or detecting a trip condition would be good enough? What is your application? Apr 5, 2017 at 7:44
  • It's a capacitive sensing application. Apr 7, 2017 at 16:54

6 Answers 6

1

Because the capacitance to be measured is well below 1 nF (50-100 pF), you'll encounter serious problems trying to measure it with the ADC of the Arduino due to its input capacitance (14 pF according to datasheet). Add the stray capacitances of wires, headers and the Arduino board itself and you'll easily end up with 25 pF input capacitance, which is in the same order of magnitude of what you're trying to measure (= A VERY BAD THING).

What to do, then? Well, the main options are these...

Use an external measurement circuit

This would mean including active devices (op amps, comparator, timers...) and precision passive components. A true HW-based solution.

The accuracy of the measurement in this case would be entirely up to the design (circuit topology and component selection) of the external circuit. The circuit would provide either an analog voltage or a signal whose frequency could be measured by the Arduino without introducing any additional significant errors.

An example using a 555 timer (an additional 4 pF compensation capacitor is needed between OUT and TH for improved accuracy):

555 timer capacitance meter

Frequency vs capacitance

Source: Use Analog Techniques To Measure Capacitance In Capacitive Sensors, an article by Martin Tomasz published in Electronic Design.

Think of the external circuit as a "sensor" or as a signal conditioning circuit for a "sensor" (the capacitance to be measured). In fact, the dynamic capacitance you're trying to measure probably comes from a sensor (moisture, humidity?), isn't it?

Connect the capacitor directly to analog pins of Uno and calibrate the readings

It may sound amazing, but something as simple as this works (at the expense of accuracy, of course):

Uno as capacitance meter

How's that? Because the input capacitance problem that we detected at the beginning is big enough to seriously impair accuracy, but doesn't make the measurement completely unfeasible. Thus, we can calibrate that effect out and still have a valid (although with reduced accuracy) measument in the target range (50-100 pF).

The calibration is done through some hardcoded default values in the following Arduino sketch (source here from braulio777). For better accuracy you should calibrate the values of IN_STRAY_CAP_TO_GND and R_PULLUP in the sketch by comparing measurements of some capacitors with their already known capacitance values (provided you can do this).

//Digital Capacitance Meter
//Measuring from 0.000pF to 1000uF

#include <LiquidCrystal.h>
LiquidCrystal lcd(11, 9, 5, 4, 3, 2);
const int OUT_PIN = A4;
const int IN_PIN = A0;
const float IN_STRAY_CAP_TO_GND = 24.48;
const float IN_CAP_TO_GND  = IN_STRAY_CAP_TO_GND;
const float R_PULLUP = 34.8;  
const int MAX_ADC_VALUE = 1023;

void setup()
{
  pinMode(OUT_PIN, OUTPUT);
  pinMode(IN_PIN, OUTPUT);
  lcd.begin(16, 2);
  }

void loop()
{
    pinMode(IN_PIN, INPUT);
    digitalWrite(OUT_PIN, HIGH);
    int val = analogRead(IN_PIN);
    digitalWrite(OUT_PIN, LOW);

    if (val < 1000)
    {
      pinMode(IN_PIN, OUTPUT);

      float capacitance = (float)val * IN_CAP_TO_GND / (float)(MAX_ADC_VALUE - val);

      lcd.setCursor(0,0);
      lcd.print("Capacitance = ");
      lcd.setCursor(0,1);
      lcd.print("                ");
      lcd.setCursor(0,1);
      lcd.print(capacitance, 3);// for the best precision
      lcd.print("pF ");

    }

    else
    {
      pinMode(IN_PIN, OUTPUT);
      delay(1);
      pinMode(OUT_PIN, INPUT_PULLUP);
      unsigned long u1 = micros();
      unsigned long t;
      int digVal;

      do
      {
        digVal = digitalRead(OUT_PIN);
        unsigned long u2 = micros();
        t = u2 > u1 ? u2 - u1 : u1 - u2;
      } 

      while ((digVal < 1) && (t < 400000L));

      pinMode(OUT_PIN, INPUT);  
      val = analogRead(OUT_PIN);
      digitalWrite(IN_PIN, HIGH);
      int dischargeTime = (int)(t / 1000L) * 5;
      delay(dischargeTime);   
      pinMode(OUT_PIN, OUTPUT);  
      digitalWrite(OUT_PIN, LOW);
      digitalWrite(IN_PIN, LOW);

      float capacitance = -(float)t / R_PULLUP
                              / log(1.0 - (float)val / (float)MAX_ADC_VALUE);

      lcd.setCursor(0,0);
      lcd.print("Capacitance = ");
      if (capacitance > 1000.0)
      {
        lcd.setCursor(0,1);
        lcd.print("                ");
        lcd.setCursor(0,1);
        lcd.print(capacitance / 1000.0, 2);
        lcd.print("uF ");

      }

      else
      {
        lcd.setCursor(0,1);
        lcd.print("                ");
        lcd.setCursor(0,1);
        lcd.print(capacitance, 2);
        lcd.print("nF ");

      } 

    while (millis() % 1000 != 0);
}
    }

The example code above outputs measured values to an LCD display. For your intended application, the best option probably is to write data to an external SD card.

Disclosure: I have not tested the code above myself.

3

For capacitance as small as pF, The RC time constant method yields dirty results on an Arduino board.

You can use the Arduino pins built in small capacitance (which is in Pf itself) to create a "capacitance divider" circuit and calculate much like you would resistance from a voltage divider circuit.

See this excellent post by Nethercott for reference Arduino capacitance meter

2
  • Nice find,+1! I'm surprised how precise this method seems to be, considering it's based on the parasitic pin properties. Though I suppose this only works well with good EMI shielding or in EMI-free conditions. Apr 5, 2017 at 15:17
  • Thank you :-) I imagine you are right, though i never had the chance to stretch this method to its limits. From my experience with it, i was able to produce satisfactory results using an Arduino Uno board & 15cm long regular jumper wires in a day-to-day environment.
    – TanyaV
    Apr 5, 2017 at 16:37
1

(fluctuate from 50pF to 100 pF).

a few ways for such small values -> assuming you can really read it like a capacitor.

1) form a lc tank and measure its frequency -> most effective in measuring small value capacitance. but needs precision inductor / calibration, and is subject to parasitics.

2) charge / discharge it via a CCS: and measure the time. simplest as a ccs can be formed via a large resistor. not effective with really small capacitance;

3) charge transfer: using the capacitance of the adc. very effective against small capacitance: < 10x of adc capacitance. needs calibration.

each has its own problems.

1
1

The standard way to measure a resistance with an Arduino is to build a voltage divider by putting it in series with a known resistance. I have tried the same approach for measuring capacitances, and it turns out it works well in the 100 pF range! The circuit is like this:

  ┌───────────── Arduino digital output
  │
──┴── known
──┬── cap
  │
  ├───────────── Arduino analog input
  │
──┴── unknown
──┬── cap
  │
 GND

The only catch is that you have to do the measurement relatively fast, otherwise you would be measuring the ratio of the leakage resistances. Here is the measurement protocol:

  1. Set both pins to OUPUT LOW in order to discharge both capacitors.
  2. Delay for one or two CPU cycles to make sure they are fully discharged.
  3. Set the analog input pin to INPUT mode: this will make it high impedance and isolate the node connected to it.
  4. Set the digital output to HIGH: this will charge the two capacitors in series to 5 V.
  5. Delay for one or two CPU cycles for the voltage to stabilize.
  6. Take an analog reading.

Now you can derive the capacitance from the reading using the same formula you would use with resistors, only with impedances (or, simply 1/C) instead of resistances:

C_unknown = C_ref × (1024 − reading) ÷ reading;

The same formula can be derived by considering that the node connected to the analog input, which includes one plate of each capacitor, has zero net charge.

A few things worth noting:

  • You will get the best resolution by choosing the reference capacitance close to the one you want to measure.
  • The delays are not actually needed: as the capacitors charge and discharge very fast, a single CPU cycle is about 24 time constants.
  • If you were to measure large capacitances (several nanofarads) with this setup, you would want protective resistors in series with the Arduino pins, but then the delays become mandatory. In the 100 pF range I would not worry about the in-rush currents.
  • If the delay in step 5 is too long, your measurement will be affected by the leakage of the caps and the analog input pin.
  • You need not worry about the 14 pF cap on the sample and hold circuit: it will slightly affect the very first measurement but, once that cap is charged, subsequent measurement will not be affected as long as the unknown cap does not change to much between consecutive measurements. You do need to calibrate out the stray capacitance of the pin and probes.
1

Each Arduino capacitance meter relies on a property of resistor capacitor (RC) circuits- the time constant. The time constant of an RC circuit is defined as the time it takes for the voltage across the capacitor to reach 63.2% of its voltage when fully charged. Larger capacitors take longer to charge, and therefore will create larger time constants. The capacitance in an RC circuit is related to the time constant by the equation:

TC = R x C

where TC = time constant in seconds R = resistance in ohms C = capacitance in farads

Rearranging the equation to solve for capacitance gives:

C = TC/R

schematic

simulate this circuit – Schematic created using CircuitLab

Example: 1 megohm * 1 microfarad = 1 second enter image description here

Each capacitance meter has an RC circuit with known resistor values and an unknown capacitor value. The Arduino will measure the voltage at the capacitor and record the time it takes to reach 63.2% of it’s voltage when fully charged (the time constant). Since the resistance value is already known, we can use the formula above in a program that will calculate the unknown capacitance.

As your Capacitance is too Small in Pico Farad Range so you can directly measure Capacitance through your analog pin like this way... Schematic You said that your capacitor in dynamic in nature so you have to take atleast 10 values and average it to get accurate value.

Code for measure Low capacitance in pF range

const int OUT_PIN = A5;
const int IN_PIN = A0;
const float IN_STRAY_CAP_TO_GND = 24.48;
const float IN_CAP_TO_GND  = IN_STRAY_CAP_TO_GND;
const float R_PULLUP = 34.8;  
const int MAX_ADC_VALUE = 1023;

void setup()
{
  pinMode(OUT_PIN, OUTPUT);
  pinMode(IN_PIN, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
    pinMode(IN_PIN, INPUT);
    digitalWrite(OUT_PIN, HIGH);
    int val = analogRead(IN_PIN);
    digitalWrite(OUT_PIN, LOW);

    if (val < 1000)
    {
      pinMode(IN_PIN, OUTPUT);

      float capacitance = (float)val * IN_CAP_TO_GND / (float)(MAX_ADC_VALUE - val);

      Serial.print(F("Capacitance Value = "));
      Serial.print(capacitance, 3);
      Serial.print(F(" pF ("));
      Serial.print(val);
      Serial.println(F(") "));
    }
    else
    {
      pinMode(IN_PIN, OUTPUT);
      delay(1);
      pinMode(OUT_PIN, INPUT_PULLUP);
      unsigned long u1 = micros();
      unsigned long t;
      int digVal;

      do
      {
        digVal = digitalRead(OUT_PIN);
        unsigned long u2 = micros();
        t = u2 > u1 ? u2 - u1 : u1 - u2;
      } while ((digVal < 1) && (t < 400000L));

      pinMode(OUT_PIN, INPUT);  
      val = analogRead(OUT_PIN);
      digitalWrite(IN_PIN, HIGH);
      int dischargeTime = (int)(t / 1000L) * 5;
      delay(dischargeTime);   
      pinMode(OUT_PIN, OUTPUT);  
      digitalWrite(OUT_PIN, LOW);
      digitalWrite(IN_PIN, LOW);

      float capacitance = -(float)t / R_PULLUP
                              / log(1.0 - (float)val / (float)MAX_ADC_VALUE);

      Serial.print(F("Capacitance Value = "));
      if (capacitance > 1000.0)
      {
        Serial.print(capacitance / 1000.0, 2);
        Serial.print(F(" uF"));
      }
      else
      {
        Serial.print(capacitance, 2);
        Serial.print(F(" nF"));
      }

      Serial.print(F(" ("));
      Serial.print(digVal == 1 ? F("Normal") : F("HighVal"));
      Serial.print(F(", t= "));
      Serial.print(t);
      Serial.print(F(" us, ADC= "));
      Serial.print(val);
      Serial.println(F(")"));
    }
    while (millis() % 1000 != 0)
      ;    
}
5
  • And what about the load capacitances of the arduino pins? Apr 5, 2017 at 7:39
  • According to the ATmega328 datasheet it's around 14 pF. Too close to the measuring range for comfort. Plus any stray capacitance due to wires and to the Uno board and headers. Apr 5, 2017 at 8:11
  • 1
    −1: This method is not really suitable for such small capacitances: you would need an insanely large resistance value, otherwise the time constant would be too short for making a good measurement. If you want to do an RC time constant measurement, you should instead use the method described in Dmitry Grigoryev's answer. Apr 5, 2017 at 10:52
  • Yeah you are right. But I have tasted it and giving me good result not so precise and accurate but satisfactory. Cool down dude I have given him a generalized Idea not the solutions for his problem. Apr 5, 2017 at 11:02
  • 1
    You gave “an idea”, and then a piece of code that does something very different from that idea. If you authored that code, why don't you explain what it actually does? If not, why don't you give credit to the original author? Apr 5, 2017 at 14:24
1

Measuring small capacitance value via RC constant estimation essentially requires precise time measurement. ATmega chips have TCNT1 timer which can be programmed to increment each clock cycle, and can be stopped by a comparator using bit ACIC in ACSR register. Running a counter at 16 MHz will give you a resolution of 62,5 ns which is enough for capacity measurements in picofarad range. Here's an example of code which uses this measurement technique.

AnalogRead is about 1000 times slower, so you'll be limited to nF range if you use it.

Note that you should calibrate your system when measuring such a small capacitance. This is done by running a measurement with your probes connected but without the target system. You will then subtract this capacitance value (typically 20-50 pF, depending on the probes you use) from your raw measurements to obtain the capacitance of your system alone.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.