Part three of a series on API design with neopixels.
In the previous post I defined a magical new API for high-level control of neopixels in my Python code. I wrote the first function
neo_range(), and tested it successfully. Woot! But what's really cool is this code can be used with
import just like built-in Python modules. It's time to give that a try!
Your Shiny New Python Module
After you've run this code at least once, you can now
import it from another Python program on the micro:bit. Create a new file in CodeSpace (call it whatever you like) and try the following test code:
from neopixel import NeoPixel from neoneopixel import neo_range MY_STRIP_LEN = 30 np = NeoPixel(pin0, MY_STRIP_LEN) # Fill the first 2 pixels with Green neo_range(np, (0,20,0), 0, 2)
Woohoo! You can import your custom module and use the
But there's a problem.
When you run this code, you'll notice that the red and blue ranges light up when you
import neoneopixel. Whoa! The test code inside your module runs when you import it! Yep, Python runs that top-level code during import.
How can you make it so that your "test code" runs only if neoneopixel is run as the main program, and not when it's imported as a module?
Dunder to the Rescue!
Python features some built-in variables you can use to determine how your code was run. To avoid "name collisions" with common variables you use in your code, some of these built-ins are surrounded by double-underscores (or "dunders" in Pythonista). For example, the global variable __name__ will be set to the string value "__main__" within the file executed as the main program.
Here's my fledgling module again, this time with an if statement to ensure the test code doesn't run when the module is used as an import.
# neoneopixel.py - A new NeoPixel module! # Fill a range with color def neo_range(np, color, start, end): for i in range(start, end): np[i] = color if __name__ == "__main__": #--- Test code for the above API --- MY_STRIP_LEN = 30 np = NeoPixel(pin0, MY_STRIP_LEN) np.clear() # Fill the first 10 pixels with Red neo_range(np, (20,0,0), 0, 10) # Fill the last 10 pixels with Blue neo_range(np, (0,0,20), 20, 30)
Much nicer! Now there's a place for code that executes only when the file is run "standalone", and you can safely import this as a module without accidentally running the tests.
neoneopixel API, continued
The next function on my wish-list is
neo_fill(). Now that I've written
neo_range() this is a piece of cake!
def neo_fill(np, color): neo_range(np, color, 0, len(np))
The trick here is to use the Python built-in
len() function, which works on neopixels just like it does on lists and strings. In this case it returns the total number of pixels. After that,
neo_range() does the heavy lifting!
Now it's time to add some motion to the party, with the
neo_sparkle() function. I create each "spark" by first rolling the dice with Python's built-in random module to select an index between
len(np) which will be the location for the flash of color. I then read the current color of that pixel and save it in a variable
bkgnd so it can be restored later. After that, it's just a matter of writing the given color at the selected index, delaying for the given duration, and restoring the
bkgnd color. Finally, one last call to
show() ensures no trace of sparkle is left behind.
from time import sleep from random import randrange def neo_sparkle(np, color, duration, count): for i in range(count): n = randrange(len(np)) # Roll the dice bkgnd = np[n] # Save the background color np[n] = color # Flash with new color np.show() sleep(duration) np[n] = bkgnd # Restore background color np.show() # Leave no trace
Notice that the for loop is just used to repeat the "spark" the given number of times. The variable
i is not used inside the loop.
Next step... The Amazing
The next post will wrap up this series in style, with dazzling chase lights!