gogreenpower
Posts: 29
Joined: Thu Jul 09, 2020 4:08 am

Do something after 3 button presses

Mon Aug 03, 2020 12:54 pm

Hi guys,

Quick question, I have a project with 3 buttons, I'm wondering if it's possible to have a function run after I press all 3 buttons 1, 2 and 3 in order, under a set time, say 2 seconds?

The buttons all perform actions already. I could add a time stamp to button 1, but then I'm lost. Or maybe to all three and if the difference is less that 2 seconds the run the function?

User avatar
rpdom
Posts: 17275
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: Do something after 3 button presses

Mon Aug 03, 2020 1:15 pm

I'm not a Python programmer, so I can't easily write the code for you, but I can suggest a method.

At start set stepno to 0

When button 1 is pressed:
Set timestamp to current time
Set stepno to 1

When button 2 is pressed:
If stepno = 1 and current time - timestamp is less than 2 seconds:
Set stepno to 2
Else:
Set stepno to 0

When button 3 is pressed:
If stepno = 2 and current time - timestamp is less than 2 seconds:
Do then thing
Else:
set stepno to 0

That should do it.
Unreadable squiggle

hippy
Posts: 7908
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Do something after 3 button presses

Mon Aug 03, 2020 2:54 pm

gogreenpower wrote:
Mon Aug 03, 2020 12:54 pm
I have a project with 3 buttons, I'm wondering if it's possible to have a function run after I press all 3 buttons 1, 2 and 3 in order, under a set time, say 2 seconds?
It's possible but it's possibly going to screw up everything else. You need to consider the user-friendliness of what you are implementing

For example, to action a button 1 push you would have to wait (almost) two seconds just in case it turns out to be a 1-2-3 sequence and not just a button 1 push. Either that or you start the button 1 action when pushed and abort it when button 2 then 3 are pushed.

Another option is to only action a button push on its release. That's usually only a short delay because most pushes are 'push then release' and one can quickly get used to that behaviour.

Then you can handle button 1 being pushed and held, with button 2 or 3 pushed on top of that. And of course 2 or 3 pushed first if you need other combinations. And the advantage is no timing required.

Paul Hutch
Posts: 551
Joined: Fri Aug 25, 2017 2:58 pm
Location: Blackstone River Valley, MA, USA
Contact: Website

Re: Do something after 3 button presses

Mon Aug 03, 2020 2:59 pm

Here's a quick untested translation into Python of rpdom's pseudo-code.

I assume buttons are True when on, if False when on, add "not" to the button comparisons, e.g. if not button_1:.

Code: Select all

timestamp = 0
stepno = 0

if button_1:
    timestamp = time.perf_counter()
    stepno = 1

if button_2:
    if stepno == 1 and time.perf_counter() - timestamp < 2:
        stepno = 2
    else:
        stepno = 0

if button_3:
    if stepno == 2 and time.perf_counter() - timestamp < 2:
        pass  # Do 3 button things
    else:
        stepno = 0

hippy
Posts: 7908
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Do something after 3 button presses

Mon Aug 03, 2020 3:48 pm

Here's an (untested) skeleton of what I was suggesting ...

Code: Select all

import time

BTN1 = ? # Set GPIO pin number of buttons
BTN2 = ?
BTN3 = ?

def Init(pin):
  # initialise GPIO pin n as button input

def Held(pin):
  # return True when button is pushed, False when not

def DoActon(action):
  print("Action : " + action)

def HandleButtonPushes():
  while True:
    time.sleep(0.1)
    if Held(BTN1):
      while True:
        time.sleep(0.1)
        if not Held(BTN1) : return DoAction("1")
        if     Held(BTN2) : return DoAction("1+2")
        if     Held(BTN3) : return DoAction("1+3")
    if Held(BTN2):
      while True:
        time.sleep(0.1)
        if not Held(BTN2) : return DoAction("2")
        if     Held(BTN1) : return DoAction("2+1")
        if     Held(BTN3) : return DoAction("2+3")
    if Held(BTN3):
      while True:
        time.sleep(0.1)
        if not Held(BTN3) : return DoAction("3")
        if     Held(BTN1) : return DoAction("3+1")
        if     Held(BTN2) : return DoAction("3+2")

def Main():
  Init(BTN1)
  Init(BTN2)
  Init(BTN3)
  while True:
    HandleButtonPushes()
    while Held(BTN1) or Held(BTN2) or Held(BTN3):
      time.sleep(0.1)
 

gogreenpower
Posts: 29
Joined: Thu Jul 09, 2020 4:08 am

Re: Do something after 3 button presses

Tue Aug 04, 2020 3:07 am

Paul Hutch wrote:
Mon Aug 03, 2020 2:59 pm
Here's a quick untested translation into Python of rpdom's pseudo-code.

I assume buttons are True when on, if False when on, add "not" to the button comparisons, e.g. if not button_1:.

Code: Select all

timestamp = 0
stepno = 0

if button_1:
    timestamp = time.perf_counter()
    stepno = 1

if button_2:
    if stepno == 1 and time.perf_counter() - timestamp < 2:
        stepno = 2
    else:
        stepno = 0

if button_3:
    if stepno == 2 and time.perf_counter() - timestamp < 2:
        pass  # Do 3 button things
    else:
        stepno = 0

This works up until button 3 is pushed then I only get the else. see code and log output.

Code: Select all

from gpiozero import Button
import time
from subprocess import Popen

timestamp = 0
stepno = 0


def tv_off():
    Popen('echo "standby 0.0.0.0" | cec-client RPI -s -d 1', shell=True) 
    print("tv off")

def B1():
    global stepno, timestamp
    timestamp = time.perf_counter()
    print("B1 press")
    stepno = 1

def B2():
    global stepno, timestamp
    if stepno == 1 and time.perf_counter() - timestamp < 2:
        stepno = 2
        print("B1 & B2 press")
    else:
        print("too Slow")
        stepno = 0

def B3():
    global stepno, timestamp
    if stepno == 2 and time.perf_counter() - timestamp < 2:
        print("B1 & B2 & B3 press")
        tv_off()
    else:
        print("too Slow")
        stepno = 0
        
button_1 = Button(27)
button_2 = Button(17)
button_3 = Button(4)

button_1.when_released = B1
button_2.when_released = B2
button_3.when_released = B3
gives this

Code: Select all

B1 press
B1 & B2 press
too Slow
I increased the time to 4 but same result. Any ideas?

pcmanbob
Posts: 9612
Joined: Fri May 31, 2013 9:28 pm
Location: Mansfield UK

Re: Do something after 3 button presses

Tue Aug 04, 2020 10:49 am

Well I made some changes to your posted code so it displays the time between button presses and added a pause to keep the program running.

Code: Select all

from gpiozero import Button
import time
from subprocess import Popen
from signal import pause

timestamp = 0
stepno = 0


def tv_off():
    #Popen('echo "standby 0.0.0.0" | cec-client RPI -s -d 1', shell=True) 
    print("tv off")

def B1():
    global stepno, timestamp
    timestamp = time.perf_counter()
    print("B1 press")
    stepno = 1

def B2():
    global stepno, timestamp
    if stepno == 1 and time.perf_counter() - timestamp < 2:
        stepno = 2
        print("B1 & B2 press", time.perf_counter() - timestamp)
    else:
        print("b2 too Slow", time.perf_counter() - timestamp)
        stepno = 0

def B3():
    global stepno, timestamp
    if stepno == 2 and time.perf_counter() - timestamp < 2:
        print("B1 & B2 & B3 press", time.perf_counter() - timestamp)
        tv_off()
    else:
        print("b3 too Slow", time.perf_counter() - timestamp)
        stepno = 0
        
button_1 = Button(27)
button_2 = Button(17)
button_3 = Button(4)

button_1.when_released = B1
button_2.when_released = B2
button_3.when_released = B3

pause()
so running it on a pi4 from the command line using python3 I can reliably get to the tv off message

Code: Select all

pi@BusterPi:~ $ python3 test1.py
B1 press
B1 & B2 press 1.8420262519999824
b3 too Slow 3.4617584569999735
B1 press
B1 & B2 press 0.5693531350000285
B1 & B2 & B3 press 1.1520384220000324
tv off
B1 press
B1 & B2 press 0.6612663980000661
B1 & B2 & B3 press 1.2886431210000637
tv off
B1 press
B1 & B2 press 0.753839542000037
b3 too Slow 2.740198859999964
B1 press
B1 & B2 press 0.5105708770000774
B1 & B2 & B3 press 1.0753631620001443
tv off
B1 press
B1 & B2 press 0.47925707100012005
B1 & B2 & B3 press 0.9802436320001107
tv off
B1 press
B1 & B2 press 0.5024890950001009
B1 & B2 & B3 press 1.0262105790000078
tv off


Edit ....

So I changed the code on my pi to only display the time between presses if you are to slow and it still works , you just need to learn to speed at which you need to press the buttons to make it work, the posted code will help you do that.
We want information… information… information........................no information no help
The use of crystal balls & mind reading are not supported

gogreenpower
Posts: 29
Joined: Thu Jul 09, 2020 4:08 am

Re: Do something after 3 button presses

Tue Aug 04, 2020 11:34 am

Yes, your right, the code was working, just my buttons.

Code: Select all

B1 press
B1 press
B1 & B2 press 0.2762681630010775
b2 too Slow 0.27698520299964
b2 too Slow 0.4666348079990712
b3 too Slow 0.9335717579997436
b3 too Slow 0.9347009009979956
b3 too Slow 1.0677250059998187
B1 press
B1 press
B1 & B2 press 0.2411515630010399
b2 too Slow 0.36857593199965777
b3 too Slow 0.8380671960003383
b3 too Slow 0.9476403650005523
B1 press
B1 press
B1 & B2 press 0.21538889899966307
b2 too Slow 0.3071169409995491
b3 too Slow 0.7972805680001329
b3 too Slow 0.9032614239986287
B1 press
B1 press
B1 & B2 press 0.22548668699892005
b2 too Slow 0.35307081199789536
b3 too Slow 0.833122669999284
b3 too Slow 0.948025689998758
B1 press
B1 press
B1 & B2 press 0.3233632490009768
b2 too Slow 0.6077957009983948
b3 too Slow 1.0895845960003498
b3 too Slow 1.0906576849993144
b3 too Slow 1.3699572999976226
b2 too Slow 15.226364193000336
b2 too Slow 15.44233420800083
B1 press
B1 press
B1 & B2 press 0.250141464999615
b2 too Slow 0.3734411040022678
b3 too Slow 0.7581466949995956
b3 too Slow 0.876921319002576
Seems I have some bounce issues.

I tried a 0.001 bounce_time, 0.01 and 0.1.

Code: Select all

Python 3.7.3 (/usr/bin/python3)
>>> %Run new_off.py
B1 press
b2 too Slow 2.455782492997969
B1 press
B1 & B2 press 0.4007845180021832
b3 too Slow 9.051954331000161
b2 too Slow 14.353104026002256
b2 too Slow 15.032857780002814
b3 too Slow 15.63174386400351
B1 press
b2 too Slow 3.6551347599997825
b2 too Slow 4.187323952002771
B1 press
B1 & B2 press 1.6357756220022566
b3 too Slow 2.7482044300013513
B1 press
B1 press
B1 & B2 press 0.5204679039998155
B1 & B2 & B3 press 1.6152661950000038
tv off
Got there in the end, but proved to unusable with the bounce_time.

I might just go with a 2 button system, B1 and B3, as I got to the B1 and B2 press quite often.

https://1drv.ms/u/s!AhF9jRtLVrOphrMcppJSBL_AsnEJBw
Link to Onedrive image. To large to attach.


I'll trying to make a really basic video player that is controlled by these 3 big buttons. They have micro-switches inside them which must have a big bounce when hit.

I guess using buttons 1 and 3, the two outside ones will suffice, hard to accidentally hit both of them in under 2 seconds.

Just thought about something else, could 2 buttons pressed at the same time work?

Paul Hutch
Posts: 551
Joined: Fri Aug 25, 2017 2:58 pm
Location: Blackstone River Valley, MA, USA
Contact: Website

Re: Do something after 3 button presses

Tue Aug 04, 2020 12:29 pm

I'd be very surprised if any switch, even homemade, had a bounce time of more than 0.2 seconds. So I suspect three switches pressed sequentially in 2 seconds should be workable even with your particular switches.

When I tried the debouncing in gpiozero a couple years ago I found it to be woefully inadequate for my needs. So I always roll my own debouncing software and/or hardware like I do on embedded micro-controller systems for work.

Here's my favorite online reference: A Guide to Debouncing, or, How to Debounce a Contact in Two Easy Pages
http://www.ganssle.com/debouncing.htm -
http://www.ganssle.com/debouncing-pt2.htm - Part 2, has software debounce techniques

gogreenpower
Posts: 29
Joined: Thu Jul 09, 2020 4:08 am

Re: Do something after 3 button presses

Tue Aug 04, 2020 12:37 pm

Actually I just solved it with time.sleep(0.3) added at the end of each button press function. Just tested 5 times, nailed it each time.

Happy days.

Now on to the next problem...

gogreenpower
Posts: 29
Joined: Thu Jul 09, 2020 4:08 am

Re: Do something after 3 button presses

Tue Aug 04, 2020 12:39 pm

Here is the working code in case anyone was interested.

Code: Select all

from gpiozero import Button
import time
from signal import pause

timestamp = 0
stepno = 0


def tv_off():
    print("tv off")

def B1():
    global stepno, timestamp
    timestamp = time.perf_counter()
    print("B1 press")
    stepno = 1
    time.sleep(0.3)#helps with button bounce

def B2():
    global stepno, timestamp
    if stepno == 1 and time.perf_counter() - timestamp < 2:
        stepno = 2
        print("B1 & B2 press", time.perf_counter() - timestamp)
    else:
        print("b2 too Slow", time.perf_counter() - timestamp)
        stepno = 0
    time.sleep(0.3)#helps with button bounce

def B3():
    global stepno, timestamp
    if stepno == 2 and time.perf_counter() - timestamp < 2:
        print("B1 & B2 & B3 press", time.perf_counter() - timestamp)
        tv_off()
    else:
        print("b3 too Slow", time.perf_counter() - timestamp)
        stepno = 0
    time.sleep(0.3)#helps with button bounce
        
button_1 = Button(27)
button_2 = Button(17)
button_3 = Button(4)

button_1.when_released = B1
button_2.when_released = B2
button_3.when_released = B3

pause()

Return to “Python”