Multi-analog output value smoothing function

Hello,

Firstly I need to mention that I am a rookie in terms of electrical engineering and C++ so please bear with me. I’m currently building a robot which will have several analog sensors attached and I want to be able to read the values of all of the sensors on the serial monitor. For experienced engineers you may know that the raw sensor readings can be a bit crazy at times which is why I included nicely designed code which does exactly what I want. See: https://www.arduino.cc/en/Tutorial/Smoothing

This is great however only really suits one sensor at a time. To be efficient, I’ve tried to create a function which generically does the ‘smoothing’ and returns the values to be updated to each individual value. See below:

const int numReadings = 10;     // maximum in array
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

int inputPin = A0;
int led = 3;

void setup() {
  // initialize serial communication with computer:
  Serial.begin(9600);
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}

int smoothValue(int workingTotal, int workingList[], int workingIndex, int sensorPin){
  // subtract the last reading from working list:
  workingTotal = workingTotal - workingList[workingIndex];
  // read from the sensor:
  workingList[workingIndex] = analogRead(sensorPin);
  // add the new reading to the total:
  workingTotal = workingTotal + workingList[workingIndex];
  // advance to the next position in the array:
  workingIndex = workingIndex + 1;

  // if we're at the end of the array...
  if (workingIndex >= numReadings) {
  // ...wrap around to the beginning:
  workingIndex = 0;
  }
  // calculate the average:
  average = workingTotal / numReadings;
  
  return workingTotal;
  return workingIndex;
  return average;
}

void loop() {
  //create vars to catch new values
  int newTotal = 0;
  int newReadIndex = 0;
  int newAverage = 0;
  //get average value from sensor
  newTotal, newReadIndex, newAverage = smoothValue(total, readings[numReadings], readIndex, inputPin);

  //update led
  analogWrite(led, newAverage);
  // send it to the computer as ASCII digits
  Serial.println(newAverage);

  //update sensor values
  total = newTotal;
  readIndex = newReadIndex;
  average = newAverage
  delay(600); // delay in between reads for stability
}

I’ve been testing the code above with a photoresistor & an LED for added indication (10Kohm & 220ohm resistors for these components). I want 0 to be the value of no light and increased value with more light. It almost works! It detects light and the value changes… but into negative numbers. In the serial view it goes crazy and I’m not sure whats causing it to be so odd. Could I receive some advice as to how to approach a generic ‘smoothing’ function? or tell me whats wrong with my attempt?

would be much appreciated,

Jack

What is the actual values (including intermediates!) you encounter? Any chance they end up between 32768 and 65536? If so, change those ints to unsigned ints.

Any change of values going >65535? Then you'll have to start using unsigned long.

Hey,

I've tried changing the variables from 'int' to 'unsigned int' and 'long' and there is no visible difference all outputs are identical. The results show the value of var average which I want to print to serial and use as a value of luminosity for my led. See the serial results here:

-28
-28
-29
-27
-30
-27
-28
-30
-30
-30
-30
-29
-30
-30
-30
-30
-30
-26
-28
(this is where I shined a torch at the LDR)
29
29
-11
-10
1
21
27
26
23
20
27
25
22
22
(this is where I took the tortch away)
-13
-13
2
-29
-28
-30
-28
-27
-30

I spammed the Serial.println() function in order to see whats inside each variable each loop and the results may explain the calculated negative numbers. Here are the results (key below):

Start total: 0
Start index: 0
RAW: 30
Output average: 0
Start total: 0
Start index: 0
RAW: 30
Output average: 0
Start total: 0
Start index: 0
RAW: 30
Output average: 0
Start total: 0
Start index: 0
RAW: 30
Output average: 0
Start total: 0
Start index: 0
RAW: 30
Output average: 0

Key:
Start total = the value of var total at the start of each loop
Start index = the value of var 'readIndex' at the start of each loop (should be progressing to 10 and resetting to 0, rinse and repeat)
RAW = the value outputted from the sensor
Output average = the value I want to display

My program isn't passing the data like it should be. Maybe returning multiple variables isn't ideal?

What is the actual voltage you measure on the pin?

The only values you can possibly get from an analogRead() is 0-1023. If you see negative results, there's something wrong with the maths.

The first suspect is the variables (ints can go negative - if you use unsigned int and you still see negative values as you suggest there's something impossible going on).

The second suspect is the algorithm itself. There's probably something wrong there. Start by printing out intermediates.

Thank you for the advice, its much appreciated.

I’m sorry I cant give you an incredibly educated answer on the pin itself. All I know is that the Arduino is outputting 5V into the photoresistor and a 10kohm resistor is connecting it to A0 & GND. The unaltered code from here: https://www.arduino.cc/en/Tutorial/Smoothing worked perfectly as intended. It is my attempt to make it a generic function to service multiple sensors which has messed it all up so bad.

I suspect I’m not returning the values properly in C++ and so that may be where it is messing up. I know that the values are not updating properly like they should be based off of my serial spree (you can see in my edited post above) and also further testing showed that the ‘workingTotal’ variable consistently = 0 which meant the algorithm was constantly dividing by zero to find the average. This is not ideal and may explain the funkiness of the outputs.

int smoothValue(...){

...
  return workingTotal;
  return workingIndex;
  return average;
}

newTotal, newReadIndex, newAverage = smoothValue(...);

Is this some new C syntax that I have missed out on? A function declared as int can only return one integer, not three. workingTotal gets assigned to newAverage and the others are basically ignored by the compiler.

Look up pass-by-reference. The function can accept references to those variables and modify them in-place.

A better way to do it is to use a class. That way the internal storage can be kept in the object instead of passed into the function every time you use the function. You can get fancy with a .begin() method to start the array with zeroes and make up your own names for the actions of putting a new value into the average or getting the result back out.

Rather than using a class, you may also pass pointers to the variables so they get updated in place.

int newTotal, newReadIndex, newAverage;
smoothValue(&newTotal, &newReadIndex, &newAverage);

void smoothValue(int *newTotal, int *newReadIndex, int *newAverage) { // You may chose other names if you like here.
  ...
  return;
}

You lost the list of values, which the original code calls workingList.