11.8. โมดูล aioble¶
ข้อกำหนด Bluetooth Core มีคำศัพท์ที่แมปกับโมดูล MicroPython สองโมดูล
bluetooth-- การเชื่อมโยง ระดับต่ำ กับ BLE controller ทำงานแบบซิงโครนัส ขับเคลื่อนด้วยอีเวนต์ผ่านคอลแบ็กสไตล์ IRQ และมีโครงสร้างรอบบัฟเฟอร์ไบต์, handle, และ GATT primitives พื้นฐาน มันเปิดเผยโปรโตคอลตามที่เป็น ไม่ใช่ตามที่แอปพลิเคชัน Python ต้องการใช้aioble-- wrapper ระดับสูงกว่า เขียนด้วย Python บนbluetoothที่แปลงทุกการดำเนินการระยะไกลให้เป็น coroutine ของasyncioและทุก BLE object (services, characteristics, connections, scan results, L2CAP channels) ให้เป็น Python class ที่ใช้งานง่าย การสแกนกลายเป็น async iterators, การเชื่อมต่อกลายเป็น async context managers, การแจ้งเตือนกลายเป็น awaitable
11.8.1. เมื่อใดควรใช้โมดูลระดับต่ำ¶
bluetooth ยังคงเป็นคำตอบที่ถูกต้องสำหรับสองกรณีที่แคบ:
คุณกำลังเขียนโค้ดประเภทที่
aiobleถูกสร้างขึ้นมา ซึ่งเป็นรูปแบบใหม่ที่ต้องการการควบคุมระดับ IRQ เหนือโปรโตคอลคุณกำลังทำงานบน hardware target ที่ไม่มีแพ็กเกจ
aiobleและ shim บางๆ รอบ controller เป็นตัวเลือกเดียว
สำหรับแอปพลิเคชัน camera ทุกตัว aioble คือคำตอบที่ถูกต้อง
11.8.2. ส่วนประกอบของโปรแกรม aioble¶
แอปพลิเคชันที่ใช้ aioble ทุกตัวมีชิ้นส่วนที่เคลื่อนไหวชุดเล็กๆ โดยไม่คำนึงถึงบทบาทที่เล่น
event loop ของ
asyncioที่ทำงานต่อเนื่อง ทุกอย่างในaiobleเป็น coroutine ดังนั้นแอปพลิเคชันจึงมีโครงสร้างเป็น task หนึ่งตัวหรือมากกว่าบน event loop เดียว ดู Asyncio สำหรับรายละเอียดเกี่ยวกับ loop, tasks, และ exceptionsวิทยุที่เปิดอยู่
aiobleจะเปิดใช้งาน BLE radio โดยอัตโนมัติเมื่อใช้งานครั้งแรก แต่ยังสามารถควบคุมได้อย่างชัดเจนด้วยaioble.config()(ซึ่งส่งต่อไปยังbluetooth.BLE.config()หลังจากตรวจสอบว่าวิทยุทำงานอยู่) และปิดด้วยaioble.stop()บทบาท หนึ่งตัวหรือมากกว่าที่กำลังทำงานพร้อมกัน ฝั่ง peripheral: ชุด GATT services ที่ลงทะเบียนแล้ว (ดู
aioble.register_services()) และ coroutineaioble.advertise()ที่กำลังทำงาน ฝั่ง central: iteratoraioble.scan()ที่กำลังทำงานหรือaioble.Device.connect()ที่รอดำเนินการ วิทยุจะมัลติเพล็กซ์งาน โดยแอปพลิเคชันมองเห็นแต่ละบทบาทเป็น task อิสระ
11.8.3. peripheral ขั้นต่ำ¶
โปรแกรม aioble ที่เล็กที่สุดที่มีประโยชน์ ซึ่งเป็น peripheral ที่โฆษณา characteristic แบบอ่านอย่างเดียวหนึ่งตัว มีความสั้นกระชับ:
import aioble
import asyncio
import bluetooth
SERVICE_UUID = bluetooth.UUID(0x181A) # Environmental Sensing
TEMP_UUID = bluetooth.UUID(0x2A6E) # Temperature
service = aioble.Service(SERVICE_UUID)
temp = aioble.Characteristic(service, TEMP_UUID, read=True)
aioble.register_services(service)
async def main():
while True:
conn = await aioble.advertise(
interval_us=250000,
name="openmv-temp",
services=[SERVICE_UUID],
)
async with conn:
await conn.disconnected()
asyncio.run(main())
central ที่ทำเพียงแค่เชื่อมต่อและอ่านครั้งเดียวก็สั้นในลักษณะเดียวกัน:
import aioble
import asyncio
import bluetooth
SERVICE_UUID = bluetooth.UUID(0x181A)
TEMP_UUID = bluetooth.UUID(0x2A6E)
async def main():
device = None
async with aioble.scan(duration_ms=5000, active=True) as scanner:
async for result in scanner:
if SERVICE_UUID in result.services():
device = result.device
break
if device is None:
return
async with await device.connect() as conn:
service = await conn.service(SERVICE_UUID)
char = await service.characteristic(TEMP_UUID)
print(await char.read())
asyncio.run(main())
ทั้งสองโปรแกรมมีประมาณสิบห้าบรรทัดและครอบคลุมขั้นตอนทั้งหมดตั้งแต่ "วิทยุปิดอยู่" ถึง "งานที่มีประโยชน์เสร็จสิ้น"
11.8.4. การปิดวิทยุ¶
บน camera ที่ใช้แบตเตอรี่ BLE radio คือสิ่งที่ใช้พลังงานมากที่สุดในงบประมาณที่เลือกได้ มีปุ่มควบคุมสองตัวที่สำคัญ
ตัวแรกเป็น แบบอัตโนมัติ: aioble จะเปิดใช้งาน radio เมื่อใช้งานครั้งแรก และ radio จะเข้าสู่โหมดพักระหว่างอีเวนต์ที่กำหนดเวลาไว้ (ชุดโฆษณา, อีเวนต์การเชื่อมต่อ, หน้าต่างสแกน) โดยอัตโนมัติ การเลือกช่วงเวลาที่ยาวขึ้นสำหรับ aioble.advertise() / aioble.scan() และการตกลงช่วงเวลาการเชื่อมต่อที่ยาวขึ้นที่ connect() จะช่วยให้ radio ปิดอยู่นานขึ้นตามสัดส่วน ตารางโฆษณาใน การโฆษณาและการสแกน เป็นแนวทางปฏิบัติที่นี่
ตัวที่สองคือการปิดแบบ ชัดเจน
import aioble
await do_burst_of_ble_work()
aioble.stop() # radio deactivated; in-flight tasks unwound
await asyncio.sleep(60) # sleep with the radio off
# ... next aioble call brings the radio back up automatically
aioble.stop() จะปิดใช้งาน BLE radio พื้นฐานและยกเลิกทุกสิ่งที่กำลังดำเนินการ การเชื่อมต่อที่เปิดอยู่จะถูกตัด, นักสแกนและผู้โฆษณาจะถูกยกเลิก, ช่องสัญญาณ L2CAP จะถูกปิด Coroutines ที่กำลังรอการดำเนินการเหล่านั้นจะ raise exceptions ตามปกติ (DeviceDisconnectedError และเพื่อนๆ) ซึ่งเป็นกลไกการล้างข้อมูลที่ block async with โดยรอบถูกเขียนขึ้นมาเพื่อจุดประสงค์นี้ การเรียก coroutine aioble ใดๆ หลังจากนั้นจะเปิดใช้งาน radio อีกครั้งจากสถานะเย็น
รูปแบบทั่วไปสำหรับ sensor cam ที่ใช้แบตเตอรี่แบบเป็นระยะๆ คือ:
ตื่นขึ้นตามกำหนดเวลา (ตัวจับเวลา, motion sensor, ปุ่ม)
ทำงาน BLE ชุดหนึ่ง ได้แก่ โฆษณา, รับการเชื่อมต่อ, ส่งค่า, ตัดการเชื่อมต่อ
เรียก
aioble.stop()และเข้าสู่โหมดพักจนถึงการตื่นครั้งถัดไป
11.8.5. สิ่งที่ aioble ไม่ทำ¶
aioble ครอบคลุม GATT, GAP, และ L2CAP โดยเจตนา ซึ่งเป็นชั้นที่แอปพลิเคชันใช้งาน สามส่วนอยู่นอกขอบเขต:
ทุกอย่างที่อยู่ต่ำกว่า link layer การเลือกช่อง, การกระโดดความถี่, การรับทราบแพ็กเกต, และการเข้ารหัสระดับ link-layer ทั้งหมดเกิดขึ้นภายใน BLE port และ controller silicon โดย
aiobleไม่เปิดเผย hooks ในระดับนั้นClassic Bluetooth
aiobleรองรับเฉพาะ BLE เท่านั้น ลิงก์เสียง, RFCOMM, A2DP, และคุณสมบัติ classic-profile อื่นๆ ไม่ใช่ส่วนหนึ่งของ APIBluetooth Mesh ชั้น mesh networking ของ Bluetooth SIG (stack แยกต่างหากบน BLE advertising) ไม่ได้ถูก implement บน camera cam สามารถโฆษณาและสังเกตการณ์ได้ แต่ไม่สามารถเข้าร่วมบทบาท relay/friend/proxy ของ mesh network ได้
11.8.6. Exceptions¶
aioble มี exception สี่ประเภท แต่ละตัวจะเกิดขึ้นจากภายใน coroutine ที่กำลังรอการดำเนินการเมื่อมีข้อผิดพลาด block async with จะคลายออกอย่างสะอาดเมื่อมันแพร่กระจาย
aioble.DeviceDisconnectedError-- ลิงก์ BLE ไปยัง peer ขาดหายขณะที่การดำเนินการ GATT (read,write,notified,indicated,subscribe,exchange_mtu, ...) กำลังดำเนินอยู่ จะ raise ภายใน coroutine ที่กำลังรอ เป็น exception ที่พบบ่อยที่สุด ควร catch ในโค้ดที่ควรเชื่อมต่อใหม่เมื่อสูญเสียการเชื่อมต่อaioble.GattError-- การดำเนินการ GATT ถึง peer แล้วแต่เสร็จสิ้นด้วย ATT status ที่ไม่ใช่ศูนย์ (write-with-response ถูกปฏิเสธ, indicate ไม่ได้รับการรับทราบ, read-not-permitted, ...) รหัสสถานะอยู่ใน attribute_statusของ exceptionaioble.L2CAPDisconnectedError-- ช่องสัญญาณ L2CAP ขาดหายขณะที่send(),recvinto(), หรือflush()กำลังดำเนินอยู่ ฝั่งใดฝั่งหนึ่งอาจปิดช่องสัญญาณ หรือการเชื่อมต่อ GAP พื้นฐานอาจขาดไปaioble.L2CAPConnectionError-- raise โดยl2cap_connect()เมื่อผู้รับฟังปฏิเสธหรือ controller ล้มเหลวในการตั้งค่าช่องสัญญาณ รหัสสถานะ Bluetooth เป็น positional argument แรก
การดำเนินการที่รับ timeout_ms อย่างชัดเจน (การเรียก connect/discovery/read/write/pair รวมถึง timeout() ในฐานะ wrapper) จะ raise asyncio.TimeoutError จาก asyncio เพิ่มเติมเมื่อ deadline หมดเวลาก่อนที่การดำเนินการจะเสร็จสิ้น