For the next spring I plan to make a fully automatic hydroponic setup. While spring is over 6 months away from now (it is pretty much autumn right now), there is still plenty to do. One of the things I need for automation is a water level sensor, so the system knows how much water is in the system (used for dosing the additives and warning me about low water level). This sensor can be used for anything that has a large height of liquid to be measured, most notably the water level in hydro- and aquaponics setups and ponds. It is not accurate to a mm, but it can tell the height within 1-2cm, more than enough for most applications.

There are many ways of measuring a level, including but not limited to: mechanical (with floats), optical, electrical (resistive), acoustically (ultrasonic) and electromechanical (pressure). The way I picked for my sensor is a Capacitive. It uses the different dielectric properties of various liquids and air to measure the level.

The advantage of capacitive level sensors is that they can be used for basically any solid and liquid. They have no moving parts and scales incredibly easy. They make no contact to the liquid or solid being measured, and so can also be used for more corrosive liquids. The disadvantage is that they need to be calibrated for the liquid being measured and that they can become weak when very large (though even at several meters this is not a problem).

The whole sensor can easily be built in a weekend if you have the materials laying around. It is made with stuff that technically isn't food safe, but it is aquarium and pond safe (silicone and PVC, both used for ponds). Initially I was going to seal the tubes with epoxy, but that can be rather toxic if not used correctly. The sensor is technically waterproof and completely weatherproof.

The sensor uses a barebone Arduino uno, but can also be controlled by any other uno like controller (when you make the electronics compartment larger). The sensor is controlled with I2C through another microcontroller that is I2C compatible.

Step 1: How It Works

To understand this sensor you will need to understand capacitors. As always wikipedia is the best source for this information: http://en.wikipedia.org/wiki/Capacitor.

The TL;DR version is this. A capacitor has 2 conducting plates (Electrodes). When a charge is applied to these plates, the space in between the plates will also get a charge. The charge that can be between these plates depends on the material in between the plates (the Dielectric). The ability of a material to be charged is called relative permittivity. Vacuum has by definition 1, air is for all intents and purposes 1 and water at room temperature has 80. It is this difference that will be measured with the capacitive level sensor.

The capacitive level sensor has the 2 conducting plates in the form of 2 electrically isolated aluminium tubes, a smaller tube in a larger tube. The space between the tubes is the dielectric. When the tube is empty, the space is occupied by air. when the tube starts to fill, more and more of the space will be occupied by water. Water holds more charge than air and thus the capacitance will rise (mostly) linearly with the water level.

Determining capacitance is done by charging the capacitor and looking at how long it takes. The sensor doesn't bother with exact values, it just looks at the time it took and compares that to minimum and maximum values.

Sounds simple enough, now lets build one.

Step 2: What Do You Need

To make the level sensor, you will need several materials and tools. I will tell what I used, but the exact material really don't matter for the design. Also the exact lengths change with the length of the tube. The longest tube is the aluminium tube, all others are as long or slightly shorter. The caps I used are Dutch PVC fittings. I do not know how this should be built in the USA, so imagination is handy here.


  • 40mm x 3.2mm PVC tube
  • 32mm x 1.6mm PP tube (fits with a 1mm gap into the 40mm pvc)
  • 5/8" electrical PVC tube (fits around the aluminium tube)
  • 8x1mm aluminium tube
  • 32mm PVC endcap
  • 32mm PVC coupling
  • 32mm PVC screwcap
  • 32mm plug
  • electrical tape
  • Aluminium tape
  • Silicone caulk
  • Wire
  • 4 pole wire
  • Tie rips
  • A straw
  • 1 barebones Arduino uno (or any other mini arduino)
  • 3 18Mohm resistors (or comparable high resistance resistors)
  • Another Arduino to check and calibrate the sensor (and an Uno to program the barebone)
  • 2 4k7ohm resistors for the I2C


  • A caulk gun
  • A cordless drill
  • A hacksaw
  • Files
  • Pliers
  • Wire cutters
  • Soldering equipment

Step 3: Cutting the Tubes to Length

All tubes need to be cut to length. Exact lengths are not relevant here, just relative lengths. The tube around which these measurements are made is the 32mm tube.

The 40mm tube needs to be cut about 5cm shorter than the 32mm tube. This is so that when the coupler and the endcap are placed, none of the 32mm tube is visible.

The 5/8"electrical PVC tube needs to be a tiny bit longer than the 32mm tube. It needs to go through the cap placed in the top.

The aluminium tube needs to be around a cm longer than the electrical PVC tube. This is so that it is easy to seal the tubes and make a connection with the aluminium tube.

Step 4: Wrapping the Outer Tube

The outer electrode is made by wrapping aluminium tape around the 32mm tube. Using a solid aluminium tube would have been possible, but it was impossible to find one that could easily fit in PVC tubes.

Spiral the tape around the tube, periodically checking if the the overlap doesn't become too big. At the end, put a 20cm piece of wire with 10cm stripped under the tape. This wire will be connected to the microcontroller later. If the entire tube is wrapped, cut the aluminium tape 1cm above the end cap and the coupling, careful not to cut the wire.

Step 5: Assembling the Inner Tube

For the inner tube the aluminium tube and the smallest PVC tube need to be merged, First use a piece of aluminium tape to tape a 10cm wire with 4cm stripped to the tube. Then use electrical tape to create a spacer for the small aluminium tube so it sits in the centre of the PVC tube. If you cut all tubes right, the aluminium tube should be a few centimetres longer than the PVC tube.

Step 6: Assembling and Sealing the Tube

With all of the tubes prepared, the whole sensor can be assembled. I apologize for the lack of pictures at this step, but my hands had silicone caulk on them most of the time, so I didn't pick up the camera as much as I should have. To compensate for this, a made a few renders with sections of how the sensor is supposed to be.

Initially I was going to seal the tubes with Epoxy. Epoxy would have made a stronger and more guaranteed seal, but is also toxic (Bisphenol A, has funny side effects) if not used properly. Rather than risking it, I went with the safer silicone.

First put a generous dot of silicone in the endcap. Also put a small dab of silicone in the bottom of the inner tube (the side with no wire). Then push the bottom of the inner tube in the centre of the endcap. Push the wrapped 32mm tube in the endcap.

It might be wise to cut a small notch in the 40mm tube, to make space for the wire to pass the coupler. Put silicone caulk all the way around the endcap, then place the 40mm tube with the notch facing up around the 32mm tube.

Cut the 32mm plug so it fits in the coupler and cut a hole big enough for the 5/8" PVC to fit through. Put the plug in the endcap and test fit to make sure the 5/8" PVC goes through the plug. Then use silicone caulk anywhere where no water should be going (which is basically everywhere) and push the coupler over the 32mm tube. The glue the screwcap in the coupler.

Let the tube dry for a few hours before you continue.

Step 7: Drill Holes

When the tube is dry enough to handle, the holes can be drilled. It is absolutely critical here that all the holes cut do not penetrate the centre tubes. Also do not drill anywhere where the outer tube was wrapped. None of the metal parts should ever make contact with the fluid being measured.It is safest to drill in the coupler and endcap.

First the hole was drilled for the wire of the outer electrode. This wire was then pushed through the hole to the watertight inside of the sensor, where later the microcontroller will be housed. Simply use a drill that is the size of the wire to drill a hole through the coupler and inside of the screwcap holder. This hole will later be sealed with silicone.

All holes fluid holes were made with a 4.8mm drill, but this was only because this was the size of the straws. At the top, 2 holes were drilled through the coupler under the red cap. These holes allow air to escape when the water level in the tube is rising or falling.

At the bottom 2 holes were drilled in the endcap. These holes are between the 5/8" inner tube and the 32mm outer tube. Short pieces of straw (3-4cm) were pushed through the holes because the silicone was still wet and I feared the holes might collapse.

Last the 5mm hole for the output wire was drilled.

Step 8: Output Wire

The output wire is a 4 pin wire that will connect the sensor through I2C to the main controller that will need the value. To get the wire in place, I used 2 tie rips. I ran the wire through the hole and tightened the first tie rip around the wire. Then I pulled the wire back until the tie rip hit the tube and I fastened another tie rip around the wire on the outside. Then I used silicone caulk to seal both the output wire and the outer electrode wire.

Step 9: Testing It

Before I added a microcontroller, I wanted to make sure the sensor actually worked. I used my trusty LC-meter to measure the capacitance of the sensor when it is empty. The value I got back is 44.6pF. This is incredibly low, but it is a measurable capacitance. Next I put the sensor in the pond to fill it up. When submerged around 50%, it had a capacitance of around 69.4pF. The sensor works.

Step 10: Adding the Brain

Barebone arduino

Now it is time to add the brain of the sensor, the microcontroller. The microcontroller needs to be as close as possible to the sensor, so it is mounted right above it. At this point I do have to admit that I slightly misjudged the available space in the top of the sensor. Initially I wanted to use an Arduino nano, but that one didn't fit. Then I tried a Trinket 5V, but that lacked some key internal hardware so it couldn't measure the capacitance. I quickly ran out of options.

I went to the last thing I had left in stock that I knew would work, a barebone Arduino Uno. It is basically an Arduino uno, but without anything that isn't absolutely critical, like a USB to serial, a PCB and power circuitry. Programming it is a bit more of a hassle, but anyone using this instructable would probably only have to program it once. Click here for more information on the barebone Arduino. ->(Also here)<-

Now for the last issue, please don't yell at me for using I2C, it is the simplest 2 way protocol I could use and for the cable length I have, it will do just fine. In the ideal world I would have rather used a RS485 protocol as well, but it require additional hardware, which I didn't want to add. I2C does work up to 4-5 meters without any additional electronics, and I had no problems with this sensor.

With that information and apology out of the way, lets go onward to success.

Measuring capacitance

To measure capacitance, we will use the CapacitiveSensor library on Arduino (http://playground.arduino.cc/Main/CapacitiveSensor?from=Main.CapSense). Using it the way it was intended would be pretty useless and leaves an inaccurate value, but the library does have one property that is really useful for us. It takes a measurable amount of time to measure the capacitance value and by running dozens (or 100) of samples and measuring how long it took, we can get a clear, consistent measurement of the capacitance. My sensor runs 100 samples for each measurement and it takes roughly 400ms to preform all those cycles when the tube is empty. When the tube fills this value rises to 690ms for 800mm.

Step 11: Soldering Electronics

A barebone Arduino needs to be soldered. I decided to dead bug mine, to save space. The complete circuit was made following the schematic, so I won't really bother with explaining pin numbers, but I followed these steps:

  1. Solder 3 18Mohm resistors in series to make 1 54Mohm resistor;
  2. Use tape to isolate the 54Mohm resistor;
  3. Solder the crystal to the Xtal pins of the arduino;
  4. Solder the 2 capacitors to the ground next to the Xtal pins;
  5. Solder the 10k resistor from the 5V to the reset;
  6. Solder all 5V lines together;
  7. Solder the 2 ground pins together;
  8. Solder wires to the I2C lines (SDA and SCL, A4 and A5);
  9. Solder I2C power wires to the 5V and ground;
  10. Solder a programming connector to the 5V, ground, reset, Rx and Tx;
  11. Solder the 54Mohm resistor in between the measurement pins;
  12. Solder a wire to one of the measurement pins and solder another wire to the ground (capacitor wires);
  13. Tin the wires coming from the tube;
  14. Solder the I2C and sensor measurement wires to the barebone Arduino;
  15. When all is tested, stuff the Arduino in the tube and test again.


Polarity seems to matter when connecting the tube to the Arduino. I found that when I connect the inner tube to the ground and start measuring on the outer tube, I get NO readable value back. The inner tube needs to be the measured tube and the outer tube needs to be hooked up to ground.

Step 12: Firmware

Programming the barebone is pretty simple. All you need is an Arduino Uno with the microcontroller removed (it will see the barebone as the controller).



I soldered a header to the 5V, ground, reset, rx and tx, so I can connect my sensor's Arduino to an Arduino uno that has the Atmega chip removed. This way I can program the Arduino the way I would with any other Arduino.

You will also need to install the capacitiveSensor library to the Arduino IDE: http://playground.arduino.cc/Main/CapacitiveSensor?from=Main.CapSense

Uploading the firmware

The firmware can be downloaded in the attachments. The CLS_Sensor_firmware is the firmware that needs to be uploaded to the capacitive level sensor. The CLS_sensor_reader firmware can be uploaded to another Arduino and used to communicate with the sensor. The reader Arduino needs to be connected to the sensor through I2C, with 2 4k7ohm pull-up resistors. In the next step all commands that can be used will be explained. You can also use snippets of code from the reader to add to your own code, so you can easily set and read the sensor without having to write the functions yourself.

How the code works

The sensor works fairly straight forward. Every N seconds it notes the time on the millis() timer. Then it starts 100 CapacitiveSensor cycles. Then it measures the time again and subtracts the start time from the end time. This is the raw value by which the sensor compares the fluid level. The sensor also makes a 0-255 value and a distance in mm from the raw value, by comparing it to the set minimum and maximum value.

Values are stored in EEPROM to make them non-volatile. When the sensor powers up, it will read the values from the EEPROM and start measuring.

Also the sensor has an I2C line to communicate with a master microcontroller. The I2C address of the sensor is 42. The master communicates with the sensor the following way.

//master request example
Wire.beginTransmission(42); //open communication
Wire.write(30); //tell the sensor what to do (30 = send raw value)
//additional writes can happen in case of 60.
Wire.endTransmission(); //end the transmission
Wire.requestFrom(42,2); //request 2 bytes from the sensor
if(Wire.available())    // if two bytes were received
  readValue[0] = Wire.read(); //read the first byte
  readValue[1] = Wire.read(); //read the second byte

The commands that can be sent to the sensor are:

  • 10: send 0-255 level value (returns 1 byte);
  • 20: send distance in mm (returns 2 bytes, first the high byte, then the low byte);
  • 30: send raw measure time (returns 2 bytes, first the high byte, then the low byte);

  • 40: set sensor 0% value (nothing additional happens);
  • 50: set sensor 100% value (nothing additional happens);
  • 60: set sensor length in the unit you want (2 additional bytes are expected to be sent that specify the length of the sensor your chosen unit)
  • 70: read the set lower limit raw value (returns 2 bytes (high byte first) with the set lower raw value);
  • 80: read the set upper limit raw value (returns 2 bytes (high byte first) with the set upper raw value);

  • 90: read the set sensor length (returns 2 bytes (high byte first) with the set length in the unit you picked)

An important bug in Wire.h

The wire library is has a curious bug in it when 2 Arduino's are communicating. When the master has sent a requestFrom to the slave, the slave can not send back this way:

byte sendValue1;
byte sendValue2;

Instead, it needs to send back the value this way:

byte sendValue[2];

If not done this way, the master will only see the last byte sent, and all others are ignored. It might seem simple, but this issue here cost me 3 days to figure out.

!!!3 DAYS!!!

(If it were possible I would also use capital 3).

Step 13: How to Use It

The first step is to mount your sensor in a place where you want to use it. I do not have the set up I want to use the sensor in yet, so I will be using a tube filled with water to calibrate and test my sensor.There are 3 things that need to be set up before the sensor can be used. These 3 steps will let the sensor know what values correspond to what values. These values are stored in the EEPROM memory, so even when the sensor is powered off, the values will be retained:

Step 1:
The measuring range of the sensor needs to be set. This is so that the sensor can return the height of the fluid in millimeters (or any other value you want to use really, it is a unit-less value). This can be done through I2C by doing this:

word setValueMM = 800;
byte tempSendValue[3] = {60, highByte(setValueMM), lowByte(setValueMM)};
Wire.write(tempSendValue, 3);

Or through the Arduino with the sensor reader firmware and the serial command: 'S'. You will have to modify the value setValueMM in the firmware of the sensor reader to the correct length.

Step 2:

Set the lower limit of the sensor. Fill the tube to the level you want to be registered as 0. This can be completely empty, this can be halfway. When the sensor is filled with the level you want to set as 0, use the following command:


Or through the Arduino with the sensor reader firmware and the serial command: 'L'.

Step 3:

Step 3 looks a lot like step 2, just have the level of the sensor to the value you want to have registered as full, 255 in case of a read 0-255, or the length you have specified in step 1. When the tube is at the full level, use this command in Arduino to set the upper limit.


Or through the Arduino with the sensor reader firmware and the serial command: 'H'.

Using it

The sensor is now ready for use. To measure it using the sensor reader Arduino, open the serial window. Here you can talk to the reader Arduino, which will in term talk to the sensor. There are 9 commands you can use, they are case sensitive:

  • 'L': set sensor lower value;
  • 'H': set sensor upper value;
  • 'S': set sensor tube length (value specified in firmware);
  • 'l': read set sensor lower value;
  • 'h': read set sensor upper value;
  • 'm': read set sensor tube length;
  • 'R': read current sensor raw value (is in ms);
  • 'P': read the sensor 0-255 value;
  • 'M': read the sensor level value (in the units you specified);

Look in the firmware of the reader for the snippets responsible for getting and setting the values. You can use these snippets of code in your own Arduino projects (or really any other microcontroller) to control the sensor.

Step 14: Future Improvements

  • Move the tubes closer together and have less air in the sensor. Currently the inner tube has a thick layer of air between it and the inner insulator. This air drastically reduces the capacitance of the sensor. By choosing either a bigger inner electrode or a smaller inner insulator, The gap can be drastically reduced.
  • Better microcontroller (and more space). Because I misjudges the space available for the controller, I ended up wasting a lot of time getting a working controller. Soldering a barebone arduino cost me a lot of extra time. If I were to design another one, I would make a bigger more accesible microcontroller space that can hold a decent controller.
  • No more I2C. I2C was the only communication protocol I had on the trinket, but it is designed for use on a circuitboard, not for use in cable separated components. This means that it can only bridge a very limited distance, if no other cables attached, 3-5 meters. RS485 would have been better, but the trinket doesn't support this.
  • Get the electrodes closer to each other. Currently, there is quite a big gap between the electrodes. This is very inefficient. A smaller gap makes a capacitor that can hold a larger charge. The gap between the tubes only needs to be big enough to not clog, there are no additional requirements to the gap.
<p>A couple of questions. Does the inner conducting plate need to be a tube or can it be a rod? Does the diameter of tube matter? Could this be scaled down so it is significantly thinner/smaller?</p>
<p>The distance between the outer and the inner tube is what matters. This needs to be as thin as possible. The relative diameters do not matter.</p>
<p>I need capacitive fluid level sensor for 1m and it construction specification multilayer tube cross section with Al . Please give some guidance for it</p>
I do not really understand the question. The way I built my tube can be scaled indefinitely.
<p>I am still Novice in Ardunio, So please Explain the Pins to be directly Connected to the Arduino Uno,</p><p>Making Portable is Different Issue.</p>
<p>The Uno has several pins that match with the schematic in step 10. These are the pins that you can connect to the Uno. These pins are A5, A4, D7 and D8. Also you will need to connect the 5V and the ground. The crystal and programming header are on a normal uno, so you won't need to add those. </p>
<p>Its a request.....!!!!!</p>
<p>Its a request.....!!!!!</p>
Could you please explain the pin inputs I don't intend to use the barebone arduino.
<p>The schematic is the same for the barebone as it is for the UNO or any other 328 based Arduino.</p>
<p>Interesting idea, but capacitance level measurement are extremely sensitive to temperature variations. I suspect that if you put this thing in a bucket of water outside and let it go for a few days where you have moderate temperature swings between daytime and nighttime, you'll see significant drift in your level data corresponding to the ambient temperature change. Commercial capacitance rods take temperature into account with complicated algorithms that include compensation for both water temperature and air temperature. </p>
<p>I won't deny this one, the temperature should influence the readings. I will get external temperature readings from the water and there is a fairly big thermal mass, but experience will have to tell if the temperature will have destructive effects on the readings and if I have to revisit this one later with an integrated temperature sensor.</p>
<p>Very interesting. Have been playing with self-build capacitors as well to measure soil humidity.<br>I was just wondering if it isn't a bit of overkill (no matter how gorgeous it looks) for something that could be done with two strips of copper (isolated in e.g. plastic) placed at either end of the pond.<br>Like you, I am working on a hydroponics set up. As a reservoir I have a 60 liter reservoir (normally used for mixing cement) and as a test set-up I have alu foil glued, at the outside at either end. I use that as the 'C' in a 74HC14 oscillator and reading clear differences between full-half, full and empty.<br>I have tried the CapacitiveSensor library for the Arduino, but was pretty unhappy with that, so I just resorted to a before mentioned oscillator and then use PulseIn to measure the frequency. (a 555 would work as well)<br>Like you I am not really interested in the true capacity, but rather in differences and till now I am quite happy with the results</p>
<p>Metal in contact with water will corrode, spoiling the readings.</p>
<p>yes I know, but i can't really place your statement as I talk about isolated electrodes that are obviously not in contact with water then.<br>The main reason that I go for capacitive sensing is to be able to avoid contact with water and thus not have my electrodes corroded. Otherwise I would just stick to having two metal probes in the soil or tank.<br>Having said that.... if one just want to know if a reservoir is full or empty, metal needs a whole bunch of corroding for it not to measure that difference anymore, but if one is interested in an exact reading, e.g. to know how full a tank is or how moist a soil, yes, then the readings get spoiled</p>
<p>My mistake, I missed where you said they are &quot;isolated ... in plastic.&quot;</p><p>Yes, this works great for a variety of fluids. The calibration will change, but it isn't like you are going to fill a container with oil and then mix water in it.</p>
<p>we all make mistakes :-) It takes a great man to admit it :-) Have a nice day :-)</p>
<p>Depends on the system. With a 2 electrode system (like mine) no electrode ever touches any water, so no problem exists. With a 1 electrode system (used for conductive fluids) the 1 electrode needs to be isolated from the water (i.e. be dry) and the other electrode is the water. All you need to do the is make sure the water is grounded, by having a metal probe in the water. </p><p>This probe does not have any current flowing and thus corrosion will not spoil the reading. As long as it still has electrical contact with the water, it will work fine. For health and longevity reasons I would still pick a stable metal such as stainless, but it is not critical for the accuracy.</p>
<p>Perhaps it is a bit of overkill. There is indeed quite a bit of material that could have been not there. The reason why I did it anyway is because I like self contained systems protected from the environment. For a hydroponics systems I could get away with 2 metal strips just fine, but by doing it like this I created something more universal and weatherproof. In the process I used more stuff, but the sensor can be used in almost any situation. And you have said it yourself, it does look gorgeous.</p><p>I spent quite so time messing with the measuring of capacitance, but I had a deadline and didn't have everything I needed in stock. What I have now on the sensor actually works great. The capacitive sensor library is indeed a bit quirky, but I didn't use it the right way and if you simply measure time, it actually works remarkably great. That being said, there are likely a lot of better ways. Mine is just good enough and I stopped looking.</p>
<p>indeed. please don't get me wrong: I am in awe about what you made and i would be inclined to make it just for its looks.<br>Having said that, you have given me inspiration and ideas of making a slightly different capacitor that -contrary to my experimental setup- is mobile.<br>But your capacitor is hardcore!!</p>
<p>Voted up, because this is exactly the method I've used for years for fluid level measurement. Albeit with 555 timers, but the same principle.</p>
love the simplicity of this idea, and you have been very thorough in your instructable. favd it!
<p>Heat-shrink tubing around the inner electrode would eliminate the airspace, but heat-shrink can be expensive in the size you'd need. Maybe an insulating coating, such as epoxy paint. If you're worried about toxicity, there are food-grade epoxies. You could also use an outer electrode in contact with the liquid. Copper pipe should work. With a conductive liquid, that would reduce the dielectric thickness to the thickness of the inner insulation. </p>
<p>I picked materials that were cheap and available, but all of those ideas will work well (maybe except for the heat-shrink). The reason why I wanted to have a 2 electrode capacitive level sensor was that it is more universal. For the 1 tube systems you need a conductive liquid, but 2 tube systems are capable of measuring everything.</p><p>Also to measure with 1 tube, you need to make electrical contact with the fluid. Something I preferred not to do with a possible fish environment. I couldn't find food safe epoxy myself, only food safe silicone, and both would have needed to be ordered.</p><p>Industry often uses 2 anodized aluminium tubes, but anodizing is spendy for people like me.</p><p>These are all the reasons that moved me to using the materials I used. The materials suggested will all work great, just not as good within the requirement I set for myself.</p>
vg, I will make it
<p>Hi - very interesting sensor and good writeup. Thanks for putting this up!</p>
<p>Very good instructable and excellent idea! Thank you for sharing this.</p>
<p>Very interesting design, I can't wait to see the hydroponic set up! Thanks for sharing!</p>

About This Instructable




More by dragonator:Solar System Orrery (3D printed) Heat exchangers and 3D printing Hand In Hand Skeleton Clock 
Add instructable to: