Averaging read values

Hello fellow people.

Sorry if I posted the wrong way, I accept corrections.

I am trying to use blink without delay at the same time with averaging of read values from analog pin.

Blink without delay is working fine as expected without the integration of averaging.

When I add that averaging, the blinking "on" and "off" time is increased to longer periods.

Does this mean these can't be done in a same code?

Please help, I have little knowledge in programing but I need to accomplish my goal.

const int LEDpin = 6;
const long onDuration = 50;
const long offDuration = 2000;
int LEDState =HIGH;
long rememberTime=0;

void setup(){
    Serial.begin(9600);
    pinMode(LEDpin,OUTPUT);
  digitalWrite(LEDpin,LEDState);
}

void loop(){
    

 float ave=0;
float total=0;
for(int i=0;i<1000;i++)
{
float val = (float) analogRead(A2);
total = total + val;

     }
    ave = total/1000;
     
    if( LEDState ==HIGH )
 {
    if( (millis()- rememberTime) >= onDuration){   
    LEDState = LOW;
    rememberTime=millis();
    }
 }
 else
 {   
    if( (millis()- rememberTime) >= offDuration){     
    LEDState =HIGH;
    rememberTime=millis();
    }
 }

 digitalWrite(LEDpin,LEDState);
    
    }

This for loop is "blocking code" and takes more than 1/10 second to execute on an Arduino Uno and the like. Since the loop function loops, you don't need a separate loop to count up and average values.

for(int i=0;i<1000;i++)
{
float val = (float) analogRead(A2);
total = total + val;

     }

Thank you for such a quick reply.

Still trying to understand your last part of your answer..... Is there any way to average values without blocking my other things?

There are many ways. One example:

float sum = 0;  //global, outside of loop
int count = 0;

void loop() {
sum += analogRead(A0);
count++;
if (count >= 1000) {
   Serial.print("Average = ");
   Serial.println(sum/count);
   count = 0;
   sum = 0;
   }

//other stuff
}
1 Like

count = 0;
sum= 0;

shouldn't be outside the if loop ?

No. Think it through. Better yet, run the code and see what happens.

"if" is not a loop. The actions that follow, enclosed in "{}" are called a code block.

1 Like

Ok I will, will update. Just little busy

const byte LEDpin = 6;
const byte Analog_pin = A2;

void setup() {
  Serial.begin(9600);
  pinMode(LEDpin, OUTPUT);
  digitalWrite(LEDpin, LEDState);
}

void loop() {
  const int onDuration = 50;
  const int offDuration = 2000;
  static uint32_t rememberTime = 0;
  bool LEDState = digitalRead(LEDpin);
  if ( millis() - rememberTime >= LEDState ? onDuration : offDuration) {
    rememberTime = millis();
    digitalWrite(LEDpin, !LEDState);
    if (LEDState)Serial.println(Reading());
  }
}

int Reading() {
  const int Amount = 100;
  uint32_t total = 0;
  for (int i = 0; i < Amount; i++)total += analogRead(Analog_pin);
  return total / Amount;
}

Is a blocking function.

2 Likes

yes, for ~10ms, but it's no problem: next action comes only after ~2000ms and only once per 2 seconds.

That is up to the OP, who is concerned about accurate timing and blocking functions.

2 Likes

Sure, in my case I don't need any disturbance, coz this is just a snippet of the whole code. In the code there is current sensing etc.

There is a website for that: https://snippets-r-us.com/

If you just want to get rid of (electronic) noise, then there is already an improvement with 5 samples. The analogRead() can be called without extra delay. About 20 to 100 is more than enough. In most cases that is fast enough to complete them all each time the loop() runs.

However, if you want to get rid of the 50/60 Hz noise from the mains, then 1000 samples might not be enough, you probably need a different filter.
Or if you want to know the average daylight with 10 samples per hour then you can use a millis-timer.

When you do this:

  int Amount;
  uint32_t total;
  ...
  return total / Amount;

then you throw away extra resolution. The averaging can be seen as "oversampling". The resolution will increase with oversampling (but the accuracy not).
You can even get more accurate by half a bit compensation: Gammon Forum : Electronics : Microprocessors : ADC conversion on the Arduino (analogRead)

If you have a millis-timer for a led, then please use one millis-timer and adjust the interval.
kolaha shows a millis-timer with different intervals, but you may use more code lines, I don't write code that compact.

1 Like

Thank for the advice on snippets, another lesson leant.

In my code the averaging is only there to smooth out the read values.

Hie, thanks this is working fine.

But I have one more question about this code.
Is it also possible "volt" be seen outside the if(){} like Serial printing the average outside the if{} . Or it's just like if I want to use "volt" I have to do it inside only?

sum += analogRead(A2);
count++;
if (count >= 100) {
float volt=sum/count;
   count = 0;
   sum = 0;
   }
   Serial.print("Average = ");
   Serial.println(volt);

If you want to use a variable everywhere, then use a global variable.
Some make all the variables global, but I prefer to make variables local if that is possible.

// global variables
float volt;

void setup()
{
}

void loop()
{
  long count;       // 'count' is local within the loop() function.
  for (int i=0; i<10; i++)   // 'i' is local within the for-loop
  {
    ...
  }
  volt = ...      // update the global variable
}
2 Likes

Some ideas....

If you have no FPU, avoid working with floats!

Analog read returns ints. A long can store a lot of accumulated ints.

What I might do in your case is to make the number of samples binary, say 4 or 8 to start so that the reads can be stored in a circular buffer with variables for head and tail, the new read always overwriting the oldest once the buffer is full and head always the newest read. With a straight buffer you would have to copy-move elements up (like a bit shift does with bits) to add a new read. With a circular buffer you increment the index then mask that value with the number of bits in the size of the buffer-1 which ...

if the buffer is 8 values, array[0] to array[7] so when the index reaches 8, ( 8 & 7 ) = 0, the index circles to 0 hence circular buffer.

And that is to keep the reads available for this shortcut.

I would keep a running total of the last X reads in a long variable to shift-right divide for the average. Before a new read, subtract the tail of the full buffer from the total, add the new read and shift to divide for the average int read value. The bigger the circular buffer the better running average you can get without EVER adding them all up in one go.

I would still have to write the code for until the buffer gets full and not report averages until the buffer is full so steps till then do less.

There are statistical means to run averages that involve adding squares and getting square roots. I had calculators that did that while I waited... I wouldn't try that with an AVR but that way you never buffer reads regardless of how many. With a PC or an ARM with FPU and 100+ MHz clock it likely wouldn't matter but a 16 MHz AVR needs shortcuts!

A 256 read buffer only needs more RAM than a 16 read buffer but should run just as fast. A long can hold 65536 int reads added up.

Convert the average read value to float volts or integer mV? Analog read steps, calculate as microvolts then round off to millivolts in integers is wayyy faster on an AVR that using floats.

1 Like

leaky integration or a running average updates the average after each sample

avg += (samp - avg) * K // K < 1

the avg should be initialized to the first sample

if K = 1/N, it takes ~3N samples for the avg to equal a constant sample value (like an RC ciruit)

the avg needs to be a float

TBC, those are two diffferent things; @gcjr shares an equation for the leaky integrator.

A leaky integrator can also be written, less efficiently yes, as

    value = alpha * value + (1 - alpha) * newReading

Or I guess

     avg = alpha * avg + (1 - alpha) * samp

with alpha set between 0 and 1. that parameter determines the rate at which the value converges to constant input.

With alpha at 0.9, for example, the equation reads

 take ninety percent of the old value and add ten percent of the new reading

older samples are successively diminished in importance.

The leaky integrator is a form of low pass filter.

a7

1 Like

You got me, I clicked on it :joy: