Build Your Own Oscilloscope
A Pico, a divider probe and two short programs turn into a working scope — then your blinker, your dimmer and your night-light become its first test subjects.
Builds on: 12.1 Program the Night-Light13.1 Sampling & Aliasing2.2 Voltage Dividers15.2 The Debugging Mindset
The mission
Every lab in this course drew an oscilloscope trace for you. This build closes the loop on the entire curriculum: you will make a real sampling oscilloscope from your Pico, use it to measure the RC charging curve, the blinker’s 3-to-6-volt sawtooth and the dimmer’s PWM — and compare physics against everything the simulators promised. An instrument you built, verifying circuits you built, using theory you own. That is what mastery looks like.
Everything needed is already on your desk: the Pico’s ADC (11.2) is the sampler, Nyquist (13.1) sets its honest limits, a voltage divider (2.2!) becomes the probe, and Thonny’s built-in plotter is the screen. New parts required: none — two resistors from your kit build the probe.
The probe: a divider with a job title
Your circuits under test run at 9 V; the Pico’s ADC dies above ~3.6 V. The fix is the very first trick this course taught you — a 3:1 voltage divider: 100 kΩ from the probe tip to GP26, 47 kΩ from GP26 to ground (÷3.13; software multiplies back). Real 10:1 scope probes are exactly this idea with fancier tolerances. Two fine points, both old friends:
- Loading (2.2's trap): the probe's ~150 kΩ hangs on whatever you touch. On the 555's output pin — a stiff, low-impedance source — it's invisible. Directly on the high-impedance cap node it would gently distort the timing. Probe outputs by preference; probe delicate nodes knowingly.
- Common ground: voltage is between two points (0.2's oldest lesson). The Pico's GND and the blinker's − rail must be wired together, or your readings are fiction.
Two firmwares, one instrument
Live mode streams readings for Thonny’s plotter (View → Plotter) — a few hundred samples per second, perfect for the slow beauties: RC curves, blinker sawtooth, the night-light’s dusk.
from machine import ADC
from time import sleep_ms
probe = ADC(26)
# LIVE MODE - open Thonny's View > Plotter and watch
while True:
v = probe.read_u16() * 3.3 / 65535
print(v * 3.128) # undo the 100k/47k divider (which divides by 3.13)
sleep_ms(2) # ~400 samples per secondBurst mode samples flat-out into memory first (tens of thousands of samples per second — MicroPython’s honest limit), then prints the capture: enough to resolve your 1.4 kHz PWM cleanly. It even measures its own sample rate, because an instrument that doesn’t know its own fs can’t warn you about Nyquist.
from machine import ADC
from time import ticks_us, ticks_diff
probe = ADC(26)
def capture(n=2000):
buf = bytearray(2 * n)
t0 = ticks_us()
for i in range(n): # tight loop: tens of kS/s
x = probe.read_u16()
buf[2*i] = x & 0xFF
buf[2*i + 1] = x >> 8
us = ticks_diff(ticks_us(), t0) / n
print("# sample interval:", us, "us -> fs =", 1e6 / us, "Hz")
for i in range(n):
x = buf[2*i] | (buf[2*i + 1] << 8)
print(x * 3.3 / 65535 * 3.128)
capture()The measurement campaign
| Target | Probe point | You should see | Closes the loop on |
|---|---|---|---|
| RC charge: 10 µF via 100 kΩ to 9 V | capacitor top | the exponential; 63% at τ ≈ 1 s | Lesson 2.3 |
| 555 blinker | pin 3 (output) | square, ~1.5 Hz — measure it against f = 1.44/((R1+2R2)C) | Lessons 3.3 & 4.1 |
| 555 blinker | pins 2+6 (gently!) | the sawtooth between ⅓ and ⅔ of 9 V | the simulator's signature trace |
| PWM dimmer | pin 3, burst mode | duty following the knob at ~1.4 kHz | Unit 8 & Nyquist (13.1) |
| Night-light at dusk | GP26 divider node | the slow slide your twin predicted | Unit 12 |
If it misbehaves
| Symptom | Likely cause | Fix |
|---|---|---|
| Readings pinned at 3.3 V | probing 9 V without the divider — the input was overdriven | build the 100k/47k probe, then check the pin still reads sensibly (above ~3.6 V you may have damaged it) |
| Readings are noise/garbage | no common ground | jumper Pico GND to the target's − rail |
| PWM looks like a slow wave | aliasing in live mode | that's Lesson 13.1 in the wild! use burst mode |
| Sawtooth timing looks stretched | probe loading the cap node | expected (2.2!) — measure frequency at pin 3 instead |
| Plotter shows nothing | plotter window closed / prints too fast | View → Plotter in Thonny; keep the sleep_ms in live mode |
Graduation
Look at the bench: a blinker designed with RC arithmetic, a dimmer steered with diodes, a night-light running your firmware, and now a measuring instrument that audits them all — every one built from parts you understand down to the drifting electron. There is no fifth tier — the ladder ends here, on purpose. What remains are the specializations: parallel branches, in any order, into CPUs, the radio spectrum, and robots. Pick whichever pulls hardest — you are fully equipped to debug the gap between the idea and the working thing. That gap has a name. It’s called engineering. Welcome to it. ⚡
⚡ Lab — Digital Twin — Rehearse the Campaign
Your Pico-scope, simulated one last time: pick a target, a sample rate and a probe, and rehearse every measurement before touching hardware.
- Probe the PWM at 1 kHz: a phantom 400 Hz wave (1.4 kHz, folded — Lesson 13.1). At 200 Hz the samples flatline, because 1 400 is an exact multiple of 200. Only the fastest rate tells the truth.
- Switch to a direct wire on a 9 V target and watch the trace clip at 3.3 V.
- Sawtooth at 1 kHz with the divider probe: the textbook capture. Do this one for real first.