The Reality of Using a Raspberry Pi as an Oscilloscope

To use a Raspberry Pi as an oscilloscope, you cannot sample analog signals directly from its GPIO pins. The Pi lacks a native analog-to-digital converter (ADC) and hardware-timed sampling on its standard pins. You must add an external ADC HAT (like the MCP3208 SPI chip) for low-frequency signals (under 10 kHz) or a dedicated USB oscilloscope adapter (like the BitScope Micro or PicoScope) for MHz-range signals.

A Raspberry Pi 5 running Python via the spidev library can poll an MCP3208 12-bit ADC at roughly 10,000 to 20,000 samples per second (SPS). This is sufficient for audio waveforms, slow-moving sensor data, and basic PWM envelope tracking. However, if you need to decode high-speed I2C, SPI, or RF envelopes, the Pi's OS-level jitter will ruin your capture. For those tasks, a $150+ USB oscilloscope tethered to the Pi via USB is mandatory. Below, we focus on the DIY MCP3208 SPI ADC approach, which costs under $15 and turns your Pi into a capable low-bandwidth scope.

Pre-Flight Verification & Meter Setup

Before connecting delicate scope probes or ADC inputs to an unknown circuit, you must verify the signal baseline and the Pi's 3.3V rail integrity. Feeding a 5V signal into a Pi 5's 3.3V GPIO or an ADC reference pin will instantly destroy the silicon. Use a digital multimeter (DMM) for this pre-flight check.

Multimeter Pre-Flight Setup Block
  • Dial Position: DC Voltage (V⎓ or VDC).
  • Lead Jacks: Black lead to COM, Red lead to V/Ω/mA.
  • Range: Auto-ranging, or manual 20V DC if your meter lacks auto-range.

Procedure: Measure the Pi's Pin 1 (3.3V) against Pin 6 (GND). Then, measure your target circuit's output voltage. If the target exceeds 3.2V, you must use a voltage divider or an op-amp buffer before it reaches the Pi's ADC.

Python Capture Script for MCP3208

Once hardware is verified, enable SPI via sudo raspi-config and install the library with pip install spidev. This complete script captures 1,000 samples from Channel 0 and exports them to a CSV for viewing in any waveform software or spreadsheet.

import spidev
import time
import csv

spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000

def read_adc(channel):
    if channel < 0 or channel > 7:
        return -1
    # MCP3208 start bit, single-ended channel select, MSB first
    r = spi.xfer2([1, (8 + channel) << 4, 0])
    return ((r[1] & 3) << 8) + r[2]

with open('scope_data.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['Time_ms', 'Voltage'])
    for i in range(1000):
        val = read_adc(0)
        voltage = (val * 3.3) / 4095
        writer.writerow([round(i * 2.5, 2), round(voltage, 4)])
        time.sleep(0.0025) # ~400 SPS reliable loop rate in Python
spi.close()

Probe Placement & Expected Readings

Proper probing technique prevents ground loops and noisy captures. Always connect the ground reference first, then the signal probe. For the Pi's 40-pin header, use female-to-female Dupont jumper wires with mini-grabber clips soldered to the ends for secure connections.

Expected Readings: Pi ADC Pre-Flight & Signal Verification
Test Point Probe Placement Expected Good Value Bad Value (Troubleshooting)
3.3V Rail Red to Pin 1, Black to Pin 6 3.28V - 3.32V DC < 3.1V (Brownout/PSU fail) or > 3.4V (Regulator fault)
ADC Ground Ref Red to Pin 9 (GND), Black to Pin 6 (GND) < 0.01V (10mV) DC > 0.05V (Ground loop or loose header pin)
SPI Clock (SCLK) Red to Pin 23, Black to Pin 6 0V idle, toggling to 3.3V during capture Stuck at 0V (SPI not enabled) or 5V (Level shifter fault)
Analog Input (CH0) Red to MCP3208 CH0 pin, Black to Pi GND Matches DMM reading ± 0.02V Stuck at 3.3V (Floating input) or 0V (Short to GND)

Critical Mistakes That Yield Misleading Waveforms

When using a software-defined scope on a non-real-time OS like Linux, you will encounter artifacts that look like real signal anomalies but are actually capture errors.

1. Aliasing from Undersampling: According to the Nyquist-Shannon sampling theorem, you must sample at least twice the frequency of your highest signal component. If you are sampling a 5 kHz audio tone but your Python loop only achieves 8,000 SPS due to OS context switching, you will see a false, lower-frequency "beat" waveform. Fix: Use hardware SPI DMA or a dedicated USB scope for anything above 2 kHz.

2. Ground Loop Hum: If your Pi is powered by a laptop USB port, and you probe a circuit powered by a separate bench supply, you may see a 50/60 Hz sine wave overlaid on your DC signal. This is a ground loop. Fix: Ensure both devices share a single, common ground point, or use an isolated USB hub for the Pi.

3. Floating ADC Inputs: An unconnected MCP3208 channel will not read 0V. The sample-and-hold capacitor retains charge, resulting in random, noisy readings between 0V and 3.3V. Fix: Tie unused channels to GND via a 10kΩ resistor.

⚠️ Safety Category (CAT) Rating Warning

The Raspberry Pi and standard DIY ADC HATs are Unclassified / CAT I at best. They are strictly for low-voltage, isolated electronics bench work. NEVER use a Pi-based oscilloscope to measure AC mains voltage (120V/240V), appliance wiring, or automotive ignition coils. Doing so lacks the required CAT II/III isolation and let-through current protection, posing a severe risk of electrocution, fire, and catastrophic destruction of your Pi and connected PC. For mains measurements, use a properly rated, isolated handheld oscilloscope or a CAT III rated true-RMS multimeter.

Frequently Asked Questions

Can I use a Raspberry Pi as an oscilloscope for AC mains voltage?

No. A Raspberry Pi and its associated ADC HATs lack the galvanic isolation, creepage distances, and high-energy fuse protection required for mains voltage. Probing 120V or 240V AC will instantly vaporize the ADC chip, send lethal voltage through the Pi's GPIO header into the main processor, and potentially electrocute the user. To log AC mains waveforms, you must use an isolated current transformer (CT) clamp or a specialized, fully isolated mains voltage sensor module (like the ZMPT101B, properly enclosed and rated) that steps the voltage down to a safe 0-3.3V analog signal before it ever reaches the Pi.

What sample rate do I need to capture I2C or SPI with a Pi?

To reliably decode digital protocols like I2C or SPI, you need a sample rate at least 5 to 10 times faster than the protocol's clock speed to accurately capture rise times and edge transitions. For a standard 100 kHz I2C bus, you need a minimum capture rate of 1 MSPS (1 million samples per second). The Pi's GPIO running Python cannot achieve this. You must use a dedicated USB logic analyzer (like a $15 Saleae clone running sigrok or PulseView on the Pi) or a USB oscilloscope. The official Raspberry Pi hardware simply does not have the hardware-timed GPIO sampling required for high-speed digital protocol decoding.

Why does my Pi oscilloscope show noisy or aliased waveforms?

Noise and aliasing on a Pi-based scope almost always stem from OS-level jitter and insufficient sampling rates. Linux is not a real-time operating system; background tasks (like Wi-Fi polling or logging) will interrupt your Python sampling loop, creating uneven time gaps between samples. When these uneven samples are plotted on a uniform time axis, high-frequency signals fold back into the visible spectrum as low-frequency noise (aliasing). To mitigate this, close all non-essential background services, use hardware SPI with DMA (Direct Memory Access) instead of software polling, or switch to a microcontroller like an ESP32 or Arduino to handle the high-speed sampling and stream the buffer to the Pi via UART for display.