Assigning percentage values to nonlinear analog input

I am attempting to assign a percentage value to a raw analog input that is nonlinear over its range.
But I am thinking there may not be a straightforward solution.

When the sensor is at the bottom end of its scale it reads 190 and at the top end 575
for a total count range of 385.
The sensor detects changes in water level on a 10 trace grid; each grid is 1 1/2" in length. When it barely contacts water at the bottom of the grid it starts reading around 180-190 counts.

Water 1st Detected: 180-190 [0.0"]
1/4 range : 450
1/2 : 515
3/4 : 550
Full range: 575 [1.5"]

I would like to convert the counts to a percentage value to represent how full the container is. But because the output is nonlinear I cannot simply assign X counts = X percent.
I looked at the map() function but I do not think it compensates for nonlinearity.

Otherwise I am left to try and create some sort of table that references a specific percentage (%) value based on the input count value.

ideas?

Right. map() is linear.

If you don't need more than 1% resolution a "table" should work. (Or a series of 100 if-statements).

Or you might try approximating the curve with 10 straight-lines and 10 associated map() calculations.

I was trying to avoid the if statement route and use as a last resort.
Is there any good links that would cover populating a table and then open the table and find a search result?

Try - GitHub - RobTillaart/MultiMap: Arduino library for fast non-linear mapping or interpolation of values

You had an excellent link there. And you being the creator
makes it even more appropo.... :+1: :+1: :+1:

Is the pot nonlinear, or is the tank nonlinear, or is it a bit of both?

A table with interpolation between the points. If you wanted to play with the STL:

#include "Arduino.h"
#include <map>

double interpolate(uint16_t count);

void setup() {
	Serial.begin(115200);
	delay(1000);

	for (uint16_t cnt = 175; cnt <= 600; cnt += 25) {
		Serial.printf("%hu, %lf\n", cnt, interpolate(cnt));
	}
}

void loop() {
}

double interpolate(uint16_t count) {
	static std::map<const uint16_t, const double> sensorMap {
			{ 190, 0.0 },
			{ 450, 0.25 },
			{ 515, 0.5 },
			{ 550, 0.75 },
			{ 575, 1.0 }
	};

	auto lowerItr = sensorMap.lower_bound(count);
	if (lowerItr == sensorMap.end()) {
		return (*(--lowerItr)).second;
	}
	if (lowerItr == sensorMap.begin()) {
		return (*lowerItr).second;
	}
	if ((*lowerItr).first == count) {
		return (*lowerItr).second;
	}

	uint16_t countUpper = (*lowerItr).first;
	double levelUpper = (*lowerItr--).second;
	uint16_t countLower = (*lowerItr).first;
	double levelLower = (*lowerItr--).second;
	double levelRange = levelUpper - levelLower;
	uint16_t countRange = countUpper - countLower;

	return levelLower + (count - countLower) * levelRange / countRange;
}
1 Like

its actually a sensor....

DIYables Water Sensor

Another interesting approach you have generously provided!
Between you and Rob I have plenty to study and experiment with.
Thank you sir.... :ok_hand:

What is required to run the map() function? I figured it was in the standard library
with all the other functions.

C:\Users\Ed\AppData\Local\Temp\.arduinoIDE-unsaved2024431-4120-rhlqsi.fxby\sketch_may31b\sketch_may31b.ino:2:10: fatal error: map: No such file or directory
   // put your setup code here, to run once:
          ^~~~~
compilation terminated.
exit status 1

Compilation error: map: No such file or directory

What board are you compiling for?

UNO WiFi Rev2

Ahhh ... that's the problem. The STL (including Containers like map) is not available on AVR-based boards.

Rut row.... that sucks....

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.