Can I pass parameter to Timer callback?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
water
Posts: 75
Joined: Sun Sep 24, 2017 9:16 am

Can I pass parameter to Timer callback?

Post by water » Fri Jan 29, 2021 8:39 pm

How to do that? :?:
Thanks.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Can I pass parameter to Timer callback?

Post by dhylands » Sat Jan 30, 2021 1:21 am

Not directly.

You can make the timer callback a bound method in a class. This example shows how this is done:
https://github.com/dhylands/upy-example ... eat_irq.py

So the timer callback function has to take exactly 1 argument, which is the timer object that the IRQ is being called for. However, since the callback is bound to self, you can reference self.ticks and self.led from within the timer callback.

davidsmith
Posts: 2
Joined: Thu Apr 23, 2020 11:35 am

Re: Can I pass parameter to Timer callback?

Post by davidsmith » Thu Jun 10, 2021 10:15 am

t1 = Timer(1)
z = 400
t1.init(period=2000, mode=Timer.ONE_SHOT, callback=lambda b: test(z))
it work for me

Jibun no kage
Posts: 144
Joined: Mon Jul 25, 2022 9:45 pm

Re: Can I pass parameter to Timer callback?

Post by Jibun no kage » Tue Jul 26, 2022 2:12 pm

@davidsmith, How did it work? Using lambda? What does the callback declaration look like? Your example is incomplete. Can you show the callback declaration please?

Jibun no kage
Posts: 144
Joined: Mon Jul 25, 2022 9:45 pm

Re: Can I pass parameter to Timer callback?

Post by Jibun no kage » Wed Jul 27, 2022 8:37 pm

Figured it out, you just qualify the argument list as usual for the call back, so lambda t: SOME_FUNCTION(SOME_ARGUMENT) for example.

DeaD_EyE
Posts: 19
Joined: Sun Jul 17, 2022 12:57 pm
Contact:

Re: Can I pass parameter to Timer callback?

Post by DeaD_EyE » Thu Jul 28, 2022 12:23 pm

You can reach this with minimal effort.
  • Anonymous function: lambda

    Code: Select all

    from machine import Timer
    
    
    my_callback = lambda timer: print(42)
    
    Timer(1).init(mode=Timer.ONE_SHOT, period=1000, callback=my_callback)
    
  • Normal function

    Code: Select all

    from machine import Timer
    
    
    def my_callback(timer):
        print(42)
    
    
    Timer(1).init(mode=Timer.ONE_SHOT, period=1000, callback=my_callback)
    
  • With a closure (modified):

    Code: Select all

    from machine import Timer
    
    
    def timer_partial(func, *args, **kwargs):
        
        # added timer as a mandatory argument
        # to filter it out 
        def inner(timer, *iargs, **ikwargs):
            return func(*args, *iargs, **kwargs, **ikwargs)
    
        return inner
    
    
    Timer(1).init(mode=Timer.ONE_SHOT, period=1000, callback=timer_partial(print, "Hello World", end="=======\n"))
    
  • With a class:

    Code: Select all

    from machine import Timer
    
    
    class MyCallback:
        def __init__(self, value):
            self.value = value
            
        def callback(self, timer):
            """
            Callback is called by Timer.
            The first argument is the instance of the class
            The second argument is the instance of the timer, which is not used here
            """
            print("===", self.value, "===")
        
        def __call__(self, timer):
            """
            This enables the possibility to call the instance directly.
            """
            self.callback(timer)
            
        def __repr__(self):
            """
            Method to have a nice representation, but it's not required.
            """
            return f"{self.__class__.__name__}(value={self.value})"
    
    
    my_callback = MyCallback(1337)
    
    # works, because of the __call__ method:
    Timer(1).init(mode=Timer.ONE_SHOT, period=1000, callback=my_callback)
    
    # without having the __call__ method:
    Timer(1).init(mode=Timer.ONE_SHOT, period=1000, callback=my_callback.callback)
    
Another example with a class and automatic initialization of Timer:

Code: Select all

from time import sleep
from machine import Timer


def partial(func, *args, **kwargs):
    def inner(*iargs, **ikwargs):
        return func(*args, *iargs, **kwargs, **ikwargs)

    return inner


class TimerCallback:
    _instances = []
    __slots__ = ("_timer", "_callbacks", "_id", "delay", "mode")

    def __init__(self, id, delay, mode=Timer.PERIODIC):
        if id in self._instances:
            raise RuntimeError(f"Timer with id {id} exists already")

        self.delay = delay
        self.mode = mode

        self._id = id
        self._callbacks = []
        self._timer = Timer(id)
        self._instances.append(id)

    def _callback(self, timer):
        for callback in self._callbacks:
            callback()

    def start(self):
        self._timer.init(mode=self.mode, period=self.delay, callback=self._callback)

    def stop(self):
        self._timer.deinit()

    def add_callback(self, callback, *args, **kwargs):
        if not callable(callback):
            raise TypeError("Callback must be callable")

        prepared_callback = partial(callback, *args, **kwargs)
        self._callbacks.append(prepared_callback)

        return prepared_callback

    def del_callback(self, prepared_callback):
        if prepared_callback in self._callbacks:
            self._callbacks.remove(prepared_callback)

    def kill(self):
        self._timer.deinit()
        if self._id in self._instances:
            self._instances.remove(self._id)


tc = TimerCallback(1, 5000)
tc.add_callback(print, "Hello", "World", sep="\t", end="\n\n\n\n")
my_callback_reference = tc.add_callback(print, "Hello World 2")
tc.start()
sleep(11)
tc.stop()

try:
    tc2 = TimerCallback(1, 300)
except RuntimeError as e:
    print(e.args[0])

tc.kill()
# will remove also the id

try:
    tc2 = TimerCallback(1, 3000)
except RuntimeError as e:
    print(e.args[0])

tc2.add_callback(print, "Lesser args")
tc2.start()

sleep(12)
tc2.kill()
Many functions we use in Micropython are implemented in C to save space on the Microcontroller.
If any possible use-case is implemented by Micropython, the firmware won't fit on all Microcontrollers.

Post Reply