This library is an extension of a previous library used for capacitive sensing specifically made to measure liquid level based on capacitance. Using a very simple set up the largest measurement error was +8mm.
Hardware
The code was tested on a 2L plastic bottle that has a cylindrical shape with the top trimmed. On a side I placed two electrodes consisting of 20mm copper tape with non-conductive glue, although conductive glue is preferred. To space them evenly, I first placed a 15mm paper tape as a spacer and guide. One electrode is connected to ground and one to the sensor pin of the microcontroller using alligator wires.
In a practical implementation the sensor pin should be connected using a shielded cable with the shield grounded to prevent noise being coupled into the wire and the whole circuit placed as near as possible to the container to shorten the wiring.
To detect a change in capacitance, two pins are needed and any microcontroller pin can be used. The Signal pin is used to charge the capacitor formed by the two electrodes, through an 1M resistor. The Sensor pin will discharge the capacitor and also detect when the capacitor is considered charged or discharged. The resistor R2 is optional but recommended to protect the pin from ESD. The value can be somewhere around 100 - 150 ohm to limit the current through the internal clamping diodes.
Software
For the library to work you need to add calibration points to the dataPoints array located in levelSense.h. These are used by the linear interpolation formula:
y = y1 + (y2-y1) / (x2-x1) * (x-x1)
to calculate data points in between. The more data points the better.
Array example. Replace data.
static const uint16_t dataPoints[][2] = { {123, 0}, // empty bottle {295, 50}, {511, 96}, {699, 140}, {800, 160}, {891, 180}, {983, 200}, {1021, 210}, {1118, 230} // full bottle };
First x column (123, 295...) represents the measured capacitance values and the second y column is the liquid level in millimeters in this example. As described in the previous post capacitive sensing library the capacitive value is unitless not capacitance in Farads. To get the values in x column use the function shown in the code example.
charge_time = proxiSense_chargeValue(sensor1);
In the example code there is a 1ms delay. If you increase to better print the values in a terminal over UART, the capacitive measurement will not work. Use a serial plotter such as https://github.com/hyOzd/serialplot instead. The code prints column x,y separated by a comma so that the serial plotter knows it has to show two channels. In the serial plotter select ASCII data and not binary.
Code example
#ifndef F_CPU #warning "F_CPU not defined. Define it in project properties." #elif F_CPU != 16000000 #warning "Wrong F_CPU frequency!" #endif #include <avr/io.h> #include <stdlib.h> #include <util/delay.h> #include "uart.h" #include "proxiSense.h" #include "levelSense.h" int main(void){ uint16_t sensor1_state = 0; uint16_t level = 0; uint16_t charge_time = 0; UART_begin(&uart0, 115200, UART_ASYNC, UART_NO_PARITY, UART_8_BIT); UART_sendString(&uart0, "Starting...\n"); proxiSense_t* sensor1 = proxiSense_init(sensor_SetSignalPin, sensor1_SetSensorPin, sensor1_ReadSensorPin); while(1){ sensor1_state = proxiSense(sensor1, 4); charge_time = proxiSense_chargeValue(sensor1); level = levelSense(charge_time); UART_sendInt(&uart0, charge_time); UART_sendString(&uart0, ","); UART_sendInt(&uart0, level); UART_sendString(&uart0, "\n"); UART_isSending(&uart0, F_CPU, 115200); _delay_ms(1); } }
Pins are defined in proxiSensePins_devBoard.h method also explained in previous post.
Notes
Larger error near the full level
This is a common result of Fringe Field Effects that results in Field Bulging. At the ends of the copper tape (the top and bottom), the electric field "bulges" outward rather than passing directly through the bottle. This makes the capacitance change non-linearly as the water surface reaches the top edge.
Because non-linearity is highest at the ends, add more calibration points near the top range to "guide" the linear interpolation.
Calibration affected by temperature and liquid type
Capacitance depends on relative permittivity/dielectric constant which varies wildly between liquids. If you calibrate for water and then fill the bottle with oil, the sensor will barely see the level change. Even different concentrations of salt or sugar in water can slightly shift the reading.
Temperature can also affect calibration. As temperature rises, the density of the liquid changes, which slightly alters its dielectric constant.
Possible improvements
Active guard
The Driven Shield or Active Guard is a technique used to provide immunity to external interference, like a human hand near the electrodes. Unlike a standard passive shield which is simply connected to Ground, a Driven Shield is driven to the exact same potential (voltage) as the sensor electrode.
If the voltage on the sensor pin and the voltage on the shield pin are identical, there is zero potential difference between them. If there is no voltage difference, no capacitance exists between the sensor and the shield. The sensor "cannot see" anything behind the shield.
To drive the shield, you need a Voltage Follower (Buffer). You cannot simply connect another GPIO pin because it won't perfectly track the RC charging curve of the sensor pin.
1. Connect the Sensor Pin (PC1) to the Non-Inverting input (+) of an Op-Amp.
2. Configure the Op-Amp as a Buffer (output connected back to the - input).
3. Connect the Op-Amp output to a third copper tape layer placed behind the sensor electrodes.
How does it work
The sensor has an electric field that radiates in all directions (360 degrees). When a hand approaches from the back or side, it intercepts those field lines, increasing the capacitance.
When you add an active shield the shield blocks the electric field from radiating toward the back. Because the shield is at the same voltage as the sensor, the sensor doesn't leak any field lines to it. and the electric field is forced to project only through the bottle and into the liquid. If a hand approaches the back of the bottle (where the shield is), the hand interacts with the shield. Since the shield is driven by a low-impedance Op-Amp output, the hand's capacitance to ground is absorbed by the Op-Amp, and the high-impedance sensor pin (PC1) never feels the change.
To implement this add an insulator layer of electrical tape or plastic film over existing electrodes then the shield formed of a wider piece of copper tape that covers the back area of the two sensor electrodes. This is connected to the Op-Amp output.
Mitigating Variations with a Second Set of Electrodes
You can use a technique called Differential Measurement or Reference Compensation. This involves adding a smaller, second set of electrodes near the bottom of the bottle.
How it works:
Reference Sensor (Bottom): You place a small pair of electrodes at the very bottom that is always submerged when there is any liquid at all.
Level Sensor (Main): existing long electrodes.
If the temperature changes or the liquid type changes, the "Reference Sensor" value will shift. Since the Reference Sensor is always at 100% "full" (relative to its own small height), you use its value to calculate a scaling factor. Apply this factor to the Main Sensor.
Example Calculation:
Level_Adjusted = Value_Main / Value_Reference
By taking a ratio, the dielectric constant cancels out. If you switch from water to a different liquid, both sensors will change by the same ratio, and calculated level will remain accurate.
Links
| The link also includes the UART library useful for debugging or calibration and the proxiSense library. | |
| v1.0 | levelSense |
Changelog |
|
| v1.0 (2026-05-16) | Public release under GNU GPL v3 license. |
References |
|
|
Microchip AN1492 Capacitive Proximity Design Guide
|
|
|
Texas Instruments FDC1004 Basics of Capacitive Sensing and Applications
|
|
Resources |
|
| A series of 10 videos summing around 5 hours. The series is about liquid level sensing, and covers in-depth subjects related to capacitive sensing
such as different ways to measure capacitance and the dielectric constant. Robert's Smorgasbord - Capacitive Liquid Level Sensing |
|
| Simply Put - How A Dialectric Works and How Capacitive Proximity Sensors Work | |

No comments:
Post a Comment