ADC, PWM and Arduino's analog* methods - Arduino Concepts for Beginners

acfb Feb 06, 2020

Analog, Digital and convert from one to the other

Let's start with definitions: analog is a signal that varies in a way that's similar (analogue) to the quantity it represents - the classical example is the voltage of an audio signal that varies in relation to the pressure of sound waves on a microphone.
On the other way, digital is a signal that represents something using a limited set of distinct values, for example 1 or 0 - the above audio signal would be then rapresented by just millions of 1s and 0s.

Microcontroller are natural digital devices, while many sensor are analog only - we need to find a way to translate between the signals understood by on and those understood by the other.

ADC, Analog-to-Digital Converter

Simply speaking (omitting errors, aliasing, jittering, and almost everything that's behind this: an ADC takes an analog signal as its input, divides it into a defined number of samples with a certain amount of time (sampling rate, ex. 256kHz means 256000 samples per seconds), and converts it into a number based on the voltage level.

How many values can be used to represent each range of analogue values is, instead, the resolution and is measured in bits. An 8-bit resolution, for example, means we can encode an analog input up to 28 values (=256, or -128 to 128 if signed, 0 to 255 if not) - if we have a 10V signal, for example, each increment would be 0,039V (10/256), or 39 mV.

DAC, Digital-to-Analog Converter

Of course, this work in the opposite way: a binary inputs of 0s and 1s is output with an analog voltage or current signal; think, for example, when we play a digital music out of a speaker.

ADC, analogRead() and analogReference()

Arduino Uno's internal ADC has a resolution of 10-bit (other models might differ, I think the Due has a 12-bit resolution), which means it can translate a signal into a number from 0 to 1023 (210 = 1024) - if we want to measure a 5V signal, for example, each step would be 4.88mV.
If we want to use this internal ADC we need to connect our analog device to one of the AX (A0->A5 in Uno) pins, and read the value using the analogRead() native function. For example:

const int analogPin = A3;
int val = 0;

void setup() {
  Serial.begin(9600);           //  setup serial

void loop() {
  val = analogRead(analogPin);  // read the input pin
  Serial.println(val);          // debug value

When nothing is specified, Arduino uses the Vcc tension (5V or 3.3V according to the model) as reference point for the analog reading; we can change this value by calling the analogReference() method:

  • analogReference(DEFAULT);
    same as not using it, the default value; can be flutuating according to the precision of the input voltage.

  • analogReference(INTERNAL);
    Uses a 1.1V built-in reference, regardless of the power voltage - better if you want a higher degree of precision. In this situation the Aref pin will OUTPUT this voltage value (we can use a 100nF capacitor between Aref and GND to stabilize the voltage).

  • analogReference(EXTERNAL);
    Uses an external source, with a voltage of 0-5V, connected to the Aref pin (which now becomes an INPUT pin)

Careful! If you don't declare it before connecting the external reference you risk damaging the microcontroller, since the internal active reference would still be in use. More on this (and the rest) on the official documentation.


We saw that the opposite of an ADC is a DAC, or Digital-to-Analog Converter; unfortunately, Arduino alone doesn't provide this feature, so if we want to create an analog signal we can either use a dedicated device - like the cheap MCP4725 board or we can approximate it by using the Pulse-Width-Modulation technique, or PWM.

In PWM we generate a square wave with varying time in which the signal is HIGH or LOW - that's the so-called duty cycle, which represents how much a signal is in the ON time (or high, or let's say 5V) or OFF time (0V)

Duty cycle

We achieve this by calling the method analogWrite(int pin, byte dutyCycle) on any of the PWM pins (3,5,6,9,10 or 11 for the Uno) and passing the percentage of dutyCycle as the 2nd argument, where 0 means (obviously) 0% and 255 means 100%. You can use it to blink a LED, for example:

👉 Remember that this is NOT an analog signal,but you can create a low-pass filter out of resistors and capacitors to "smooth" the curve out and approximete it even more.

If you want to dive deeper the Secrets of Arduino PWM page on the official docs provide interesting ideas.