Warm-Up no.03, Bröther, may I have some lööps?

Introduction

So last week, we talked simply about lights. This week we’ll talk about for loops. For loops are important in that they allow you to automate some of the tedium that you may have encountered last week. We want to do this because, typing the same text multiple times is:

  • not “fun”
  • introduces the possibility of human error
  • is not expandable

Review

Here are all the commands that we went over last week:

COMMANDWHAT IT DOES
cp.pixels.fill((255, 255, 255))lighting up all neopixels (remember double paranthesis!)
cp.pixels[0] = (255, 255, 255)lighting up a single neopixel
time.sleep(0.2)waiting, in seconds
print("your message")talk to the terminal
variable_name = valuecreate a variable with the value we specify to use or manipulate later

What is a for loop for?

A for loop is one of the ways we can do the same thing multiple times; in the case of the CPX it will be to light up lights, or to control the animation of lights.

A (potentially bad) metaphor for understanding for loops

Here’s a metaphor, it isn’t perfect, but imagine this. You are a king (assume all the kingdoms in this universe do not see the role of king as gender-based), and a neighboring king (your political enemy) has sent over a bushel of apples as a “gift.” You suspect at least one of them might be poisoned. You contract me; a lowly jester; to test the apples for poison. In my loyalty to your kingdom, I agree. How might you command me to check the bushel of apples for poison? Just think about the actual words you would say.

Okay. Now pretend I, the jester, am the computer and you have to tell me (in Python) to perform the same task. You might say something like this▼

For bushel(50):
  king.decrees.check_for_poison(“apple”)

Assuming we have these objects/variables to access (bushel and the king’s decrees) this is one way it might look. This is to say that you would not say… “pick up that apple, bite it to check for poison, set it aside, pick up the next apple, check it for poison by biting it ,okay cool, put it aside”….etc. At some point in theory we’d get the picture.

The little brain in our CPX needs to be told (at least for now) how many times to check a given thing, or when to stop.

The simplest for loops we could use with the neopixels would look like this▼

# make each neopixel light up pink in sequence
from adafruit_circuitplayground import cp
import time

while True:
  # go through each neopixel
  # "p" is for pixel
  for p in range(10):
    #light the current neopixel up pink
    cp.pixels[p] = (255, 0, 255)

    #wait
    time.sleep(0.05)

    #turn off the current neopixel
    cp.pixels[p] = (0, 0, 0)

What is happening in our for loop?

Our for loop starts with “for p in range(10):” this means, we will go through a range of values from 0 - 10 (0 is assumed in this case) and we will use the variable “p” for the number, as it represents which "pixel" we want to target.

Here's what's happening in our loop:

  • p will start at 0 (assumed from just saying "10")

  • the indented code gets executed (turn pixels on, wait, then off)

  • "behind the scenes" things the for loop now does:

    • 1 will get added to p
    • p will be checked against the top of our range; in this case 10.
    • if p is less than 10, the code will execute again (ie loop)
    • if p is 10, the code will move on

Note that p is a variable, meaning it can be anything. People often use i for a for loop. You could use the word “neopixel” to be super clear about what you’re modifying. I used “p” for pixel as it is just less typing.

Doing it Live

(Bill O'Reilly freaking out at the teleprompter and "doing it live" uploaded by user Scoots McGee 08/01/2014. Originally from the early 1990's when Bill O'Reilly was on Inside Edition and leaked to YouTube in 2008)

If you want to see this happen “live” we can slow our loop down and put a bunch of print statements. Run the following code with the serial console open to see the print statements happening alongside your CPX.

The + str(p) part is what is called "concatenation." This is because We want to see what the value of p is, and we can't simply type the letter p here. str() converts something into a string.

Any regular text in quotation marks is called a string. So "turning off neopixel" + str(p) is putting two strings together, or in nerd terms "concatenating" them.

# make each neopixel light up pink in sequence
from adafruit_circuitplayground import cp
import time

while True:
    # go through each neopixel
    print("starting the loop")
    for p in range(10):
        print("--------------------")
        print("time through the loop #" + str(p))
        print("lighting up neopixel " + str(p))
        #light the current neopixel up pink
        cp.pixels[p] = (255, 0, 255)

        #wait
        print("waiting")
        time.sleep(1.0)
        
        #turn off the current neopixel
        print("turning off neopixel " + str(p))
        cp.pixels[p] = (0, 0, 0)
        print("--------------------")
    print("loop has ended")

Adding "flavors" to loops

In the parentheses before the colon we can have, up to three numbers. When we have two numbers we are defining the beginning and end of the range▼

from adafruit_circuitplayground import cp
import time

while True:
  # make pixels 0 through 4 red
	for red_pixels in range(0, 5):
		cp.pixels[red_pixels] = (255, 0, 0)
		time.sleep(0.1)
		cp.pixels[red_pixels] = (0, 0, 0)
  
  # make pixels 5 through 9 blue
	for blue_pixels in range(5, 10):
		cp.pixels[blue_pixels] = (0, 0, 255)
		time.sleep(0.1)
		cp.pixels[blue_pixels] = (0, 0, 0)

In this example we are targeting neopixels 0 - 4 to make them red, then neopixels 5 - 9 to make them blue. Note that I didn’t use “p” here for the variable the for loop uses.

Reminder on for loop functionality

You may notice in the above for loops we go from 0 - 5 and then 5 - 10. This may not make sense with me then saying we go from 0 - 4 and then 5 - 9.

As a reminder, loops will start with 0, and then add 1 (this is a nerd term called "incrementing"), and then check if we've reached the top of the range.

So when red_pixels above is 4, it will add 1 then check if we've reached the top of the range (5), and since it has, it will then stop the loop and move on. The same thing will happen at 9 in the blue_pixels loop.

I strongly suggest trying code with print statements and slowed down if this doesn't make sense.

Here’s an example using all three possible parameters▼

# importzzzzzzzz z z z 
from adafruit_circuitplayground import cp
import time

while True:
  # go through each pixel, BUT
  # go up two at a time
	for i in range(0, 10, 2):
		cp.pixels[i] = (255, 255, 0)
		time.sleep(0.1)
		cp.pixels[i] = (0, 0, 0)

When we use three numbers, the numbers determine the start and end of the range, and the third number determines how big each step is. If we only use one or two parameters, the computer assumes we mean to increment (gradually increase) by 1. We can use this to move by 2’s, 4’s 387234’s. We can also use it to move backwards with negative numbers. Here’s the last exercise from last week but with for loops. Note how few lines of code we are using to express the same thing.

# this is code from the last question from the hw

# imports
from adafruit_circuitplayground import cp
import time

# gimme the loop, gimme the loop
while True:
  # go up from 0 - 9, by one, through each pixel
	for x in range(0, 10, 1):
		cp.pixels[x] = (255, 0, 255)
		time.sleep(0.2)
	
  # go down from 9 - 0 backwards through each pixel
	for x in range(9, -1, -1):
		cp.pixels[x] = (0, 0, 0)
		time.sleep(0.2)

Not just for targeting individual neopixels

We can use the variable in the for loop to target whatever we want; any number that is changing, or any “group” (array, tuple) of content. Here we are using the variable in the for loop to change the brightness/color of the pixel.

# make all pixels "glow" pink
from adafruit_circuitplayground import cp
import time

# dat loooo o o o o o p tho
while True:
    # go up from no brightness to max brightness
    for brightness in range(0, 256):
        cp.pixels.fill((brightness, 0, brightness))
        time.sleep(0.002)

    # go down from max brightness to zero
    for brightness in range(255, -1, -1):
        cp.pixels.fill((brightness, 0, brightness))
        time.sleep(0.002)

Other kinds of output (sound)

We can also output lil sounds on the Circuit Playground Express. The below code will make a scale of notes play and a related neopixel light up.

We just use cp.start_tone() and cp.stop_tone() and with start tone we just give it the frequency we want.


# importzzzzzzzzzzzzzzzz
from adafruit_circuitplayground import cp
import time

# our list of note frequencies
# this page has a list of note frequencies
# https://www.liutaiomottola.com/formulae/freqtab.htm
notes = (262, 294, 330, 349, 392, 440, 494, 523)

# just one loop (no while True 
# so that we don't annoy
# everyone in the room)
for pixel, note in enumerate(notes):

    # light up a pixel
    cp.pixels[pixel] = (255,0,255)

    # play a note
    cp.start_tone(note)

    # wait a lil
    time.sleep(0.05)
    
    # turn off the neopixel
    cp.pixels[pixel] = (0,0,0)

    # STOP sound
    cp.stop_tone()

    # wait again
    time.sleep(0.05)

# WE'RE DONE