Value Comparison...how do i code this?

Hi Everyone,

I have a question that needs programming help..

I have an Uno with a few sensors connected to it including pH and EC. I have the sensors taking readings every few seconds and displaying the data via serial port.

I need to be able to be able to compare a reading with the previous reading and determine if its within 10% of each other. This need to happen for up to 10 consecutive readings and if they are within the tolerable level then a buzzer sounds/another action takes place. If not, then the comparison continues until 10 consecutive readings, that are within the 10% tolerance of the last reading, come to fruition.

Would anyone have any idea how to code this?

Cheers

Each time you take a reading compare it with the previous reading which you will have saved in a variable. If the readings are within 10% of each other increment a counter, otherwise set the counter to zero. If the counter value reaches 10 then you have 10 readings each within 10% of the previous one.

However, note that over 10 readings the percentage change could be 100%. Is that what you want or should they all be within 10% of the initial reading in the series ?

That's a good point! I might have to reduce the percentage. Or is there a way to compare say the first reading and the tenth reading?

Are you able to provide an example sketch of what you just said? struggling to picture it...still a novice

It seems like my answers favor arrays. Here is another solution using an array. It is a combination of UKHeliBob's solution but will make sure that all values are within 10% of eachother as well.

Create an array of 10 elements. Start filling the array in a cascading manner so that the array will end up having 10 consecutive readings arranged from largest to smallest.

Between each reading, compare this reading to the previous reading. If this reading and the previous reading is more that 10%, empty the array and start over. If this reading and the previous reading is less than 10% different than the previous reading, continue storing values to the array. Once the array has 10 values, compare the first (biggest) and last (smallest) values to see if they are within 10% of eachother. If they aren't within 10% of eachother, that means that the readings were trending in some direction and should be thrown out. Empty the array and start over.

ogurki:
That's a good point! I might have to reduce the percentage. Or is there a way to compare say the first reading and the tenth reading?

I suspect your system needs more thought.

There is a difference between a system in which 9 values are each within 10% of the first value and 10 values in which the greatest difference between any two of them is 10%. If you compare everything to the first value there could be two readings (not necessarily consecutive) with a variation of 19.99% between them. Would that be acceptable?

I do agree with @ty_ger07's suggestion to start-over as soon as any test fails. But what does starting-over mean. Does it mean abandoning all the readings so far or does it just mean dropping the first value?

I suggest you write down some example values and work through the process with pencil and paper until you are satisfied that you understand all the possible quirks in the system. It will be much easier to do that before you start writing your program.

...R

@Robin2, yes, my suggestion regarding throwing all the data away if the min and max values are more than 10% apart after comparing 10 values (which are each within 10% of each reading), is a problem. It is especially a problem if it so happened that part of those values would be within 10% tolerance of a group of data which is read proceeding when that data was thrown away. You might get half of the magic values in one group and half of the magic values in another group and end up throwing them away and not finding the magic moment.

A better solution would be:

Start the program by gathering 10 values into an array of 10 elements(before doing anything else).
Then, start finding the min value in the array and the max value in the array. Compare the min and the max to see if they are 10% or less apart. If they aren’t 10% or less apart, throw out the oldest value and add in the newest value. Compare again.

Rinse and repeat. This way, you only have to compare the lowest and highest value each time (since all the other values will be within that range) and you never throw the baby out with the bathwater.

Since I am only a beginner and don’t know C very well (I am much better at simple languages like PHP), the following code could have errors I am not aware of or floating point issues, but consider it and make minor adjustments as necessary.

#include <stdio.h>

int a[10];
int i;
int max;
int min;
int start_incrementer;
int value_read;

void setup()
{
	pinMode(2, INPUT);  // or whichever pin
	a[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	i = 0;
	max = 0;
	min = 1023;
	start_incrementer = 0;
	value_read = 0;
}

void loop()
{
	// analogRead a pin or wherever you want the data to come from
	value_read = analogRead(2);
	
	// make sure at least 10 values are gathered before starting to compare the array elements
	// this makes sure you avoid reading the initial array of 0s so you don't find an artificial match at the start of the program
	if(start_incrementer < 10)
	{
		// shift oldest to the end of array and newest at the start of the array
		a[9] = a[8];
		a[8] = a[7];
		a[7] = a[6];
		a[6] = a[5];
		a[5] = a[4];
		a[4] = a[3];
		a[3] = a[2];
		a[2] = a[1];
		a[1] = a[0];
		a[0] = value_read;
		
		// add this loop to the counter
		start_incrementer++;
	}
	else
	{
		// before adding more to the array, see if we have found a match
		
		// find max value in the array
		// initialize max variable with the minimum value possible
		// in this case, 0 for an analogRead
		max = 0;
		for(i=0; i<10; i++)
		{
			if(a[i] > max)
			{
				max = a[i];
			}
		}
		
		// find min value in the array
		// initialize min variable with the max value possible
		// in this case, 1023 for an analogRead
		min = 1023;
		for(i=0; i<10; i++)
		{
			if(a[i] < min)
			{
				min = a[i];
			}
		}
		
		// are max and min within 10% of eachother?
		if(max/min < 1.10)
		{
			// do something
			// we found an instance where all values are within 10% of eachother
		}
		
		// add to the array and keep looking for more matches
		// shift oldest to the end of array and newest at the start of the array
		// the 11th oldest value naturally falls off the end
		a[9] = a[8];
		a[8] = a[7];
		a[7] = a[6];
		a[6] = a[5];
		a[5] = a[4];
		a[4] = a[3];
		a[3] = a[2];
		a[2] = a[1];
		a[1] = a[0];
		a[0] = value_read;
	}
}

I think that will do what you are after except that I don’t know your bigger vision and don’t know how you are receiving the data. If you are receiving the data at certain intervals or at an otherwise slow rate, you may need to sync the sampling of the code so that the same sample is not taken 10 times within the same 1000ths of a second and considered 10% in tolerance with itself.

The code seems to work! I have to make one minor adjustment to get it to compile and have added a few line items to identify when the readings have stabilised. The sensor takes readings every second and judging by the way the code responds it seems to be take that second reading and compare as expected :slight_smile:

Changes made include change int to char for the readings and move the char a[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; line to the top. I will add the code to this page once i figure out how too… :confused:

So another two questions for you. How could i recall the 10 readings, once stabilised, so i can log them onto an SD card?

And how would i determine the"percentage" tolerance achieved between the lowest and highest readings so i can print to screen?

Your help is muchly appreciated!

UKHeliBob:
Each time you take a reading compare it with the previous reading which you will have saved in a variable. If the readings are within 10% of each other increment a counter, otherwise set the counter to zero. If the counter value reaches 10 then you have 10 readings each within 10% of the previous one.

However, note that over 10 readings the percentage change could be 100%. Is that what you want or should they all be within 10% of the initial reading in the series ?

Actually 1.110 = 2.59, or 260% maximum change from the original reading if you are only checking if it’s within 10% of the immediate last reading.

Going downward, 0.910 = 35%.

Often you can simplify code like this:

		a[9] = a[8];
		a[8] = a[7];
		a[7] = a[6];
		a[6] = a[5];
		a[5] = a[4];
		a[4] = a[3];
		a[3] = a[2];
		a[2] = a[1];
		a[1] = a[0];
		a[0] = value_read;

by using code like this:

#define ELEMENTS(x)   (sizeof(x) / sizeof(x[0])   // Put near top of file, after any #includes
// ... more code...

    memmove(&a[1], a, (ELEMENTS(a) - 1) * (sizeof(int) );
    a[0] = value_read;

I’m wondering if a running average and comparing it to the “tolerable level” would work?