Release 2016.8.17 / Update 2017.2.5

Reduce fluctuations of analogRead

Use ‘analogRead’

It is so very easy to read analog value of variable resistor in Arduino.

vr_breadboard

After wiring as of the above to Arduino, you write a sketch.

int vol_val = 0;             // 変数Vol_valを作成、とりあえず0を代入

void setup() {
  pinMode(A0, INPUT);        // A0ピンを入力に指定
  Serial.begin(9600);        // PCとのシリアル通信を9600bpsにして開始
}

void loop() {
  vol_val = analogRead(A0);  // 変数vol_valにアナログ読み取りしたA0ピンの値を代入
  Serial.print(vol_val);     // vol_valに代入された値をPCへシリアルプリント
  Serial.println();          // 改行シリアルプリント
}

Now you can see the value by Serial monitor in Arduino IDE.

Arduino reads comparisons what divided from 5 voltage into by variable resistor. If you remap the value in what you want to control, you can do anything, like motor speed control.

read_vr_monitor_01

But there is a problem. This value has fluctuations. I think it is caused by ‘analog’ as itself.

‘5 voltage’ does not mean exactly 5 volt. There are small changes always. For example, even if you flow exact 500ml water through the pipe, you can not always keep the surface of the water height. There are deviation always in real world, I think.

It goes on if I never mind. But I do. I refuse to use controller of convulsions.

Honestly, this is a first step problem of ‘analogRead’. And you can find so many ways to solve it on web. I have categorize them into 3 ways.

  1. Taking break after each ‘analogRead’
  2. Decrease its resolution
  3. Taking an average

I have already a conclusion, but it is not these methods. But at first, let me explain these way.

Stabilizing ‘analogRead’ in basic ways.

1.Taking break after each ‘analogRead’

At the first sketch sample, Arduino reads madly A0 pin. At the first sketch sample, Arduino reads madly A0 pin. It reads fluctuations because it is so fast. ‘Fast’ means under milliseconds.

Then, fluctuations will be gone, if there is a interval for each read. So, let put ‘delay’ in.

int vol_val = 0;

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {
  vol_val = analogRead(A0);
  Serial.print(vol_val);
  Serial.println();
  delay(1000);
}

You can see ‘delay’ at line 12. Now, Arduino reads A0 pin at each 1 seconds. But ‘delay’ stops all task, so this causes bad response.

read_vr_monitor_02_delay

I use ‘millis’ to make practical interval simply.

int vol_val = 0;
unsigned long time_read = 0;  // interval for analog reading

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {
  int intval = millis() - time_read;  // get current time

  if (intval >= 1000)           // excute new reading if intval over 1000ms
  {
    vol_val = analogRead(A0);
    Serial.print(vol_val);
    Serial.println();

    time_read = millis();       // set read time to 'time_read'
  }
}

I can get interval time by subtracting previous reading time ‘time_read’ from present time ‘millis’. If that time has been over 1000 millisecond, Arduino starts to read. Then, ‘time_read’ is updated after the read.  This is a temporary timer work, I usually use.

read_vr_monitor_03_mills

As a result, this is not effective way.

2. Decrease its resolution

Arduino UNO has ten bit resolution, that means it has 1024 range for analog reading. Strictly, its range is from 0 to 1023. So, divide the resolution to dull response.

int vol_val = 0;

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {
  vol_val = analogRead(A0);
  vol_val = vol_val / 10;     // decrease resolution
  vol_val = vol_val * 10;     // increase resolution
  Serial.print(vol_val);
  Serial.println();

}

I divide value by 10 so that it is abandoned. The resolution is decreased to one-tenth of. Instead, it will be stable.

Perhaps, this is useful in several situations. But analogWrite needs to be range from 0 to 255. I will try to fit in a range by dividing at 4, then.

int vol_val = 0;

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {
  vol_val = analogRead(A0);
  vol_val = vol_val / 4;     // decrease resolution by one-fourth
  vol_val = vol_val * 4;     // restoring resolution
  Serial.print(vol_val);
  Serial.println();

}

read_vr_monitor_05_low_resPWM

There are fluctuations in value still.

3. Taking an average

It is same way of taking average in your classroom test. Reading several times, then divide by the repeated count.

I will use ‘for’ so that code to be short.

int vol_val = 0;

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {

  for (byte i = 0 ; i < 30 ; i++)  // repeat 30 times
  {
    vol_val += analogRead(A0);     // add the value of A0 pin to 'vol_val'
  }

  vol_val = vol_val / 30;          // divide value by one-thirty

  Serial.print(vol_val);
  Serial.println();

  vol_val = 0;                     // reset 'vol_val'
}

Yet, still there is tiny fluctuations. But perhaps, this is the most stable methods in three solutions.

read_vr_monitor_06_avaraged

These are basic ways of avoid from fluctuation on ‘analogRead’.  Sometimes they combines the methods for more stability.

But each methods has a weak point like ‘low resolution’, or ‘not perfect’.

To be selfish, I had been searching for reasonable way quite a time. Then finally, I got a methods on Web.

Updating little at a time

My friend who is expert of science says “this is low pass filtering”. But I can’t figure out how it works. I’m not a man of math.

Anyway, this is simple.

int vol_val = 0;

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {

  vol_val = (vol_val * 0.9) + (analogRead(A0) * 0.1);


  Serial.print(vol_val);
  Serial.println();

}

Just it.

Arduino just get a value by adding 10% of new value and 90% of previous value. In the C language, it is possible to do a calculation of the decimal point by putting a decimal point.

read_vr_monitor_07_lowpass

Almost there is no fluctuations. I can’t believe that I got such stabilization in a such simple way.

But there is some down of resolution. This caused by decimal calculation, maybe. Still, the range is over 1000.

Although this methods is very useful, there is caution. Each updating is done in a little. So its response goes slow if there is ‘delay’ on a basic loop. You can’t benefit from this methods unless total sketch flows smoothly.

In the end, I wrote a sketch combining ‘little methods’ and ‘average methods’.

int vol_val = 0;

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {
  int read_val = 0;

  for (byte i = 0 ; i < 10 ; i++)
  {
    read_val += analogRead(A0);
  }

  vol_val = vol_val * 0.9 + (read_val / 10) * 0.1;


  Serial.print(vol_val);
  Serial.println();

}

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA


This site uses Akismet to reduce spam. Learn how your comment data is processed.