Lab 27: Sunrise
Pixel says...
Today your strip becomes a sky! We'll blend the colors slowly, from a quiet night to a bright morning. It's going to be a glow-rious sunrise!
Program file: 27-sunrise.py
What you'll learn
- How to blend two colors to find every color in between
- How a list can hold a set of colors in order
- How a function hands back a new color with
return - How
rangecan count backward to play the sunrise in reverse
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.pyfile saved on the Pico (see Getting Code onto the Kit) - Thonny open and connected to your Pico
- The fade from Lab 04: Dimmer, because this lab fades between two colors instead of only changing brightness
The program
This program fades the whole strip from a dark night color to a bright daytime color, waits, and then fades back again.
# Lab 27: Sunrise
# Filename: 27-sunrise.py
# Version: 1.0.0
#
# The whole strip glows like the sky. The colors blend from night to dawn to
# sunrise to day. After a pause the sun sets and the colors blend backward.
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)
night = (0, 0, 8)
dawn = (30, 0, 30)
sunrise = (80, 20, 0)
day = (80, 60, 20)
sky_colors = [night, dawn, sunrise, day]
STEPS = 60 # how many small steps to blend from one color to the next
STEP_DELAY = 0.1 # seconds per step
def blend(color1, color2, fraction):
# fraction 0.0 gives color1, 1.0 gives color2, and 0.5 is halfway between
red = int(color1[0] + (color2[0] - color1[0]) * fraction)
green = int(color1[1] + (color2[1] - color1[1]) * fraction)
blue = int(color1[2] + (color2[2] - color1[2]) * fraction)
return (red, green, blue)
def fade(color1, color2):
for step in range(STEPS + 1):
color = blend(color1, color2, step / STEPS)
for i in range(NUMBER_PIXELS):
strip[i] = color
strip.write()
sleep(STEP_DELAY)
while True:
# sunrise: blend through the colors in order
for i in range(len(sky_colors) - 1):
fade(sky_colors[i], sky_colors[i + 1])
sleep(3)
# sunset: blend back through the colors in reverse order
for i in range(len(sky_colors) - 1, 0, -1):
fade(sky_colors[i], sky_colors[i - 1])
sleep(3)
Run it. All 30 pixels glow the same color. The strip starts a very dim blue, turns purple, then orange, and ends as a warm yellow. That sunrise takes about 18 seconds. After a 3-second pause, the colors run backward for a sunset. This program keeps going until you press Stop.
This is how a wake-up light works. It brightens slowly, so waking up feels gentle. Yours does it in seconds.
How it works
Name the colors
These lines give each color of the sky a name and put them in order.
night = (0, 0, 8)
dawn = (30, 0, 30)
sunrise = (80, 20, 0)
day = (80, 60, 20)
sky_colors = [night, dawn, sunrise, day]
Each color is a tuple of red, green, and blue amounts, as in Lab 03. The last line makes a list, which is an ordered group of values inside square brackets. Positions start at 0, so sky_colors[0] is night and sky_colors[3] is day.
Key idea
The color numbers stay small on purpose, because all 30 pixels are lit at once. The brightest color is day. Its numbers add up to 160, and 30 pixels showing it draw about 377 mA. A USB port supplies about 500 mA. Full white would need about 1,800 mA. Read more in Why Some Programs Use Small Numbers
Two settings
These two settings control how smooth and how slow the sunrise is.
STEPS = 60 # how many small steps to blend from one color to the next
STEP_DELAY = 0.1 # seconds per step
Each change from one sky color to the next is cut into 60 small steps. The program waits 0.1 seconds after each step.
Blend two colors
Blending means finding a color that is part of the way from one color to another. Programmers also call it linear interpolation (moving in a straight line from one value to another). This function does the blending.
def blend(color1, color2, fraction):
# fraction 0.0 gives color1, 1.0 gives color2, and 0.5 is halfway between
red = int(color1[0] + (color2[0] - color1[0]) * fraction)
green = int(color1[1] + (color2[1] - color1[1]) * fraction)
blue = int(color1[2] + (color2[2] - color1[2]) * fraction)
return (red, green, blue)
A function is a named block of code you can run again and again, like wheel() in Lab 09. This one has three parameters (named slots for values you hand in). It hands back a new color with return. The fraction is a decimal from 0.0 to 1.0.
Square brackets pick one item by its position, the same way strip[0] picks a pixel. So color1[0] is the first number of color1, which is its red amount.
Look at the red line. It starts at the red of color1. Then it adds a fraction of the distance from color1 to color2. At 0.0, it adds nothing, so you get color1. At 1.0, it adds the whole distance, so you get color2. At 0.5, it adds half.
Here is blend(night, dawn, fraction) for three fractions. Green stays at 0 for both colors.
fraction |
Red | Green | Blue | Color |
|---|---|---|---|---|
| 0.0 | 0 | 0 | 8 | night |
| 0.5 | 15 | 0 | 19 | halfway between |
| 1.0 | 30 | 0 | 30 | dawn |
The int() function cuts off the decimals. A strip needs whole numbers, so a red of 7.5 becomes 7. You can explore the same idea in the Math Functions in Animation MicroSim. Open its Linear Interpolation tab.
Fade from one color to the next
This function shows every step of a blend on the strip.
def fade(color1, color2):
for step in range(STEPS + 1):
color = blend(color1, color2, step / STEPS)
for i in range(NUMBER_PIXELS):
strip[i] = color
strip.write()
sleep(STEP_DELAY)
The range function stops one number before its stop value, as you saw in Lab 04. So range(STEPS + 1) counts 0 through 60. That is 61 steps. The fraction is step / STEPS. In MicroPython, the / sign gives a decimal number, so the fraction climbs from 0/60, which is 0.0, up to 60/60, which is 1.0. The last step lands exactly on color2.
Each step does four things. It blends a color, sets all 30 pixels to it, calls strip.write() one time, and sleeps for 0.1 seconds. One fade takes 61 × 0.1 = 6.1 seconds.
Walk through the list
The main loop plays the sunrise by fading between each pair of neighbors in the list.
while True:
# sunrise: blend through the colors in order
for i in range(len(sky_colors) - 1):
fade(sky_colors[i], sky_colors[i + 1])
sleep(3)
...
The len() function counts the items in a list, so len(sky_colors) is 4. Subtract 1 and the loop counts 0, 1, and 2. That gives three fades: night to dawn, dawn to sunrise, and sunrise to day.
Why subtract 1? Four colors have only three gaps between them, like four fence posts have three gaps. A fourth trip would ask for sky_colors[4], which does not exist.
Play it backward
The sunset uses the same fade function, but it walks the list from the end.
# sunset: blend back through the colors in reverse order
for i in range(len(sky_colors) - 1, 0, -1):
fade(sky_colors[i], sky_colors[i - 1])
sleep(3)
This range has three parts: start at 3, stop before 0, and step by -1. It counts 3, 2, and 1, like the backward count in Lab 04. The three fades are day to sunrise, sunrise to dawn, and dawn to night.
How long is one whole day?
Each fade takes 61 steps of 0.1 seconds. Here is the whole timeline.
| Part | Time |
|---|---|
| Sunrise: 3 fades × 61 steps × 0.1 s | 18.3 s |
| Pause | 3 s |
| Sunset: 3 fades × 61 steps × 0.1 s | 18.3 s |
| Pause | 3 s |
| One full day | 42.6 s |
Writing to the strip adds a tiny bit more time to each step.
Try it yourself
- Make a quick demo. Change
STEP_DELAY = 0.1toSTEP_DELAY = 0.02. How long does a sunrise take now? Work it out with 3 × 61 × 0.02. You should get 3.66 seconds. - Add a color. Put a pink between
dawnandsunrise, and keep the numbers small. How many fades are in the sunrise now, and how long does it take? - Advanced: light only a few pixels near one end, as if they were the sun. In
fade, changerange(NUMBER_PIXELS)torange(5).
Here are the two lines for challenge 2. The loops use len(sky_colors), so they adjust to the longer list by themselves.
pink = (60, 10, 30)
sky_colors = [night, dawn, pink, sunrise, day]
Here is the inner loop for challenge 3. Only pixels 0 to 4 get the color, and the other 25 pixels stay dark.
for i in range(5):
strip[i] = color
Check your understanding
- What color does
blend(night, dawn, 0.0)give back? What aboutblend(night, dawn, 1.0)? - Why does
fadeuserange(STEPS + 1)instead ofrange(STEPS)? - Why does the sunrise loop use
len(sky_colors) - 1? - What numbers does
range(len(sky_colors) - 1, 0, -1)count through? - About how many seconds does one sunrise take? Show your math.
Lab complete!
You blended colors into a sunrise! Blending is how programs make smooth color changes, from glowing skies to game effects.
What's next: In Lab 28: Binary Clock, the strip turns into a clock that tells time in 1s and 0s.