11.8. המודול aioble¶
מפרט הליבה של Bluetooth מספק אוצר מילים שממופה לשני מודולים של MicroPython.
bluetooth– הקישור הנמוך-רמה לבקר ה-BLE. סינכרוני, מונחה-אירועים דרך פונקציית callback בסגנון IRQ, ובנוי סביב חוצצי בתים, ידיות והפרימיטיבים החשופים של GATT. הוא חושף את הפרוטוקול כפי שהוא, לא כפי שיישומי Python רוצים לצרוך אותו.aioble– עוטף גבוה-רמה יותר, כתוב ב-Python מעלbluetooth, שהופך כל פעולה מרוחקת ל-coroutine שלasyncioוכל אובייקט BLE (שירותים, מאפיינים, חיבורים, תוצאות סריקה, ערוצי L2CAP) למחלקת Python ארגונומית. סריקות הופכות לאיטרטורים אסינכרוניים; חיבורים הופכים למנהלי הקשר אסינכרוניים; התראות הופכות לניתנות-להמתנה.
11.8.1. מתי לפנות למודול הנמוך-רמה¶
bluetooth עדיין התשובה הנכונה לשני מקרים צרים:
אתם כותבים את סוג הקוד ש-
aiobleעצמו בנוי ממנו – דפוס חדש הזקוק לשליטה ברמת IRQ על הפרוטוקול.אתם רצים על יעד חומרה שבו חבילת
aiobleאינה זמינה, ושכבת תיווך דקה סביב הבקר היא האפשרות היחידה.
עבור כל יישום מצלמה, aioble היא התשובה הנכונה.
11.8.2. חלקים של תוכנית aioble¶
לכל יישום מבוסס aioble יש קבוצה קטנה של חלקים נעים, ללא קשר לאילו תפקידים הוא ממלא.
לולאת אירועים ארוכת-טווח של
asyncio. כל דבר ב-aiobleהוא coroutine, כך שהיישום בנוי כמשימה אחת או יותר על לולאת אירועים יחידה. ראו Asyncio עבור הלולאה, המשימות והחריגות בפירוט.רדיו שמופעל.
aiobleמפעיל את רדיו ה-BLE באופן מובלע בשימוש הראשון, אך ניתן גם לשלוט בו במפורש באמצעותaioble.config()(המעבירה ל-bluetooth.BLE.config()לאחר הבטחה שהרדיו פעיל) ולכבותו באמצעותaioble.stop().תפקיד אחד או יותר בפעולה בו-זמנית. בצד ה-peripheral: קבוצה רשומה של שירותי GATT (ראו
aioble.register_services()) ו-coroutine רץ שלaioble.advertise(). בצד ה-central: איטרטור רץ שלaioble.scan()אוaioble.Device.connect()ממתין. הרדיו ממרבב את העבודה; היישום רואה כל תפקיד כמשימה עצמאית.
11.8.3. Peripheral מינימלי¶
תוכנית aioble השימושית הקטנה ביותר – peripheral המפרסם מאפיין יחיד לקריאה בלבד – קצרה:
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. כיבוי הרדיו¶
על מצלמה המופעלת בסוללה, רדיו ה-BLE הוא הצרכן הגדול ביותר על שיקול דעת בתקציב. שני כפתורים חשובים.
הראשון הוא מובלע: aioble מפעיל את הרדיו בשימוש ראשון, והרדיו ישן בין אירועים מתוזמנים (פרצי פרסום, אירועי חיבור, חלונות סריקה) אוטומטית. בחירת מרווחים ארוכים יותר ב-aioble.advertise() / aioble.scan() והסכמה על מרווח חיבור ארוך יותר בזמן connect() שומרות את הרדיו כבוי באופן יחסי יותר מהזמן. טבלת הפרסום ב-פרסום וסריקה היא המדריך המעשי כאן.
השני הוא כיבוי מפורש
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 הבסיסי ומפרק כל דבר שבפעולה – חיבורים פתוחים נופלים, סורקים ומפרסמים מבוטלים, ערוצי L2CAP נסגרים. coroutines שהמתינו לפעולות אלו מעלים את החריגות הרגילות שלהם (DeviceDisconnectedError וחבריה), שהוא מנגנון הניקוי שעבורו נכתבו בלוקי ה-async with הסובבים. קריאה לכל coroutine של aioble לאחר מכן מפעילה את הרדיו שוב מקור.
הדפוס הטיפוסי עבור מצלמת חיישן תקופתית המופעלת בסוללה הוא:
התעוררות לפי לוח זמנים (טיימר, חיישן תנועה, כפתור).
הרצת פרץ עבודת ה-BLE – פרסום, קבלת חיבור, דחיפת הערך, ניתוק.
קריאה ל-
aioble.stop()ושינה עד ההתעוררות הבאה.
11.8.5. מה ש-aioble אינו עושה¶
aioble מכסה במכוון את GATT, GAP ו-L2CAP – השכבות שיישום משתמש בהן. שלושה חלקים מחוץ לתחום:
כל דבר מתחת לשכבת הקישור. בחירת ערוץ, קפיצת תדרים, אישורי מנות והצפנה ברמת הקישור כולם מתרחשים בתוך פורט ה-BLE וסיליקון הבקר;
aiobleאינו חושף ווים ברמה זו.Bluetooth קלאסי.
aiobleהוא BLE בלבד. קישורי שמע, RFCOMM, A2DP ותכונות פרופיל-קלאסי אחרות אינן חלק מה-API.Bluetooth Mesh. שכבת רשת ה-mesh של ה-Bluetooth SIG (מחסנית נפרדת מעל פרסום BLE) אינה ממומשת על המצלמה. המצלמה יכולה לפרסם ולצפות, אך אינה יכולה להשתתף בתפקידי הממסר / החבר / הפרוקסי של רשת mesh.
11.8.6. חריגות¶
ארבעה סוגי חריגות יוצאים מ-aioble. כל אחת נורית מתוך coroutine שהמתין לפעולה כאשר משהו השתבש; בלוקי async with נפרשים בצורה נקייה כשהן מתפשטות.
aioble.DeviceDisconnectedError– קישור ה-BLE לעמית נפל בזמן שפעולת GATT (read,write,notified,indicated,subscribe,exchange_mtu, …) הייתה בפעולה. מועלית בתוך ה-coroutine שהמתין. החריגה הנפוצה ביותר ללא ספק; תפסו אותה בכל קוד שאמור להתחבר מחדש בעת אובדן.aioble.GattError– פעולת GATT הגיעה לעמית אך הושלמה עם סטטוס ATT שונה מאפס (כתיבה-עם-מענה נדחתה, indicate לא אושר, read-not-permitted, …). קוד הסטטוס נמצא בתכונה_statusשל החריגה.aioble.L2CAPDisconnectedError– ערוץ ה-L2CAP נפל בזמן ש-send(),recvinto(), אוflush()היו בפעולה. אחד הצדדים אולי סגר את הערוץ, או שחיבור ה-GAP הבסיסי נעלם.aioble.L2CAPConnectionError– מועלית על ידיl2cap_connect()כאשר המאזין סירב או שהבקר נכשל בהקמת הערוץ. קוד סטטוס ה-Bluetooth הוא הארגומנט המיקומי הראשון.
פעולות הלוקחות timeout_ms מפורש (קריאות החיבור / הגילוי / הקריאה / הכתיבה / הצימוד, בתוספת timeout() כעוטף) מעלות בנוסף asyncio.TimeoutError מ-asyncio כאשר המועד האחרון חולף לפני שהפעולה הושלמה.