Timer callback and infinite loop not playing

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
Gordon_Hardman
Posts: 68
Joined: Sat May 03, 2014 11:31 pm

Timer callback and infinite loop not playing

Post by Gordon_Hardman » Thu Oct 30, 2014 4:11 pm

A am new to Micropython, so forgive a noob question..

I am trying to have a system where most code executes in an infinite loop. A "background" timer runs at a 100msec rate, and the associated callback sets a flag, which is used in the infinite loop. I have seen on the forum that interrupts should not allocate memory, and perhaps not do other things, so I merely want to set a flag. I don't care precisely when the 100msec tasks are done, just that they get done around every 100 msec. The tasks, may grow in size and include all sorts of things, which might not play well with an interrupt, so I want to move them safely outside the interrupt.

The simplest code is attached. I see LED(1) toggling (on an o'scope!) but nothing else. It looks like maybe a scoping problem? Clearly hmsInt() is being called, so hmsFlag must be being set True. But it does not seem to be seen in the infinite loop. And no error messages.

Clearly imperfect understanding on my part, so any insights would help!

Code: Select all

# 
main.py -- put your code here!
import pyb

hms = 0  # hundred msec ticks
hmsFlag = False
seconds = 0 # seconds

def hundredmsTasks():
    pyb.LED(2).toggle()
    if hms < 100:
        hms += 1
    else:
        hms = 0
        print("!")

def hmsInt():
    pyb.LED(1).toggle()
    hmsFlag = True
            
tim7 = pyb.Timer(7, freq=100)
tim7.callback(lambda t: hmsInt())

while True:
    if hmsFlag:
        hundredmsTasks()
        hmsFlag = False
        
    # other code goes here
    

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

Re: Timer callback and infinite loop not playing

Post by dhylands » Fri Oct 31, 2014 12:05 am

You need to add a couple of global statements.

Code: Select all

def hmsInt():
    pyb.LED(1).toggle() # orange
    hmsFlag = True
In this version, hmsFlag is a local variable to the hmsInt function. If you change it to:

Code: Select all

def hmsInt():
    global hmsFlag
    pyb.LED(1).toggle() # orange
    hmsFlag = True
then it will modify the global hmsFlag.

Similarly, your hundredsmsTasks function needs a

Code: Select all

global hms
statement (in this case you'll get an error without it because you're trying to read the local before it was assigned).

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Timer callback and infinite loop not playing

Post by pythoncoder » Fri Oct 31, 2014 7:58 am

In fact a global declaration in hundredmsTasks() is optional, and an error won't be thrown in its absence. This is because hundredmsTasks() reads hms before it tries to write to it, and Python's scoping rules ensure that, having failed to look up the name in the function's scope it looks in the global scope*.

On the other hand hmsInt() only writes to hmsFlag so Python assumes that the intent is to instantiate a local variable. In this instance the global declaration is necessary to ensure Python looks for the variable in the global scope rather than creating a local variable with the same name as the global.

This isn't intended as Python pedantry: it's a bit of a "gotcha" in Python and it's worth having an understanding of these rules. Demonstration:

Code: Select all

>>> rats = 1
>>> def rat():
...   if rats:
...     print("rats")
...   else:
...     print("No rats")
... 
>>> rat()
rats
>>> rats = 0
>>> rat()   
No rats
>>> 
* Strictly it's slightly more involved: look up "Python scoping rules" for a full exposition.
Regards, Pete
Peter Hinch
Index to my micropython libraries.

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

Re: Timer callback and infinite loop not playing

Post by dhylands » Fri Oct 31, 2014 2:32 pm

pythoncoder wrote:In fact a global declaration in hundredmsTasks() is optional, and an error won't be thrown in its absence. This is because hundredmsTasks() reads hms before it tries to write to it, and Python's scoping rules ensure that, having failed to look up the name in the function's scope it looks in the global scope*.
ok - then I think there may be a bug in micropython? Because without the global statement in hundredmsTasks, I got the following error:

Code: Select all

>>> import hsm
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "hsm.py", line 31, in <module>
  File "hsm.py", line 14, in hundredmsTasks
NameError: local variable referenced before assignment

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Timer callback and infinite loop not playing

Post by pythoncoder » Fri Oct 31, 2014 5:10 pm

Oops! Micropython is behaving correctly and the global statement is required. In the example I gave, the function doesn't assign to the global, only reading its value, so no error is thrown. The global declaration is required because hundredmsTasks() modifies the global variable. I said Python scoping rules are tricky. I forgot just how tricky. Sorry about that.

Regards, Pete
Peter Hinch
Index to my micropython libraries.

Gordon_Hardman
Posts: 68
Joined: Sat May 03, 2014 11:31 pm

Re: Timer callback and infinite loop not playing

Post by Gordon_Hardman » Mon Nov 03, 2014 3:43 pm

Thanks to both of you! I should have realized, as this is exactly how it works in Synapse Wireless' SNAP embedded Python too. Don't normally think about globals in Python for PCs, with multitasking or threads- just use FIFOs...

Post Reply