uasyncio - How detect the end task in another task.

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
User avatar
pythoncoder
Posts: 4351
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uasyncio - How detect the end task in another task.

Post by pythoncoder » Wed Jul 22, 2020 4:05 pm

Maybe you need to read up on Python classes. There are three types of variables associated with classes: class variables, instance variables, and variables local to a method.

Code: Select all

class foo:
    my_class_variable = 0
    def __init__(self):
        my_instance_variable = "hello"  # These enable you to share data between methods
    def my_method(self):
        print(self.my_instance_variable)  # this will work
        self.my_instance_variable = "goodbye"  # This will work too
        x = 42   # local variable accessible to no other method
You probably don't need to learn about or use class variables to begin with.
Peter Hinch

Primesty
Posts: 17
Joined: Sun Jun 28, 2020 11:06 pm

Re: uasyncio - How detect the end task in another task.

Post by Primesty » Wed Jul 22, 2020 9:37 pm

Hi Peter,

Thanks for the quick Python re-fresher! I'll look into it!

Primesty
Posts: 17
Joined: Sun Jun 28, 2020 11:06 pm

Re: uasyncio - How detect the end task in another task.

Post by Primesty » Sat Jul 25, 2020 9:40 pm

Okay, so I was able to implement a first test case for a class like you mentioned, Peter.

This works

Code: Select all

from machine import Pin, Timer, I2C
from rotary_irq_esp import RotaryIRQ
import ssd1306
import dht
import network
import time
import uasyncio

sensor = dht.DHT22(Pin(14))

led = Pin(2, Pin.OUT)

class temp_reader():
  
  sleep = 1 #static instance variable for testing
  
  def __init__(self, led):
    self.led = led
    
  async def blink(self):
    while True:
        self.led.on()
        await uasyncio.sleep(temp_reader.sleep)
        self.led.off()
        await uasyncio.sleep(temp_reader.sleep)
        

  async def cancel(self):
    await uasyncio.sleep(10) #stops the loop after 10 seconds

def main():
  
  test = temp_reader(led) #instantiates instance of class
  loop = uasyncio.get_event_loop()
  loop.create_task(test.blink())
  loop.run_until_complete(test.cancel()) #makes the whole thing run until cancel function kicks in

main()
The weird thing is that I sometimes have to re-boot the ESP32 and on top of that I sometimes get this error and it won't reboot until I take it out of the bread-board off all connections. This is just one example of similar messages in an endless loop.

Code: Select all

rst:0x10 (RTCWDT_RTC_RESET),boot:0x33 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371 
ets Jun  8 2016 00:22:57
Now when I try to add the temperature sensor to the mix, the behavior is a little weirder:

Code: Select all


from machine import Pin, Timer, I2C
from rotary_irq_esp import RotaryIRQ
import ssd1306
import dht
import network
import time
import uasyncio

sensor = dht.DHT22(Pin(14))

led = Pin(2, Pin.OUT)

class temp_reader():
  
  sleep = 1 #static instance variable for testing
  
  def __init__(self, led, sensor):
    self.led = led
    self.sensor = sensor
    
  async def blink(self):
    while True:
        self.led.on()
        await uasyncio.sleep(temp_reader.sleep)
        self.led.off()
        await uasyncio.sleep(temp_reader.sleep)
        
  async def temp(self):
    while True:
      self.sensor.measure()
      temp = self.sensor.temperature()
      print('Temperature: %3.1f C' %temp)
      await uasyncio.sleep(5)
    

  async def cancel(self):
    await uasyncio.sleep(10) #stops the loop after 10 seconds

def main():
  
  test = temp_reader(led, sensor)
  loop = uasyncio.get_event_loop()
  loop.create_task(test.blink())
  loop.create_task(test.temp())
  loop.run_until_complete(test.cancel()) #makes the whole thing run until cancel function kicks in

main()
Sometimes it works...
Ready to download this file,please wait!
.........
download ok
exec(open('./main.py').read(),globals())
Temperature: 21.6 C
Temperature: 21.6 C
More often than not, it gives me either this error:
Ready to download this file,please wait!
.........
download ok
exec(open('./main.py').read(),globals())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 48, in <module>
File "<string>", line 46, in main
File "/lib/uasyncio/core.py", line 180, in run_until_complete
File "/lib/uasyncio/core.py", line 109, in run_forever
File "<string>", line 31, in temp
File "dht.py", line 16, in measure
OSError: [Errno 110] ETIMEDOUT
Or it says reflush-tree false and I disconnect and connect and if the ESP32 is in the bread-board, it wants me to re-burn micropython until I take it out of the bread-board. Any idea why I'm seeing this erratic behavior?

I think once this runs reliably, it should be on to the encoder co-routine...

As always, any feedback is more than welcome!

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

Re: uasyncio - How detect the end task in another task.

Post by pythoncoder » Sun Jul 26, 2020 9:10 am

Let's stick with the simple LED flash example. If we can solve its problems, then we may be in with a chance with the other samples.

There is nothing obviously wrong with your code, which makes me suspect the import statements. What are they causing to run? In particular

Code: Select all

from rotary_irq_esp import RotaryIRQ
Is some interrupt handler running in the background? I suggest commenting it out and re-trying. The flash read error is very odd: I can't say I've ever seen that.

As it stands your code will only run once before needing a soft boot. Please read this section of my tutorial to learn how to fix this.
Peter Hinch

Primesty
Posts: 17
Joined: Sun Jun 28, 2020 11:06 pm

Re: uasyncio - How detect the end task in another task.

Post by Primesty » Sun Jul 26, 2020 1:45 pm

Hey Peter,

I've implemented your suggestions and reduced the code to the blink example:

Code: Select all

from machine import Pin, Timer, I2C
import dht
import time
import uasyncio
import gc

led = Pin(2, Pin.OUT)

class temp_reader():
  
  sleep = 1 #static instance variable for testing
  
  def __init__(self, led):
    self.led = led
    
  async def blink(self):
    while True:
        self.led.on()
        await uasyncio.sleep(temp_reader.sleep)
        self.led.off()
        await uasyncio.sleep(temp_reader.sleep)
        
  async def cancel(self):
    await uasyncio.sleep(10) #stops the loop after 10 seconds

def main():
  try:
    test = temp_reader(led)
    loop = uasyncio.get_event_loop()
    loop.create_task(test.blink())
    loop.run_until_complete(test.cancel()) #makes the whole thing run until cancel function kicks in
  except:
    print('Interrupted')
  finally:
    loop = uasyncio.new_event_loop() #or loop = uasyncio.new_event_loop() or loop.uasyncio.new_event_loop()


main()

gc.collect()
gc.threshold(gc.mem_free() // 4 + gc.mem_alloc())

The only thing that happens now when I try to resolve the soft-reboot is that I get an error that says this:
Ready to download this file,please wait!
........
download ok
exec(open('./main.py').read(),globals())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 39, in <module>
File "<string>", line 36, in main
AttributeError: 'module' object has no attribute 'new_event_loop'
I've also tried just 'uasyncio.new_event_loop()' like in your example on the tutorial but that throws the same error. Any idea why?

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

Re: uasyncio - How detect the end task in another task.

Post by pythoncoder » Mon Jul 27, 2020 6:16 am

You're using an old version of uasyncio. You need version 3. This is available in daily firmware builds. So install the latest daily build.

You can check the version as follows:

Code: Select all

import uasyncio as asyncio
print(asyncio.__version__)
Old versions will throw an exception. V3 will (currently) produce (3, 0, 0).
Peter Hinch

Primesty
Posts: 17
Joined: Sun Jun 28, 2020 11:06 pm

Re: uasyncio - How detect the end task in another task.

Post by Primesty » Mon Jul 27, 2020 1:35 pm

Gotcha!

Yeah, like you said, I didn't get a version number.

As for firmware, I'm currently running
esp32-idf3-20191220-v1.12.bin
I understand other builds are available here http://micropython.org/download/esp32/ right?

For a daily build, do I essentially pick one of the unstable ones? What is the difference between ESP-IDF v3.x and ESP-IDF v4.x ? Is one better than the other?

I had originally installed *uasyncio* via

Code: Select all

upip.install('micropython-uasyncio')
in uPyCraft and it generated a *lib* folder in the filesystem.

Do I understand you correctly and uasyncio will be included in the daily build and I won't have to install it manually?

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

Re: uasyncio - How detect the end task in another task.

Post by pythoncoder » Mon Jul 27, 2020 2:34 pm

Yes, install the latest daily build and don't worry about the "unstable" name. It will be in the forthcoming V1.13 release build. You followed the right procedure for uasyncio V2, but the new version is much improved and needs no installation.

As for the ESP_IDF I don't know the answer to this. ISTR V3 supports some features not yet supported on V4. You might try forum and GitHub searches. I use V3 daily builds in my testing.
Peter Hinch

Primesty
Posts: 17
Joined: Sun Jun 28, 2020 11:06 pm

Re: uasyncio - How detect the end task in another task.

Post by Primesty » Mon Jul 27, 2020 9:50 pm

Awesome! I've installed
esp32-idf3-20200727-unstable-v1.12-662-g8da40baa4.bin
and *uasyncio* worked right out of the box and I'm getting version 3.0.0!

The blink example runs fine now on the bread-board with i2c ports connected as well as GPIO 14 for the DHT22 sensor.

Code: Select all

from machine import Pin, Timer, I2C
import dht
import time
import uasyncio
import gc

led = Pin(2, Pin.OUT)

class temp_reader():
  
  sleep = 1 #static instance variable for testing
  
  def __init__(self, led):
    self.led = led
    
  async def blink(self):
    while True:
        self.led.on()
        await uasyncio.sleep(temp_reader.sleep)
        self.led.off()
        await uasyncio.sleep(temp_reader.sleep)
        
  async def cancel(self):
    await uasyncio.sleep(10) #stops the loop after 10 seconds

def main():
  try:
    test = temp_reader(led)
    loop = uasyncio.get_event_loop()
    loop.create_task(test.blink())
    loop.run_until_complete(test.cancel()) #makes the whole thing run until cancel function kicks in
  except:
    print('Interrupted')
  finally:
    loop = uasyncio.new_event_loop() #or loop = uasyncio.new_event_loop() or loop.uasyncio.new_event_loop()


main()

gc.collect()
gc.threshold(gc.mem_free() // 4 + gc.mem_alloc())
I haven't gotten any other instances of this error:

Code: Select all

rst:0x10 (RTCWDT_RTC_RESET),boot:0x33 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371 
ets Jun  8 2016 00:22:57
While I do sometimes get re-flush tree false - the scripts upload fine. I suspect that some of the pins I had connected to the rotary encoder might have caused this. Since I removed these connections, and only have I2C and the temp sensor connected, I'm not getting any errors.

I assume that clears me for the next step: to implement the temp_measure coro and store the results in an instance or class variable, right?

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

Re: uasyncio - How detect the end task in another task.

Post by pythoncoder » Tue Jul 28, 2020 5:19 am

The way you start your code is legacy stuff. It's still supported in usayncio V3 (and CPython 3.8) but it isn't current. Check out this section of my tutorial for the preferred way.
Peter Hinch

Post Reply