pythonclasscallbacktypeerroresp32

(Micro-) Python callback function inside class throws type error


I'm more or less new to Python and still struggling a bit with OOP (coming from C - not C++). I want reuse and extend a rotary encoder class and just wanted to add a switch with an interrupt callback routine. But it always throws an error:

from rotary_encoder.rotary_irq_esp import RotaryIRQ
from machine import Pin



class RotaryEncoder(RotaryIRQ):
  # rotary options
  RANGE_MODE = const(1) # 1->UNBOUNDED, 2->WRAP, 3->BOUNDED
  PIN_CLK = const(7)
  PIN_DT = const(8)
  PULL_UP = True

  # switch options
  PIN_SWITCH = const(6)
  PULL_UP_SWITCH = True
  
  def __init__(self, pin_num_clk=PIN_CLK, pin_num_dt=PIN_DT, min_val=0, max_val=10, incr=1,range_mode=RANGE_MODE, pull_up=PULL_UP,pin_switch=PIN_SWITCH,pull_up_switch=PULL_UP_SWITCH):
    super().__init__(pin_num_clk, pin_num_dt, min_val, max_val, incr, range_mode, pull_up)
    # add switch
    if pull_up_switch:
      self.switch = Pin(pin_switch,Pin.IN, Pin.PULL_UP)
      self.switch.irq(trigger=Pin.IRQ_FALLING, handler=self._rotary_switch_callback)
    else:
      self.switch = Pin(pin_switch,Pin.IN, Pin.PULL_DOWN)
      self.switch.irq(trigger=Pin.IRQ_RISING, handler=self._rotary_switch_callback)

  def _rotary_switch_callback(self):
    pass

Do you have any idea why it's not working??

The error is: TypeError: function takes 1 positional arguments but 2 were given

I tried several things but nothing worked. In another component I saw a callback function definition inside a class without (self) argument. This will also not work here (and by the way confuses me a lot why a function inside a class which is not a static method or class method is there without a (self) argument - but this is another topic :-)

I tried

handler=self._rotary_switch_callback)
handler=_rotary_switch_callback)

def _rotary_switch_callback(self):
def _rotary_switch_callback():

If I use the code outside a class it works but an additional argument is the instance itself. No idea how I should transfer this into the class.

def rotary_switch_callback(rotary_switch):
   pass 
rotary_switch = Pin(6,Pin.IN, Pin.PULL_UP)
rotary_switch.irq(trigger=Pin.IRQ_FALLING, handler=rotary_switch_callback)

If I do the following it (of course) does not work :-)

self.switch = Pin(pin_switch,Pin.IN, Pin.PULL_DOWN)
self.switch.irq(trigger=Pin.IRQ_RISING, handler=self._rotary_switch_callback)

def _rotary_switch_callback(self, self.switch):
  pass

I have no real idea why it does not work but I think I can't give an instance.instance as argument if the instance itself is the argument. Any idea how I can solve this the "correct" way without ugly tricks?


Solution

  • According to the MicroPython documentation:

    handler is an optional function to be called when the interrupt triggers. The handler must take exactly one argument which is the Pin instance.
    

    This means the handler function must accept one argument, which will be the Pin instance that triggered the interrupt.

    When you set up an interrupt like this:

    self.switch.irq(trigger=Pin.IRQ_RISING, handler=self._rotary_switch_callback)
    

    the handler function is expected to take two arguments: self and the Pin instance. (The self argument is implicit in instance methods.)

    So, you need to modify your _rotary_switch_callback method to accept the Pin instance as a parameter:

    def _rotary_switch_callback(self, pin):
        pass