Capacitive sensing using a microcontroller, in this case an Arduino, is reasonably well documented online. There are fairly complete tutorials for how to measure how much liquid is in a vessel and touch sensing applications like buttons and even proximity detection. The kind of sensor that I needed though, a liquid level sensor to be used partially submersed in the liquid it was measuring, was difficult to find. With some understanding of what makes a capacitive sensor works and a picture of a working probe of a similar kind, I put together a simple probe that can be made with only some basic materials and have documented it here.
I started by looking at existing probes, there are touch sensors, like this one , and there are vessel measuring devices like this one , but neither will work for this application. I want to be able to measure the liquid the sensor is in, which in either of these examples, just shorts out the electrodes making a resistor instead of a capacitor. I knew from research that the essence of sensing liquid level in this way is that the capacitor which is created with two electrodes and an insulator between them, but the liquid between the plates has a dielectric value different from the air, so by changing the amount of liquid between the plates, you change the capacitance of the sensor. The trouble was isolating those plates from the liquid that would short them out while still having it be able to pass freely between them.
After researching and some thinking, I found that the plates only had to be insulated from each other, they didn't necessarily have to be insulated from their surrounding liquid. That realization made me consider this sensor design, the coaxial electrode. Basically, you have a tube with an insulated wire running inside it, then you can immerse the tube partially in liquid but still keep any current from flowing through it, since one electrode is still fully insulated. With this design in mind, I headed to the hardware store to look for parts.
Building the sensor
Since this was for an outdoor project, I wanted to be sure that it wouldn't rust, so I looked exclusively at stainless and aluminum parts for the metal components. Based on availability, I ended up going with an aluminum tube and stainless hardware. I wanted the probe to be small so it could be at least partly hidden when installed, so I went with the smallest aluminum pipe they had, 1/2". I bought a set of 3/4" 6x32 stainless bolts as well as nuts and washers to assemble everything. Then I took a length of insulated wire from my stash, trying to look for something with insulation that would hold up to the rigors of outdoor use.
The assembly was quite simple. First you cut the tube to length, for me, this was 12". Then you drill a hole in the top and bottom for the screws, straight through the center of the tube. Insert the bottom screw and fasten it in place, then take the wire, a little more than twice the length of the tube, and insert it in a U shape into the end with the screw in it, half on each side of the screw so that the U will rest on the screw if the ends are pulled from the other side. Then I twisted the wire several times and put in the upper screw, again having wires pass on each side, and used a nut and washer to secure it in place.
I pulled the wire tails from the end and tied them together, trying to keep the wire in the center of the tube taught. Between the twisting and the tieing, it should be relatively centered in the tube. Then you strip one end of wire coming out, this will be one electrode. Your other electrode is the screw terminal you made by putting the top screw in.
A quick test in a glass of water shows the changing capacitance, though a very small one. A larger pipe or a larger internal electrode (like using a small pipe inside of a large one) will make a larger value capacitor, as it's directly related to the surface area of the electrode plates, but is not required.
Connecting and programming the microcontroller
The Arduino's way of measuring capacitance involves charging it and measuring the time it takes for the voltage to rise on the other electrode – at a certain threshold it compares the current time to when it began charging. Because of this method, by using a larger and larger resistor between the power source and the measurement pin, you can slow the charging speed and lengthen the time it takes to reach the threshold. This means that while a larger capacitor from a larger probe will give you more of a change in capacitance to read – theoretically more accurate given so-so sampling hardware – but by slowing the charging speed you can get the same finer granularity in the resulting reading. The disadvantage of the larger resistor is that it charges slower (that is intentional!), which means it takes longer for the microcontroller to read the sensor, and you can make fewer readings in a given time.
I followed the circuit and capacitor/resistor values recommended in the CapacitiveSensor library documentation(found here), including the extra fixed capacitor to even out readings. That setup, using a one megaohm resistor and a 100 picofarad capacitor gave me these readings with some basic code:
Hooked up to a spare Arduino nano programmed with just a bit of code, I got a functioning level probe. You can see in the later screenshot the process of filling the pitcher being measured and then removing the probe, but you can also see its inaccuracy. The first 3 lines are with no water, yet the values vary quite a bit, the 2000 value lines are all full, again varying a bit. When I remove the sensor it is a fairly continuous fall, but then I hold it and the values are all 0 from the influence of me touching it. This was made obvious when I set it down on the stone counter top (alternating holding it and setting it down in the last three lines) and it read as if there were some liquid present. While there is some level of inconsistency, you can also see that the data is overwhelmingly useful, as you can see the clear increase with increased water level, even with the significant level of noise. With a little bit of calibration, you can get a fairly accurate measurement in your height units of choice, given a specific liquid and rigid mounting. It's worth noting that the function I used in this code, the default one CapacitiveSensor(), auto-calibrates. For my purposes, this was great for testing but useless for operation, as my probe started submerged. With the auto-calibration, it would then calibrate to the lowest level on startup, and only register changes in level. Once I realized this was the case, I switched to use the CapacitiveSensorRaw() function, which instead gives you the raw sensor reading, which is important if you want an absolute level based on your probe instead of just the difference from the lowest recorded value.
This simple probe does a good job, once I had it working I built it into my PondMaster robot project, where I used it to measure the water level in our family's pond, so that a microcontroller could turn off the pump if the level was too low. To use it outdoors, I built the capacitor and resistor onto the top of the sensor, then covered the end with liquid electrical tape and covered that with a PVC cap, secured in place with screws. This lets it operate with just power, ground, and the sensing pin at the end of a cable and out in the elements.