BFM1143 Embedded System Programming - Week 5: ADC & Sensor Interfacing

Analog Signals

  • An analog signal is a continuous-time signal representing physical parameters with smoothly varying values over time.
  • It allows for an infinite number of values within a range, unlike digital signals which have discrete states.

Core Characteristics

  • Continuity: No gaps in the signal; it can have any value within a range.
  • Amplitude Range: Often voltage-based, such as 0-5V for Arduino Uno analog inputs.
  • Time-Varying: Changes over time in real-time scenarios.
  • Smooth Curves: Graphs are smooth, without steps or jumps.

Analog Signal Parameters

  • Amplitude (A): The peak value of the signal.
  • Peak-to-Peak Amplitude (Vpp): The difference between the maximum and minimum values of the signal.
  • RMS Voltage (Vrms): The root mean square voltage, representing the effective voltage of the signal.
  • Period (T): The time it takes for one complete cycle of the signal.
  • Frequency (f): The number of cycles per second, f=1/Tf = 1/T.
  • Phase (ϕ\phi): The initial angle of the signal at time zero.
  • Offset (Vo): The DC component of the signal, shifting the entire waveform up or down.

Analog vs Digital Signals

FeatureAnalog SignalDigital Signal
NatureContinuous (smooth curve)Discrete (step-wise values)
ValuesInfinite within a rangeFinite - typically two (0 and 1)
FormVarying voltage over timeHigh or Low, ON or OFF, 0s and 1s
RepresentationReal-world phenomenaBinary logic
SusceptibilityProne to noise/interferenceMore noise-resistant
ProcessingNeeds ADC for microcontrollersDirectly compatible with digital logic

Real-World Examples

Real-Life SourceSignal BehaviorType of Sensor
Voice from microphoneVaries with loudness/pitchMicrophone
Temperature in a roomIncreases or decreases smoothlyLM35 / Thermistor
Light intensityGradual brightness changesLDR (Light Dependent Resistor)
Potentiometer knobChanges voltage based on positionPotentiometer

Importance in Embedded Systems

  • Sensors produce analog data like temperature, light, and pressure.
  • Arduino reads analog signals to:
    • Detect environmental conditions.
    • Make decisions (e.g., turn on fan when hot).
    • Control devices like LEDs, motors, and servos.

ADC (Analog-to-Digital Converter)

  • ADC translates continuous analog voltage (e.g., 0-5V) into a discrete digital value that a microcontroller can process.
  • Analog → Digital = Physical quantity → Binary number
  • The analogRead() function in Arduino Uno (ATmega328P) performs this conversion.

ADC Block Diagram

  1. Sampler: Takes samples from the continuous analog signal at a specific sampling frequency, converting the signal into continuous amplitude-discrete time.
  2. Holding Circuit: Holds the sampled value until the next sample arrives.
  3. Quantizer: Converts the continuous amplitude-discrete time signal into a discrete time-discrete amplitude signal by splitting the samples into small parts.
  4. Encoder: Generates the digital signal in binary form.

ADC Characteristics in Arduino Uno (ATmega328P)

FeatureSpecification
Resolution10-bit → 0 to 1023
Voltage Range0V to VrefV_{ref} (usually 5V)
Number of Channels6 analog input pins (A0–A5)
Default Ref.5V (from USB or external supply)
Internal Ref.1.1V (optional via analogReference(INTERNAL))

Internal Reference Voltage (Vref)

  • The internal reference voltage is a fixed, stable voltage generated inside the microcontroller used as the upper limit for ADC conversions.
Why Use an Internal Reference?
  • The default 5V supply can fluctuate, introducing noise or inaccuracy.
  • Solution: Use the internal 1.1V reference for more stable and precise ADC results, especially when measuring low-voltage sensors.
Available Reference Options
Reference TypeVoltageDescription
DEFAULT~5.0VVcc (from USB or external)
INTERNAL1.1VInternal stable reference (built-in)
EXTERNALCustomVoltage applied to AREF pin (< 5V) !
Use Case Example
  • Measuring voltage from an LM35 temperature sensor (10mV per °C):
    • Using 5V reference: 1C1^{\circ}C change = ~2 digital steps (low precision).
    • Using 1.1V internal ref: 1C1^{\circ}C change = ~9 steps (better resolution).

Basic Conversion Process

  1. Sensor produces analog voltage (e.g., 2.3V).
  2. ADC samples this voltage and converts it to a digital number.
    • Based on:
      • Reference voltage
      • Resolution (bit depth)
Example:
  • With 10-bit ADC & 5V reference.

Example Code

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

void loop() {
  int rawValue = analogRead(A0);
  float voltage = rawValue * (5.0 / 1023.0);
  Serial.print("Raw ADC: ");
  Serial.print(rawValue);
  Serial.print(" Voltage: ");
  Serial.println(voltage, 3); // 3 decimal places
  delay(500);
}

How to Use INTERNAL Reference

void setup() {
  analogReference(INTERNAL); // Set ADC reference to 1.1V
  Serial.begin(9600);
}

void loop() {
  int sensorValue = analogRead(A0);
  float voltage = sensorValue * (1.1 / 1023.0); // Use 1.1V as reference
  Serial.println(voltage);
  delay(500);
}

Resolution in ADC

  • Resolution is the smallest measurable change in analog input that the ADC can detect.
  • Higher resolution (more bits) means finer granularity of measurements.
  • Formula for Resolution: Resolution=Vref2nResolution = \frac{V_{ref}}{2^n}
    • Where:
      • VrefV_{ref} is the reference voltage.
      • nn is the bit depth of the ADC (10 for Arduino Uno).

Resolution Calculation for Arduino Uno (10-bit ADC)

  • ADC range is 0 to 1023 (21012^{10} - 1).
  • VrefV_{ref} is 5V (default).
  • Resolution=5V210=5V10240.0049V4.9mVResolution = \frac{5V}{2^{10}} = \frac{5V}{1024} \approx 0.0049V \approx 4.9 mV

Sampling Rate in ADC

  • Sampling Rate is the number of times per second the analog signal is sampled (measured) by the ADC.
  • Measured in samples per second (SPS) or Hertz (Hz).
  • For Arduino Uno (ATmega328P):
    • The default sampling rate for analogRead() is approximately 9.6 kHz (9600 samples per second).

Calculation of Arduino Sampling Rate

  • ADC Clock is derived from the system clock (16 MHz) and divided by a prescaler.
  • Sampling rate is affected by the ADC clock and conversion time per sample.
  • The formula for the sampling rate (samples per second) is:
    • SamplingRate=ADCClockConversionTimeSampling Rate = \frac{ADC Clock}{Conversion Time}
  • For Arduino Uno (16 MHz clock and default prescaler of 128):
    • ADC conversion time per sample is approximately 104 µs.
    • SamplingRate=16×106128×104×1069.6kHzSampling Rate = \frac{16 \times 10^6}{128 \times 104 \times 10^{-6}} \approx 9.6 kHz

Why is Sampling Rate Important?

  • Higher Sampling Rate = More Accurate Representation.
  • Trade-Offs:
    • Higher Sampling Rate → More power consumption, higher processing load.
    • Lower Sampling Rate → Lower accuracy for high-frequency signals.
Example Scenarios:
  • Scenario 1: Low Sampling Rate (Slow Signal)
    • Measuring a slowly varying signal like temperature; a lower sampling rate is sufficient.
  • Scenario 2: High-Frequency Signal
    • Measuring an audio signal or fast-changing waveform; a sampling rate of 9.6 kHz might not be enough, as you may lose important details. A faster microcontroller or external ADC with a higher sampling rate may be necessary.

Practical Considerations

  1. Analog Signal Frequency: Ensure the signal you're measuring is below half of the sampling rate to avoid aliasing (Nyquist Theorem).
    • With a sampling rate of 9.6 kHz, you can accurately sample signals up to 4.8 kHz.
  2. Arduino’s Sampling Rate Limitation: The Arduino Uno is limited by the ADC clock and processing power.
    • If you need a faster sampling rate, consider using external high-speed ADCs.
Practical Example:
  • Reading data from an accelerometer, which typically has a high-frequency signal (up to several kHz), may require a faster ADC.

Optimizing Sampling Rate in Arduino

  • To increase the sampling rate of analogRead() on Arduino Uno:
    • Reduce the delay between readings.
    • Use analogRead() with a lower resolution (if lower precision is acceptable).
    • Consider using direct register access to bypass some of the default overhead in Arduino's analogRead().

Practical Setups

Practical Setup #1 - Potentiometer

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

void loop() {
  int potValue = analogRead(A0);
  Serial.println(potValue);
  delay(100);
}
  • Open Tools > Serial Plotter in Arduino IDE to visualize the potentiometer values.

Practical Setup #2 - LM35 Temperature Sensor

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

void loop() {
  int raw = analogRead(A1);
  float voltage = raw * (5.0 / 1023.0);
  float temperatureC = voltage * 100;
  Serial.print("Temp: ");
  Serial.print(temperatureC);
  Serial.println(" °C");
  delay(1000);
}
Challenge
  • Read both potentiometer and LM35.
  • Display both in Serial Monitor.

Improving Accuracy in ADC Readings

  • Raw analog readings can fluctuate due to:
    • Electrical noise
    • Sensor instability
    • Rapid environmental changes
  • Unstable readings lead to inaccurate measurements.

Common Accuracy-Improving Techniques

TechniqueDescription
Averaging (Smoothing)Take multiple samples and compute their average.
Median FilteringRemove outliers by choosing the middle value.
Software DebouncingIgnore fast spikes/noise in analog inputs.
Shielding/GroundingPhysically protect wires from EMI (hardware-level).
Low-pass FilteringFilter high-frequency noise in software or hardware.

Benefits of Averaging

  • Reduces noise from erratic ADC readings.
  • Especially useful in:
    • Temperature sensors (LM35, DHT11 analog mode)
    • Light sensors (LDRs)
    • Potentiometers for stable input tracking
  • Adds minimal computational overhead.

Rolling Average (Moving Average Filter)

  • Keeps a fixed number of past readings, adds the new one, removes the oldest, and averages the rest.
  • Keeps your data responsive but smooth.
Use When:
  • You want stable but fast-reacting values.
  • You're smoothing data in real time (e.g., sensor-based UI sliders).

Exponential Smoothing (Low-Pass Filter)

  • Blends the previous smoothed value and the new sensor reading using a weighting factor (alpha).
  • Formula: SmoothValue=α×Raw+(1α)×SmoothValueSmoothValue = \alpha \times Raw + (1 - \alpha) \times SmoothValue
const int sensorPin = A0;
float alpha = 0.1; // Smoothing factor (try 0.05 - 0.3)
float smoothValue = 0;

void setup() {
  Serial.begin(9600);
  smoothValue = analogRead(sensorPin); // Initialize with first reading
}

void loop() {
  int raw = analogRead(sensorPin);
  smoothValue = alpha * raw + (1 - alpha) * smoothValue;
  Serial.println((int) smoothValue); // Print smoothed value as int
  delay(50);
}

Rolling vs. Exponential Smoothing

FeatureRolling AverageExponential Smoothing
Memory UsageHigher (needs array)Very low
Reacts to ChangesSlower (delayed by N)Faster
Best ForStable, noise-heavy dataReal-time feedback with smoothness
ComplexityModerateSimple