Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    Multiple pins + Multiple callbacks not working! Urgent!

    Scheduled Pinned Locked Moved LoPy
    lopypytrackinterruptbuttoncallback
    5 Posts 3 Posters 1.4k Views 3 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • K Offline
      kbman99
      last edited by kbman99

      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 neither P9 or P11s 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!")
      
      
      1 Reply Last reply Reply Quote 0
      • K Offline
        kbman99
        last edited by

        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)
        
        T 1 Reply Last reply Reply Quote 0
        • T Offline
          timh @kbman99
          last edited by

          @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

          K 1 Reply Last reply Reply Quote 0
          • S Offline
            Sympatron
            last edited by

            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.

            1 Reply Last reply Reply Quote 0
            • K Offline
              kbman99 @timh
              last edited by

              @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.

              1 Reply Last reply Reply Quote 0

              Hello! It looks like you're interested in this conversation, but you don't have an account yet.

              Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

              With your input, this post could be even better 💗

              Register Login
              • 1 / 1
              • First post
                Last post

              Pycom on Twitter

              Pycom