Multiple pins + Multiple callbacks not working! Urgent!
-
Hey guys,
I've run into an issue with less than 24 hours till my Senior design project is due. Basically I have a device with multiple buttons which will then send different status codes based on which button is pushed. I implemented button debouncing as well as a manager which keeps track of when a button was last pushed so data won't constantly be sent over and over.There is actually a similar issue to this in the thread where code similar to mine is used and a reply from someone which seems to have the exact same issue found here- https://forum.pycom.io/topic/588/interrupt-button-debounce-with-long-short-press/14
My main issue is that, when I create the 3 different buttons I am using, only the first button I ever press is able to be pressed afterwards (or result in the callback being called). All buttons get setup and I can access them inside the REPL, however when they get pressed, even after confirming that they were pressed via accessing the pin values, the callbacks never go off and I just can't seem to figure out why. I even made specific functions for each to ensure that wasn't an issue.
So basically pressing any of the 3 buttons effectively doesn't allow the other buttons to be pushed. So pressing the button connected to
P8
makes it so that neitherP9
orP11
s callback every goes back even though I can see that their callback is still bound to a function and access their values.At this point I'm completely lost. I'm unsure if it's not possible to do what I am attempting to do, but if anyone can help I would very much appreciate it.
# Button.py from time import sleep_ms, ticks_ms, ticks_diff, time from machine import Pin, disable_irq, enable_irq, Timer from _thread import start_new_thread import gc import rgb class Manager: def __init__(self, sensor, node, status, delay=5): self.pressed = False self.last_pressed = 0 self.current_status = None self.status_time = 0 self.delay = delay self.sensor = sensor self.node = node self.status = status self.alarm = None def can_press(self): print("last pressed = {}\n delay = {}\ntime = {}\ndiff between = {}".format(self.last_pressed, self.delay, time(), time() - self.last_pressed)) print("--------------------------") print('\n') return time() - self.last_pressed > self.delay and not self.pressed def set_alarm(self): return Timer.Alarm(self.alarm_func, s=10, arg=[self.sensor, self.node, self.status], periodic=True) def alarm_func(self): pass def button_depressed(self): # print("reinitiating alarm") self.pressed = False self.last_pressed = time() # self.alarm = self.set_alarm() # print(self.alarm) def button_pressed(self): # print("cancelling alarm") # self.alarm.cancel() self.pressed = True # print(self.alarm) class BUTTON: def __init__(self, sensor, node, manager, status_alert=0, pid='P10', longms=1000): self.manager = manager self.pressms = 0 self.longms = longms self.pin = Pin(pid, mode=Pin.IN, pull=Pin.PULL_DOWN) self.pin.callback(Pin.IRQ_RISING, self.press) self.sensor = sensor self.node = node self.status_alert = status_alert self.args = (self.sensor, self.node, self.status_alert) print("Button setup!") def check_delay(self): return self.manager.can_press() def long(self): pass def short(self): pass def press(self, pin): print("{} pressed!".format(pin)) # state = disable_irq() # If never pressed, store press time if self.pressms == 0: self.pressms = ticks_ms() else: # If pressed within 500 ms of first press, discard (button bounce) if ticks_diff(self.pressms, ticks_ms()) < 500: return # Wait for value to stabilize for 10 ms i = 0 while i < 10: sleep_ms(1) if self.pin() == 0: i = 0 else: i += 1 if not self.check_delay(): self.manager.button_pressed() print("CANT PRESS YET! HAHA") print("button done") print('-------------------') print('\n') # enable_irq(state) rgb.blink_red(1, 1, 1) self.manager.button_depressed() return self.manager.button_pressed() # Measure button press duration while self.pin() == 1: i += 1 if i > self.longms: break sleep_ms(1) # Trigger short or long press if i > self.longms: start_new_thread(self.long, self.args) else: start_new_thread(self.short, self.args) # Wait for button release. while self.pin() == 1: pass self.pressms = 0 # enable_irq(state) rgb.blink_green(3) self.manager.button_depressed() gc.collect() print("button done!!")
And the main file is here
# main.py # main.py -- put your code here! from lora.abp_node_US915 import setup_node import gps_setup from pytrack import Pytrack import time from machine import Timer, Pin, PWM from _thread import start_new_thread import servo import struct import gc from LIS2HH12 import LIS2HH12 from button import Manager, BUTTON gc.enable() def send_coords_button1(sensor, node, status): pitch = sensor.pitch() roll = sensor.roll() pitch_data = struct.pack('>f', pitch) roll_data = struct.pack('>f', roll) # coords = args[0].coordinates(debug=True) # lat = struct.pack('>f', coords[0]) # long = struct.pack('>f', coords[1]) status = bytes([status]) # pkt = lat + long + status pkt = pitch_data + roll_data + status print("Raw packet data: ", pkt) node.send(pkt) print("Packet sent via BUTTON successfully") print(" Free memory: ", gc.mem_free()) gc.collect() print(" Free memory after GC collect: ", gc.mem_free()) print("----------------------------------------------") def send_coords_button2(sensor, node, status): pitch = sensor.pitch() roll = sensor.roll() pitch_data = struct.pack('>f', pitch) roll_data = struct.pack('>f', roll) # coords = args[0].coordinates(debug=True) # lat = struct.pack('>f', coords[0]) # long = struct.pack('>f', coords[1]) status = bytes([status]) # pkt = lat + long + status pkt = pitch_data + roll_data + status print("Raw packet data: ", pkt) node.send(pkt) print("Packet sent via BUTTON successfully") print(" Free memory: ", gc.mem_free()) gc.collect() print(" Free memory after GC collect: ", gc.mem_free()) print("----------------------------------------------") def send_coords_button3(sensor, node, status): pitch = sensor.pitch() roll = sensor.roll() pitch_data = struct.pack('>f', pitch) roll_data = struct.pack('>f', roll) # coords = args[0].coordinates(debug=True) # lat = struct.pack('>f', coords[0]) # long = struct.pack('>f', coords[1]) status = bytes([status]) # pkt = lat + long + status pkt = pitch_data + roll_data + status print("Raw packet data: ", pkt) node.send(pkt) print("Packet sent via BUTTON successfully") print(" Free memory: ", gc.mem_free()) gc.collect() print(" Free memory after GC collect: ", gc.mem_free()) print("----------------------------------------------") node = setup_node() py = Pytrack() acc = LIS2HH12() gps = gps_setup.setup_gps() manager = Manager(acc, node, 1) button_1 = BUTTON(acc, node, manager, pid='P8', status_alert=1) print("Button_1 value is " + str(button_1.pin())) button_1.short = send_coords_button1 button_1.long = send_coords_button1 button_2 = BUTTON(acc, node, manager, pid='P11', status_alert=2) print("Button_2 value is " + str(button_2.pin())) button_2.short = send_coords_button2 button_2.long = send_coords_button2 button_3 = BUTTON(acc, node, manager, pid='P9', status_alert=3) print("Button_3 value is " + str(button_3.pin())) button_3.short = send_coords_button3 button_3.long = send_coords_button3 print("Setup complete!")
-
@timh I kind of get what you're going for, but the issue is mostly that when attempting to use any sort of debouncing with multiple buttons (each button with their own callback), the first button pressed is the only button that the callback will run for until the device is reset (even though the device detects the other button is push as confirmed through looking at the pin value).
So if I understand you correctly, I could simply provide arguments for each pin's callback function to identify which one was activated. That would work, but when the callback won't even go through for any button other than the first one pressed, that doesn't really work.
I just worked around it and made a function to detect sequential long presses. Since I have 3 different status codes, I made it as such
- 1 long press sends status code 1
- 2 long presses sends status code 2
- 3 long presses sends status code 3
A user is notified of what press they are on based on the LED colors, and 3 green blinks represents that the status/data has been sent.
-
I had similar issues. The reason it did not work for me was that all callbacks (i.e. Timer, Pin, etc) are pushed to the same queue and executed sequentially.
That means that the next callback will only be called, when the last one finished. Maybe one of your callbacks is not finishing properly.
-
@kbman99 You can use your simple function.
Include the arg (pin identifier for instance) when you register each all back on the PIN.
arg is an optional argument to pass to the callback. If left empty or set to None, the function will receive the Pin object that triggered it.
or use the PIN object itself to identify which index. (You could even attach the last pressed ms to the pin)
p2.callback(Pin.IRQ_RISING, press,'P2')
Then use a global dictionary, or a list (and just have the arg be the index.) which will hold the last pressed time for each pin
Then in your call back use the arg to look up the value of the last pressed.
T
-
I tried an implementation that didn't use a whole bunch of classes and just inline and no luck. It works without all the functions running with a simple callback to a function which prints the IDs of the pins, but what use is that when I can't use debouncing....
# main.py gc.enable() pressed = False last_pressed = 0 pressms = 0 longms = 1000 def help(type, pin): print("Pin {} pressed, running {} func".format(pin.id(), type)) def button_pressed(): global pressed pressed = True def button_depressed(): global pressed global last_pressed pressed = False last_pressed = time.time() def can_press(delay): global last_pressed global pressed return time.time() - last_pressed > delay and not pressed def press(pin): global pressms global longms # If never pressed, store press time if pressms == 0: pressms = ticks_ms() else: # If pressed within 500 ms of first press, discard (button bounce) if ticks_diff(pressms, ticks_ms()) < 500: return # Wait for value to stabilize for 10 ms i = 0 while i < 10: sleep_ms(1) if pin() == 0: i = 0 else: i += 1 if not can_press(5): button_pressed() rgb.blink_red(1, 1, 1) button_depressed() return button_pressed() # Measure button press duration while pin() == 1: i += 1 if i > longms: break sleep_ms(1) # Trigger short or long press if i > longms: start_new_thread(help, ('short', pin)) else: start_new_thread(help, ('long', pin)) # Wait for button release. while pin() == 1: pass pressms = 0 rgb.blink_green(3) button_depressed() gc.collect() return 0 p1 = Pin('P8', mode=Pin.IN, pull=Pin.PULL_DOWN) p1.callback(Pin.IRQ_RISING, press) p2 = Pin('P11', mode=Pin.IN, pull=Pin.PULL_DOWN) p2.callback(Pin.IRQ_RISING, press) p3 = Pin('P9', mode=Pin.IN, pull=Pin.PULL_DOWN) p3.callback(Pin.IRQ_RISING, press)