modsbyus
Posts: 10
Joined: Fri Jul 03, 2015 11:34 pm

How to count button presses during a specified amount of time

Tue Aug 01, 2017 10:52 pm

I am trying to count button presses within a 5 second period. My problem is that I dont know how to look for button presses for only 5 seconds.
I want to press my button on gpio 20 then after that count any additional presses until 5 seconds has passes from the time of the first press.

Massi
Posts: 1591
Joined: Fri May 02, 2014 1:52 pm
Location: Italy

Re: How to count button presses during a specified amount of time

Wed Aug 02, 2017 7:26 am

well, you could set up a callback on the button with a watchdog 5 seconds long.

Untested:

Code: Select all

#!/usr/bin/env python3
#coding=utf-8

import time
import pigpio

#setup vars
gpio = 7 #where the switch is connected
debounce = 1000 #debounce time, in us
clickPeriod = 5000 #control period for clicks. in ms
clickCount = -1 #number of clicks in the period

def intCallback(g, level, tick):
	global clickCount
	if level == pigpio.HIGH:
		#button release
		if clickCount == -1:
			#this is the starting click. Setting up watchdog
			pi.set_watchdog(g, clickPeriod)
		clickCount += 1
	elif level == pigpio.TIMEOUT:
		#kill watchdog and print result
		pi.set_watchdog(g, 0)
		print("Total clicks in {}ms period: {}".format(clickPeriod, clickCount)
		clickCount = -1

pi = pigpio.pi()
pi.set_mode(gpio, pigpio.INPUT)
pi.set_glitch_filter(gpio, debounce)
pi.set_pull_up_down(gpio, pigpio.PUD_UP) #this depends on how the switch is connected. In this case it is between GPIO and GND
cb = pi.callback(gpio, pigpio.RISING_EDGE, intCallback)

try:
	while True:
		time.sleep(1)
      
except (KeyboardInterrupt, SystemExit) as e:
	print("Clean exit")

except Exception as e:
	print("Bad exit")

finally:
	cb.cancel()   
	pi.stop()

modsbyus
Posts: 10
Joined: Fri Jul 03, 2015 11:34 pm

Re: How to count button presses during a specified amount of time

Wed Aug 02, 2017 8:55 am

My daughter and I have been working on this for a while and have never found any reference to that. Thank you tremendously. Would you please leave a link that further explains this watchdog timer and it's use?

Massi
Posts: 1591
Joined: Fri May 02, 2014 1:52 pm
Location: Italy

Re: How to count button presses during a specified amount of time

Wed Aug 02, 2017 11:46 am

as you can see the code is based on pigpio module
ref: http://abyz.co.uk/rpi/pigpio/index.html
on the GPIO used for the button is set up a callback (= a function called by the system when a event - rising or falling edge - is found on the gpio)
the pigpio's "watchdog" waits for some events on the GPIO for the specified amount of time. after that time, the callback is called with a "special" level, so you can know that the time has passed.

have you tried the code? does it work?
feel free to ask if any part is not clear (or, probably, if it's not working :))

modsbyus
Posts: 10
Joined: Fri Jul 03, 2015 11:34 pm

Re: How to count button presses during a specified amount of time

Wed Aug 02, 2017 12:00 pm

We haven't had a chance yet. We are US east coast, so we are just waking up here. My daughter should be trying this today and corresponding her troubles and successes.

MsCheesyPuff
Posts: 7
Joined: Wed Aug 02, 2017 3:14 pm

Re: How to count button presses during a specified amount of time

Wed Aug 02, 2017 6:24 pm

Hi, this is his daughter. I've ran the code after downloading the pigpio library:
[#!/usr/bin/env python3
#coding=utf-8

import time
import pigpio

#setup vars
gpio = 20 #where the switch is connected
debounce = 1000 #debounce time, in us
clickPeriod = 5000 #control period for clicks. in ms
clickCount = -1 #number of clicks in the period

def intCallback(g, level, tick):
global clickCount
if level == pigpio.HIGH:
#button release
if clickCount == -1:
#this is the starting click. Setting up watchdog
pi.set_watchdog(g, clickPeriod)
clickCount += 1
elif level == pigpio.TIMEOUT:
#kill watchdog and print result
pi.set_watchdog(g, 0)
print("Total clicks in {}ms period: {}".format(clickPeriod, clickCount))
clickCount = -1

pi = pigpio.pi()
pi.set_mode(gpio, pigpio.INPUT)
pi.set_glitch_filter(gpio, debounce)
pi.set_pull_up_down(gpio, pigpio.PUD_UP) #this depends on how the switch is connected. In this case it is between GPIO and GND
cb = pi.callback(gpio, pigpio.RISING_EDGE, intCallback)

try:
while True:
time.sleep(1)

except (KeyboardInterrupt, SystemExit) as e:
print("Clean exit")

except Exception as e:
print("Bad exit")

finally:
cb.cancel()
pi.stop()
]

The program ran successfully, but there aren't any outputs, no syntaxes, errors messages, nothing is printed either. What problems could cause this the happen? Thank you so much for helping us!

Massi
Posts: 1591
Joined: Fri May 02, 2014 1:52 pm
Location: Italy

Re: How to count button presses during a specified amount of time

Thu Aug 03, 2017 6:38 am

how did you phisically connect the switch?
are you sure you are using the GPIO 20 (pigpio uses BCM numbers, see www.pinout.xyz)

That said, at least you should see the "clean exit" message when you ctrl+c to close the program.
Try adding a "print something" in the callback, just to check if it's called (and when)
debug it a little :)

MsCheesyPuff
Posts: 7
Joined: Wed Aug 02, 2017 3:14 pm

Re: How to count button presses during a specified amount of time

Thu Aug 03, 2017 1:36 pm

Yes, it does show 'Clean Exit' when I use Ctrl C. However, when I try to call the function, I didn't have any arguments. As I was in the process in adding arguments, I noticed that 'tick' was nowhere in the program besides declaring the function intCallback. Is 'tick' necessary and what would it's use be?

Massi
Posts: 1591
Joined: Fri May 02, 2014 1:52 pm
Location: Italy

Re: How to count button presses during a specified amount of time

Thu Aug 03, 2017 1:59 pm

you don't have to manage those arguments, since it is pigpio itself to send arguments to the callback.
As you can see here:
http://abyz.co.uk/rpi/pigpio/python.html#callback
the set up callback receives 3 arguments.
"g" is our gpio, we know it, it's used since you can use the same callback on different gpios
"level" is the level that has triggered the callback (we are waiting for a rising edge, so this should be always high)
"tick" is the system tick when the level change occurred. Very useful when you need to precisely monitor timings between callbacks, useless in our case (the watchdog is counting this..)

I can't test it today, please add to the callback a "debug" print, just to see if it's called in any way, and try to see what it prints and when.
It should print when you RELEASE the button

Code: Select all

def intCallback(g, level, tick):
        print("{} {} {}".format(g,level,tick))
        global clickCount
it's so hard to debug a sample code having no raspberry under hand :)

MsCheesyPuff
Posts: 7
Joined: Wed Aug 02, 2017 3:14 pm

Re: How to count button presses during a specified amount of time

Thu Aug 03, 2017 3:08 pm

The code is now successfully working but it's running too fast, instead of running for 5 seconds it only runs for 1 second. Why would this be, would I need to increase the milliseconds or is there another solution? You've been very helpful. Many thanks!

Massi
Posts: 1591
Joined: Fri May 02, 2014 1:52 pm
Location: Italy

Re: How to count button presses during a specified amount of time

Thu Aug 03, 2017 9:10 pm

what did you need to correct in code? i'm quite curious :)
the code should count clicks for 5000ms starting from the first click, isn't it?

Massi
Posts: 1591
Joined: Fri May 02, 2014 1:52 pm
Location: Italy

Re: How to count button presses during a specified amount of time

Fri Aug 04, 2017 7:39 pm

well, i just discovered how watchdog works :)
it's giving the timeout only when for 5 seconds nothing changes on the GPIO
Not right for what we are trying to do.. At least, not that easy :)

New version

Code: Select all

#!/usr/bin/env python3
#coding=utf-8

import time
import pigpio

#setup vars
gpio = 16 #where the switch is connected
debounce = 1000 #debounce time, in us
clickPeriod = 5000 #control period for clicks. in ms
clickCount = 0 #number of clicks in the period
startTick = None

def intCallback(g, level, tick):
	print("Callback: level {} - tick {}".format(level,tick))
	global clickCount, startTick
	if level == pigpio.HIGH:
		#button release
		if startTick is None:
			#this is the starting click. Setting up watchdog
			print("Starting count..")
			startTick = tick
			clickCount = 0
			pi.set_watchdog(gpio,clickPeriod) #if no clicks will come, this will give the timeout
		else:
			print("Adding one..")
			clickCount += 1
			#need to update the watchdog
			pi.set_watchdog(gpio,clickPeriod-pigpio.tickDiff(startTick, tick)/1000) #next timeout
	elif level == pigpio.TIMEOUT:
		#over
		print("Total clicks in {}ms period: {} (real: {})".format(clickPeriod, clickCount,pigpio.tickDiff(startTick, tick)/1000))
		pi.set_watchdog(gpio,0)
		startTick = None

pi = pigpio.pi()
pi.set_mode(gpio, pigpio.INPUT)
pi.set_glitch_filter(gpio, debounce)
pi.set_pull_up_down(gpio, pigpio.PUD_UP) #this depends on how the switch is connected. In this case it is between GPIO and GND
cb = pi.callback(gpio, pigpio.RISING_EDGE, intCallback)

try:
	while True:
		pi.write(gpio,0)
		time.sleep(0.1)
		pi.write(gpio,1) #this triggers the count
		time.sleep(1)
		pi.write(gpio,0)
		time.sleep(1)
		pi.write(gpio,1) #click one
		time.sleep(1)
		pi.write(gpio,0)
		time.sleep(1)
		pi.write(gpio,1) #click 2
		time.sleep(1)
		pi.write(gpio,0)
		time.sleep(5)
      
except (KeyboardInterrupt, SystemExit) as e:
	print("Clean exit")

except Exception as e:
	print("Bad exit")

finally:
	cb.cancel()   
	pi.stop()
Obviously to test your switch take out all the "try" part (leave only a sleep, like in the first version)
This returns something like

Code: Select all

pi@baguette:~/wip $ python3 clicks.py
Callback: level 1 - tick 374457983
Starting count..
Callback: level 1 - tick 376460608
Adding one..
Callback: level 1 - tick 378463313
Adding one..
Callback: level 2 - tick 379459176
Total clicks in 5000ms period: 2 (real: 5001.193)
Callback: level 1 - tick 384570349
Starting count..
Callback: level 1 - tick 386573024
Adding one..
Callback: level 1 - tick 388575704
Adding one..
Callback: level 2 - tick 389571826
Total clicks in 5000ms period: 2 (real: 5001.477)
Callback: level 1 - tick 394682759
Starting count..
Callback: level 1 - tick 396685544
Adding one..
Callback: level 1 - tick 398688280
Adding one..
Callback: level 2 - tick 399684102
Total clicks in 5000ms period: 2 (real: 5001.343)
^CClean exit
Seems good, isn't it?

MsCheesyPuff
Posts: 7
Joined: Wed Aug 02, 2017 3:14 pm

Re: How to count button presses during a specified amount of time

Sat Aug 05, 2017 12:58 am

I've been able to fix the timing, but it starts when the program starts instead of waiting for the button press.

Code: Select all

def intCallback(g, level, tick): 
    print('{} {} {}'.format(g, level, tick))
    global clickCount
    if level == pigpio.HIGH:
        #button release
        print ('button pressed')
        if clickCount == -1:
            #this is the starting click. Setting up watchdog
            pi.set_watchdog(g, clickPeriod)
        clickCount = clickCount + 1
    elif level == pigpio.TIMEOUT:
        #kill watchdog and print result
        pi.set_watchdog(g, 0)
        print("Total clicks in {}ms period: {}".format(clickPeriod, clickCount))
        clickCount = -1
Your code looks fantastic! Unfortunately I won't be able to test it quite yet, I'm going on a family outing and I will get back to you as soon as possible. Thank you again! :D

MsCheesyPuff
Posts: 7
Joined: Wed Aug 02, 2017 3:14 pm

Re: How to count button presses during a specified amount of time

Mon Aug 07, 2017 1:13 pm

I'm trying to test out your code and my switch but I get the error:
AttributeError: 'NoneType' object has no attribute 'send'

But, I think the reason this is happening is that I'm using Python 2 instead of using Python 3 like you are. I don't know much about Python 3 but, wouldn't that impact the way the program works?

Massi
Posts: 1591
Joined: Fri May 02, 2014 1:52 pm
Location: Italy

Re: How to count button presses during a specified amount of time

Mon Aug 07, 2017 1:26 pm

MsCheesyPuff wrote:
Mon Aug 07, 2017 1:13 pm
I'm trying to test out your code and my switch but I get the error:
AttributeError: 'NoneType' object has no attribute 'send'
This is a PiGPIO error i usually see on i2c transactions, not our case, and should happen occasionally.
Is it giving this error to you every time? In this case you should dig deeper in the code adding the traceback for errors..
But, I think the reason this is happening is that I'm using Python 2 instead of using Python 3 like you are. I don't know much about Python 3 but, wouldn't that impact the way the program works?
no, the code should work with python2 and python3
try calling it with a simple "python3 scriptname.py" (you should have python3 installed..)

MsCheesyPuff
Posts: 7
Joined: Wed Aug 02, 2017 3:14 pm

Re: How to count button presses during a specified amount of time

Tue Aug 08, 2017 9:30 pm

I finally got some results. But, instead of waiting for a button press, it's automatically making one for me. As I was debugging I noticed that you had written for my Raspberry Pi to make button presses instead of having to wait for actual button presses from my hardware. The code works, but it isn't exactly what I needed for my program. However, it's brilliant and I really appreciate you helping me. :)

Massi
Posts: 1591
Joined: Fri May 02, 2014 1:52 pm
Location: Italy

Re: How to count button presses during a specified amount of time

Wed Aug 09, 2017 4:32 am

as i told you before, you have to turn this

Code: Select all

	while True:
		pi.write(gpio,0)
		time.sleep(0.1)
		pi.write(gpio,1) #this triggers the count
		time.sleep(1)
		pi.write(gpio,0)
		time.sleep(1)
		pi.write(gpio,1) #click one
		time.sleep(1)
		pi.write(gpio,0)
		time.sleep(1)
		pi.write(gpio,1) #click 2
		time.sleep(1)
		pi.write(gpio,0)
		time.sleep(5)
to this

Code: Select all

	while True:
		time.sleep(1)

User avatar
OutoftheBOTS
Posts: 114
Joined: Tue Aug 01, 2017 10:06 am

Re: How to count button presses during a specified amount of time

Wed Aug 09, 2017 7:37 am

I am a bit old school but my solution would have been def a routine that returns true or false. this routine needs to test if a button is pressed, if not pressed then return false if true then start a while loop while the button is true so that it will stay in this loop until the button is release then return true. Then the main program will take start time and set counter = 0 then have a while loop for start time + 5 secs and call the above routine and if true then counter +=1. After it exits the whil + 5 secs loop the value of counter will = the amount of tiem the button was pressed and released :)

MsCheesyPuff
Posts: 7
Joined: Wed Aug 02, 2017 3:14 pm

Re: How to count button presses during a specified amount of time

Wed Aug 16, 2017 4:10 pm

In my original code, that was what I had but the button I'm using to count the button presses, I believe it's pulling ground, not allowing my LED to light up, why would this be? Here is my code for the button press and LED:

Code: Select all

import time
import RPi.GPIO as GPIO
from time import sleep
from sys import exit

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(17, GPIO.OUT)
GPIO.output(17, False)

debounce = 1000
ClickPeriod = 5000 #time period for presses in milliseconds
ClickCount = 0 #number of clicks in ClickPeriod

print ('LED ready')
while True:
    try:
        if (GPIO.input(20) == False):
            while (GPIO.input(20) == False):
                GPIO.output(17, True)
                GPIO.output(17, False)
                GPIO.output(17, True)
                GPIO.output(17, False)
        sleep(1)
    except KeyboardInterrupt:
        print ('Clean Exit')
        exit()
GPIO.cleanup()

Return to “Python”

Who is online

Users browsing this forum: No registered users and 10 guests