Trellis M4 CircuitPython Demo

We've explained how to use the features in the Adafruit CircuitPython TrellisM4 library. Now we'll show how to use the library to write a CircuitPython program. In this section, you'll learn how to initialise the library for use in your code, and then use Python lists and sets to keep track of LED states and button presses.

This program begins with all the NeoPixels turned off. You can then press the buttons to toggle the associated NeoPixels on and off in a rainbow pattern spread over all of the LEDs.

Imports and Setup

The first thing you need to do is import the library and then set it up for use. This is super simple. You being your program with the following code:

Download: file
import adafruit_trellism4

trellis = adafruit_trellism4.TrellisM4Express()

This is how you'll begin any program that will be using the TrellisM4 library. You can include other libraries in your code by adding them after the import line and setting them up after the trellis line.

wheel for the Rainbow

Next, a helper function called wheel is defined. This code allows us to generate the rainbow pattern seen when you toggle all of the buttons on.

Download: file
def wheel(pos):
    if pos < 0 or pos > 255:
        return 0, 0, 0
    if pos < 85:
        return int(255 - pos * 3), int(pos * 3), 0
    if pos < 170:
        pos -= 85
        return 0, int(255 - pos * 3), int(pos * 3)
    pos -= 170
    return int(pos * 3), 0, int(255 - (pos * 3))

For more information on how wheel is used, check out the Wheel Explained section here.

A Set of Button Presses

With basic code, when you press a button, the button press gets sent over and over as long as you hold the button. This happens very quickly, too quickly for you to easily press it once and have the button press sent only once. Since we're trying to use the button as a toggle, we need it to respond only once until it is pressed again. So, we're going to use sets to keep track of which buttons have been pressed.

A set is an unordered collection with no duplicate elements. Since we're not keeping track of the order in which we press the buttons, we don't care that it's unordered. Since a set eliminates duplicates, we don't have to worry about it including the same button press twice. You can use math on sets to do things including subtracting one set from another. Subtracting one set from another provides you with the elements in one set that are not in another. This allows you to determine the difference between the two sets. We will be comparing two sets. So sets are perfect for our needs!

This code creates two sets and then compares them. This allows for us to keep track of the buttons currently being pressed and compare them to to the buttons previously pressed to determine what buttons have been released.

First, we create an empty set called current_press. Then we begin our loop and create a set called pressed that contains all the buttons being pressed (trellis.pressed_keys) at the beginning of the loop. If no buttons are pressed, pressed will be empty.

Download: file
current_press = set()

while True:
    pressed = set(trellis.pressed_keys)
    for press in pressed - current_press:
      ...  # LED toggle code lives here.
    current_press = pressed

At the end of the loop, we set current_press = pressed so current_press contains all of the buttons currently pressed when the loop ends. Then, the loop begins again, and we check to see which buttons are being pressed by checking pressed. This means we now have two sets: one containing the buttons pressed when the loop ended, and one containing the buttons pressed when the loop begins.

Now, we do some math so we can compare the two. press - current_press will provide a collection of buttons still being pressed, and we can now apply the LED toggle code to only those buttons!

A Multidimensional List of NeoPixel States

This example toggles the NeoPixels on or off when each button is pressed. Whether the pixel is turned on or off is dependent on the pixel's current state; if the pixel is on, the button press turns it off, and if the pixel is off, the button press turns it on. We need to track the current state of every pixel to be able to determine what state to toggle it to. For that, we're going to use a multidimensional list.

A list is a collection of items that are both ordered and changeable. We want to use the (x, y) coordinates of the pixels to keep track of which pixel is which, so we want to keep all of the list entries in a particular order. We are going to be tracking two states for each pixel, so we need to be able to change each entry in the list to reflect the current state. Lists are exactly what we need!

Since we are going to be using (x, y) coordinates of the pixels to determine which pixel we are addressing, we need to create a list that is a representation of the NeoPixel grid. For this, we need to create a multidimensional list, which is a list made up of lists.

First we create the initial list and call it led_on.

Download: file
led_on = []

If you were to print(led_on), it would look like this:

[]

Then we create the multidimensional aspect of the list. We use append to add width number of empty lists to the initial list, and then we populate them with height number of entries, which we're setting to False because the LEDs are not on to begin with. As we initialised the Trellis without specifying a rotation value, width is equal to 8 and height is equal to 4.

Download: file
for x in range(trellis.pixels.width):
    led_on.append([])
    for y in range(trellis.pixels.height):
        led_on[x].append(False)

Let's take a look at the two parts of this section of code.

We append x, or 8, empty lists to our initial list:

Download: file
for x in range(trellis.pixels.width):
    led_on.append([])

If you were to print(led_on) now, it would look like this:

[[], [], [], [], [], [], [], []]

Then we append four entries of False to each of the x empty lists:

Download: file
    for y in range(trellis.pixels.height):
        led_on[x].append(False)

Finally, if you were to print(led_on) now, it would look like this:

[[False, False, False, False], [False, False, False, False], [False, False, False, False], [False, False, False, False], [False, False, False, False], [False, False, False, False], [False, False, False, False], [False, False, False, False]] [False, False, False, False]]

Each four member list represents an x coordinate, 0 - 7. Each False represents a y coordinate, 0 - 3.  For example, if (3, 2) were set to True, the list would look like this:

[[False, False, False, False], [False, False, False, False], [False, False, False, False], [False, False, True, False], [False, False, False, False], [False, False, False, False], [False, False, False, False], [False, False, False, False]] [False, False, False, False]]

Now we have our representation of the NeoPIxel grid and we can start toggling!

Extra Credit: List Comprehensions

We used five lines of code to create our list. It was easy to see what was happening in each section of code, and to explain how it was affecting the creation of our list. It turns out, however, that there's a way to do it in one line of code!

List comprehensions provide a more concise way to create lists. They consist of brackets containing an expression followed by a for clause, potentially followed by further for or if clauses. As you can see, we used two for clauses in our list creation code. Our list comprehension consists of two for clauses as well.

You can replace every line of code discussed in the previous section with the following line:

Download: file
led_on = [[False for y in range(trellis.pixels.height)] for x in range(trellis.pixels.width)]

It does exactly the same thing as the code in the previous section: creates a multidimensional list made up of x lists, each containing y entries of False. And, it's much shorter. Handy!

NeoPixels On: True or False?

Now that we have our button presses tracked and our multidimensional list, we can easily toggle the NeoPixels. We're going to use the list to track whether they are on or off. Each time we press a button, we'll check to see whether it's associated list entry is True or False, If it's False, that means the LED is currently off. If it's True, it means the LED is currently on. Based on these results, that button press will change the list entry and turn the LED to the appropriate status. Let's take a look!

Here is our LED toggle code:

Download: file
        x, y = press
        if not led_on[x][y]:
            print("Turning on:", press)
            pixel_index = ((x + (y * 8)) * 256 // 32)
            trellis.pixels[x, y] = wheel(pixel_index & 255)
            led_on[x][y] = True

        else:
            print("Turning off:", press)
            trellis.pixels[x, y] = (0, 0, 0)
            led_on[x][y] = False

press is already an x, y coordinate, but we need to have access to x and y separately. So, we first unpack press with x, y = press.

We need access to x and y to use our list. Remember, we're using a multidimensional list containing x number of lists made up of y number. of entries. So, to access them, we'll use led_on[x][y].

Next, we have an if/else block. The if not led_on[x][y]: says if the led_on list entry found at [x][y] is False, run the code indented below it. The else says, "otherwise," which is to say, if the led_on list entry found at [x][y] is True, run the code indented below it.

If the list entry is False when we press a button, we send a print statement, turn on the LED associated with that button to the rainbow color that matches it's location in the rainbow spread over all the buttons. Then, we set the list entry to True.

Download: file
        if not led_on[x][y]:
            print("Turning on:", press)
            pixel_index = ((x + (y * 8)) * 256 // 32)
            trellis.pixels[x, y] = wheel(pixel_index & 255)
            led_on[x][y] = True

If the list entry is True when we press a button, we send a print statement, turn the LED associated with that button off. Then, we set the list entry to False.

Download: file
        else:
            print("Turning off:", press)
            trellis.pixels[x, y] = (0, 0, 0)
            led_on[x][y] = False

You may have noticed that to address the pixels, we use trellis.pixels[x, y], but to address the list we use led_on[x][y]. This is because the NeoPixel code is specially designed to work with an [x, y] coordinate assignment. The multidimensional list, however, only works by addressing [x] and [y] separately.

Whew! That's it! Now it's time to press, press, press!

NeoTrellis M4 NeoPixel Toggle Code

Here's the full program. Copy it to code.py on your NeoTrellis M4 and start toggling!

import adafruit_trellism4

trellis = adafruit_trellism4.TrellisM4Express()


def wheel(pos):
    if pos < 0 or pos > 255:
        return 0, 0, 0
    if pos < 85:
        return int(255 - pos * 3), int(pos * 3), 0
    if pos < 170:
        pos -= 85
        return 0, int(255 - pos * 3), int(pos * 3)
    pos -= 170
    return int(pos * 3), 0, int(255 - (pos * 3))


led_on = []

for x in range(trellis.pixels.width):
    led_on.append([])
    for y in range(trellis.pixels.height):
        led_on[x].append(False)

trellis.pixels.fill((0, 0, 0))

current_press = set()

while True:
    pressed = set(trellis.pressed_keys)

    for press in pressed - current_press:
        x, y = press

        if not led_on[x][y]:
            print("Turning on:", press)
            pixel_index = ((x + (y * 8)) * 256 // 32)
            trellis.pixels[x, y] = wheel(pixel_index & 255)
            led_on[x][y] = True

        else:
            print("Turning off:", press)
            trellis.pixels[x, y] = (0, 0, 0)
            led_on[x][y] = False

    current_press = pressed
This guide was first published on Oct 31, 2018. It was last updated on Oct 31, 2018. This page (Trellis M4 CircuitPython Demo) was last updated on Sep 18, 2019.