initialize smoothing array with sensor data instead of zeros

I am new to Arduino and have never done any coding before (literally zero experience with this). I have been reading a lot and trying to learn over the past few days and would really appreciate any help anyone would be willing to give.

I have a 5v sensor hooked up to input A0. I expect the voltage from the 5V input to slowly vary over time (hours/days) and wish to ignore the slow change but would like to note large jumps or drops in voltage (in this case + or - .1V or more) I have used the standard smoothing array code and combined with an if, else if statement to count the number of times the voltage jumps more than .1V and subtract from the count each time the voltage drops by more than .1V

I have run in to some issues.

First, because the smoothing array initializes with 0’s, I get some false positives for my if else if statement. (lets say the sensor sees 2V for the first several min of operation), if I’m smoothing 10 values, the sketch performs like this after the first few readings.

Readings 1st loop (0+0+0+0+0+0+0+0+0+2)/10 = .2
Sensor reading =2, sketch adds 1 to count because 2 is > .1+.2
2nd loop (0+0+0+0+0+0+0+0+2+2)/10 =.4
Sensor reading =2, sketch adds 1 to count because 2 is > .1+.4
Result “Count” goes +1 each time through the loop when the voltage hasn’t really increased by .1.
This continues to happen as the array is filled with real data. How do I tell the sketch to fill the array with actual sensor data instead of 0’s for the first 10 readings?

Second, once the sketch has been running and the array is full of real sensor data and a voltage jump or drop occurs, if it is a large jump or drop, because of the smoothing of the array, the count can change more than once.

Example, if the sensor has been seeing 2v and the voltage jumps to 4v this happens,

1st loop, (2+2+2+2+2+2+2+2+2+2)/10=2
Sensor reads 4, count is increased by 1
2nd loop (2+2+2+2+2+2+2+2+2+4)/10= 2.2
Sensor reads 4, count is increased by 1 again.

How do I program the array to reset and fill itself with sensor data after a positive if, if else condition which causes the count to increase or decrease so that the count doesn’t artificially change because of the nature of the smoothing array?

Third, for my if, else if statement:
Should I be using: count++ and count-- or ++count and --count?
I don’t understand the difference (yes I read the explanation online) I tried the sketch both ways and seemed to get similar results using either.

Fourth, How do my delay’s look? I previously had just one delay at the end but I realized that if the voltage jumped, the loop would first add that increased voltage to the array (which would artificially raise the average) and then compare that same number to the array, meaning if the voltage really did increase just .1v it wouldn’t read as a +1 to the count because the average would be raised by that .1 reading before the if, else if statement was able to run. so the .1 increase compared to the average would now look like a .09 increase.

Fifth, For my project, the voltage jumps/drops sometimes happen quickly (within a 1 second period) and sometimes the voltage creeps up over a period of say as much as 5 or 10 seconds. Ideally what I would like to do is have the smoothing array keep track of the past average and have a second array take measurements over a period of 10 seconds and compare that average to the past average and if the two are +/-.1v from each other, the count is adjusted properly. But I have No clue how to do this from a coding standpoint.

p.s could someone better explain how the for statement works in the setup? I think this is what sets all the values in the array to 0 but im not sure how or how to manipulate it to fill with sensor data instead.

Here is my code:

// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int numReadings = 10;

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 Count = 0;

   
void setup()
{
 // set up the LCD's number of columns and rows:
lcd.begin(16, 2);
lcd.print("Sensor Count");
 for (int thisReading = 0; thisReading < numReadings; thisReading++) {
   readings[thisReading] = 0;
}

}
void loop()
{

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

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

 // calculate the average:
 average = total / numReadings;
delay(3000)  ;  
 // read the analog in value:
 int sensorValue = analogRead(inputPin);      
// map it to the range of the analog out:
  float voltage = average * (5.0 / 1023.0);
  
if ((sensorValue * (5.0 / 1023.0)) > (.1 + voltage))
{
 Count++;
}
else if ((sensorValue * (5.0 / 1023.0)) < (voltage - .1))
{
 Count--;
}
lcd.setCursor(0, 1);
lcd.print(voltage);
lcd.print("V ");
lcd.setCursor(14,1);
lcd.print(Count);

    
delay(3000)  ;   
}

Here is a solution to your problem of starting a rolling average (when you have fewer samples than the buffer size):

// rolling average demo
// by aarg 2016-02-13

void setup() {
  Serial.begin(9600);
}

const int MAXFRAMESIZE = 100;
const int RANDOM_RANGE = 1000;

int currentSampleCount = 1;
long rollingAverage;
long sample;

void loop()
{
// simulate an input sample with a random number
  sample = random(RANDOM_RANGE) - RANDOM_RANGE / 2;

// combine the old average value with the new sample, weighted to reflect
// the size of the window of previous samples
//
  rollingAverage = (rollingAverage * currentSampleCount + sample) / (currentSampleCount+1);

// show the current average
  Serial.println(rollingAverage);

// in the beginning, few real samples exist
// so gradually increase the size of the virtual sample frame
// until it reaches the maximum

// eventually, the rolling average will reflect the average
// of all the values that were processed in <framesize> number of samples

  if (currentSampleCount <= MAXFRAMESIZE)
  {
    ++currentSampleCount;
  }

  delay(50);
}

By the way, go back and edit your post to put the code inside code tags.

Thanks! I'll try to break down that code and make it work for my application. Would still appreciate anyone else's help with my other questions!

How do I tell the sketch to fill the array with actual sensor data instead of 0's for the first 10 readings?

I've made some sound activated lighting effects where I compare the current sound volume to a 20-second moving average. I have a 20-second "warm-up" or pre-calibration period where I fill-up the array before running the main loop that activates the lights. (The lights are actually running a "test-pattern" during warm-up.)

Second, once the sketch has been running and the array is full of real sensor data and a voltage jump or drop occurs, if it is a large jump or drop, because of the smoothing of the array, the count can change more than once.

If I understand the issue... You can set a "flag" (a binary true/false variable) as soon as you go 0.1V over (or below) the average.

Then with an if-condition, ignore any more "triggers" until the readings are no longer 0.1V above average. And at that point, reset the flag and start checking for above (or below) average readings again.

Fourth, How do my delay's look?...

... Ideally what I would like to do is have the smoothing array keep track of the past average and have a second array take measurements over a period of 10 seconds and compare that average to the past average and if the two are +/-.1v from each other, the count is adjusted properly

Sure... You can have a short-term average and a long-term average, and you can compare the two. But, you won't be able to use delay(). You'll need to use the timing techniques from [u]Blink Without Delay[/u] or [u]Demonstration code for several things at the same time[/u].

P.S.

I'm not following exactly following your code or what you're trying to accomplish, but three might be some flaws in your logic... Since "Count" is incremented and decremented, you could come back after a few days and you might see a count of zero (or some other low number) that doesn't represent what's been happening. Or, over a long period of time, Count may grow to a very large positive or negative number.

aarg:
Here is a solution to your problem of starting a rolling average (when you have fewer samples than the buffer size):

// rolling average demo

// by aarg 2016-02-13

void setup() {
  Serial.begin(9600);
}

const int MAXFRAMESIZE = 100;
const int RANDOM_RANGE = 1000;

int currentSampleCount = 1;
long rollingAverage;
long sample;

void loop()
{
// simulate an input sample with a random number
  sample = random(RANDOM_RANGE) - RANDOM_RANGE / 2;

// combine the old average value with the new sample, weighted to reflect
// the size of the window of previous samples
//
  rollingAverage = (rollingAverage * currentSampleCount + sample) / (currentSampleCount+1);

// show the current average
  Serial.println(rollingAverage);

// in the beginning, few real samples exist
// so gradually increase the size of the virtual sample frame
// until it reaches the maximum

// eventually, the rolling average will reflect the average
// of all the values that were processed in number of samples

if (currentSampleCount <= MAXFRAMESIZE)
  {
    ++currentSampleCount;
  }

delay(50);
}




By the way, go back and edit your post to put the code inside code tags.

I tried this and was having trouble. I realized that int currentSampleCount = 1 should actually be int currentSampleCount = 0. I tried it with a constant number as the sample and with currentSampleCount = 1 the actual sample never met the average.

I would initialise the array with 10 quick readings during the setup(). Or you could take just one reading and copy it 10 times. Even if that one initial reading had some noise, it would at least be close to the true value. It's only going to add a few microseconds to the startup time.

Another good way to reduce the noise in analogRead() is to take 10 or 100 readings very rapidly in just a few milliseconds. Then insert that averaged value into your medium-term array of averaging values.

Definitely get rid of the big delay(). It doesn't give accurate timing. Depending on what happens during an individual run through loop(), the time can vary. The fixed delay() can't measure this or adjust for it. Use the technique in the blinkWithoutDelay example and you will get accurate timing, which is essential to get predictable results from these kinds of time-based averaging.

You can simulate an RC network with this kind of code:

float fastRollingSample;
float slowRollingSample;

void loop() {
  int sensorReading = /* read the sensor, or whatever */;

  fastRollingSample = (fastRollingSample * .75) + (sensorReading * .25);
  slowRollingSample = (slowRollingSample * .99) + (sensorReading * .01);

  // compare the fast and slow samples, and do something if they are very different.
}

By comparing the two readings, you can see changes. By tracking your sample rate, you can take your samples at known intervals. For instance, this code take samples every 4ms:

float fastRollingSample;
float slowRollingSample;
unsigned long lastSample;

void loop() {
  unsigned long ms = millis;
  if((ms / 4) == (lastSample/4)) return;

  lastSample = ms;
  int sensorReading = /* read the sensor, or whatever */;

  fastRollingSample = (fastRollingSample * .75) + (sensorReading * .25);
  slowRollingSample = (slowRollingSample * .99) + (sensorReading * .01);

  // compare the fast and slow samples, and do something if they are very different.

}

As for the details of how the values of the rolling averages vary with your signal, easiest way to simulate it is in excel.

If you want to wait until you have at least 200 samples:

float fastRollingSample;
float slowRollingSample;
unsigned long lastSample;
it nsamples = 0;

void loop() {
  unsigned long ms = millis;
  if((ms / 4) == (lastSample/4)) return;

  lastSample = ms;
  int sensorReading = /* read the sensor, or whatever */;

  fastRollingSample = (fastRollingSample * .75) + (sensorReading * .25);
  slowRollingSample = (slowRollingSample * .99) + (sensorReading * .01);

  if(nsamples < 200) {
    nsamples ++;
    return;
  }
  // else don't increment nsamples, so that it stays at 200 and doesn't roll over.

  // compare the fast and slow samples, and do something if they are very different.

}

qksilvr: I tried this and was having trouble. I realized that int currentSampleCount = 1 should actually be int currentSampleCount = 0. I tried it with a constant number as the sample and with currentSampleCount = 1 the actual sample never met the average.

Thanks! I see that now. Good catch!

MorganS:
I would initialise the array with 10 quick readings during the setup(). Or you could take just one reading and copy it 10 times. Even if that one initial reading had some noise, it would at least be close to the true value. It’s only going to add a few microseconds to the startup time.

This is what I want to do, I just don’t know how to write the code to do so! I tried modifying the for loop to

for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = analogRead(A0);

but it didnt actually change my results. I assumed that doing this would put the analog read value in the each slot of the array.

I assumed that doing this would put the analog read value in the each slot of the array.

It did. Did you print the values, too?

but it didnt actually change my results.

Some proof? Complete code and some output to prove this will be essential to helping solve your problem.

PaulS:
It did. Did you print the values, too?
Some proof? Complete code and some output to prove this will be essential to helping solve your problem.

/*

  Smoothing

  Reads repeatedly from an analog input, calculating a running average
  and printing it to the computer.  Keeps ten readings in an array and
  continually averages them.

  The circuit:
    * Analog sensor (potentiometer will do) attached to analog input 0

  Created 22 April 2007
  By David A. Mellis  <dam@mellis.org>
  modified 9 Apr 2012
  by Tom Igoe
  http://www.arduino.cc/en/Tutorial/Smoothing

  This example code is in the public domain.


*/


// Define the number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
const int numReadings = 10;

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;

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] = analogRead(A0);
  }

  Serial.println(readings[numReadings]);
}

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

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

  // calculate the average:
  average = total / numReadings;
  // send it to the computer as ASCII digits

  Serial.println("Average = ");
  Serial.println(average);
  Serial.println("Sensor Read = ");
  Serial.println(analogRead(inputPin));
  
  delay(1000);        // delay in between reads for stability

Serial monitor initial results:

259
Average =
0
Sensor Read =
503
Average =
0
Sensor Read =
505
Average =
0
Sensor Read =
505
Average =
0

Average stays at 0 sensor read (pressure sensor for testing) stays around 500-510

for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = analogRead(A0);

but it didnt actually change my results. I assumed that doing this would put the analog read value in the each slot of the array.

It did load the array but you never created a non zero total. The averaging algorithm expects either zero values in the starting array, or a starting total with a loaded array.

Also,

Serial.println(readings[numReadings]); will be looking at a value for readings[10] which is outside of the array boundary. Arrays are zero indexed, and the largest value in the array is readings[9].
/*

  Smoothing

  Reads repeatedly from an analog input, calculating a running average
  and printing it to the computer.  Keeps ten readings in an array and
  continually averages them.

  The circuit:
      Analog sensor (potentiometer will do) attached to analog input 0

  Created 22 April 2007
  By David A. Mellis  <dam@mellis.org>
  modified 9 Apr 2012
  by Tom Igoe
  http://www.arduino.cc/en/Tutorial/Smoothing

  This example code is in the public domain.


*/


// Define the number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
const int numReadings = 10;

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;

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] = analogRead(inputPin);
    total += readings[thisReading];//add this line
  }

  //Serial.println(readings[numReadings]);//readings[10] does not exist in boundary of array
}

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

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

  // calculate the average:
  average = total / numReadings;
  // send it to the computer as ASCII digits

  Serial.println("Average = ");
  Serial.println(average);
  Serial.println("Sensor Read = ");
  Serial.println(analogRead(inputPin));

  delay(1000);        // delay in between reads for stability
}