To use the PyBadge buttons, we will need a few pieces of code. Here's how the buttons function:

  • Pushing Up make the lights cycle faster
  • Pushing Down makes the lights cycle slower
  • Pushing Right makes the lights appear to move to the right
  • Pushing Left makes the buttons appear to move to the left
  • Pushing the B Button makes the lights dimmer
  • Pushing the A Button makes the light brighter

CircuitPython Button Code

BUTTON_LEFT = const(128)
BUTTON_UP = const(64)
BUTTON_DOWN = const(32)
BUTTON_RIGHT = const(16)
BUTTON_SEL = const(8)
BUTTON_START = const(4)
BUTTON_A = const(2)
BUTTON_B = const(1)

pad = GamePadShift(digitalio.DigitalInOut(board.BUTTON_CLOCK),
                   digitalio.DigitalInOut(board.BUTTON_OUT),
                   digitalio.DigitalInOut(board.BUTTON_LATCH))

def check_buttons(buttons):
    global direction, speed, brightness
    if (buttons & BUTTON_RIGHT) > 0:
        direction = -1
    elif (buttons & BUTTON_LEFT) > 0:
        direction = 1
    elif (buttons & BUTTON_UP) > 0 and speed < 10:
        speed += 1
    elif (buttons & BUTTON_DOWN) > 0 and speed > 1:
        speed -= 1
    elif (buttons & BUTTON_A) > 0 and brightness < 0.5:
        brightness += 0.025
    elif (buttons & BUTTON_B) > 0 and brightness > 0.025:
        brightness -= 0.025

current_buttons = pad.get_pressed()
last_read = 0
while True:
    for color in range(0, 360, speed):
        if (last_read + 0.1) < time.monotonic():
            buttons = pad.get_pressed()
            last_read = time.monotonic()
        if current_buttons != buttons:
            check_buttons(buttons)
            current_buttons = buttons

Originally GamePadShift was designed for reading the button inputs for games, but we are slightly repurposing it for use in changing settings. This means we need to detect when a button is pressed and released. We do this by monitoring the state of all the buttons and respond only when there is a change. Let's look at the code a little closer.

BUTTON_LEFT = const(128)
BUTTON_UP = const(64)
BUTTON_DOWN = const(32)
BUTTON_RIGHT = const(16)
BUTTON_SEL = const(8)
BUTTON_START = const(4)
BUTTON_A = const(2)
BUTTON_B = const(1)

There are eight buttons on the PyBadge and each corresponds to a bit in the byte of data that is returned. In the first part, we define which button corresponds to the appropriate value. We do that by defining each of the buttons as bit masks. Bit masking works by defining which bits we want to look at and which we want to hide or "mask". For instance we have BUTTON_SEL defined as 8, which is the same as 0x00001000 in binary. This is because we want to only look at the fourth bit from the right and ignore all the others.

If we get back a value from GamePadShift that is something like 0x01001000 then we know that the BUTTON_SEL is being pressed at the same time as BUTTON_UP which has a bit mask value of 64 or 0x01000000.

In the next segment of code we setup the GamePadShift object and call it pad.

pad = GamePadShift(digitalio.DigitalInOut(board.BUTTON_CLOCK),
                   digitalio.DigitalInOut(board.BUTTON_OUT),
                   digitalio.DigitalInOut(board.BUTTON_LATCH))

We do that by passing the Clock, Latch, and Out pins of the built-in shift register to the module. This is an SN74HC165 Parallel-Load Shift Register, which works by first "latching" the current input values of the buttons being pressed. Then the Out pin is read and the data is shifted each time a Clock pulse is sent. This way we can get any combination of the eight inputs from the shift register. This is all handled in the background which makes it much easier to use.

Finally let's take a look at the button code in the main loop, which handles the button presses.

current_buttons = pad.get_pressed()
last_read = 0
while True:
    for color in range(0, 360, speed):
        # Reading buttons too fast returns 0
        if (last_read + 0.1) < time.monotonic():
            buttons = pad.get_pressed()
            last_read = time.monotonic()
        if current_buttons != buttons:
            # Respond to the buttons
            if (buttons & BUTTON_RIGHT) > 0:
                direction = -1
            elif (buttons & BUTTON_LEFT) > 0:
                direction = 1
            elif (buttons & BUTTON_UP) > 0 and speed < 10:
                speed += 1
            elif (buttons & BUTTON_DOWN) > 0 and speed > 1:
                speed -= 1
            elif (buttons & BUTTON_A) > 0 and brightness < 0.5:
                brightness += 0.025
            elif (buttons & BUTTON_B) > 0 and brightness > 0.025:
                brightness -= 0.025
            current_buttons = buttons

We start by grabbing the current value of the buttons and setting the time of the last read to 0. After that, we have a for loop to cycle through all of the different colors. I have omitted that code so that we can focus on the button code only.

We then compare the last time the buttons were read with the current time. time.monotonic() will return the time that has elapsed in seconds since the processor was powered up. We only want to read the buttons every 0.1 seconds. Since we don't need the buttons to be as responsive as it would need to be for a game, this works great for this purpose.

In the last bit, it just has a bunch of else statements that check the bit mask of the passed in button value as well as checking the limits. If you would like to add functionality to the Start and Select buttons, this would probably be the easiest place to add it.

This guide was first published on May 28, 2019. It was last updated on May 28, 2019.

This page (PyBadge Buttons) was last updated on Mar 18, 2021.

Text editor powered by tinymce.