Due to high demand expect some shipping delays at this time, orders may not ship for 1-2 business days.
0

Making a button increment a counter and change output
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Making a button increment a counter and change output

by wildestpixel on Sat Mar 20, 2021 2:31 am

I can simply increment a value by doing something like this

Code: Select all | TOGGLE FULL SIZE
import time
import board
from digitalio import DigitalInOut, Direction, Pull

btn = DigitalInOut(board.SWITCH)
btn.direction = Direction.INPUT
btn.pull = Pull.UP
Counter = 0


while True:
    if not btn.value:
        Counter = Counter + 1
        print(Counter)
    else:
        #print("BTN is up")
        pass

    time.sleep(0.1) # sleep for debounce

However what I want to achieve is to change code output based on a button with an idea like this :

Code: Select all | TOGGLE FULL SIZE
import time
import board
from digitalio import DigitalInOut, Direction, Pull

btn = DigitalInOut(board.SWITCH)
btn.direction = Direction.INPUT
btn.pull = Pull.UP

Birds = "Feathers"
Snakes = "Scales"
Aeroplanes = "Wings"

things = [ Birds, Snakes, Aeroplanes ]

while True:
    if not btn.value:
        things = things + 1
        print(things)
    else:
        #print("BTN is up")
        pass

    time.sleep(0.1) # sleep for debounce


My intention there is to get the sequence of Feathers, Scales & Wings printed.

However this doesn't work as it throws error :

Code: Select all | TOGGLE FULL SIZE
TypeError: unsupported types for __add__: 'dict', 'int'


Any pointers (solutions) as my code is always very one dimensional and I want to learn how to step it up a little and work to change display output in sequence from a button press ultimately.

Thanks in advance,
Last edited by wildestpixel on Sat Mar 20, 2021 3:19 am, edited 1 time in total.

wildestpixel
 
Posts: 60
Joined: Wed Oct 23, 2019 1:14 am

Re: Making a button increment a counter and change output

by pjr4171 on Sat Mar 20, 2021 3:17 am

I think you need to use your counter idea in conjunction with the list

Maybe something like

if not btn.value:
counter = (counter + 1) % len(things) # this means you will cycle around the list
print(things[counter])

(sorry about lack of indent)

pjr4171
 
Posts: 15
Joined: Wed Mar 10, 2021 1:54 am

Re: Making a button increment a counter and change output

by wildestpixel on Sat Mar 20, 2021 3:24 am

Works perfectly many thanks indeed :)

wildestpixel
 
Posts: 60
Joined: Wed Oct 23, 2019 1:14 am

Re: Making a button increment a counter and change output

by pjr4171 on Sat Mar 20, 2021 3:40 am

No problem.

With a little more work you can extend this to what you really want - i.e. cycling through functionality when the button is pressed.
Assuming you can express your functionalities with function calls (with no arguments to make life easier) you could define functions e.g.

def birds():
...
def snakes():
....

def planes():
...

and have

List = [birds, snakes, planes] # list is now a list of functions

and then

List[counter]()

List[counter] gives you the required function and the () at the end applies the function

pjr4171
 
Posts: 15
Joined: Wed Mar 10, 2021 1:54 am

Re: Making a button increment a counter and change output

by wildestpixel on Fri Apr 30, 2021 3:06 pm

Taking this onto a another level with the new Pimoroni Keybow 2040 - I want to be able to switch off my wonderful rainbow light show at night so that I can get some sleep

There is a function that acts when a key is held that I can trigger, that I'm trying to make a counter increment with so that the output is either 0 or 1 and therefore either rainbow or rainbowoff are called. Script works to a degree, but it doesn't increment the counter it always prints 0 to the REPL, other ways I've tried crash the script. I'm utterly stuck as its unfortunately a bit out of my very linear reach. Would be amazing to be able to use the key held function to with off my rainbow lights .......

Code: Select all | TOGGLE FULL SIZE
import board
import math
from keybow2040 import Keybow2040, number_to_xy, hsv_to_rgb

import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode

# Set up Keybow
i2c = board.I2C()
keybow = Keybow2040(i2c)
keys = keybow.keys

# Set up the keyboard and layout
keyboard = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(keyboard)

# A map of keycodes that will be mapped sequentially to each of the keys, 0-15
keymap =    ["rad1",
             "rad1",
             "mpc toggle",
             "toggle_disp1",
             "rad2",
             "rad2",
             "mpc stop",
             "volm",
             "rad3",
             "rad3",
             "mpc prev",
             "vold",
             "rad4",
             "rad4",
             "mpc next",
             "volu"]

# The colour to set the keys when pressed, yellow.
rgb = (255, 255, 255)

# Increment step to shift animation across keys.
step = 0


def rainbow():
    for i in range(16):
        x, y = number_to_xy(i)
        hue = (x + y + (step / 20)) / 8
        hue = hue - int(hue)
        hue = hue - math.floor(hue)
        r, g, b = hsv_to_rgb(hue, 1, 1)
        # Display it on the key!
        keys[i].set_led(r, g, b)

def rainbowoff():
    for i in range(16):
        x, y = number_to_xy(i)
        hue = (x + y + (step / 20)) / 8
        hue = hue - int(hue)
        hue = hue - math.floor(hue)
        r, g, b = hsv_to_rgb(hue, 1, 1)
        # Don't display it on the key!
        keys[i].set_led(0, 0, 0)

def countup(counter):
    counter = (counter + 1) % len(things)

counter = 0
things = [ rainbow, rainbowoff ]

# Attach handler functions to all of the keys
for key in keys:
    # A press handler that sends the keycode and turns on the LED
    @keybow.on_press(key)
    def press_handler(key):
        keycode = keymap[key.number]
        layout.write(keycode)
        keyboard.send(Keycode.ENTER)
        key.set_led(*rgb)

    @keybow.on_hold(key)
    def hold_handler(key):
        countup(counter)
        print(counter)

while True:
    # Always remember to call keybow.update()!
    keybow.update()

    step += 1
    things[counter]()

wildestpixel
 
Posts: 60
Joined: Wed Oct 23, 2019 1:14 am

Re: Making a button increment a counter and change output

by pjr4171 on Fri Apr 30, 2021 10:16 pm

In
Code: Select all | TOGGLE FULL SIZE
def countup(counter):
    counter = (counter + 1) % len(things)
the counter you are assigning your increment to is a local variable in the function and when you exit the function that update will be lost. That's why you are always getting 0 (because that is the initial value of the global version of counter).

One way to fix this is with code like
Code: Select all | TOGGLE FULL SIZE
def countup():
    global counter
    counter = (counter + 1) % len(things)
but using globals is usually frowned upon.
Another approach (perhaps overkill) is to define a counter class - e.g.
Code: Select all | TOGGLE FULL SIZE
class Counter(object):
    def __init__(self):
        self._counter = 0

    def inc(self):
        self._counter += (counter + 1) % len(things)

    @property
    def counter(self):
        return self._counter
 

Then, at the top level, you can write
counter = Counter()
and whenever you want to increment it write
counter.inc()
and whenever you want to reference it write
counter.counter

pjr4171
 
Posts: 15
Joined: Wed Mar 10, 2021 1:54 am

Re: Making a button increment a counter and change output

by pjr4171 on Sat May 01, 2021 12:07 am

Sorry should have been

Code: Select all | TOGGLE FULL SIZE
   def inc(self):
        self._counter += (self._counter + 1) % len(things)

pjr4171
 
Posts: 15
Joined: Wed Mar 10, 2021 1:54 am

Re: Making a button increment a counter and change output

by wildestpixel on Sat May 01, 2021 1:14 am

Once again you have helped me so much and aided my learning process , for simplicity went with global, although can appreciate the overall downfall of that approach in a more complex code schema - needs locking down within that class ultimately.

Here the finished code that now works to switch off the lovely lights when wildestpixel wants to sleep.

Code: Select all | TOGGLE FULL SIZE
import board
import math
from keybow2040 import Keybow2040, number_to_xy, hsv_to_rgb
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode

i2c = board.I2C()
keybow = Keybow2040(i2c)
keys = keybow.keys
keyboard = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(keyboard)

keymap =    ["rad1",
             "rad1",
             "mpc toggle",
             "toggle_disp1",
             "rad2",
             "rad2",
             "mpc stop",
             "volm",
             "rad3",
             "rad3",
             "mpc prev",
             "vold",
             "rad4",
             "rad4",
             "mpc next",
             "volu"]

rgb = (255, 255, 255)
step = 0

def rainbow_on():
    for i in range(16):
        x, y = number_to_xy(i)
        hue = (x + y + (step / 20)) / 8
        hue = hue - int(hue)
        hue = hue - math.floor(hue)
        r, g, b = hsv_to_rgb(hue, 1, 1)
        keys[i].set_led(r, g, b)

def rainbow_off():
    for i in range(16):
        keys[i].set_led(0, 0, 0)

def countup():
    global counter
    counter = (counter + 1) % len(things)

counter = 0
things = [ rainbow_on, rainbow_off ]

for key in keys:
    @keybow.on_press(key)
    def press_handler(key):
        keycode = keymap[key.number]
        layout.write(keycode)
        keyboard.send(Keycode.ENTER)
        key.set_led(*rgb)

    @keybow.on_hold(key)
    def hold_handler(key):
        countup()
        print(counter)
        print(things[counter])
        keyboard.send(Keycode.CONTROL, Keycode.C)

while True:
    keybow.update()
    step += 1
    things[counter]()

wildestpixel
 
Posts: 60
Joined: Wed Oct 23, 2019 1:14 am

Please be positive and constructive with your questions and comments.