Lab 37: Modes Auto Cycle
Pixel says...
This one has no buttons, but it holds the secret of every multi-pattern light show. One number decides which pattern is on. Let's teach the program to change that number by itself!
Program file: 37-modes-auto-cycle.py
What you'll learn
- What a mode is, and how a mode variable remembers it
- How
run_mode()usesif,elif, andelseto pick a pattern - How to switch on a timer with
ticks_ms()instead ofsleep(4) - How
% NUM_MODESwraps the mode back to the start
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
This lab needs no buttons.
The program
This program fills the whole strip with one color for four seconds, then switches to the next color: red, green, blue, and around again.
# Lab 37: Modes Auto Cycle
# Filename: 37-modes-auto-cycle.py
# Version: 1.0.0
#
# Automatically cycle through three light patterns, staying on each one
# for a few seconds before moving to the next. No buttons yet - this is
# the "mode variable" pattern that the button-driven modes lesson builds
# on.
from machine import Pin
from neopixel import NeoPixel
from utime import sleep, ticks_ms
import config
# hardware settings from config.py
NEOPIXEL_PIN = config.NEOPIXEL_PIN
NUMBER_PIXELS = config.NUMBER_PIXELS
strip = NeoPixel(Pin(NEOPIXEL_PIN), NUMBER_PIXELS)
SECONDS_PER_MODE = 4 # how long to stay on each mode before switching
NUM_MODES = 3
def fill_strip(color):
for i in range(NUMBER_PIXELS):
strip[i] = color
strip.write()
def run_mode(mode):
if mode == 0:
fill_strip((200, 0, 0)) # mode 0: solid red
elif mode == 1:
fill_strip((0, 200, 0)) # mode 1: solid green
else:
fill_strip((0, 0, 200)) # mode 2: solid blue
mode = 0
run_mode(mode)
mode_start_time = ticks_ms()
while True:
elapsed_seconds = (ticks_ms() - mode_start_time) / 1000
if elapsed_seconds >= SECONDS_PER_MODE:
mode = (mode + 1) % NUM_MODES # advance to the next mode, then wrap
run_mode(mode)
mode_start_time = ticks_ms()
print("mode:", mode)
sleep(0.1)
Run it. The strip glows red. About four seconds later it turns green, then blue, then red again. The Shell prints the new mode number at each switch. The loop repeats forever, so press the Stop button in Thonny when you are done.
How it works
One number picks the pattern
A mode is one way the strip can behave. A mode variable is a number that remembers which mode is on right now.
mode = 0
run_mode(mode)
mode_start_time = ticks_ms()
Here mode 0 means solid red, mode 1 means solid green, and mode 2 means solid blue. The program starts in mode 0, shows it, and notes the time on the clock.
In Lab 31: Button Modes, buttons change the mode number instead of a timer. That lab has twelve modes. This lab practices the same idea with three. The State Machine for a 3-Mode LED Controller MicroSim draws the three modes as boxes with arrows between them. Here, the clock is what moves along an arrow.
Run the mode
The run_mode() function looks at the mode number and lights the matching color.
def run_mode(mode):
if mode == 0:
fill_strip((200, 0, 0)) # mode 0: solid red
elif mode == 1:
fill_strip((0, 200, 0)) # mode 1: solid green
else:
fill_strip((0, 0, 200)) # mode 2: solid blue
The elif word means "else if". Python checks the tests in order and runs the first one that is true. If mode is not 0 and not 1, the else part runs, so mode 2 lights blue.
The fill_strip() helper works as in Lab 35.
def fill_strip(color):
for i in range(NUMBER_PIXELS):
strip[i] = color
strip.write()
Each color uses 200 on one channel, and the other two channels are 0. With all 30 pixels lit, that is about 471 mA, which is under the 500 mA that a USB port supplies. See How Bright Can You Go?
Check the clock
Now the main loop decides when it is time to switch.
while True:
elapsed_seconds = (ticks_ms() - mode_start_time) / 1000
if elapsed_seconds >= SECONDS_PER_MODE:
mode = (mode + 1) % NUM_MODES # advance to the next mode, then wrap
run_mode(mode)
mode_start_time = ticks_ms()
print("mode:", mode)
sleep(0.1)
The call ticks_ms() reads a clock that counts milliseconds (thousandths of a second). Subtracting mode_start_time gives the time since the mode began. Dividing by 1000 turns milliseconds into seconds.
When elapsed_seconds reaches SECONDS_PER_MODE (4), it is time to switch. The mode goes up by one. The % NUM_MODES part wraps it: 2 + 1 is 3, and 3 % 3 is 0, so blue is followed by red.
Then mode_start_time = ticks_ms() restarts the stopwatch. Every mode gets its own full four seconds, no matter when the last switch happened.
Why check the clock instead of sleeping?
One way to wait four seconds is sleep(4). But while the Pico sleeps, it does nothing else for four whole seconds.
This loop naps for only a tenth of a second (sleep(0.1)), then wakes up and checks the clock. It wakes ten times every second. That keeps the loop responsive, so you could add other work inside it, such as checking a button.
Because the check happens every 0.1 seconds, each mode lasts about four seconds, plus up to a tenth of a second.
Try it yourself
- Change
SECONDS_PER_MODE = 4toSECONDS_PER_MODE = 1. Watch the faster show. - Add a fourth mode. Change
NUM_MODES = 3toNUM_MODES = 4. Then replacerun_mode()with this version, which adds a dim cyan (green plus blue):
def run_mode(mode):
if mode == 0:
fill_strip((200, 0, 0)) # mode 0: solid red
elif mode == 1:
fill_strip((0, 200, 0)) # mode 1: solid green
elif mode == 2:
fill_strip((0, 0, 200)) # mode 2: solid blue
else:
fill_strip((0, 64, 64)) # mode 3: dim cyan
Check your understanding
- What does the mode variable store?
- What does
run_mode()do with the number it receives? - How does the program know that four seconds have passed?
- Why does the program reset
mode_start_timeafter each switch? - You add a fourth mode but leave
NUM_MODES = 3. What happens?
Lab complete!
Your program now switches modes on its own! One number, one clock, and one wrap-around were all it took.
What's next: In Lab 38: Traffic Light, three pixels act like a traffic light, and each color stays on for a different amount of time.