This blog post is a tutorial making a capacitance meter using the C-meter click onboard one Arduino Uno click shield from MikroElektronika and one Arduino Uno (or compatible) board. Of course, considering the simple design of the C-meter click, one can follow this tutorial and build the capacitance meter from scratch. This simple project can be used to display only the capacitance; it doesn’t measure other parameters such as leakage, equivalent series resistance (ESR), and inductance.
A quick look at the C-meter click schematic reveals an astable oscillator built with the popular NE555:
The schematic is the same as the astable configuration on page 11 of the NE555 datasheet, with some minor changes imposed by the mikroBUS standard. The board is designed to work from 5V power, with the possibility to change the output logic levels via an SMD jumper, thus making the board usable also with 3.3V logic.
Also, one must observe the presence of a 470pF capacitor permanently connected. Thus, considering Cx the capacitor we desire to measure, we will have:
The capacitor will be charging and discharging between the threshold-voltage level (≈0.67 × VCC) and the trigger-voltage level (≈ 0.33 × VCC). Charge and discharge times (and, therefore, the frequency and duty cycle) are independent of the supply voltage.
The output high period Th is given by the formula:
The low period Tl is given by the formula:
The period of the generated signal is:
where the period T is in seconds, and the capacity Cx is in Farads.
Well, how do we match this function with our click board design? First, let’s see what happens when we leave only the 470pF capacitor:
This is well within the range of the pulseIn() function so that we can measure it. The total period will be:
Which corresponds to a frequency of
With my frequency meter I’ve measured 23.926kHz, a bit off the theoretical value, but easily explained by the influence of tolerances of various parts.
Measuring capacity with Arduino Uno
Lucky us, in Arduino IDE we have a dedicated function to measure time intervals, pulseIn(); which
Reads a pulse (either
LOW) on a pin. For example, if
pulseIn()waits for the pin to go from
HIGH, starts timing, then waits for the pin to go
LOWand stops timing. Returns the length of the pulse in microseconds or gives up and returns 0 if no complete pulse was received within the timeout.
The value returned by this function is an unsigned long. That is a range of 0 to 4,294,967,295. Be warned; the Arduino website states that the timing of this function has been determined empirically and there is a high chance of errors in longer pulses. The pulseIn() function works on pulses from 10 microseconds to 3 minutes in length.
Totally within the range measurable with an Arduino. The code used to measure capacitance and frequency is listed below:
With the above code, I’ve measured periods ranging from 40.5μs to 43μs, and frequencies ranging from 23255 kHz to 24691kHz.
If we go to compute the capacity, it will range from 520pF to 552pF,
Note the high error margin at the value of Cx = 470pF, mainly due to the quantization error of the pulseIn function. From the values measured with the frequency meter, we can compute a capacity value of 537nF. That is an error of ±3% at this range.
What about the maximum range?
Yep, which is the largest capacity value that can be measured? Back to some calculations…
We know that pulseIn() can return a maximum value of 4,294,967,295. We also know that the high period Th is greater than the low period Tl, and as such is likely to overflow first.
So, we revisit the formula:
and we have:
However, the Arduino site states that the pulseIn() function works fine for up to three minutes – that’s 180 seconds. So, the above calculation becomes a bit more restrictive:
Wait! It doesn’t work!
Surprize, surprise. For values over 3.3μF, it doesn’t work. Why? WHY???
It seems that the problem lies within the pulseIn() function, and the problem is at least a few years old, considering this post on the Arduino forum. Specifically, the pulseIn() function times out after about 170ms, and doesn’t wait for a full period to complete.
There are two possible workarounds: one possibility is to increase the timeout; the second possibility is to revert to the pulseInLong() function for larger periods of time, also passing a large timeout value to this function. As a side note, pulseInLong() relies on using millis(), and is a bit less precise.
I decided to use both methods, and I finally came with this code:
With the above code, I’ve measured capacitors up to 1000μF, with a precision of 3%.