Smoothing for Proximity Sensor LED & Initialization Issues

Hello,

As a learning exercise, I am trying to use a IR-proximity sensor to control the brightness of a LED. However, the performance isn't as expected.

  1. the LED brightness is very flickery: it is plugged into a PWN slot, but doesn't respond smoothly to proximity sensing. The values coming in from the the sensor do jitter slightly, and a was included to try and smooth things out. (doesn't work)

  2. tried the function to convert it to 8bit PWN, but am unable to make the LED turn totally off when nothing is near the sensor. (mapped from 50-615)

  3. sideqn: what is the difference in using different baudrates? why does 9600 seem to be the default one?

  4. any suggestions on improvement to the code or how it might be taken/tweaked further?

Thanks for reading, appreciate any help offered.

-lenn

setup: Arduino Deumilanove, Sharp 2Y0A21 Sensor, generic LED
picture of setup:

//IN
#define BAUDRATE 9600
#define IRPIN A0 //analogue pin for reading sensor
#define WAIT 10 //millisecond delay
int In_Val = 0;

//OUT
#define LED 9 //PWM for variable LED brightness
int Out_Val = 300;


void setup(){
  Serial.begin(BAUDRATE); //essential for serial communication
  pinMode (IRPIN, INPUT); //define the pins
  pinMode (LED, OUTPUT);
}

void loop(){
  In_Val = analogRead(IRPIN); //read the value from the sensor
  Out_Val = map (In_Val, 50, 614, 0, 255); //IRsenor bottoms out readings at 50-60ish, this remaps it to 8bit value
  analogWrite (LED, Out_Val); 
  Serial.println(In_Val); //spits out sensor values on console
  delay(WAIT);
}
  1. The values coming in from the the sensor do jitter slightly

You could average several readings to smooth it out. You can multiply the total (n-1)/n and add the new value to get a moving weighted sum, then divide by 'n' or just re-map the sum rather than the average.

  1. tried the function to convert it to 8bit PWN, but am unable to make the LED turn totally off when nothing is near the sensor. (mapped from 50-615)

You can keep track of the highest and lowest values you get from the sensor to dynamically determine the range.

  1. sideqn: what is the difference in using different baudrates? why does 9600 seem to be the default one?

9600 is a good balance between speed and stability. The faster you push data the more subject it will be to errors.

  1. any suggestions on improvement to the code or how it might be taken/tweaked further?

Like I said above: running average of input and dynamic determination of range.

int In_Val = 5120; 
int minInput = 4000;
int maxInput = 5000;

void loop(){
  In_Val = (In_Val * 9) / 10 + analogRead(IRPIN); //read the value from the sensor and add it to the moving sum
  if (In_Val < minInput)  minInput = In_Val;
  if (In_Val > maxInput)  maxInput = In_Val;
 
  Out_Val = map (In_Val, minInput, maxInput, 0, 255); //IRsenor bottoms out readings at 50-60ish, this remaps it to 8bit value
  analogWrite (LED, Out_Val); 
//  Serial.println(In_Val); //spits out sensor values on console
//  delay(WAIT);
}

John, thanks alot for your help. It helped immensely. A code was cobbled together to smooth the readings and it works fine, but some parts of the code still confuse me.

if you don't mind,

Code Questions:

  1. why must one use const when defining numReadings? when removed, it gives errors.

  2. when doing the smooth operation, total = total - readings [index] . how can one call an array to index without defining it prior? and what is the use of defining readings [numReadings] in that case?

  3. is it possible to write this as a for loop?

  4. when performing smoothing, why is total = total - readings[index] necessary?

Performance Issues

  1. when initializing, there are anomalous values which make the boundary values inaccurate. to sidestep that, a min condition that bounces the values back up was included. is there a more elegant method of doing this?

Muchos appreciation to any further clarification offered, and thanks both ways!

cheers,
-lenn

//SMOOTH
const int numReadings = 10; //number of values to average
int readings[numReadings];       //int numReadings = 10; this gives errors. why is the const necessary? the readings from the analog input
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average
.
.
.
<setup().....>
.
.
.
<loop()......
>
//Smoothing (weighted mean)

  total= total - readings[index];     // subtract the last reading:     
  readings[index] = analogRead(IRPIN);   // read from the sensor:  
  total= total + readings[index];      // add the reading to the total:   
  index = index + 1;     // advance to the next position in the array:                   

  if (index >= numReadings) {             // if we're at the end of the array wrap around to the beginning:
    index = 0;       
  }        
  
  average = total / numReadings;      // calculate the average:   
  Serial.println(average, DEC);    // send it to the computer

//End Smoothing

  inVal = average;  //read the value from the sensor and add it to the moving sum
  
//Define Boundaries for Mapping
  if (inVal < minInput){  //dynamic boundary: the max and min values get better with time. i.e., the machine learns
    minInput = inVal;
  }

  if (inVal < 20){  //to bypass startup jitter, which makes lower bounds inaccurate for mapping*note: is there a more elegant method?
    minInput = 200;
  }

  if (inVal > maxInput){ -=
    maxInput= inVal;
  }
  
//???????????can this be recalibrated every 10 cycles to make it adaptive? what other methods might be useful? 
//End Define Boundaries for Mapping

  outVal = map (inVal, minInput, maxInput, 0, 255); //remaps to 8bit value
  analogWrite (LED, outVal);
  1. why must one use const when defining numReadings? when removed, it gives errors.

When you declare the array the size has to be a compile-time constant.

  1. when doing the smooth operation, total = total - readings [index] . how can one call an array to index without defining it prior? and what is the use of defining readings [numReadings] in that case?

I think that globals, unless otherwise initialized, are initialized to zero.

  1. is it possible to write this as a for loop?

You could use a 'for' loop to add all the elements together each time but that would be slower and would get the same answer.

  1. when performing smoothing, why is total = total - readings[index] necessary?

If you don't subtract out the value N values back you won't have room for the new element. By subtracting the old value before replacing it with the new value you can get the sum of the elements without doing the sum each time. If you set numreadings to 100 it is much faster to do one subtract and one add then 100 adds.

  1. when initializing, there are anomalous values which make the boundary values inaccurate. to sidestep that, a min condition that bounces the values back up was included. is there a more elegant method of doing this?

You could initialize the readings to some value in the middle of the expected range.

john, thank you for the knowledge. its made the learning curve a lot easier.

regards,
Lennard

I'm working on a similar project using ir prox sensors, I managed to make a the incoming data smooth and also implemented the min and max function to get the range, but what I have noticed is that over time sometimes the incoming value goes beyond the max and min momentary, it then redefines the range but the new max or min is never achieved again so my range is now say working from 20% to 80% instead of 0-100.

I've tried to tally when incoming data is the same for a long time (and then see if that value is close to the ax or min to re-adjust them but it's not really working out great. Has anyone else had these issues? Any suggestions on how keep the values within a realistic range?