Button-Controlled Color
Now let's connect your two buttons to the LED strip. Button 1 will move forward through a list of colors, and Button 2 will move backward.
Pixel says...
This is where buttons and light finally meet! You've read buttons and you've lit up strips — now you get to control one with the other.
What you'll learn
- How to store a list of colors and step through it with an index
- How to use the modulo operator (
%) to wrap around from the last color back to the first - How to fill an entire strip with one color
What you'll need
- Your Pico, NeoPixel strip, and two buttons from the earlier lessons
- The kit's
config.pyfile
Stepping Through a List
A list index is a number that points at one item in a list. Here, the
index starts at 0 (red) and moves up by one each time Button 1 is
pressed:
color_index = (color_index + 1) % len(colors)
The % len(colors) part is the modulo operator. It divides by the
length of the list and keeps only the remainder, which wraps the index
back to 0 once it goes past the last color. Button 2 does the same
thing in reverse, subtracting one instead of adding.
The Full Program
# Press Button 1 to move forward through a list of colors.
# Press Button 2 to move backward through the list.
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)
# A small list of colors to cycle through.
colors = [
(255, 0, 0), # red
(255, 80, 0), # orange
(255, 255, 0), # yellow
(0, 255, 0), # green
(0, 0, 255), # blue
(130, 0, 200), # purple
]
color_index = 0
DEBOUNCE_MS = 200
last_press = 0
def fill_strip(color):
for i in range(config.NUMBER_PIXELS):
strip[i] = color
strip.write()
def button_handler(pin):
global color_index, last_press
now = ticks_ms()
if now - last_press < DEBOUNCE_MS:
return
if pin == button1:
color_index = (color_index + 1) % len(colors)
else:
color_index = (color_index - 1) % len(colors)
fill_strip(colors[color_index])
print("color index:", color_index)
button1.irq(trigger=Pin.IRQ_FALLING, handler=button_handler)
button2.irq(trigger=Pin.IRQ_FALLING, handler=button_handler)
fill_strip(colors[color_index]) # show the first color right away
while True:
sleep(1)
Press Button 1 a few times and watch the strip step through red, orange, yellow, green, blue, and purple. Press Button 2 to step back the other way. Keep pressing Button 2 past red — the strip wraps around to purple.
Pixel thinks...
That wraparound is the modulo operator at work again. -1 % 6 in
Python gives 5, not a negative number — which is exactly why the
index lands back on the last color in the list instead of breaking.
Try It Yourself
- Add two more colors to the
colorslist. - Change
fill_stripso it only lights up the first half of the strip.
Check Your Understanding
- What does
color_indexkeep track of? - What happens when
color_indexwould go below0? Why? - What does
fill_stripdo to every pixel in the strip? - What would you change to add a seventh color to the list?
You've got this!
You just built a color picker with two buttons. Next, you'll control something you can't see directly: speed.
What's next: in Button-Controlled Speed, your buttons will speed up and slow down a moving pixel.