稼働中

Raspberry Pi Pico(s_23)uasyncio の動作例 await

async/await

Raspberry Pi PicoのMicroPythonにはmodule’uasyncio’があります。
uasyncioを使うと非同期動作するそうです。_threadのような並行処理ができるようです。
uasyncio…ややこしい感じなので避けてました。
素人なので詳しくは説明できません。わかる範囲で簡単な動作を確認してみようと思います。

uasyncioを使ってIRセンサー FC-51の検知動作を確認します。
IRセンサー FC-51の外観、接続例は記事d_28を参照ください。
※開発環境はThonnyです。ThonnyでMicroPythonをRaspberry Pi Pico with RP2040にインストールして使っています。

uasyncio

uasyncioのasync/await構文で並行処理できるそうです。素人なので詳しくはわかりません。雰囲気だけ確認します。

以下のスクリプトは、await uasyncio.sleepを使って
LED1を0.5sec-ON > 0.5sec-off
LED2を0.3sec-ON > 0.3sec-off
するだけです。
uasyncio.create_taskで並行処理する動作を設定します。
動作がわかるように各所にprint文で経過時間を表示出力しています。


#!/usr/bin/env python
# -*- coding: utf-8 -*-
#b_test_01.py
from machine import Pin
import time
import uasyncio

#OUT Pin 
led1=Pin(22, Pin.OUT, value=0)  #Y-LED
led2=Pin(26, Pin.OUT, value=0)  #R-LED
      
async def LED1():
    global st                       #開始時間
    et=uasyncio.ticks()             #測定時間
    print(f'LED1--- {et-st}')       #et-st 経過時間
    led1.high()
    await uasyncio.sleep(0.5)       #①
    et=uasyncio.ticks()
    print(f'LED1(H) {et-st}')       #④
    led1.low()
    await uasyncio.sleep(0.5)
    et=uasyncio.ticks()
    print(f'LED1(L) {et-st}')       #⑤

async def LED2():
    global st
    et=uasyncio.ticks()
    print(f'LED2--- {et-st}')
    led2.high()
    await uasyncio.sleep(0.3)       #②
    et=uasyncio.ticks()
    print(f'LED2(H) {et-st}')       #③
    led2.low()
    await uasyncio.sleep(0.3)
    et=uasyncio.ticks()
    print(f'LED2(L) {et-st}')       #④

async def main():
    global st               #開始時間
    st=uasyncio.ticks()
    task1 = uasyncio.create_task(LED1())
    task2 = uasyncio.create_task(LED2())
    await task1
    await task2
    et=uasyncio.ticks()     #測定時間
    print('end',et-st)      #et-st 経過時間 ⑦
    
uasyncio.run(main())

実行すると以下のようにThonnyのShell部に表示されました。並行動作しているのがわかります。時間の短いLED2が先に終了しています。全体の経過時間はLED1のON-OFFの約1secで終了しています。


>>> %Run -c $EDITOR_CONTENT
LED1--- 1               #①のawait
LED2--- 2               #②のawait
LED2(H) 305             #③ LED2-ON-0.3sec後
LED1(H) 502             #④ LED1-ON-0.5sec後
LED2(L) 607             #⑤ LED2-OFF-0.3sec後
LED1(L) 1003            #⑥ LED2-OFF-0.5sec後
end 1005                #⑦ end
>>> 

わかりにくいですが以下に図にしてみました。
開始はほぼ同じです。並行動作するのでLED2は0.6secで終わりLED1は1.0secで終了します。非同期動作(並行処理)はこんな感じなのだと思います。
uasyncio await
※水色:LED-ON、黄色:LED-OFF

次にLED1のONの部分をawait uasyncio.sleep(0.5)の部分をawait uasyncio.sleep(0.2)とtime.sleep(0.3)にしてみます。


async def LED1():
    global st
    et=uasyncio.ticks()
    print(f'LED1--- {et-st}')
    led1.high()
    await uasyncio.sleep(0.2)
    et=uasyncio.ticks()
    print(f'LED1(H) {et-st}')
    time.sleep(0.3)
    et=uasyncio.ticks()
    print(f'LED1(S) {et-st}')
    led1.low()
    await uasyncio.sleep(0.5)
    et=uasyncio.ticks()
    print(f'LED1(L) {et-st}')

実行すると以下のようになりました。


>>> %Run -c $EDITOR_CONTENT
LED1--- 1
LED2--- 2
LED1(H) 202     # LED1 await uasyncio.sleep(0.2)が済
LED1(S) 503     # LED1 time.sleep(0.3)が済
LED2(H) 504     # LED2 await uasyncio.sleep(0.3)済+0.2待機
LED2(L) 805     # await uasyncio.sleep(0.3)済
LED1(L) 1003
end 1004
>>> 

図にすると以下のような感じです。LED1がtime.sleep(0.3)の期間にLED2のONが終わるので、LED1がtime.sleepが終わるまで待機になります。LED1-off awaitになったらLED2-off awaitになるのでLED2-offは約0.8sec(805)で終わり、LED1は1secで終わります。
async/awaitは非同期処理、time.sleepは同期処理になるようです。

uasyncio.sleep
※水色:LED-ON、黄色:LED-OFF、灰色:time.sleep、紫色:待機

IRセンサー FC51の検知

uasyncioを使うと並行処理できるのでセンサーの検出確認を常時しながら他処理ができそうです。
uasyncioを使ってIRセンサー FC51の検知動作を確認します。
具体的にはLED1の点滅とIRセンサーの検出を常時行いながら、物体検出後に5回LED2を点滅をさせようと思います。

スクリプトは以下のようにしました。


#led_while_01_task.py
from machine import Pin
import time
import uasyncio

#OUT Pin
led1=Pin(22, Pin.OUT, value=0)  #R-LED(常時点滅)
led2=Pin(26, Pin.OUT, value=0)  #Y-LED(検出点滅)

#FC-51 IR Detector
dpin=Pin(27, Pin.IN, Pin.PULL_DOWN)  # 検出 dpin='L'

#LED1は常時点滅
async def LED1():
    global detect
    print('LED1------')
    while True:
        led1.high()
        print('LED1  on')           #①awaitで待機
        await uasyncio.sleep(0.2)
        led1.low()
        await uasyncio.sleep(0.2)
        print('------LED1 ',detect)

#FC-51常時監視 検出したらdetect=1
async def Sens():
    global detect               # detect:検出フラグ
    print('Sens------')
    while True:        
        if dpin.value()==0:
            print('detect')
            detect=1
        print('------Check',detect)     #③awaitで待機
        await uasyncio.sleep(0.5)

#検出後の処理  led2点滅 5回
async def LED2():
    global detect               # detect:検出フラグ
    print('LED2------')
    while True:
        if detect==1:
            for i in range(5):
                print(f'LED2  blink({i})')
                led2.high()
                await uasyncio.sleep(0.2)
                led2.low()
                await uasyncio.sleep(0.2)
            print('Reset---------')
            detect=0
        else:
            print('LED2 wait 1sec')     #②awaitで待機
            await uasyncio.sleep(1)
        print('------LED2 ',detect)

#非同期処理
async def main():
    global detect               # detect:検出フラグ
    detect=0
    task1 = uasyncio.create_task(LED1())
    task2 = uasyncio.create_task(LED2())
    task3 = uasyncio.create_task(Sens())

    await task1
    await task2
    await task3

uasyncio.run(main())

部分説明

task1(async def LED1())はLED1を0.2sec間隔で点滅
task2(async def LED2())は検出フラグを’1’になればLED2を0.2sec間隔で5回点滅させます。点滅後は検出フラグを’0’にします。
task3(async def Sens())はIRセンサー(FC-51)の出力を0.5sec間隔で確認します。検出したら検出フラグを’1’にします。

task1、task2は常時並行動作しており、検出後はtask1、task2、task3が並行動作している感じです。
task3終了後はtask1、task2は常時並行動作に戻ります。

ちなみに、uasyncio.gather を使えば簡潔になります。
async def main():
global detect # detect:検出フラグ
detect=0
await uasyncio.gather(LED1(),LED2(),Sens())
で良いと思います。たぶん..。

実行結果

結果は以下のようになりました。※Thonnyのshellに表示されます。
出力表示だけなのでわかり難いですが、task1、task2、task3が並行処理されています。


>>> %Run -c $EDITOR_CONTENT
LED1------
LED1  on            #① LED ON(点滅)
LED2------
LED2 wait 1sec      #②
Sens------
------Check 0       #③ 未検出 フラグ0
------LED1  0
LED1  on            # LED ON(点滅)継続
detect              # 物体検知 フラグ1
------Check 1
------LED1  1
LED1  on            # LED ON(点滅)継続
------LED2  1       # LED2 点滅開始
LED2  blink(0)      # LED2 点滅1回
detect
------Check 1
------LED1  1
LED1  on
LED2  blink(1)      # LED2 点滅2回
------Check 1
------LED1  1
LED1  on
LED2  blink(2)      # LED2 点滅3回
------Check 1
------LED1  1
LED1  on
LED2  blink(3)      # LED2 点滅4回
------LED1  1
LED1  on
------Check 1
LED2  blink(4)      # LED2 点滅5回
------LED1  1
LED1  on
------Check 1
Reset---------      # LED2 点滅後のフラグリセット
------LED2  0       # 未検出
LED2 wait 1sec
------LED1  0       # 未検出
LED1  on
detect              # 物体検知 フラグ1
------Check 1
------LED1  1
LED1  on
detect              # 物体検知 フラグ1
------Check 1
------LED2  1
LED2  blink(0)
------LED1  1
LED1  on
(以下省略)
Traceback (most recent call last):
(省略)
KeyboardInterrupt: 
>>> 

まとめ

uasyncioのasync/awaitの使い方が少しわかったような気がします。