0

NEED HELP WITH PHYTON PROGRAMMING BUTTONS
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

NEED HELP WITH PHYTON PROGRAMMING BUTTONS

by Kyuga on Tue Jan 28, 2020 7:30 am

Hi everyone! I'm currently building the lightsaber prop example with the feather m4 express and prop-maker. I have some doubts about how to program my button in such a way that it contemplates long click short click and multiple clicks.

1) So this are the 3 cases my button should have:

a) When buton is keep pressed for more than 1/2 seconds power-on or power-off the saber.

b) When button is pressed just once fast do the blaster hit effect (sound and flash of light)

c) When buton is pressed 3 times fast do Lockup sound and light effect. And other 3 times to get out of this function.

This is my current code, I progressed a bit with the programming of items a) and b), but I don't know if it's correct. And regarding item c) I don´t know how to do it..
For this I used the time.monotonic() function (I think it's the equivalent of millis() in Python ):

Code: Select all | TOGGLE FULL SIZE
[...]

prev = 0
buttonState = 0  # 0 = not pressed   --- 1 = long pressed --- 2 short pressed
DURATION_IN_MILLIS = 1000


while True:
  buttonState = 0
  if not switch.value:     # button pressed?
    prev = time.monotonic()
    buttonState = 1  # long pressed
    while((time.monotonic()-prev)<=DURATION_IN_MILLIS)
      if switch.value:
        buttonState = 2  # short pressed
        break

 
  if buttonState == 0  # Nothing is pressed. If on then do idle, hit and swing stuff
 
    if mode >= 1:      # It is ON
        x, y, z = accel.acceleration # Read accelerometer
        accel_total = x * x + z * z
        # (Y axis isn't needed for this, assuming Hallowing is mounted
        # sideways to stick.  Also, square root isn't needed, since we're
        # just comparing thresholds...use squared values instead, save math.)
                     
        if accel_total > HIT_THRESHOLD:  # Large acceleration = HIT
            play_wav(random.choice(hit_sounds))  # Start playing 'hit' sound
            led.color = COLOR_HIT  # Set color to hit color
            mode = 3  # HIT mode
        elif mode == 1 and accel_total > SWING_THRESHOLD:  # Mild = SWING
            play_wav(random.choice(swing_sounds))  # Randomly choose from available swing sounds
            led.color = COLOR_SWING  # Set color to SWING COLOR
            mode = 2  # SWING mode
        elif mode == 1:
            # Idle color
            led.color = COLOR_IDLE
        elif mode > 1:  # If in SWING or HIT mode...
            if audio.playing:  # And sound currently playing...
                if mode == 2:  # If SWING,
                    led.color = COLOR_SWING
                else:
                    led.color = COLOR_HIT  # Set color to hit color
            else:  # No sound now, but still SWING or HIT modes
                play_wav('idle', loop=True)  # Resume idle sound
                mode = 1  # Return to idle mode
               
   
  elif buttonState == 1  # Button is pressed long
    if mode == 0:                       # If currently off...
        enable.value = True
        power('on', 1.7, False)         # Power up!
        play_wav('idle', loop=True)     # Play background hum sound
        mode = 1                        # ON (idle) mode now
    else:                               # else is currently on...
        power('off', 1.15, True)        # Power down
        mode = 0                        # OFF mode now
        enable.value = False
    while not switch.value:             # Wait for button release
        time.sleep(0.2)                 # to avoid repeated triggering
           
 
  elif buttonState == 2  # Button is pressed short
    # This could be used for blaster hit animation and sound
    if mode >= 1:
        play_wav(random.choice(blaster_sounds))  # Start playing 'blaster hit' sounds
        led.color = COLOR_BLASTER



2) I tried to program the lightsaber on and off animation for a 3 watt high power RGB LED. Is this code ok?

Code: Select all | TOGGLE FULL SIZE
def power(sound, duration, reverse):
    """
    Animate 3W LED with accompanying sound effect for power on / off.
    @param sound:    sound name (similar format to play_wav() above)
    @param duration: estimated duration of sound, in seconds (>0.0)
    @param reverse:  if True, do power-off effect (reverses animation)
    """
   
    #gc.collect()   # Tidy up RAM now so animation's smoother
   
    play_wav(sound, loop=False)  #PLAY THE ON OR OFF SOUND
    if reverse:
        LEDPWM(True) #Power Down light animation (fade out)
    elif:
        LEDPWM(False) #Power Up light animation (fade in)
       
    while audio.playing:     # Wait until audio done
        pass


def LEDPWM(reverse)
       
    if reverse:
         # Fade from full red to nothing
        for i in range(255):
            led.color = (255-i,0,0)
            time.sleep(duration)
    else:
        # Fade from nothing up to full red.
        for i in range(255):
            led.color = (i,0,0)
            time.sleep(duration)




Here is the full code for reference:

Code: Select all | TOGGLE FULL SIZE
# pylint: disable=bare-except

import time
import random
import math
from digitalio import DigitalInOut, Direction, Pull
import audioio
import busio
import board
import adafruit_rgbled
import adafruit_lis3dh
import gc

# CUSTOMIZE YOUR COLOR HERE:
# (red, green, blue) -- each 0 (off) to 255 (brightest)
#COLOR = (255, 0, 0)  # red
#COLOR = (100, 0, 255)  # purple
# COLOR = (0, 100, 255) #cyan

# "Idle" color is about 4/5 brightness, "swinging" color is full brightness...
COLOR_IDLE = (240, 0, 0) # 240 FOR LOWER RED INTENSITY
COLOR_SWING = (255, 0, 0) # 240 FOR FULL RED INTENSITY
COLOR_HIT = (255, 255, 255)  # "hit" color is white
COLOR_BLASTER = (0, 0, 255)  # "Blaster hit" color is blue


# CUSTOMIZE SENSITIVITY HERE: smaller numbers = more sensitive to motion
HIT_THRESHOLD = 350 # 250
SWING_THRESHOLD = 125


POWER_PIN = board.D10   # TODO: ADAPT FOR ONLY 1 BUTTON, POWER WILL BE ALWAYS ON UNLESS KILL PIN IS INSIDE, MAYBE SOLDER THIS PIN
SWITCH_PIN = board.D9   

enable = DigitalInOut(POWER_PIN) 
enable.direction = Direction.OUTPUT
enable.value =False

switch = DigitalInOut(SWITCH_PIN)
switch.direction = Direction.INPUT
switch.pull = Pull.UP


red_led = board.D11
green_led = board.D12
blue_led = board.D13

# Create the RGB LED object
led = adafruit_rgbled.RGBLED(red_led, green_led, blue_led)


audio = audioio.AudioOut(board.A0)     # Speaker
mode = 0                               # Initial mode = OFF


# time.sleep(0.1)

# Set up accelerometer on I2C bus, 4G range:
i2c = busio.I2C(board.SCL, board.SDA)
accel = adafruit_lis3dh.LIS3DH_I2C(i2c)
accel.range = adafruit_lis3dh.RANGE_4_G


def play_wav(name, loop=False):
    """
    Play a WAV file in the 'sounds' directory.
    @param name: partial file name string, complete name will be built around
                 this, e.g. passing 'foo' will play file 'sounds/foo.wav'.
    @param loop: if True, sound will repeat indefinitely (until interrupted
                 by another sound).
    """
    print("playing", name)
    try:
        wave_file = open('sounds/' + name + '.wav', 'rb')
        wave = audioio.WaveFile(wave_file)
        audio.play(wave, loop=loop)
    except:
        return
       
# List of swing wav files without the .wav in the name for use with play_wav()
swing_sounds = [
    'swing1',
    'swing2',
    'swing3',
    'swing4',
    'swing5',
    'swing6',
    'swing7',
    'swing8',
]
# List of hit wav files without the .wav in the name for use with play_wav()
hit_sounds = [
    'hit1',
    'hit2',
    'hit3',
    'hit4',
    'hit5',
    'hit6',
    'hit7',
    'hit8',
]
# List of blaster wav files without the .wav in the name for use with play_wav()
blaster_sounds = [
    'blaster',
    'blaster2',
    'blaster3',
    'blaster4',
]

def power(sound, duration, reverse):
    """
    Animate 3W LED with accompanying sound effect for power on / off.
    @param sound:    sound name (similar format to play_wav() above)
    @param duration: estimated duration of sound, in seconds (>0.0)
    @param reverse:  if True, do power-off effect (reverses animation)
    """
   
    #gc.collect()   # Tidy up RAM now so animation's smoother
   
    play_wav(sound, loop=False)  #PLAY THE ON OR OFF SOUND
    if reverse:
        LEDPWM(True) #Power Down light animation (fade out)
    elif:
        LEDPWM(False) #Power Up light animation (fade in)
       
    while audio.playing:     # Wait until audio done
        pass


def LEDPWM(reverse)
       
    if reverse:
         # Fade from full red to nothing
        for i in range(255):
            led.color = (255-i,0,0)
            time.sleep(duration)
    else:
        # Fade from nothing up to full red.
        for i in range(255):
            led.color = (i,0,0)
            time.sleep(duration)
       
    #TODO: make it so it would be easy to change colors (for green and blue).



# Main program loop, repeats indefinitely
while True:

    if not switch.value:                    # button pressed?
        if mode == 0:                       # If currently off...
            enable.value = True
            power('on', 1.7, False)         # Power up!
            play_wav('idle', loop=True)     # Play background hum sound
            mode = 1                        # ON (idle) mode now
        else:                               # else is currently on...
            power('off', 1.15, True)        # Power down
            mode = 0                        # OFF mode now
            enable.value = False
        while not switch.value:             # Wait for button release
            time.sleep(0.2)                 # to avoid repeated triggering

    elif mode >= 1:                         # If not OFF mode...
        x, y, z = accel.acceleration # Read accelerometer
        accel_total = x * x + z * z
        # (Y axis isn't needed for this, assuming Hallowing is mounted
        # sideways to stick.  Also, square root isn't needed, since we're
        # just comparing thresholds...use squared values instead, save math.)
        if accel_total > HIT_THRESHOLD:  # Large acceleration = HIT
            play_wav(random.choice(hit_sounds))  # Start playing 'hit' sound
            led.color = COLOR_HIT  # Set color to hit color
            mode = 3  # HIT mode
        elif mode == 1 and accel_total > SWING_THRESHOLD:  # Mild = SWING
            play_wav(random.choice(swing_sounds))  # Randomly choose from available swing sounds
            led.color = COLOR_SWING  # Set color to SWING COLOR
            mode = 2  # SWING mode
        elif mode == 1:
            # Idle color
            led.color = COLOR_IDLE
        elif mode > 1:  # If in SWING or HIT mode...
            if audio.playing:  # And sound currently playing...
                if mode == 2:  # If SWING,
                    led.color = COLOR_SWING
                else:
                    led.color = COLOR_HIT  # Set color to hit color
            else:  # No sound now, but still SWING or HIT modes
                play_wav('idle', loop=True)  # Resume idle sound
                mode = 1  # Return to idle mode


"""
#IN PROGRESS Main program loop FOR LONG CLICK AND SHORT CLICK

prev = 0
buttonState = 0  # 0 = not pressed   --- 1 = long pressed --- 2 short pressed
DURATION_IN_MILLIS = 1000


while True:
  buttonState = 0
  if not switch.value:     # button pressed?
    prev = time.monotonic()
    buttonState = 1  # long pressed
    while((time.monotonic()-prev)<=DURATION_IN_MILLIS)
      if switch.value:
        buttonState = 2  # short pressed
        break

 
  if buttonState == 0  # Nothing is pressed. If on then do idle, hit and swing stuff
 
    if mode >= 1:      # It is ON
        x, y, z = accel.acceleration # Read accelerometer
        accel_total = x * x + z * z
        # (Y axis isn't needed for this, assuming Hallowing is mounted
        # sideways to stick.  Also, square root isn't needed, since we're
        # just comparing thresholds...use squared values instead, save math.)
                     
        if accel_total > HIT_THRESHOLD:  # Large acceleration = HIT
            play_wav(random.choice(hit_sounds))  # Start playing 'hit' sound
            led.color = COLOR_HIT  # Set color to hit color
            mode = 3  # HIT mode
        elif mode == 1 and accel_total > SWING_THRESHOLD:  # Mild = SWING
            play_wav(random.choice(swing_sounds))  # Randomly choose from available swing sounds
            led.color = COLOR_SWING  # Set color to SWING COLOR
            mode = 2  # SWING mode
        elif mode == 1:
            # Idle color
            led.color = COLOR_IDLE
        elif mode > 1:  # If in SWING or HIT mode...
            if audio.playing:  # And sound currently playing...
                if mode == 2:  # If SWING,
                    led.color = COLOR_SWING
                else:
                    led.color = COLOR_HIT  # Set color to hit color
            else:  # No sound now, but still SWING or HIT modes
                play_wav('idle', loop=True)  # Resume idle sound
                mode = 1  # Return to idle mode
               
   
  elif buttonState == 1  # Button is pressed long
    if mode == 0:                       # If currently off...
        enable.value = True
        power('on', 1.7, False)         # Power up!
        play_wav('idle', loop=True)     # Play background hum sound
        mode = 1                        # ON (idle) mode now
    else:                               # else is currently on...
        power('off', 1.15, True)        # Power down
        mode = 0                        # OFF mode now
        enable.value = False
    while not switch.value:             # Wait for button release
        time.sleep(0.2)                 # to avoid repeated triggering
           
 
  elif buttonState == 2  # Button is pressed short
    # This could be used for blaster hit animation and sound
    if mode >= 1:
        play_wav(random.choice(blaster_sounds))  # Start playing 'blaster hit' sounds
        led.color = COLOR_BLASTER
   
 


"""

Kyuga
 
Posts: 18
Joined: Sun Jan 26, 2020 6:23 am

Re: NEED HELP WITH PHYTON PROGRAMMING BUTTONS

by dastels on Tue Jan 28, 2020 11:07 am

Have a look at the debouncer module: https://learn.adafruit.com/debouncer-library-python-circuitpython-buttons-sensors. Since that guide was written, timing information has been added to the library and might be just what you need. Here's the example:

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

button = DigitalInOut(board.D4)
button.direction = Direction.INPUT
button.pull = Pull.UP
switch = Debouncer(button)

while True:
    switch.update()
    if switch.fell:
        print('pressed')
        print('was released for ', switch.last_duration)
    elif switch.rose:
        print('released')
        print('was pressed for ', switch.last_duration)
    else:
        print('Stable for ', switch.current_duration)
    time.sleep(0.1)


Dave

dastels
 
Posts: 1643
Joined: Tue Oct 20, 2015 3:22 pm

Re: NEED HELP WITH PHYTON PROGRAMMING BUTTONS

by Kyuga on Tue Jan 28, 2020 3:09 pm

dastels wrote:Have a look at the debouncer module: https://learn.adafruit.com/debouncer-library-python-circuitpython-buttons-sensors. Since that guide was written, timing information has been added to the library and might be just what you need. Here's the example:

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

button = DigitalInOut(board.D4)
button.direction = Direction.INPUT
button.pull = Pull.UP
switch = Debouncer(button)

while True:
    switch.update()
    if switch.fell:
        print('pressed')
        print('was released for ', switch.last_duration)
    elif switch.rose:
        print('released')
        print('was pressed for ', switch.last_duration)
    else:
        print('Stable for ', switch.current_duration)
    time.sleep(0.1)


Dave



Very interesting! I think that in my case I could simply use the Rose parameter, and check the "switch.current_duration". Maybe something like this:


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

button = DigitalInOut(board.D4)
button.direction = Direction.INPUT
button.pull = Pull.UP
switch = Debouncer(button)

while True:
    switch.update()
    if switch.rose:  # button released
        if switch.last_duration < 0.1  # was pressed for 100 miliseconds, short clic
             #Do stuff
        elif switch.last_duration >= 1  # was pressed for 1 second, long clic
             #Do other stuff
    else:
        print('Stable for ', switch.current_duration)
    time.sleep(0.1)

Kyuga
 
Posts: 18
Joined: Sun Jan 26, 2020 6:23 am

Re: NEED HELP WITH PHYTON PROGRAMMING BUTTONS

by dastels on Tue Jan 28, 2020 3:43 pm

That would do it.

dastels
 
Posts: 1643
Joined: Tue Oct 20, 2015 3:22 pm

Please be positive and constructive with your questions and comments.