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で終了します。非同期動作(並行処理)はこんな感じなのだと思います。
※水色: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は同期処理になるようです。
※水色: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の使い方が少しわかったような気がします。