0

Does CircuitPython have a non-blocking Timerfunction?
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Does CircuitPython have a non-blocking Timerfunction?

by StefanL38 on Sun Dec 06, 2020 7:36 am

Hi everybody,

to get right down to the point: Without a non-blocking-timer-function I find CircuitPython almost useless.
So does there exist one?

I found an Issue on GitHub about that but to me it seems to be not solved /finished
https://github.com/adafruit/circuitpython/issues/644

Read the docs https://circuitpython.readthedocs.io/en ... nit__.html
just says something about time.monotonic()

best regards Stefan

StefanL38
 
Posts: 9
Joined: Fri Apr 03, 2020 3:48 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by dastels on Sun Dec 06, 2020 2:56 pm

Yes, this is generally done using time.monotonic(). In the main loop you have something llike:

Code: Select all | TOGGLE FULL SIZE
   
now = time.monotonic()
if now >= time_to_check:          #only check each second
    # do stuff that should happen once per second
    time_to_check = now + 1.0


Start by having a variable to track the time that something should happen, then check in the loop if it's that time yet, if so do the thing and set the next time. You can have multiple things happening this way at different times/intervals with a time variable and if clause for each.

Dave

dastels
 
Posts: 5394
Joined: Tue Oct 20, 2015 3:22 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by dastels on Sun Dec 06, 2020 2:57 pm


dastels
 
Posts: 5394
Joined: Tue Oct 20, 2015 3:22 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by StefanL38 on Sun Dec 06, 2020 3:34 pm

Hi Dave,

thank you for answering.

so time.monotonic() delivers a float.
I have already read that floats are not exact. I did some experiments with
things like
Code: Select all | TOGGLE FULL SIZE
    now = time.monotonic()
    now_ns = time.monotonic_ns()
    print ('Now:',now * 100.0 , '  nanosec:', now_ns)


But I'm unsure how many decimals I can trust.
or should I just use monotonic_ns() for timings in the 5 to 100 ,illiseconds-range?


OK. Works pretty similar to millis() in C++. So I looked up how int is defined in python. Is this right unlimited number of bits?

So just as a thought-experiment: I could setup a microcontroller running python to control a nuclear heating let it run for millions of years and the timing is still working? so the guinea-pig will still be nicely dried like in day of the tentacle? ;-))

Of course with this basic functions I can setup timing-fucntions.
Is there a common "library" or "modul" (don't know how it is called in python )
or some kind of code-utility-collection that gives more functionality than just monotonic()?

best regards Stefan

StefanL38
 
Posts: 9
Joined: Fri Apr 03, 2020 3:48 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by StefanL38 on Sun Dec 06, 2020 4:04 pm

So I tried to code a function that results in true/false depending on how much time has passed by. But it does not work yet.
Wah am I doing wrong?

Code: Select all | TOGGLE FULL SIZE
import board
import digitalio
import time

def TimePeriodIsOver(TimerVar, Period):
    now = time.monotonic()
    if TimerVar - now >= (Period /1000):
       TimerVar = now + Period
       return True
    else:
       return False   

TimerVar = time.monotonic()

led = digitalio.DigitalInOut(board.D13)   
                                           
led.direction = digitalio.Direction.OUTPUT
duration = 0.1                             

print('Hello 4 World!')                   
now = time.monotonic()
now_ns = time.monotonic_ns()
print ('Now:',now, '  nanosec:', now_ns)

while True:                   
   
    if TimePeriodIsOver(TimerVar, 500) == True:
      if led.value == True:                     
        led.value = False
        now = time.monotonic()
        now_ns = time.monotonic_ns()
        print ('Now:',now, '  nanosec:', now_ns)
       
      else: 
        led.value = True
   

best regards Stefan

StefanL38
 
Posts: 9
Joined: Fri Apr 03, 2020 3:48 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by dastels on Sun Dec 06, 2020 10:33 pm

Yes, monotonic() returns a float representing a number of seconds.Yes, floats are not exact.

I haven't experiences problems with it yet, but I just use it to 3 digits to the right of the decimal (i.e. millisecond accuracy). It's worked well enough.

monotonic_ns() is a good alternative for shorter time periods.

I don't know off hand of a module that builds on top of monotonic().

Dave

dastels
 
Posts: 5394
Joined: Tue Oct 20, 2015 3:22 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by dastels on Sun Dec 06, 2020 10:52 pm

Updating the value of TimerVar in TimePeriodIsOver() just updates the local copy, not TimerVar that's passed in. You can either have it return TimerVar along with the boolean:

Code: Select all | TOGGLE FULL SIZE
def TimePeriodIsOver(TimerVar, Period):
    now = time.monotonic()
    if TimerVar - now >= (Period /1000):
       return True, now + Period
    else:
       return False, TimerVar


Then when you call it:
Code: Select all | TOGGLE FULL SIZE
result, TimerVar = TimePeriodIsOver(TimerVar, 500)
if result:
  ...


(side note: you don't need to compare a boolean to True or False, it already is True or False)

The other option is to get rid of the parameter and use the global TimerVar:

Code: Select all | TOGGLE FULL SIZE
def TimePeriodIsOver(Period):
    global TimerVar
    now = time.monotonic()
    if TimerVar - now >= (Period /1000):
       TimerVar = now + Period
       return True
    else:
       return False   


But that isn't as clear.

Dave

dastels
 
Posts: 5394
Joined: Tue Oct 20, 2015 3:22 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by StefanL38 on Mon Dec 07, 2020 4:26 am

Hi Dave,

thank you very much for showing how it canbe done. I did some more research and found an articel about how something similar like "pass by reference" can be done in python
https://realpython.com/python-pass-by-reference/

now I have two questions: This articel shows how to use the new assignment expressions ":=" which is implemeneted in Python 3.8

So is CircuitPython already based of python 3.8 and does have this new assignment-expression ":="?

If yes - How would my TimePeriodIsOver-function be coded
to have the code as compact as possible similar to this

if (TimerVar := TimePeriodIsOver(TimerVar,Period) is True)

This function shall do two things in just one line of code:

1: be usable directly in if-conditions
2: update variable TimerVar

best regards Stefan

StefanL38
 
Posts: 9
Joined: Fri Apr 03, 2020 3:48 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by dastels on Mon Dec 07, 2020 10:56 am

CircuitPython is a fork of MicroPython which is a from the ground up re-implementation of Python 3. (3.4ish IIRC). So it won't have all the latest Python feathers. Indeed, some are just not feasible because of the constrained environment. If you're ever wondering about whether something is supported, just pull up the CircuitPython REPL and try it.

Personally, I don't care for pass by reference (although it can be far more efficient when dealing with things larger than simple numbers). Python doesn't really benefit much from having in-out parameters (effectively returning values via parameters) since it has the ability to return multiple values from functions.

Dave

dastels
 
Posts: 5394
Joined: Tue Oct 20, 2015 3:22 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by danhalbert on Mon Dec 07, 2020 12:01 pm

The assignment operator was recently implemented in MicroPython here: https://github.com/micropython/micropython/pull/4908. We are probably going to merge from upstream eventually and can incorporate this addition.

danhalbert
 
Posts: 2297
Joined: Tue Aug 08, 2017 12:37 pm

Re: Does CircuitPython have a non-blocking Timerfunction?

by enauman on Sat Feb 20, 2021 12:36 pm

Thank you for this, more concise than what I had been doing!

dastels wrote:Yes, this is generally done using time.monotonic(). In the main loop you have something llike:

Code: Select all | TOGGLE FULL SIZE
   
now = time.monotonic()
if now >= time_to_check:          #only check each second
    # do stuff that should happen once per second
    time_to_check = now + 1.0


Start by having a variable to track the time that something should happen, then check in the loop if it's that time yet, if so do the thing and set the next time. You can have multiple things happening this way at different times/intervals with a time variable and if clause for each.

Dave

enauman
 
Posts: 1
Joined: Sat Dec 26, 2020 12:16 am

Please be positive and constructive with your questions and comments.