ranging cap meter not quite correct

This is my schematic and sketch for a ranging capacitance meter. It compiles ok and uploads, but returns some strange values.
I need a wiser head to point out errors.
On upload, nothing in serial (com3) until I touched all analog pin inputs with my finger. Then see the attached results jpg.
Its sort of working.
I monitored the volts across the cap with a multimeter (0-5v.dc). After upload I got 0.5v. all the time up to the first readout.
I hit the reset button and volts dropped to 0 briefly, then when I "fingered" the ana IN pins I got similar readout as above,
but the multimeter now showed (and held) 2.5v.
I cant see why the void loop is not completing.

sketch_ranging_cap_meter_pgc13.pdf (57.6 KB)

sketch_ranging_cap_meter_pgc13.ino (6.32 KB)

cap meter pgc board (shield) circuit.bmp (2.77 MB)

On a quick look:
-Serial.begin(9600); should be on the void setup() and not in void loop(). There is no need to repeat that constantly.
-while (analogRead(ledPin)>0){ //until full discharge. Doing nothing while that condition is true can result problematic.
}. Getting a real zero reading maybe difficult as the cap discharge takes a long time. Noise could also be blocking the program from exiting that while loop.
-Too many while() loops is risky. The loop gets stuck very easily unless its properly done and the exit condition can be satisfied at some point.
-Try replacing the while() loops by if() whenever possible and check the results. I suspect one of them is blocking the whole thing all together; but I haven't checked thoroughly.

Also take a look at

http://wordpress.codewrite.co.uk/pic/2014/01/21/cap-meter-with-arduino-uno/

and

... these all offer some insight in to how to do this.

Thanks all.
I see your point about discharge to 0 adrian, I might try a 4000 delay instead.
I can't think how to replace the WHILES's with IF unless I can implement some sort of goto or subroutine is this possible?

Something like this;
int ValueRead=analogRead(0);// its better this way as you may need to use the value later on avoiding having to read it again. That's if you can live with the old value. If the loop is slow you might need to read again to update.
if(ValueRead>0)
{
Discharge();
}
else
{
StopDischarge()
}

void Discharge() //the function or method Discharge
{
//Anything you need to do to keep discharging
}
void StopDischarge() //the function or method StopDischarge
{
//Anything you need to do to stop discharging
}

The Arduino loop will do whaetver you say Discharge() is and it will continue right there when that (subroutine as you call it) finishes. It will check again in the next loop though and will keep discharging until so, without clogging the program as while() does.

In "modern times" they call what you (and me) knew as "subroutines" functions or methods a more appropriate term for Object Oriented Programming :roll_eyes:. I haven't found an equivalent for GOTO though. The lines are not numbered as they were before or at least you can't see it, so there is no reference where to jump. The way to do a GOTO is just calling a "function" or "method" when you need it. The program returns automatically to where it was. No need for Return or End. You need to forget about BASIC. ;). I had to do it too. Takes a while to "unlearn" it though.

excellent guidance- thanks adrian I will try as suggested.
Basic was sooo easy to cobble a working program, even if was a programmers rats nest. Now we have to consider
the hardware and more rigid program structure.
I have to rewrite each of the range functions to eliminate conditional loops. As Oates said
"I may be some time"

I know how it is man, I had the same questions not too long ago... It took me while to figure it out though, so I tried to save you some time ;). In very little you will realize, this kind of coding is even easier than BASIC and the others. The computer will save you a lot of time resolving the lines numbering and jumps (I don't know how) without you having to do it. I suggest you start by looking at some examples first ,to get a better idea about how to do it and not make the same mistakes I did, trying to program in Basic at first. :fearful:
A few days ago, I posted an example which might help you figuring it out: :cold_sweat:
http://forum.arduino.cc/index.php?topic=217512.0
Good Luck with your Project.

One q. adrian, I find it odd that you can call the discharge method for the first time before it has been defined by "void Discharge" or does the compiler sort it out for you.
It would be logical to avoid executing the definitions in every void loop, if they could go in declare or void setup.
Steep elementary learning curve made easier-thanks

galen:
One q. adrian, I find it odd that you can call the discharge method for the first time before it has been defined by "void Discharge" or does the compiler sort it out for you.

Arduino does that for you. Arduino processes your ino files and puts the declarations at the top of the file.
Have plenty of learning fun

Best regards
Jantje

Right, following good advice, heres my Mk2 sketch. After Adrian pointed to problems discharging the cap to 0v., I realised
that it could be avoided by measuring the charge time from 0.4 v (sensorValue82) to 4v. (sensorValue 818 ) and using the equation;
T = -RC ln(1-V/Vo)
thus T = -RC ln(-9) = RC * 2.197
and C = T/2.197R where C is farads T is seconds R is ohms.
prototype using pin9 charging only ie thro 1Meg resistor.
Will not need a discharge before pin 10 and 11 charging methods as they run in parallel
prototype also has lots of debug data printout
I could not get the analog read to trigger "if" for a single voltage value, but a small range worked.
The ReadValue jumps from 0 to 1022 in a couple of voidloops so I guess there is a problem here

cap meter pgc board (shield) circuit.bmp (2.77 MB)

ranging_cap_meter_Mk2.ino (4.21 KB)

sketch_cap_meter_Mk2PGCdebugger.pdf (57.5 KB)

-unsigned long timevariable??? time can't be negative I think, so better only long timevariable.
-Do you really need long variables. Does your time can get longer than what an int can take? Please check that.
-Try using micros() instead. You may be able to boost accuracy, but you will probably need longs, which you already have.
-From the results, the loop is taking about 214msec. Isn't that too slow?. That can be contributing to the jumps from 0-1022 although I don't think that's the only reason. There maybe more.
-Sending tons of huge strings to the serial monitor is good for step by step checking or presenting examples as the case now. But...,it slows down the thing, I guess. Communication is slow. Can your system take that slow motion even when you want accurate time readings with small jumps intervals? :roll_eyes:

Hi, the analog pin that you are measuring the charging voltage on is on the wrong side of the cap, its reading 5V all the time, it should be where the cap and the charging current limit resistors join.

Tom...... :slight_smile:

Thanks Tom. I tried the analog read connection to the other side of the cap with exactly the same results.
I will put 1k in the discharge connection.
The basic problem is that the cap reaches sensorValue 1022 in only a couple of voidloops so there is no resolution of measurement. I need to slow down the charging process or as adrian suggests,use micros.
Ithink millis will be more "robust" so I am now working on fully charging the capacitor to 5v. Then set pins 9,12,and13 INPUT.
Then charge leakage will slowly reduce the analogRead each void loop. Test for >1v (201)
I will have ElapsedTime =EndTime-StartTime.
Capacitance is proportional to ElapsedTime or calculate from C = T/Rln(V/Vo) where C is farads T is seconds R is ohms.
C = T/R
ln(1/5) or C = T/R*1.610
For large caps I may be able to use a 1Meg leakage capacitor(always in circuit) to swamp the effect of leakage resistance and use this value in the equation. For small caps I can calibrate elapsed time against known capacitors.

I have been working for a long time now on something which even when is for a different purpose; is very similar in operation. I ran into several of the same problems you are having now.
Some of the experiences learnt while doing it are these:
-To gain in accuracy, the loop should be as fast as possible or at least the reading and comparison with the target voltage, has to be performed several times within the same loop. That will allow you to detect when the threshold is surpassed almost immediately, otherwise the error introduced in the measurement will be too much and probably unacceptable.

  • Increase the discharge time (increase the discharge resistor) as much as possible. That will make any delays in the detection insignificant with respect to the total time, thus reducing the error. In my case, the discharge time is about 100 sec; but its a different application though.
    -Try using micros();
    -Create a function to read the voltage and make the comparison. Call that function inside the loop several times, whenever you think there has been too much time elapsed which may compromise accuracy.
    -Set a target loop or inter-readings time, that will satisfy the speed required for the readings and comparisons intervals which will introduce an error in the measurement, acceptable for your needs. For example, less than 1% of the time constant you are determining.
    -Use precise 1% or lower discharge resistors. They should also be high stability and low temp coefficient. If not, there will have unacceptable temp drift.
    You can increase the loop speed by:
    -Reducing the info sent to se serial monitor, both in length and quantity.
    -Avoid complicated math operations. I'm not sure; but floating point math may be slower.
    -delay() is forbidden!
    -Do not use while(). If there is no other option call the ReadAndCompare() function from inside it.
    -Reduce the amount of instructions to a minimum possible. Perform any non essential tasks after the discharge time has been measured. Then there is plenty of time for non essential calculations, etc.
    -Another possibility is to set a while loop to ReadAndCompare() only and when the value is found the exit condition is met. That way, it will search constantly in very short time intervals, which may increase your accuracy. In this case, you need to guarantee the condition will be met at some point. Set an alternative exit route from the while loop with a push button or similar, in case there is a failure and the program gets stuck.
    Hope this help you with your Project.

Hi, I need to ask.

  1. have you developed this program step by step, that is did you get the analog read and measuring the charge on the cap going first, then once that worked got the autorange implemented, if not then I suggest you start again. If you did can you show us some working sketches.

  2. Why are you not measuring charge /discharge volts from the junction of the cap and charge/discharge resistors.

  3. Is the reason for having the end of the cap connected to pin 13 so you can avoid diodes.

  4. I also think that you will not get a capacitor fully discharged due to volt drop on switching components in arduino.

  5. Can you tell me the pin status when you are charging the cap, if pin 13 is high then the analog voltage measurement will be 1023 all the time, if you move the analog input to where I suggest in 2) the voltage will be the charging volts but approaching 0 as it charges, to do that you will have to pull the charge pin low, but what about the influence of the other charge pins being held high?

It looks like you have had an idea and instead of proving the principle, which I have no problem with, you have tried to develop all the bells and whistles at the same time.
Go back to one cap and one charge resistor and one discharge resistor and get this bit right.
Also respect the current capabilities of the arduino I/O and that even a 100uF Cap with 3V can do some damage shorted through an arduino port.

Tom.....hope to help... :slight_smile:

TomGeorge:
Hi, I need to ask.

  1. have you developed this program step by step, that is did you get the analog read and measuring the charge on the cap going first, then once that worked got the autorange implemented, if not then I suggest you start again. If you did can you show us some working sketches.

  2. Why are you not measuring charge /discharge volts from the junction of the cap and charge/discharge resistors.

  3. Is the reason for having the end of the cap connected to pin 13 so you can avoid diodes.

  4. I also think that you will not get a capacitor fully discharged due to volt drop on switching components in arduino.

  5. Can you tell me the pin status when you are charging the cap, if pin 13 is high then the analog voltage measurement will be 1023 all the time, if you move the analog input to where I suggest in 2) the voltage will be the charging volts but approaching 0 as it charges, to do that you will have to pull the charge pin low, but what about the influence of the other charge pins being held high?

Tom, I have already addressed all the points you raised in your first post , including the position of the AnalogRead connection and a discharge current limiting resistor.
Guilty as charged for bells and whistles, but of course I did start with a working sketch and always posted results.(see my previous)

Your statement 5) is just wrong. With my connection, the analogRead will only approach 1023 when the capacitor is almost fully charged. Its a question of timescale.
The last part of the statement makes no sense to me. There is only one charge pin and during charge it is set OUTPUT and HIGH.
A new basic skeltch is needed avoiding discharge to 0v. and rapid rate of charging except through multiMeg resistors.
In this post sketch I start by charging a cap to 5v. and then monitor leakage voltage.
As the voltage leaks to earth, “my connection” voltage falls from 1023, (see results)
Your connection does not vary from about 6 (see results-tom’s connx) over 61 loops while my
Connection falls from 5v. to 1v.

Remember that your connection is on the positive side of a 100k resistor pinned to earth (0v.)
Charge is going to leak from the positive plate.
Also the charge/discharge currents are flowing to both plates in the same (but charge opposite to discharge) direction,.
AnalogRead is measuring the voltage referenced to earth of the positive end of the 100k resistor
in your case, but including the ESR of the capacitor in my connection.
Kirchoff’s law does not apply to a charging or discharging capacitor ie not in equilibrium.
So plate currents do not have to be the same.
The charge leaks from the positive plate and its current is falling exponential. Not so the negative plate.
Lets stick to evidence based assertions.

pgc sketch to measure capacitance ranging over 2 ranges.
0-0.5 uF and 0.5-50000 uF (more if you are patient and increase charge time)

theory.
new revision switch ranging cap meter
Measures the time taken to leak discharge from 5v (sensorvalue1023) to 1v.(sensorvalue202)

uses the equation, T = -RC ln(V/Vo)

and C = -T/Rln(V/Vo) where C is farads T is seconds R is ohms.
thus C = -T/(R
ln0.2) = T/(R*2.39)
prototype uses charge earth pin9 only

notes- It works,but it needs a "leak resistor" of 2Meg or switchable 4Meg permanently across the cap
Without this resistor I used finger touch across the cap to discharge it slowly.The led dimmed and went out and the
1v. level was triggered,but of course that is not quantitative.
(Curiously after I removed my finger, the led very slowly came back to full brightness.[could use for measurement of 0 to 63% charge])
I think this is leakage in the “charge” direction via the Arduino pins to the positive plate.(not seen with leakage resistor In place)
If ChargeGroundPin9 is set to INPUT, the 1v. trigger level is not detected, even if the capacitor starts off charged.
The 2 or 4 Meg resistor "underswamps" the inherant leakage resistance and can be used in the above equation to calculate the capacitance. (taking regard to the leak resistance value chosen)
Inspection of results shows that arduino detects my range of trigger points culminating in loop61
SensorValue 199 and ElapsedTime 4021 for my test capacitor of 0.33 uF
Loop time is about 150 Ms
The results are using a 2Meg leak resistor. With the 4Meg it takes about twice the loops and gives twice the ElapsedTime.

Mk6_cap_meter.ino (2.81 KB)

cap_meter_Mk6_pgc.pdf (53.4 KB)

sorry Tom, I did not have room to include sketch and results for your connection in my last post.
here they are

Mk6_cap_meter_toms_connx.ino (2.9 KB)

Hi, fine see what you are now doing.
Just remember that if you want to isolate an output, declare it an input as you have done, but why then write LOW to it?

pinMode(ledPin, INPUT);                 //only leakage current flows
digitalWrite(ledPin,LOW);

Anyhow, goodluck, I watch on the sidelines.

Tom...... :slight_smile:

I tested my Mk6 with a 22uF electro and 4Meg leak resistor.
first I removed all the debug prints and just left loop count . The loop takes about 11 mS.
It took 353seconds to detect 1v.
ElapsedTime was 353363 mS
Loops was 32560
cap value was 35357 nF
for a 100pF ceramic however (2Meg leak) the value was way high ElapsedTime 7022 cap value 351nF
for a 0.33uF poly (2Meg) still high ElapsedTime10632 cap value1063nF
It appears that there is not a linear relation between ElapsedTime and capacitance.
Perhaps the mysterious "charging leak" is responsible, as it would affect smaller capacitors more than large ones, increasing their apparent value.
Anyone got the math to define the effect of a constant rather low "charge current" on the discharge curve
Intuition suggests that as the discharge current falls off, the low charge current will distort the curve more.

One thing at a time.
The method in theory should work as it is already proven science. Implementing it in practice is a different story. Trying to achieve "auto ranging" at the same time is introducing additional problems, before implementing the basic solution to have a working circuit. So, I would try to go step by step and have a working thing first and then auto ranging.
-It looks this is a typical case where the measuring method is affecting the variable being measured.
The Arduino A0 and pin 13 are connected to the cap under test. Maybe in the picofarad region for the tested caps, the effect of the leakage currents involved are in the same orders of magnitude as the discharge currents through the discharge resistors. That could be contributing to the nonlinearities and errors you are getting in the results.
Possible solutions:
-Use a buffer OpAmp (as voltage follower) with BIFET high input impedance to tap the voltage on the cap and send it to A0. This will minimize loading the cap with the instrument of measurement.
-The discharge resistor should be high as to provide enough time for the readings to take place; but not too high as to be comparable with the leakage currents of the cap being measured. Also if the discharge takes too long then the voltage presented to the Arduino A/D converter may take too long to change one quantifiable step, reducing the resolution in the time measurement.
-Make sure when the measurement is done only the discharge resistor and the OpAmp input are connected to the cap. For that, you can use relays as the switching devices to avoid any leaks which will influence the measurement.
-Concentrate on having a working circuit first and then move into auto ranging.