Blinking
A wink is one eye. A blink is both eyes at once, and it means something entirely different — not a signal to you, but a sign of life. This lab closes both eyes on a button press, which turns the face from something that reacts to a timer into something that reacts to you.
Reading a Button
Both buttons in this kit are wired the same way: one leg to a GPIO pin, the other leg to GND, with
the pin configured as a PULL_UP input.
1 | |
PULL_UP holds the pin at 1 until a press pulls it down to 0. So a pressed button reads 0,
which feels backwards the first time and never again.
| Pin reading | What it means |
|---|---|
| 1 | Not pressed — the internal pull-up resistor is holding the line high |
| 0 | Pressed — the button has connected the pin to GND |
Button A is GP14 and button B is GP15, set once in config.py as BUTTON_A_PIN and
BUTTON_B_PIN. Every kit in this book uses those same two pins. On the Waveshare RP2040-LCD-1.28
the board has no buttons of its own, so these go on free GPIO pins along the edge.
Why Debounce Exists
A button's metal contacts physically bounce for a few milliseconds when they meet, so one press can look like five to a program fast enough to notice. Waiting 20 ms and checking again is the whole fix.
1 2 3 4 5 | |
Sample Program Code
The mouth is drawn once and never touched again. set_eyes() erases both eye boxes and rebuilds
them in whichever state you ask for.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | |
Here's the resting face, between blinks:

Why wait_for_release() Matters
Without it, a finger held on the button for half a second would trigger dozens of blinks — the loop
runs far faster than you can lift your hand. wait_for_release() turns "the button is down" into
"the button was just pressed," which is almost always what you actually mean.
1 2 3 | |
This Loop Is Blocking, and That Is a Real Cost
While blink_once() runs its sleep(0.15), nothing else in the program happens — no second button, no timer, no animation. That is fine here and a serious problem later, which is exactly what the Don't Block the Loop lab is about.
Things to Try
- Delete the debounce sleep and press the button twenty times. Count how many blinks you get. The extra ones are real electrical events, not a software bug.
- Change the blink hold from 0.15 to 0.6 seconds. A slow blink reads as sleepy or bored; a fast one reads as alert. You are tuning personality with a single number.
- Blink twice per press. Two
blink_once()calls with a short gap read very differently from one long blink. - Remove
wait_for_release()and hold the button down. Now you know what it was preventing.
References
- Winking with a Smile — one eye instead of two, and why that changes the meaning
- Don't Block the Loop — how to blink on a timer without freezing everything else
- Reading Two Buttons — the same pattern, doubled, with counters on screen