70 Signal Processing Labs and Assessments
70.1 Learning Objectives
By the end of this chapter, you will be able to:
- Configure ESP32 ADC: Set up analog-to-digital conversion with appropriate resolution and sampling rate
- Implement Digital Filters: Apply moving average, median, and low-pass filters in firmware
- Perform FFT Analysis: Use frequency analysis to identify signal components and noise
- Design Filter Pipelines: Combine anti-aliasing, digital filters, and downsampling for optimal performance
- Validate Understanding: Demonstrate mastery through hands-on exercises and knowledge checks
Prerequisites: - Signal Processing Overview - Sampling and Nyquist theorem - Aliasing and ADC - Quantization and filtering - Voice Compression - Audio signal processing - Sensor Dynamics - Temporal response
Practical: - Hardware Prototyping - ESP32 setup - Sensor Labs - Additional sensor exercises
70.2 Prerequisites
- All previous signal processing chapters: This is the capstone practical exercise
- ESP32 familiarity: Basic understanding of microcontroller programming
- C programming: Variables, arrays, loops, functions
70.3 Signal Processing Lab: ESP32 Wokwi Simulation
70.3.1 Interactive Lab Overview
This lab uses Wokwi ESP32 simulator - no physical hardware required! Youβll explore:
- ADC Resolution Effects: Compare 8-bit, 10-bit, and 12-bit quantization
- Filter Comparison: Test moving average, median, and low-pass filters
- FFT Frequency Analysis: Identify signal components and noise frequencies
- Multi-Stage Pipeline: Design complete signal processing chain
Time Required: 60-90 minutes (all exercises)
70.3.2 Getting Started with Wokwi
Access the Lab: 1. Open Wokwi ESP32 Simulator 2. Create new ESP32 project 3. Copy lab code from exercises below 4. Click βRunβ to start simulation
Simulator Tips: - Use Serial Monitor to view output - Pause simulation to inspect values - Adjust potentiometer to change analog input - Reset to restart from beginning
70.4 Exercise 1: Understanding ADC Resolution
Objective: Compare 8-bit vs 12-bit ADC precision
Setup: - Connect potentiometer to GPIO34 (ADC1_CH6) - Read analog values at different resolutions - Calculate voltage from ADC reading
Code Template:
#include <Arduino.h>
void setup() {
Serial.begin(115200);
analogReadResolution(12); // Change to 8, 10, or 12
pinMode(34, INPUT);
}
void loop() {
int adc_value = analogRead(34);
float voltage = (adc_value / 4095.0) * 3.3; // For 12-bit
Serial.print("ADC: ");
Serial.print(adc_value);
Serial.print(" | Voltage: ");
Serial.print(voltage, 4); // 4 decimal places
Serial.println(" V");
delay(500);
}Tasks: 1. Run with 8-bit resolution (0-255 range) 2. Run with 12-bit resolution (0-4095 range) 3. Calculate step size for each: 3.3V / (2^bits - 1) 4. Observe precision difference in Serial Monitor
Expected Results: - 8-bit: Step size = 12.9 mV, Β±0.4% precision - 12-bit: Step size = 0.8 mV, Β±0.025% precision
Question: If your sensor accuracy is Β±1%, which ADC resolution is sufficient?
70.5 Exercise 2: Comparing Filter Types
Objective: Test median vs moving average filters on noisy data
Setup: - Simulated noisy temperature sensor - Inject random spikes to test outlier rejection - Compare filter outputs
Code Template:
#include <Arduino.h>
// 5-point moving average
float movingAverage(float newValue) {
static float buffer[5] = {0};
static int index = 0;
buffer[index] = newValue;
index = (index + 1) % 5;
float sum = 0;
for (int i = 0; i < 5; i++) sum += buffer[i];
return sum / 5.0;
}
// 5-point median filter
float medianFilter(float newValue) {
static float buffer[5] = {0};
static int index = 0;
buffer[index] = newValue;
index = (index + 1) % 5;
// Sort (bubble sort for simplicity)
float sorted[5];
memcpy(sorted, buffer, sizeof(buffer));
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4-i; j++) {
if (sorted[j] > sorted[j+1]) {
float temp = sorted[j];
sorted[j] = sorted[j+1];
sorted[j+1] = temp;
}
}
}
return sorted[2]; // Middle value
}
void setup() {
Serial.begin(115200);
}
void loop() {
// Simulated sensor with noise and occasional spike
float trueValue = 23.5;
float noise = random(-10, 10) / 100.0; // Β±0.1Β°C
float spike = (random(0, 100) < 5) ? 50.0 : 0; // 5% chance of spike
float reading = trueValue + noise + spike;
float avg = movingAverage(reading);
float med = medianFilter(reading);
Serial.print("Raw: ");
Serial.print(reading);
Serial.print(" | Avg: ");
Serial.print(avg);
Serial.print(" | Median: ");
Serial.println(med);
delay(200);
}Tasks: 1. Run simulation and observe outputs 2. Note how moving average reacts to spikes 3. Note how median filter handles spikes 4. Which filter is better for spike rejection?
Expected Results: - Moving Average: Spikes corrupt output for 5 samples - Median Filter: Spikes have no effect if <50% of buffer
70.6 Exercise 3: FFT Frequency Analysis
Objective: Use Fast Fourier Transform (FFT) to identify signal frequencies
Setup: - Generate composite signal (1 Hz + 10 Hz + noise) - Apply FFT to find frequency components - Visualize frequency spectrum
Code Template:
#include <Arduino.h>
#include "arduinoFFT.h"
#define SAMPLES 128
#define SAMPLING_FREQ 100 // 100 Hz
double vReal[SAMPLES];
double vImag[SAMPLES];
ArduinoFFT<double> FFT = ArduinoFFT<double>(vReal, vImag, SAMPLES, SAMPLING_FREQ);
void setup() {
Serial.begin(115200);
}
void loop() {
// Generate composite signal
for (int i = 0; i < SAMPLES; i++) {
float t = i / (float)SAMPLING_FREQ;
vReal[i] = sin(2.0 * PI * 1.0 * t) // 1 Hz component
+ 0.5 * sin(2.0 * PI * 10.0 * t) // 10 Hz component
+ (random(-10, 10) / 100.0); // Noise
vImag[i] = 0;
}
// Perform FFT
FFT.windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.compute(FFT_FORWARD);
FFT.complexToMagnitude();
// Find peak frequency
double peak = FFT.majorPeak();
Serial.println("Frequency Spectrum:");
for (int i = 0; i < SAMPLES/2; i++) {
double freq = (i * SAMPLING_FREQ) / (double)SAMPLES;
Serial.print(freq, 2);
Serial.print(" Hz: ");
Serial.println(vReal[i], 2);
}
Serial.print("Major Peak at: ");
Serial.print(peak, 2);
Serial.println(" Hz\n");
delay(5000);
}Tasks: 1. Run FFT and observe frequency spectrum 2. Identify peaks at 1 Hz and 10 Hz 3. Change signal frequencies and re-run 4. What happens if sampling rate is too low (aliasing)?
Expected Results: - Peaks at 1 Hz and 10 Hz visible - Noise spread across all frequencies - If fs < 20 Hz, aliasing occurs (10 Hz appears as lower frequency)
70.7 Exercise 4: Designing a Multi-Stage Filter Pipeline
Objective: Combine anti-aliasing, digital filtering, and downsampling
Setup: - Sample at 1000 Hz - Apply low-pass filter (cutoff 100 Hz) - Downsample to 250 Hz for transmission
Code Template:
#include <Arduino.h>
// Simple low-pass IIR filter (exponential moving average)
float lowPassFilter(float newValue, float alpha = 0.1) {
static float filtered = 0;
filtered = alpha * newValue + (1 - alpha) * filtered;
return filtered;
}
int downsampleCounter = 0;
void setup() {
Serial.begin(115200);
}
void loop() {
// Sample at 1000 Hz (1 ms period)
float rawValue = analogRead(34) * (3.3 / 4095.0);
// Apply low-pass filter (anti-aliasing)
float filtered = lowPassFilter(rawValue, 0.2); // ~80 Hz cutoff
// Downsample 4:1 (1000 Hz β 250 Hz)
downsampleCounter++;
if (downsampleCounter >= 4) {
downsampleCounter = 0;
Serial.print("Filtered & Downsampled: ");
Serial.println(filtered, 4);
}
delayMicroseconds(1000); // 1 kHz sampling
}Tasks: 1. Implement pipeline and observe output 2. Try removing low-pass filter - what happens? 3. Try different downsample ratios (2:1, 8:1) 4. Calculate data rate reduction (1000 Hz β 250 Hz = 75% savings)
Expected Results: - Smooth signal at 250 Hz output rate - High-frequency noise removed - 4Γ reduction in data transmission
70.8 Learning Outcomes Checklist
After completing the labs, you should be able to:
70.9 Key Takeaways from the Lab
- ADC Resolution: 12-bit is sufficient for most IoT sensors (Β±0.025% precision)
- Filter Selection: Use median for spikes, low-pass for gradual noise
- FFT Analysis: Reveals hidden frequency components and noise sources
- Pipeline Design: Combine filters and downsampling to reduce data rate
- Practical Constraints: Balance precision, speed, and power consumption
70.10 Whatβs Next
Continue Learning: - Sensor Labs: Apply signal processing to real sensors - Multi-Sensor Fusion: Combine signals from multiple sources - Edge Computing: Process signals locally to reduce cloud bandwidth
Challenge Projects: 1. Build a vibration monitor using accelerometer + FFT 2. Create a smart thermostat with filtering and deadband control 3. Design a water quality sensor with multi-parameter fusion
Advanced Topics: - Kalman filtering for sensor fusion - Adaptive filters for time-varying noise - Machine learning on sensor data (feature extraction from FFT)