Language: EN

entradas-analogicas-en-arduino

Analog inputs in Arduino

In previous posts, we have seen how to use our Arduino’s digital inputs. We have also used these inputs to read the state of a pushbutton. In this post, we will see the analog inputs, their operation, and characteristics.

Analog inputs work in a similar way to digital inputs, so in practice, the assembly and final code are very similar. Therefore, most of what we saw in this post is applicable. However, in certain aspects they are radically different, so to understand their use and functionality correctly, it is necessary to see a minimum of theory.

What is an analog input?

An analog signal is a magnitude that can take any value within an interval - Vcc and + Vcc. For example, an analog voltage signal between 0V and 5V could be 2.72V, or any other value with any number of decimals. In contrast, remember that a digital voltage signal could theoretically only register two values (in the example, 0V or 5V).

As a general rule, in automatons analog inputs are scarcer, slower, and more expensive than digital inputs. In the case of Arduino, we have a variable number of analog inputs, which in the case of Arduino Uno and Mini Pro are 6, and in the Arduino Mega are 16. This is a more than respectable number of analog inputs, which rivals or exceeds traditional automatons of much higher cost.

As we saw when explaining digital inputs, a digital input involves a process of transforming a signal into a digital value, attributing a HIGH value to the measurements that exceed a threshold value, and LOW to those that remain below. On the contrary, an analog input provides a measurement encoded as a digital value with a number N of bits.

It is important to understand that in the real world any voltage signal is always analog. A digital value is a concept, an abstraction. However, it is worth noting that the measurement provided by an analog input is also a digital value, so it is also an abstraction. This leads us to the concept of measurement precision.

Precision of the measurement

To understand the precision of an analog input, it is necessary to understand how an analog-to-digital converter (ADC) works, which is its fundamental component. An ADC is a device that converts an analog measurement into a digital measurement encoded with a number N of bits.

There are many ways to build an ADC, but the important thing is to understand that in reality we do not measure the analog value with all its decimals, but rather we “classify” it within 2^N levels, which define 2^N-1 intervals. The width of this interval measured in mV is the precision of the signal. The greater the number of bits, the greater the number of intervals, the smaller the width of the interval, and therefore the better the precision of the measurement.

In the case of Arduino Uno, Mini Pro, and Mega, the analog inputs have 10 bits of resolution, which provides 1024 digital levels, which at 5V results in a measurement precision of +-2.44mV. Arduino Due has a resolution of 12 bits, 4096 digital levels, which results in a precision of 0.61 mV.

Relative precision

So far we have assumed an automaton powered between 0V and 5V, measuring an analog voltage signal between 0V and 5V. In this case, with a 10-bit ADC, we have a precision of 4.88mV, which represents a relative precision with respect to the input signal of 0.1% (1/1024).

However, suppose we measure a signal that varies between 0V and 1V. In this case, with the same 10-bit ADC, we would have the same absolute precision of 4.88mV, but a lower relative precision with respect to the signal, which would drop to 0.5%.

That is, if we measure a signal that varies within a lower limit of Vcc, we are losing relative precision. This is the consequence of not taking advantage of the entire measurement range, so in reality the ADC behaves as if it had a lower number of bits.

Analog voltage reference (aref)

To resolve this situation, Arduino allows changing the voltage taken as a reference by the analog-to-digital converter. The reference value is changed with the AnalogRef function, and the possible values are:

  • DEFAULT: Default value, corresponding to Vcc (5V or 3.3V, depending on the model)
  • INTERNAL: Corresponds to 1.1V (in Atmega 168 and 328)
  • EXTERNAL: Voltage applied externally on the Vref pin (always between 0 and Vcc
  • INTERNAL1V1 and INTERNAL2V56, corresponding to 1.1V and 2.56V (only in Mega)

In the case of using the external voltage reference (EXTERNAL), if we know with total certainty that a signal will not exceed a certain voltage value, for example 0.7V, we can provide this value as a reference through the Aref Pin. The measurement will be made taking this voltage as a reference instead of Vcc, so we recover all the relative precision.

If we modify the reference voltage, we must define the mode using the AnalogRef function before making any analog reading.

If we introduce a voltage value in the Aref pin we must not exceed this value in the analog inputs. Also, under no circumstances will we exceed the voltage supply of Arduino. Otherwise, we could damage the analog pins.

Connection of analog inputs in Arduino

Suppose we have an analog sensor that provides an analog signal between 0V and 5V. The connection scheme is similar to the one we use to perform the digital reading.

arduino-entradas-analogicas-1

Arduino code

The code to perform the reading is really simple, and similar to what we saw for digital inputs. We simply perform the reading using AnalogRead() and store the value returned.

const int sensorPin = A0;    // select the input for the sensor
int sensorValue;     // variable that stores the raw value (0 to 1023)

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

void loop() 
{
  sensorValue = analogRead(sensorPin);   // perform the reading

  //send message to serial port based on the value read
  if (sensorValue > 512) 
  {
    Serial.println("Greater than 2.5V");
  }
  else 
  {
    Serial.println("Less than 2.5V");
  }
  delay(1000);
}

Try it online

The value returned by the AnalogRead() function is encoded as an integer 0 to 1023. If you want to convert this value into a voltage value, you can use the following variation:

const int sensorPin = A0;   // select the input for the sensor
int sensorValue;      // variable that stores the raw value (0 to 1023)
float value;        // variable that stores the voltage (0.0 to 5.0)

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

void loop() 
{
  sensorValue = analogRead(sensorPin);        // perform the reading
  value = fmap(sensorValue, 0, 1023, 0.0, 5.0);   // change scale to 0.0 - 5.0

  Serial.println(value);              // display the value through serial
  delay(1000);
}

// scale change between floats
float fmap(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

Try it online

However, keep in mind that floating-point operations (with decimals) are much slower than with integers so try to avoid having to perform this conversion and work whenever possible with integers.

Sampling frequency

With the code used, the sampling frequency is approximately 9600 Hz, that is, about 100 microseconds for the measurement. In comparison, the digitalRead function has a frequency of 15000Hz, about 66 microseconds per measurement, slightly faster.

However, through other codes, the analog reading can be increased to approximately 1.5 Mhz, or 660 nanoseconds per input. In contrast, digital inputs can be accelerated to almost 15 Mhz, 66 nanoseconds, reading all the inputs simultaneously. Therefore, we see that digital inputs can actually be much faster than analog inputs.

Reading values greater than 5V

If you need to read an input of a higher voltage level, for example 12V, you must perform a voltage adaptation. The best way to perform the adaptation is to use a simple voltage divider.

arduino-entradas-analogicas-2-2

With this configuration, the digital pin of the Arduino will receive a voltage that varies between 0 to 3.84V so, as we explained, we would be losing relative precision. One option would be to adjust the resistors so that the limits are as close as possible to 0 and 5V, or use another voltage divider to feed the Aref pin.

The values of the resistors to be used depend on the voltage you want to read, and the impedance of the sensor. In general, they must meet the following conditions:

  • They must convert the signal into a lower but similar range to the supply voltage.
  • They must be much higher than the equivalent impedance of the device to be measured.
  • They must be negligible with respect to the impedance of the Arduino input.
  • They must limit the current flowing through them to minimize losses.
  • They must be able to dissipate the power they are going to support.

You can use the voltage divider calculator to calculate resistance values that meet these requirements.

Do not use this system to read voltages higher than 35V, or for alternating current devices without being very sure of what you are doing. It is very likely that the resistors will not hold.

In the next post, we will see how to use the analog inputs to read the state of a potentiometer or the value of a variable resistance, something common when reading sensors whose measurement is made through the measurement of their resistance.

Download the code

All the code in this post is available for download on Github. github-full