Buttons Move a Pixel
In earlier lessons, pixels moved on their own, one step per loop. In this lesson, a pixel only moves when you tell it to — Button 1 moves it forward, Button 2 moves it backward.
Pixel says...
This one feels different the moment you try it. You're not just watching an animation anymore — you're driving it!
What you'll learn
- How to store a pixel's position in a variable instead of a loop counter
- How to move that position forward or backward from a button interrupt
- How to erase the old pixel before drawing the new one
What you'll need
- Your Pico, NeoPixel strip, and two buttons from the earlier lessons
- The kit's
config.pyfile
Position as a Variable
Every moving-pixel lesson so far used a loop counter that always went up.
Here, position is a regular variable that the button handler changes
directly — up for Button 1, down for Button 2. The main loop's only job is
to draw whatever position currently is.
# Use the buttons on the controller to move a light up and down the strip.
from machine import Pin
from neopixel import NeoPixel
from utime import sleep, ticks_ms
import config
strip = NeoPixel(Pin(config.NEOPIXEL_PIN), config.NUMBER_PIXELS)
button1 = Pin(config.BUTTON_PIN_1, Pin.IN, Pin.PULL_UP)
button2 = Pin(config.BUTTON_PIN_2, Pin.IN, Pin.PULL_UP)
position = config.NUMBER_PIXELS // 2 # start in the middle of the strip
DEBOUNCE_MS = 200
last_press = 0
def button_handler(pin):
global position, last_press
now = ticks_ms()
if now - last_press < DEBOUNCE_MS:
return
if pin == button1:
position += 1
else:
position -= 1
position = position % config.NUMBER_PIXELS # wrap around the ends
last_press = now
button1.irq(trigger=Pin.IRQ_FALLING, handler=button_handler)
button2.irq(trigger=Pin.IRQ_FALLING, handler=button_handler)
last_position = position
strip[position] = (25, 0, 0)
strip.write()
while True:
if position != last_position:
strip[last_position] = (0, 0, 0) # erase the old spot
strip[position] = (25, 0, 0) # light the new spot
strip.write()
last_position = position
sleep(0.02)
Press Button 1 a few times — the red pixel steps forward. Press Button 2 — it steps back. Keep pressing past either end of the strip, and it wraps around to the other side.
Pixel thinks...
Notice the main loop checks if position != last_position before
doing anything. Redrawing the same pixel over and over would waste
time — we only need to update the strip when something actually moved.
You'll find this same idea saved in your kit as
50-buttons-move-light.py.
Try It Yourself
- Change the starting color from red to a color of your choice.
- Make the pixel leave a short trail behind it instead of disappearing completely.
Check Your Understanding
- What does the
positionvariable represent? - Why does the code erase
last_positionbefore lighting upposition? - What does
position % config.NUMBER_PIXELSdo whenpositiongoes past the end of the strip? - What is different about how this program tracks position compared to the very first Moving Pixel lesson?
You've got this!
You just built direct, physical control over a pixel's position. That's the same core idea behind game controllers and robot arms.
What's next: in Modes, you'll learn how one program can hold several different patterns and switch between them.