Skip to content

Lab 17: Moving Rainbow with a Color List

Pixel says...

Pixel waves hello A rainbow on the move! This time we'll write out each color by hand. Then we'll slide the whole rainbow down the strip.

Program file: 17-moving-rainbow.py

What you'll learn

  • How to keep seven colors in a tuple and step through them
  • How strip[index + i] draws the rainbow starting at any pixel
  • Why the last position needs extra clean-up
  • How this list version differs from the color wheel version in Lab 13

What you'll need

  • Your base kit: a Pico, a breadboard, and the 30-pixel LED strip, wired as shown in the Kit User's Guide
  • The config.py file saved on the Pico (see Getting Code onto the Kit)
  • Thonny open and connected to your Pico

The program

This program draws a seven-color rainbow at the start of the strip, slides it to the far end, erases it, and starts over.

17-moving-rainbow.py
# Lab 17: Moving Rainbow with a Color List
# Filename: 17-moving-rainbow.py
# Version: 1.0.0
#
# A seven-color rainbow, built from a hand-made list of colors, slides
# along the strip.

from machine import Pin
from neopixel import NeoPixel
from utime import sleep
import config

# hardware settings from config.py
NEOPIXEL_PIN = config.NEOPIXEL_PIN
NUMBER_PIXELS = config.NUMBER_PIXELS

strip = NeoPixel(Pin(NEOPIXEL_PIN), NUMBER_PIXELS)
# Color RGB values
red = (255, 0, 0)
orange = (255, 60, 0)
yellow = (255, 150, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
indigo = (75, 0, 130)
violet = (138, 43, 226)
color_names = ('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet')
num_colors = len(color_names)
colors = (red, orange, yellow, green, blue, indigo, violet)

while True:
    for i in range(0, NUMBER_PIXELS - num_colors + 1):
        index = 0
        # draw the rainbow
        for color in colors:
            strip[index + i] = colors[index]
            index += 1
        strip.write()
        sleep(.05)
        # erase the current pixel
        strip[i] = (0,0,0)
        # erase the last draw
        if i == NUMBER_PIXELS - num_colors:
            strip[i+1] = (0,0,0)
            strip[i+2] = (0,0,0)
            strip[i+3] = (0,0,0)
            strip[i+4] = (0,0,0)
            strip[i+5] = (0,0,0)
            strip[i+6] = (0,0,0)
            strip.write()

Run it. A rainbow of seven pixels appears at the start of the strip and slides toward the other end. Then it vanishes and appears at the start again.

Lab 13 also slides a short rainbow, but it makes its colors with the wheel() function. Here you choose every color yourself, so you control every number.

How it works

A tuple of colors

color_names = ('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet')
num_colors = len(color_names)
colors = (red, orange, yellow, green, blue, indigo, violet)

A tuple is an ordered group of values inside parentheses. Each item has a position number that starts at 0. So colors[0] is red, and colors[6] is violet. Each color is itself a tuple of red, green, and blue amounts.

The len() function counts the items in a tuple. The color_names tuple exists only so num_colors can count to 7.

Draw the rainbow at a starting pixel

index = 0
# draw the rainbow
for color in colors:
    strip[index + i] = colors[index]
    index += 1
strip.write()

The variable i (from the outer loop below) is the starting pixel. The variable index counts from 0 to 6. Each time around, strip[index + i] puts color number index on pixel i + index. When i is 5, red lands on pixel 5, orange on 6, and so on up to violet on 11.

The loop variable color is not used. The code picks each color with colors[index] instead. After all seven colors are set, one strip.write() shows them.

Slide the rainbow

for i in range(0, NUMBER_PIXELS - num_colors + 1):

This loop gives i the values 0 to 23. The last spot where all seven pixels fit is 23 to 29. The math is 30 - 7 + 1 = 24, and range stops one number short of 24.

sleep(.05)
# erase the current pixel
strip[i] = (0,0,0)

After a short pause, strip[i] = (0,0,0) turns off the rainbow's first pixel, the red one. This only changes the Pico's memory. On the next pass, the rainbow draws on pixels i+1 to i+7, which covers the other six pixels. Pixel i stays dark, so the rainbow moves one step. One trip takes 24 steps of 0.05 seconds, about 1.2 seconds.

Erase the last rainbow

if i == NUMBER_PIXELS - num_colors:
    strip[i+1] = (0,0,0)
    strip[i+2] = (0,0,0)
    ...
    strip[i+6] = (0,0,0)
    strip.write()

At the last spot, i is 23. The line above cleared only pixel 23. Pixels 24 to 29 would stay lit. These six lines turn them off, and then strip.write() sends the dark strip. The for loop ends, and the while True: loop starts again with i at 0.

This rainbow does not wrap around like the bands in Lab 15. It disappears at the end and appears at the start.

Heads up

The six erase lines match exactly seven colors. With eight colors, the last pixel stays lit. With six colors, the program stops with an IndexError, because the strip has no pixel 30. The second challenge below fixes this.

Try it yourself

  1. Write the colors in reverse order. Change the colors = ... line so violet comes first and red comes last. Which end of the rainbow leads now?
  2. Replace the six erase lines with a loop, so the program works for any number of colors. Try it on your own first. Then compare your loop with this one.
Your change
if i == NUMBER_PIXELS - num_colors:
    for k in range(1, num_colors):
        strip[i+k] = (0,0,0)
    strip.write()

Check your understanding

  1. What is colors[2]?
  2. Why does the loop stop at i equal to 23 on a 30-pixel strip?
  3. What does strip[i] = (0,0,0) erase? Why do the other rainbow pixels not need erasing?
  4. Why does the program need six extra erase lines at the end?
  5. Does this rainbow wrap around the end of the strip like the bands in Lab 15?

Lab complete!

Pixel celebrates You built a rainbow from a list and sent it sliding down the strip! Tuples and loops work as a team.

What's next: In Lab 18: Candle Flicker, random numbers make the strip glow like a candle flame.