Microcontrollers 1

Lecture: Analog to Digital Conversion (ADC)

Contents

  • The Analog and Digital Worlds
  • What is an ADC and How Does It Work?
  • Resolution: Converting Analog to Bits
  • ADC on the Arduino Platform
  • Practical 1: Reading a Potentiometer
  • Visualizing Data with the Serial Plotter
  • Practical 2: Reading a Joystick
  • Advanced: Real-time Plotting with Python

Learning Objectives

By the end of this session, you will be able to:

  • Explain the purpose and function of an Analog-to-Digital Converter (ADC).
  • Describe the concepts of resolution and reference voltage.
  • Use the analogRead() function to read analog sensor values on an Arduino.
  • Build circuits to interface a potentiometer and a joystick with an Arduino.
  • Visualize sensor data using the Arduino Serial Plotter and a custom Python script.

The Real World is Analog

Most physical phenomena we want to measure are analog in nature. This means their values are continuous and can take any value within a given range.

  • Temperature: Can be 23°C, 23.1°C, 23.11°C, etc.
  • Light Intensity: Varies smoothly from dark to bright.
  • Sound: Is a continuous pressure wave.
  • Position of a knob: Can be turned to any position.

These are not just simple ON/OFF or 1/0 values.

The World of Microcontrollers is Digital

Microcontrollers, like the ATmega328P on the Arduino Uno, operate in a digital world.

  • They understand discrete values: HIGH/LOW, 1/0, 5V/0V.
  • They process information in bits and bytes.
  • They cannot directly understand the infinite range of an analog signal.

The Challenge: How can a digital microcontroller understand the analog real world?

The Bridge: Analog-to-Digital Converter (ADC)

An ADC is a crucial component that translates a continuous analog voltage into a discrete digital number that the microcontroller can process.

Analog Signal (e.g., Voltage) → ADC → Digital Value (e.g., 0-1023)

Think of it as a translator between two different languages: the language of the physical world and the language of the computer.

How an ADC Works: The Core Process

The conversion process involves two main steps:

  1. Sampling: The ADC measures (samples) the analog voltage at specific, regular intervals. This creates a series of "snapshots" of the signal's value at different points in time.

  2. Quantization: Each sampled voltage is then mapped to the closest available digital value. This is like rounding a decimal number to the nearest integer.

The result is a sequence of digital numbers that approximates the original analog signal.

Visualizing Sampling and Quantization

Imagine measuring a smooth curve (analog signal) but only being able to use a staircase (digital steps) to represent it.

  • The red dots represent the sampling points.
  • The blue steps represent the quantized digital values assigned at each sample.
  • The ADC approximates the smooth analog curve with these discrete steps.
Graph illustrating sampling points (red) and quantized digital steps (blue) over a sine wave.
Sampling (red dots) and quantization (blue steps) of a continuous analog waveform.

ADC Terminology: Resolution

Resolution determines the number of discrete steps the ADC can use to represent the analog signal. It is measured in bits (n).

  • The total number of steps is 2n.
  • A higher resolution means more steps, resulting in a more accurate digital representation of the analog signal.

Example:

  • An 8-bit ADC has 28 = 256 steps (0-255).
  • A 10-bit ADC has 210 = 1024 steps (0-1023). ← This is what the Arduino Uno uses!
  • A 12-bit ADC has 212 = 4096 steps (0-4095).

ADC Terminology: Reference Voltage (Vref)

The Reference Voltage (Vref) is the maximum voltage that the ADC can measure.

  • It defines the "top" of the measurement range.
  • Any voltage equal to or greater than Vref will result in the maximum digital value (e.g., 1023 for a 10-bit ADC).
  • Any voltage at 0V will result in the minimum digital value (0).

On a standard Arduino Uno, the default Vref is 5V.

Analog to Bits of Data

The ADC maps the input analog voltage (Vin) to a digital value based on Vref and its resolution (n).

The key relationship is:

Digital Value = (Vin / Vref) × (2n - 1)

For an Arduino Uno (10-bit ADC, 5V Vref):

Digital Value = (Vin / 5.0) × 1023

Conversion Example

Let's say an analog sensor outputs 2.5V. What digital value will the Arduino's 10-bit ADC produce with a 5V reference?

  • Vin = 2.5V
  • Vref = 5.0V
  • n = 10 (so 2n-1 = 1023)
Digital Value = (2.5 / 5.0) * 1023
Digital Value = 0.5 * 1023
Digital Value = 511.5

Since the digital value must be an integer, it will be rounded to 511 or 512 (typically truncated to 511).

Real-World ADC Examples

Many common sensors produce an analog voltage output that can be read by an ADC:

  • Potentiometer: A variable resistor used as a voltage divider to create a variable voltage (e.g., for a volume knob).
  • Photoresistor (LDR): Changes resistance based on light level.
  • Thermistor / TMP36 Sensor: Changes resistance or voltage based on temperature.
  • Analog Joystick: Essentially two potentiometers for X and Y axis control.
  • Microphone Sensor Module: Converts sound pressure waves into a varying voltage.

ADC with Arduino: The `analogRead()` function

Arduino makes reading analog values incredibly simple with a built-in function.

Syntax:

analogRead(pin)

  • pin: The name of the analog input pin you want to read from (A0, A1, A2, etc.).
  • Returns: An integer value from 0 to 1023.
// Read the value from analog pin A0
int sensorValue = analogRead(A0);
// Print the value to the serial monitor
Serial.println(sensorValue);

Analog Pins on Arduino

The Arduino Uno has dedicated pins for analog input. These are connected to the microcontroller's internal ADC.

  • They are labeled A0 through A5 on the board.
  • These 6 pins form a multiplexer, which means they all feed into the single ADC on the chip, one at a time.
  • While they are primarily for analog input, they can also be used as digital I/O pins if needed.
Arduino Uno R3 board with analog input pins A0–A5 highlighted
Arduino Uno R3 showing analog input header (A0–A5) used by the ADC multiplexer.

Practical 1: Reading a Potentiometer

A potentiometer (or 'pot') is a perfect first analog sensor. It's a variable resistor that acts as an adjustable voltage divider.

Objective: Read the position of a potentiometer knob and display its corresponding digital value (0-1023) on the computer.

Components needed:

  • Arduino Uno
  • 10kΩ Potentiometer
  • Jumper Wires
  • Breadboard

Potentiometer Circuit Diagram

A potentiometer has three pins:

  1. Outer Pin 1: Connect to Ground (GND) on the Arduino.
  2. Center Pin (Wiper): Connect to an analog input pin (e.g., A0) on the Arduino. This is the pin that outputs the variable voltage.
  3. Outer Pin 2: Connect to 5V on the Arduino.
Potentiometer circuit diagram
Potentiometer connected to Arduino Uno (A0, 5V, GND).

Turning the knob moves the wiper, changing the voltage on pin A0 from 0V to 5V.

Arduino Code: Reading the Potentiometer

This code will continuously read the voltage from pin A0 and print the resulting digital value to the Serial Monitor.

The `setup()` function initializes serial communication. The `loop()` function reads the pin and prints the value every 100 milliseconds.

void setup() {
  // Initialize serial communication at 9600 baud
  Serial.begin(9600);
}
void loop() {
  // Read the analog value from pin A0
  int potValue = analogRead(A0);
  // Print the value to the Serial Monitor
  Serial.println(potValue);
  // Wait for 100ms before the next reading
  delay(100);
}

Visualizing Data: The Serial Monitor

After uploading the code, you can see the raw data from the potentiometer.

  1. Connect your Arduino.
  2. Upload the code.
  3. Go to Tools → Serial Monitor (or click the magnifying glass icon).
  4. Make sure the baud rate in the bottom-right corner is set to 9600.

As you turn the potentiometer knob, you will see a stream of numbers changing from 0 to 1023.

Plotting Data: The Serial Plotter

Reading numbers is useful, but a graph is often better. The Arduino IDE has a built-in tool for this.

  1. With the same code running, close the Serial Monitor.
  2. Go to Tools → Serial Plotter.

A new window will open, showing a real-time graph of the values being sent by the Arduino.

Turn the potentiometer knob and watch the line on the graph move up and down between 0 and 1023. This is a powerful and easy way to visualize analog sensor data!

Practical 2: Reading a Joystick

An analog joystick is like two potentiometers in one package, plus a push-button.

  • One potentiometer measures movement along the X-axis.
  • The other measures movement along the Y-axis.

Objective: Read the X and Y positions of a joystick and display their values.

Joystick Circuit Diagram

Most joystick modules have 5 pins:

  • GND: Connect to Arduino GND.
  • +5V: Connect to Arduino 5V.
  • VRx: X-axis output. Connect to an analog pin (e.g., A0).
  • VRy: Y-axis output. Connect to another analog pin (e.g., A1).
  • SW: Switch/button output (we will ignore this for now).
Joystick circuit diagram
Joystick module connected to Arduino Uno (VRx to A0, VRy to A1, 5V, GND).

Arduino Code: Reading X and Y Axes

This code reads both analog pins A0 (X-axis) and A1 (Y-axis) and prints them to the serial monitor in a comma-separated format.

When the joystick is centered, both values should be near the middle of the range (~512).

void setup() {
  Serial.begin(9600);
}
void loop() {
  // Read the X and Y axis values
  int xValue = analogRead(A0);
  int yValue = analogRead(A1);
  // Print the values
  Serial.print("X: ");
  Serial.print(xValue);
  Serial.print(", Y: ");
  Serial.println(yValue);
  delay(100);
}

Joystick on the Serial Plotter

To plot both X and Y values simultaneously on the Serial Plotter, we need to format the output correctly.

The plotter will draw a separate line for each value if they are separated by a space or a comma.

Let's modify the `println` statements.

// ... inside loop() ...
int xValue = analogRead(A0);
int yValue = analogRead(A1);
// Print X, then a comma, then Y
Serial.print(xValue);
Serial.print(",");
Serial.println(yValue);
delay(100);

Advanced: Real-time Plotting with Python

The Arduino Serial Plotter is great for quick checks, but it's limited. What if we want more control?

  • Custom plot labels and titles.
  • Save the data to a file.
  • Perform complex mathematical analysis.
  • Create a 2D plot of X vs Y.

We can achieve this by sending the serial data from the Arduino to a Python script on our computer for processing and visualization.

Setup: Python Libraries

We need two main Python libraries:

  • pyserial: To read data from the serial port (the same port the Arduino uses).
  • matplotlib: A powerful library for creating static, animated, and interactive visualizations.

You can install them using pip:

pip install pyserial
pip install matplotlib

Arduino Side: Sending Clean Data

Our Python script needs data in a consistent, easy-to-parse format. A comma-separated string followed by a newline character is ideal.

The previous joystick code is already perfect for this.

void loop() {
  int xValue = analogRead(A0);
  int yValue = analogRead(A1);
  // Send "x_val,y_val"
  Serial.print(xValue);
  Serial.print(",");
  Serial.println(yValue);
  delay(50); // A shorter delay for smoother plotting
}

Python Side: Reading the Serial Port

First, we import the `serial` library and create a serial object. You must specify the correct port name (e.g., 'COM3' on Windows, '/dev/ttyACM0' on Linux) and the baud rate (9600).

The code reads one line of data, decodes it from bytes to a string, and removes any whitespace.

import serial
# NOTE: Change 'COM3' to your Arduino's port
arduino_port = 'COM3'
baud_rate = 9600
# Connect to the serial port
ser = serial.Serial(arduino_port, baud_rate)
print(f"Connected to {arduino_port}")
# Read and print one line
line = ser.readline().decode('utf-8').strip()
print(line)

Python Side: Parsing the Data

Once we have the string (e.g., "512,480"), we need to split it at the comma and convert the two parts into numbers (integers).

A `try-except` block is good practice to handle potential errors if the data is not in the expected format.

line = "512,480" # Example line
try:
    # Split the string by the comma
    parts = line.split(',')
    # Convert parts to integers
    x_val = int(parts[0])
    y_val = int(parts[1])
    print(f"X: {x_val}, Y: {y_val}")
except (ValueError, IndexError):
    # Handle cases where data is incomplete
    print("Could not parse data:", line)

Python Side: Plotting with Matplotlib

We'll create a 2D scatter plot to show the joystick's position. The X value from the joystick will be the plot's x-coordinate, and the Y value will be the y-coordinate.

We use `plt.ion()` for interactive mode, allowing the plot to update in real-time.

import matplotlib.pyplot as plt
# Enable interactive mode
plt.ion()
# Create a figure and axis for the plot
fig, ax = plt.subplots()
ax.set_xlim(0, 1023) # Set axis limits
ax.set_ylim(0, 1023)
ax.set_title("Real-time Joystick Position")
ax.set_xlabel("X-axis")
ax.set_ylabel("Y-axis")
# Plot a single point
point, = ax.plot(512, 512, 'ro') # 'ro' = red circle

Python Side: The Main Loop

Now we combine everything into a loop that continuously:

  1. Reads a line from the Arduino.
  2. Parses the X and Y values.
  3. Updates the position of the point on the plot.
  4. Redraws the plot.

This loop runs until you stop the script (e.g., with Ctrl+C).

Complete Python Script (Part 1: Setup)

import serial
import matplotlib.pyplot as plt
import time
# --- Setup Serial Connection ---
# NOTE: Change 'COM3' to your Arduino's port
try:
    ser = serial.Serial('COM3', 9600, timeout=1)
except serial.SerialException as e:
    print(f"Error opening serial port: {e}")
    exit()
time.sleep(2) # Wait for connection to establish
# --- Setup Matplotlib Plot ---
plt.ion()
fig, ax = plt.subplots()
point, = ax.plot(512, 512, marker='o', linestyle='none', color='r')
ax.set_title("Real-time Joystick Position")
ax.set_xlim(0, 1023)
ax.set_ylim(0, 1023)
ax.grid(True)

Complete Python Script (Part 2: Loop)

# --- Main Loop ---
try:
    while True:
        if ser.in_waiting > 0:
            line = ser.readline().decode('utf-8').strip()
            try:
                x_val_str, y_val_str = line.split(',')
                x_val = int(x_val_str)
                y_val = int(y_val_str)
                # Update plot data
                point.set_data(x_val, y_val)
                fig.canvas.draw()
                fig.canvas.flush_events()
            except (ValueError, IndexError):
                print(f"Could not parse: {line}")
except KeyboardInterrupt:
    print("Plotting stopped by user.")
finally:
    ser.close()

Summary

  • The real world is analog; microcontrollers are digital.
  • The ADC is the bridge between these two worlds.
  • Resolution (e.g., 10-bit) and Reference Voltage (e.g., 5V) determine the ADC's precision.
  • Arduino's analogRead(pin) function makes it easy to get a digital value (0-1023).
  • We can read sensors like potentiometers and joysticks.
  • The Serial Plotter is a quick tool for visualization.
  • Python with pyserial and matplotlib offers powerful, customizable, real-time data plotting.

What's Next?

Filtering and Debouncing Techniques


Q & A