← MicroPython on Pi Pico and Projects

Project 2.4

Project 2.4: Animating the NeoPixel Bar

An animation is a list of lists, and the knob can scrub through it

Worksheet

This is for after Project 2.3’s bar is working. Keep the same wiring: the NeoPixel stick on GP17 and the potentiometer on GP26. Nothing new to plug in this time. The new idea is in the data.

An animation is a list of frames

A flip book is a stack of pictures, and playing the animation just means showing them in order. Do the same thing with the bar. Each frame is a list of 8 colors, one per pixel. The animation is a list of frames.

Picture it as a grid. Rows are moments in time, columns are pixels. That is the same grid[row][col] idea from the Square Grid problem set: to find the color of pixel 3 during frame 5, you look in row 5, column 3, which in Python is frames[5][3]. The first index picks the frame, the second index picks the pixel.

A starter animation

Give your colors one-letter names so the code looks like the animation it makes. Every brightness value stays at 20 or below, the same rule as Project 2.3.

R = (20, 0, 0)
OFF = (0, 0, 0)

frames = [
    [R,   OFF, OFF, OFF, OFF, OFF, OFF, OFF],   # frame 0
    [OFF, R,   OFF, OFF, OFF, OFF, OFF, OFF],   # frame 1
    # TODO: your own frames
]

Read down the two rows. In frame 0 the red pixel is at the start of the strip, and in frame 1 it has moved one pixel over. You can see the movement without running anything. Your job is to write the rest of the animation.

Plan it on paper first

Before you type anything, sketch your animation on graph paper. Draw 8 columns, one for each pixel, and one row for each frame. Color in the squares or write a letter in each one. A bouncing pixel, two pixels chasing each other, and a bar that fills up and empties are all good ideas.

Then type your frames in, one row of your sketch to one line of code. If your code has the same shape as your sketch, you are on the right track.

Part A: loop it

Play your frames in order, over and over, with a short pause between them. Add this to the setup from Project 2.3 (np is your NeoPixel(Pin(17), 8)):

while True:
    # TODO: show each frame in order, one pixel at a time
    time.sleep_ms(150)

Change the number in time.sleep_ms() and see what it does to the speed of your animation. Then try adding more frames.

Part B: scrub it with the knob

Now take the loop away. Instead of playing the frames by itself, let the potentiometer choose which frame is showing, like dragging the slider on a video. Turn the knob all the way one way and you see the first frame. Turn it the other way and you see the last one, with the rest in between.

Start from the reading loop in Project 2.3, the one that builds samples and works out volts. Then, where it used to work out lit, work out a frame number instead:

    frame_number = ...   # TODO: turn volts into an index into frames

Once you have the number, copy that frame onto the strip with the same inner loop you wrote in Part A, so the frame you see matches the knob. You will not need the outer loop this time.

Stretch: a history bar

Make the bar draw a graph of the last 8 readings, so you can see the knob’s recent past. Keep a list of the most recent readings, and each time through the loop add the newest one to the end and remove the oldest one from the front:

history = [0] * 8          # starts full of zeros, so it is always 8 long

# inside the loop, after you work out volts:
history.append(volts)      # newest reading goes on the end
history.pop(0)             # the oldest reading comes off the front

Because one goes on as one comes off, the list stays 8 elements long, and history[0] is always the oldest reading and history[7] the newest. Then color each pixel from its own stored reading. A pixel could be brighter for a higher voltage, or a different color for a lower one, but keep every value at 20 or below. A list that you add to at one end and take from the other is called a queue, and you will meet queues again later.

Troubleshooting

If you get an IndexError, there are two usual causes. Either one of your frames has fewer than 8 colors, so asking for frames[f][i] runs off the end of that frame, or your frame number got too big and went past the last frame in frames. Print the frame number and len(frames) to see which. If the error only happens at one position of the knob, it is almost always the second one.

If nothing changes on the strip, check that np.write() runs after the pixels are set, and go back to Project 2.3’s troubleshooting for the wiring checks.

When your animation works, move on to Project 2.5, an optional part where you trade the knob for a light sensor and the same code keeps working.