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.

Example:
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.begin(9600);
Serial.println("============================================================");
Serial.println(" Welcome to : Capacitance Meter 1.0");
Serial.println("============================================================");
}

void loop(){

if (digitalRead(beginPin) == HIGH){

capEstLong = 0;

//microFarad run-through

digitalWrite(microFaradPin, LOW); //ground the capacitor
Serial.println("INITIALISING...");
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);
delay(1000);
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);
Serial.println("F");
} else {
Serial.println("NA (possibly too small)");
}

Serial.println("============================================================");


} //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.

Chris

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.

11 comments:

Peter said...

This is a great project both for fault finding and for parts salvage.

In both cases the printed value of the cap can't be trusted, it needs to be compared with actual behaviour.

Thanks for sharing this.

Anonymous said...

This article goes into the interrupt side of things and might be useful

http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/

chris said...

Thanks for the input guys.

I did think about the actual values for the capacitor compared to the stated. I wasn't sure what to do to try and get rid of that possible error, besides buying a real capacitance meter, I couldn't think of anything. Any Ideas? Buying a real capacitance meter would be completely against the idea behind building the circuit in the first place!

Thanks anonymous, whoever you are. That article may end up being quite useful.

Kyle G. said...

Do you have access to a oscilloscope? You can measure them using that with the right knowledge (basically tho same method your using)

chris said...

I wish!

I plan on trying to make one soon, proboably with a USB compatible PIC, with my monitor as the scope's screen.

Normal RS232 serial communication wont be able to transmit fast enough to detect a high(ish) input frequency - about 11KHz max. with 115200 baud, 10 bit ADC and no start/stop/parity bits.

Hence the USB - minimal speed is about 1.2Mbps which is already 10 times faster, bringing it up to about 100KHz. If I can get 48Mbps working properly, that'll give me about 4MHz with no start/stop/parity bits. I'll just have to make sure the ADC doesn't take too long to settle and that the PIC can take a fast enough crystal.

It'll be a challenge.

Kyle G. said...

I wight be able to access one. Perhaps I could measure a few and send them to you (of course I'm looking to build one of these too. It would be super awesome for home brew capacitors.)

chris said...

Thanks very much for the offer, but, without the actual capacitors, I cannot really do anything.

What really needs to be done is you getting yours set up, in it's final configuration (i.e. on perf-board or something - not the breadboard), get the value from the scope, then get the value from the Arduino and plot the graph.

When capacitors are measured in different circuits, the integer value from the Arduino will differ; especially with the lower end - stray capacitance and whatnot all over the place!

Let me know how you get on when you get round to it, I'll be interested to hear.

Thanks again,

Chris

Kyle G. said...

Well I'll definitley let you know mhen mine is up and running, though it might be a couple of weeks (Im finishing up a couple projects atm)

And btw I meant send you the actual capacitors, not just the results.

Makoto said...

With the diodes in place, it'll drop the output voltage of the arduino by 0.6 volts (assuming they're silicon diodes). Won't that affect the way you're calculating the capacitance?

chris said...

Very true Makoto,
That would have been taken into consideration if the calculations were done with the true values of the voltage, not just the 10 bit value from the ADC on the arduino. Since everything will stay (roughly) proportional, it didn't matter to me, especially considering the fact it was just a proof of concept for myself. When I remake the circuit, i'll try to make it as accurate as possible.

Thanks for the comment,

Chris

Riaan said...

I could be completely of the mark here (I blame it on too much coffee and too little sleep), but do I understand correctly that you are essentially measuring the first time constant up to 63.2%?

How about taking a second measurement for the second 63.2% charge and then comparing results?

I would display both those values as well as an average. If all 3 are close together, the reading is probably accurate. If not, it could be a faulty cap or it could be stray capacitance, etc...

Does this make sense?