Sunday, 22 June 2008

Capacitance Measurer v1.0

Question: is measurer a real word? - google doesn't think so.

I, like many people I'm sure, find certain capacitor markings a little cryptic. So, with the help of my Arduino Decimilla, I set about building a device to measure the capacitance of a selected capacitor.

First, a little of the theory behind its operation:

Capacitors charge and discharge at certain 'speeds' based on the series resistance and its total capacitance. Skipping all of the semi-complicated maths, the time it takes for a capacitor to reach 63.2% of full charge is known as 1 RC time constant. The RC time constant is simply the product of the series resistance and the capacitance of the capacitor - R * C.

100uF capacitor in series with a 10K resistor will take 1 second to reach 63.2% of full charge, then another 1 second to charge up to 63.2% of the remaining capacitance and so on... Effectively, it will reach full charge (or as near as makes no difference) but theoretically it wont.

With this knowledge, I may be able to time how long it took for the capacitor to reach 63.2% charge, or, since they are proportional, 63.2% of the arduino's voltage level. However, I don't know how to implement a timer in the arduino language. I stuck with the 63.2% level but it didn't have any physical (as in physics) relevance from then on. I had to rely on the fact that the value given by the Arduino will be proportional to the actual time constant (although no connection was made) and therefore the capacitance (fixed resistance). I knew that as the capacitance increased, so would the value output by the arduino.

Here is the schematic I have produced for my circuit:

NOTE: Sorry, I've just noticed I missed out 3 of the PWM pins on the Arduino. I'll get round to sorting that soon.

The switch 'S1' is used to start the measurement. 'S2' is used to manually ground the capacitor, just to make sure. The diodes were for my peace of mind (protecting the arduino) - they may not be needed. The transistor is used to ground the capacitor before and after a reading. You may want to put a resistor in series with the transistor's collector or something to limit the current, thinking about, I probably should have. The resistors 'R1' and 'R4' were calculated to take into account my maximum and minimum capacitance values and my maximum acceptable time to make the reading (20 seconds).

Now, here's the code:

int firstRCV = 646; //(2^16)-1 * 63.2% (for first time constant)
unsigned int timerCount = 0; //incremented to get *RCTC's below
int checkPin = 0; //analog pin for checking the voltage level
int microFaradPin = 7; //pin used for readings between 1uF and 1000uF
int picoFaradPin = 6; //pin used for readings between 1pF and 1uF
int beginPin = 9; //switch pin to start reading
int groundingPin = 10; //set HIGH to ground the capacitor
int uRCTC = 0; //uF RC Time Consant
int pRCTC = 0; //pF RC Time Constant
int uConstant = 26; //divide uRCTC by this to get capacitance (in uF)
int pConstant = 2100; //divide pRCTC by this to get capacitance (in pF)
unsigned int capacitanceEstimation;//variable used to temporarily hold the capacitance estimate
long capEstLong;
char units;

void setup(){
pinMode(checkPin, INPUT);
pinMode(microFaradPin, OUTPUT);
pinMode(picoFaradPin, OUTPUT);
pinMode(beginPin, INPUT);
pinMode(groundingPin, OUTPUT);
digitalWrite(microFaradPin, LOW);
digitalWrite(picoFaradPin, LOW);
digitalWrite(groundingPin, HIGH);

Serial.println(" Welcome to : Capacitance Meter 1.0");

void loop(){

if (digitalRead(beginPin) == HIGH){

capEstLong = 0;

//microFarad run-through

digitalWrite(microFaradPin, LOW); //ground the capacitor
digitalWrite(groundingPin, HIGH);
delay(1000); //wait a second to make sure
digitalWrite(groundingPin, LOW);
Serial.println("CHECKING RANGE 1uF - 2200uF... (This may take a minute)");
digitalWrite(microFaradPin, HIGH); //set uF pin high to charge capacitor

uRCTC = get_uRCTC(); //function to get the number of time periods until the 63.2% level is reached

digitalWrite(microFaradPin, LOW); //set the pin low again
digitalWrite(groundingPin, HIGH);

//Serial.print("400us periods : "); //un-comment to get the integer values
//Serial.println(uRCTC, DEC); //un-comment to get the integer values

//capacitance estimation
capacitanceEstimation = uRCTC / uConstant; //get the actual capacitance value using the gradient of the graph plotted
capEstLong = (long)capacitanceEstimation;

//units for the capacitance; micro for now, but if in pico range, it will change to 'p'
units = 117;

//should we use the mega resistance to get a value for a picoFarad capacitor?
//if it is 0, the capacitance is less than 1uF (integer part of number)
if (capacitanceEstimation == 0){

//picoFarad run-through
units = 112;
Serial.println("NOT IN RANGE 1uF - 2200uF");
digitalWrite(picoFaradPin, LOW);
digitalWrite(groundingPin, HIGH);
digitalWrite(groundingPin, LOW);
Serial.println("CHECKING RANGE 1pF - 1uF... (This may take a minute)");
digitalWrite(picoFaradPin, HIGH);

pRCTC = get_pRCTC();

digitalWrite(picoFaradPin, LOW); //set the pin low again
digitalWrite(groundingPin, HIGH);

//Serial.println(pRCTC, DEC); //un-comment to get the integer values

//capacitance estimation
if (pRCTC > 5){
capEstLong = ((long)pRCTC * 10000) / (long)pConstant;
if (capEstLong > 1000){capEstLong = (capEstLong / 100) * 100;}
if (capEstLong > 10000){capEstLong = (capEstLong / 1000) * 1000;}
if (capEstLong > 100000){capEstLong = (capEstLong / 10000) * 10000;}
if (capEstLong > 1000000){capEstLong = (capEstLong / 100000) * 100000;}
if (capEstLong > 10000000){capEstLong = (capEstLong / 1000000) * 1000000;}

Serial.print("Capacitance Estimation : ");
if (capEstLong > 0){
Serial.print(capEstLong, DEC);
Serial.print(units, BYTE);
} else {
Serial.println("NA (possibly too small)");


} //if - beginPin is HIGH

} //loop

int get_uRCTC(){
timerCount = 0;
while (analogRead(checkPin) <= firstRCV) { delayMicroseconds(400); timerCount++; } return timerCount; } int get_pRCTC() { timerCount = 0; while (analogRead(checkPin) <= firstRCV) { timerCount++; } return timerCount; }

If you decide to make one, you'll need to change some of the constants (but actually variables in the code) according to the integer values given by the Arduino. The code above worked for me in a breadboard, but, resistances may vary with the components you use. I admit, the code is not the nicest I've seen, but, for a personal proof of concept it's fine for me.

I calibrated my sensor by plotting a graph using capacitors with known values and the integer thrown out by the code to the serial port. (un-comment some lines in the code to get the integer value). Here is one of my graphs, it's pointless showing both:

There were some points further up the line, but they seem to have mystically disappeared. If you get you spreadsheet application to show the equation of a linear trend line, you can get the gradient of the line. By dividing the integer value from the arduino by the gradient of the line, you get the supposed capacitance. This is where the 'constants' in the code come from.

Overall, this was quite a fun project to do, as well as aiding my learning - both physics/electronics and arduino programming. And, surprisingly, it was fairly accurate in the 1uF to 2200uF range - usually within 1uF or 2uF of the capacitors markings at the higher end, and dead on in the lower end. Considering the tolerance of the capacitors, this doesn't seem too bad! Not quite so accurate in the lower capacitance range though - usually within 300pF of the stated capacitance at the top end, much closer at the lower end.

Hope that was worth posting, Thanks for reading.

Any comments/suggestions greatly appreciated.


P.S. If anyone knows how to implement a timer in the arduino language, even on the AVR side of things, I'd love to know.