Digital filter problem

Hello.

So I started to play a little with Arduino. Now I’m working on simple thermometer for my friend.
I will use thermistor and ADC for measuring the temp, but I want to filter the ADC reading to avoid the “flickering”.
From school I have one digital filter. The filter worked fine under PIC, FreeScale and even Atmel Studio, but not with the arduino.
Here is the code:

unsigned long filter(unsigned int input, unsigned long feedback, unsigned int speed)
{
	unsigned long TEMP = ((unsigned long)(input))<<16;

	if(input>=feedback)
	{
		TEMP=input-feedback;
		TEMP=(TEMP>>speed)+feedback;		
	}
	else
	{
		TEMP=feedback-input;
		TEMP=feedback-(TEMP>>speed);
	}

	return(TEMP);
}

The usage is e.g. :

temperature = filter(analogRead(0), temperature, 3)

With Arduino the value of temperature is lower then it should be and it change with the parameter speed.
Any tips?
Thank you :slight_smile:

Anyone?
I'm not sure, but maybe is problem in the data types...

It would be useful to see your code, your inputs and outputs.

I have a temperature sensor that (upon first testing) produced wildly erratic results. Readings would bounce around over a range of about 20 degrees.

The solution I came up with was to take 100 sample readings. Arrange these in order of magnitude and then discard all but the 10 values in the middle. I then take an average of those 10. It works a treat, I now get a rock steady output and yet it still responds immediately to any temperature range change.

Even though this sounds like overkill, the whole task only takes a tad over 10 microseconds. Since I only update the display once a second, this is an ideal solution for my project.

Nothing special…

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

unsigned long filter(unsigned int input, unsigned long feedback, unsigned int speed)
{
	unsigned long TEMP = ((unsigned long)(input))<<16;

	if(input>=feedback)
	{
		TEMP=input-feedback;
		TEMP=(TEMP>>speed)+feedback;		
	}
	else
	{
		TEMP=feedback-input;
		TEMP=feedback-(TEMP>>speed);
	}

	return(TEMP);
}

void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  lcd.print("Teplota1:");
  lcd.setCursor(14,0);
  lcd.print((char)223);
  lcd.setCursor(15,0);
  lcd.print("C");  
}

void loop() {
  
int teplota1_adc = 0;
float teplota1 = 0.0;

teplota1_adc = filter(analogRead(1),  teplota1_adc, 3);
 teplota1 = (float)teplota1_adc*5/1024;
  
  lcd.setCursor(9,0);
  lcd.print(teplota1);  
  }

The sensor is not problem (I don’t have it yet, so I use potentiometer for testing :D)
That’s why I also have not calculation to real values.
But I should be able to get output 0.00-5.00 like without the filter (directly from adc)
But with the filter I get 0.00-0.62 with speed 3, 0.00-1,25 with speed 2…
I use Arduino nano and LCD keypad shield

kenF: For now I also solved it by averaging 10 samples each 50ms (so I should get temperature change in about 0,5s), but I would like to know, why the filter doesn’t work in Arduino, otherwise it worked fine in 3 others programs :slight_smile:

Without compiling, but instead, just stepping though your filter with some example figures I think it’s a bit glytchy.

for example, if you have a temperature of 50 degrees and then the sensor says it’s 51 degrees, using a speed of 3 the filter will simply ignore this new value. no matter how many times the sensor tells you that it is now 51 degrees.

Follow my workings below. Did I mess up somewhere?

unsigned long filter(unsigned int input, unsigned long feedback, unsigned int speed)
{
//assuming  input 51, feedback 50, speed 3

       unsigned long TEMP = ((unsigned long)(input))<<16;
        //temp now holds 3342336 (but irrelevent, it will get overwritten shortly)
	 
	if(input>=feedback)  //IT IS
	{
		TEMP=input-feedback;   //51 - 50 =1  SO TEMP NOW HOLDS 1
                      
                //    SHIFTING 1 BY 3 PLACES TO THE RIGHT MAKES IT ZERO
		TEMP=(TEMP>>speed)+feedback; //TEMP now holds 50		
	}
	else//  SKIP THIS BECAUSE CONDITION WAS TRUE NOT FALSE
	{
		TEMP=feedback-input;
		TEMP=feedback-(TEMP>>speed);
	}

	return(TEMP);//return 50
}

So looking at that shift of Temp, the only way a new value will have any effect is if it is GREATER than 2^speed

(ie if Speed is 3 then input has have a difference of more than 8 with respect to temperature before it will be recognised)

Well it looks right. But when you apply it on value from adc (which is 0-1023) the change of 1 is not big deal...
As I said I didn't invented this, I have it from school...

BTW I tested also another filter (which is used in multiwii for filtering values from acc etc) and it also didn't worked well.
But the filter itself is not good for this use, because it filter only big changes and the small let be...
But that's almost what you needed KenF :slight_smile:

#define smooth 5

byte filter(byte input)
{
	static unsigned int psum=0;
	static byte pvec[smooth];
	static byte ind=0;
	
	psum += input;
	psum -= pvec[ind];
	pvec[ind] = input;
	ind++;
	ind %= smooth;
	input = (byte) (psum / smooth);
	
	return input;
}

I thing that there must be some problem with overflow, because the result from this second filter is also 1.25, (that's 255 from filter)
Isn't the int only 8-bit?
EDIT: well the second filter use (byte) so it's definitely 8-bit :smiley:
after retyping to unsigned int the second filter looks working...

Benik3:
Well it looks right. But when you apply it on value from adc (which is 0-1023) the change of 1 is not big deal...
As I said I didn't invented this, I have it from school...

But that's almost what you needed KenF :slight_smile:

If by "almost" you mean "the opposite of" then I agree with you :slight_smile:

In my application the temperature sensor has a difference of 20mv for each degree centigrade.
You'll recall I had noise giving results in the range of 20 degrees, Assuming the centre of this range to be the true reading means that those deviations were about 10 degrees, That's 100ma (a difference of about 20 on the analogue reading)

Yet a real temperature difference can be as little as a tenth of a degree. 1ma (that's an analogue reading difference of 0.2) Although this seems impossible to detect, the averaging of multiple values does the job nicely.

It seems to me I know where is the problem in code reply #4.

void loop() {
  
int teplota1_adc = 0; // <- this variable keeps the "history", and shouldn't be initialized in the loop, declare its above the loop().
float teplota1 = 0.0;

teplota1_adc = filter(analogRead(1),  teplota1_adc, 3);
 teplota1 = (float)teplota1_adc*5/1024;

. . . which is why we ask you to post all your code.

Magician:
It seems to me I know where is the problem in code reply #4.

Thanks Magician, so stupid thing :blush: