Electronic – How to convert readings from a DIY capacitive liquid level sensor to a level

capacitorsensor

Background

I have made a tubular capacitive liquid level sensor (This is a professional one, for example). A 555 circuit outputs a frequency that is inversely proportional to the capacitance. This is read into the microcontroller (Arduino Nano clone).

The sensor will be used for monitoring our water tanks that supply our house.
There will be this one for the tank that gets municipal water, and another one for the tanks that collect rain water.

It is communicated with (and powered) over a Cat5 cable acting as a Modbus trunk.

The sensor consists of a DIY probe, made of aluminum and PVC pipes, and a custom board (made with stripboard).
The main components of the custom board are:

  • Arduino Nano
  • NE555
  • ISL81487LIPZ (MAX485 replacement; DigiKey)
  • DC-DC buck-converter

The problem

I have taken readings and put them into an Excel spreadsheet, along with the depth of water when they were taken. They seem to be logarithmic, but they are not. I have taken the natural logarithm and normalized the values, and plotted this versus the percentage of submersion of the sensor.

This is the graph:

graph of readings

X is percentage submerged, Y is reading. "Log Normalized" is the plot of the normalized logarithm values. "Linear" is the plot of the wanted values. "Diff" is the difference between the "Log Normalized" value and the "Linear" value for each pair of those values.

In the final application, I will have a reading, and will need to find the depth of the water the sensor is in.

Summed up,
I need a way to translate a reading to a level.

I need it it to be

  1. easy to implement (I am not a master mathematician 😉 )
  2. work with 32-bit floating point numbers
  3. fast

The reason it needs to be fast is because of other compensation algorithms that will need to be added, e.g. when the water level drops, the reading doesn't change immediately, it changes as the probe dries. These algorithms will need to call the level calculating one quite often.

What I Am Using at the Moment

I am currently using the formula described in my answer.
It gives acceptable accuracy (for the purpose of the sensor), is simple, is fast, and just "feels right".

Other Stuff

The spreadsheet is here. Note: I am still actively using it. There are formulas in place (which my answer here is based on), and some other experiments off to the side.

Images of the Fritzing Breadboard, the electronics, and are here.
The Fritzing file is here.
(I couldn't get Fritzing to comply with editing the schematic. It crashed whenever I moved more than one component at a time, or any large component.)

Cross-posted on math.stackexchange.com.

Best Answer

You can use a cubic in 1000/reading:

static const float coeff[4]= { 1.62914270704482, -7.9851592788591,  38.5529790479781, -23.7806334509969}; 

x =  1000.0/(float) reading; 

//  Evaluate using Horner's Rule 
result = coeff[0];
for (int i=1; i<4; i++)  
    result = result*x + coeff[i]; 

I see a maximum error of +0.41/-0.58% of span at the given points and evaluation takes 1 divide and 3 multiplications so it should be pretty fast even on an 8-bit micro. Coefficients were optimized to minimize sum of squares of errors rather than worst absolute error.

Interested to see the comparison with your transcendental functions cos/log.