bluetooth — Bluetooth ברמה נמוכה

מודול זה מספק ממשק לבקר ה-Bluetooth המובנה. הוא תומך ב-Bluetooth Low Energy (BLE) בתפקידי Central, Peripheral, Broadcaster ו-Observer, וכן ב-GATT Server וב-Client ובערוצי L2CAP מוכווני חיבור. התקן יכול לפעול בכמה תפקידים בו-זמנית. כמו כן נתמכים Pairing ו-Bonding.

ממשק API זה נועד להתאים לפרוטוקול ה-Bluetooth ברמה נמוכה ולספק אבני בניין לרמות הפשטה גבוהות יותר, כגון סוגי התקנים ספציפיים.

טיפ

עבור רוב היישומים, מומלץ להעדיף את הספרייה ברמה גבוהה יותר aioble, המספקת עטיפה מבוססת asyncio סביב מודול זה. ראו aioble — BLE אסינכרוני.

class BLE

class bluetooth.BLE

מחזירה את אובייקט ה-BLE היחיד (singleton).

תצורה

active(active: bool | None = None, /) bool

משנה אופציונלית את מצב הפעילות של רדיו ה-BLE, ומחזירה את המצב הנוכחי.

יש להפעיל את הרדיו לפני השימוש בכל שיטה אחרת במחלקה זו.

config(param: str, /) Any
config(*, **kwargs: Any) None

מקבלת או קובעת ערכי תצורה של ממשק ה-BLE. לקבלת ערך יש לציין את שם הפרמטר כמחרוזת בין מירכאות, ומתבצעת שאילתה של פרמטר אחד בלבד בכל פעם. לקביעת ערכים יש להשתמש בתחביר מילות מפתח, וניתן לקבוע פרמטר אחד או יותר בכל פעם.

הערכים הנתמכים כעת הם:

  • 'mac': הכתובת הנוכחית שבשימוש, בהתאם למצב הכתובת הנוכחי. מחזיר tuple בצורת (addr_type, addr).

    ראו gap_scan לפרטים על סוג הכתובת.

    ניתן לבצע שאילתה זו רק כאשר הממשק פעיל כעת.

  • 'addr_mode': קובע את מצב הכתובת. הערכים הם:

    ערך

    שם

    התנהגות

    0x00

    PUBLIC

    שימוש בכתובת הציבורית של הבקר.

    0x01

    RANDOM

    שימוש בכתובת סטטית מחוללת.

    0x02

    RPA

    שימוש בכתובות פרטיות הניתנות לפענוח (resolvable).

    0x03

    NRPA

    שימוש בכתובות פרטיות שאינן ניתנות לפענוח (non-resolvable).

    כברירת מחדל, הממשק ישתמש בכתובת PUBLIC אם היא זמינה, אחרת ישתמש בכתובת RANDOM.

  • 'gap_name': מקבל/קובע את שם התקן ה-GAP המשמש את שירות ה-Generic Access (UUID 0x1800), מאפיין Device Name (UUID 0x2a00). ניתן לקבוע זאת בכל עת ולשנות פעמים רבות.

  • 'rxbuf': מקבל/קובע את גודל החוצץ (buffer) הפנימי בבתים המשמש לאחסון אירועים נכנסים. חוצץ זה הוא גלובלי לכל מנהל ההתקן של ה-BLE ולכן מטפל בנתונים נכנסים עבור כל האירועים, כולל כל המאפיינים. הגדלתו מאפשרת טיפול טוב יותר בנתונים נכנסים בפרצים (לדוגמה תוצאות סריקה) ויכולת לקבל ערכי מאפיינים גדולים יותר.

  • 'mtu': מקבל/קובע את ה-MTU שישמש במהלך חילופי ATT MTU. ה-MTU המתקבל יהיה המינימום בין ערך זה לבין ה-MTU של ההתקן המרוחק. חילופי ATT MTU לא יקרו אוטומטית (אלא אם ההתקן המרוחק יוזם זאת), ויש ליזום אותם ידנית באמצעות gattc_exchange_mtu. השתמשו באירוע _IRQ_MTU_EXCHANGED כדי לגלות את ה-MTU עבור חיבור נתון.

  • 'bond': קובע האם Bonding יופעל במהלך ה-Pairing. כאשר מופעל, בקשות Pairing יקבעו את דגל ”bond“ והמפתחות יאוחסנו בשני ההתקנים.

  • 'mitm': קובע האם נדרשת הגנת MITM עבור ה-Pairing.

  • 'io': קובע את יכולות הקלט/פלט של התקן זה.

    האפשרויות הזמינות הן:

    קבוע

    ערך

    יכולת

    _IO_CAPABILITY_DISPLAY_ONLY

    0

    תצוגה בלבד

    _IO_CAPABILITY_DISPLAY_YESNO

    1

    תצוגה עם קלט כן/לא

    _IO_CAPABILITY_KEYBOARD_ONLY

    2

    מקלדת בלבד

    _IO_CAPABILITY_NO_INPUT_OUTPUT

    3

    ללא קלט או פלט

    _IO_CAPABILITY_KEYBOARD_DISPLAY

    4

    מקלדת ותצוגה

  • 'le_secure': קובע האם נדרש Pairing מסוג ”LE Secure“. ברירת המחדל היא false (כלומר, מאפשר ”Legacy Pairing“).

טיפול באירועים

irq(handler: Callable[[int, Tuple], Any | None], /) None

רושמת פונקציית callback עבור אירועים מערימת ה-BLE. ה-handler מקבל שני ארגומנטים, event (שיהיה אחד מהקודים שלהלן) ו-data (שהוא tuple של ערכים ספציפי לאירוע).

הערה: כאופטימיזציה למניעת הקצאות מיותרות, הרשומות addr, adv_data, char_data, notify_data ו-uuid ב-tuples הן מופעי memoryview לקריאה בלבד המצביעים על ה-ringbuffer הפנימי של bluetooth, ותקפים רק במהלך הקריאה לפונקציית מטפל ה-IRQ. אם התוכנית שלכם צריכה לשמור אחד מערכים אלה לגישה לאחר שמטפל ה-IRQ חזר (למשל על ידי שמירתו במופע מחלקה או במשתנה גלובלי), עליה ליצור עותק של הנתונים, באמצעות bytes() או bluetooth.UUID(), כך:

connected_addr = bytes(addr)  # equivalently: adv_data, char_data, or notify_data
matched_uuid = bluetooth.UUID(uuid)

לדוגמה, מטפל ה-IRQ עבור תוצאת סריקה עשוי לבדוק את adv_data כדי להחליט אם זה ההתקן הנכון, ורק אז להעתיק את נתוני הכתובת לשימוש במקום אחר בתוכנית. וכדי להדפיס נתונים מתוך מטפל ה-IRQ, יידרש print(bytes(addr)).

מטפל מנתב בדרך כלל לפי קוד האירוע ופורק את ה-tuple של המטען (payload) הספציפי לאירוע:

def bt_irq(event, data):
    if event == _IRQ_CENTRAL_CONNECT:
        conn_handle, addr_type, addr = data
        ...
    elif event == _IRQ_SCAN_RESULT:
        addr_type, addr, adv_type, rssi, adv_data = data
        ...

כל קוד אירוע, המטען שהוא מספק, ותיאור קצר מופיעים להלן. עבור אירועים שבהם מצוין השדה status, status הוא 0 בהצלחה וערך לא-אפס ספציפי למימוש בכשל.

קבוע

ערך

אירוע

tuple של מטען (payload)

_IRQ_CENTRAL_CONNECT

1

התקן central התחבר ל-peripheral זה.

(conn_handle, addr_type, addr)

_IRQ_CENTRAL_DISCONNECT

2

התקן central התנתק מ-peripheral זה.

(conn_handle, addr_type, addr)

_IRQ_GATTS_WRITE

3

לקוח מחובר כתב למאפיין או למתאר (descriptor) מקומי. השתמשו ב-gatts_read כדי לאחזר את הערך החדש.

(conn_handle, attr_handle)

_IRQ_GATTS_READ_REQUEST

4

לקוח מחובר ביצע קריאה. החזירו קוד שגיאה לא-אפס מהטבלה שלהלן כדי לדחות את הקריאה, או 0 / None כדי לקבל אותה.

(conn_handle, attr_handle)

_IRQ_SCAN_RESULT

5

חבילת פרסום (advertising) בודדת התקבלה במהלך סריקה פעילה.

(addr_type, addr, adv_type, rssi, adv_data)

_IRQ_SCAN_DONE

6

הסריקה הנוכחית הסתיימה, או משום שחלף משך הזמן שהוגדר או משום ש-gap_scan(None) נקראה.

()

_IRQ_PERIPHERAL_CONNECT

7

קריאת gap_connect קודמת הצליחה.

(conn_handle, addr_type, addr)

_IRQ_PERIPHERAL_DISCONNECT

8

התקן peripheral מחובר התנתק.

(conn_handle, addr_type, addr)

_IRQ_GATTC_SERVICE_RESULT

9

שירות אחד נמצא על ידי gattc_discover_services.

(conn_handle, start_handle, end_handle, uuid)

_IRQ_GATTC_SERVICE_DONE

10

גילוי השירותים הסתיים.

(conn_handle, status)

_IRQ_GATTC_CHARACTERISTIC_RESULT

11

מאפיין אחד נמצא על ידי gattc_discover_characteristics.

(conn_handle, end_handle, value_handle, properties, uuid)

_IRQ_GATTC_CHARACTERISTIC_DONE

12

גילוי המאפיינים הסתיים.

(conn_handle, status)

_IRQ_GATTC_DESCRIPTOR_RESULT

13

מתאר (descriptor) אחד נמצא על ידי gattc_discover_descriptors.

(conn_handle, dsc_handle, uuid)

_IRQ_GATTC_DESCRIPTOR_DONE

14

גילוי המתארים (descriptors) הסתיים.

(conn_handle, status)

_IRQ_GATTC_READ_RESULT

15

קריאת gattc_read קודמת החזירה נתונים.

(conn_handle, value_handle, char_data)

_IRQ_GATTC_READ_DONE

16

קריאת gattc_read קודמת הסתיימה.

(conn_handle, value_handle, status)

_IRQ_GATTC_WRITE_DONE

17

כתיבת gattc_write קודמת אושרה.

(conn_handle, value_handle, status)

_IRQ_GATTC_NOTIFY

18

שרת מרוחק שלח התראה (notification) שאינה מאושרת.

(conn_handle, value_handle, notify_data)

_IRQ_GATTC_INDICATE

19

שרת מרוחק שלח התרעה (indication) מאושרת.

(conn_handle, value_handle, notify_data)

_IRQ_GATTS_INDICATE_DONE

20

התרעה (indication) שנשלחה קודם לכן אושרה על ידי הלקוח (או שתם הזמן הקצוב לה).

(conn_handle, value_handle, status)

_IRQ_MTU_EXCHANGED

21

חילופי ATT MTU הושלמו (ביוזמת אחד הצדדים).

(conn_handle, mtu)

_IRQ_L2CAP_ACCEPT

22

התקן מרוחק ביקש חיבור L2CAP ב-PSM שהתקן זה מאזין עליו. החזירו מספר שלם לא-אפס כדי לדחות, או 0 / None כדי לקבל.

(conn_handle, cid, psm, our_mtu, peer_mtu)

_IRQ_L2CAP_CONNECT

23

ערוץ L2CAP נוצר כעת, או על ידי קבלת בקשה נכנסת או על ידי השלמת l2cap_connect יוצא.

(conn_handle, cid, psm, our_mtu, peer_mtu)

_IRQ_L2CAP_DISCONNECT

24

ערוץ L2CAP התנתק. status הוא 0 עבור ניתוק נקי, או לא-אפס אם ניסיון חיבור יוצא נכשל.

(conn_handle, cid, psm, status)

_IRQ_L2CAP_RECV

25

נתונים הגיעו בערוץ L2CAP. קראו ל-l2cap_recvinto כדי לקרוא אותם.

(conn_handle, cid)

_IRQ_L2CAP_SEND_READY

26

קריאת l2cap_send קודמת שהחזירה False התרוקנה והערוץ מוכן שוב. status לא-אפס משמעו שחוצץ השידור גלש ועל היישום לשלוח את הנתונים מחדש.

(conn_handle, cid, status)

_IRQ_CONNECTION_UPDATE

27

ההתקן המרוחק עדכן את פרמטרי החיבור (interval, latency, supervision timeout).

(conn_handle, conn_interval, conn_latency, supervision_timeout, status)

_IRQ_ENCRYPTION_UPDATE

28

מצב ההצפנה של חיבור השתנה, בדרך כלל לאחר השלמת Pairing או Bonding.

(conn_handle, encrypted, authenticated, bonded, key_size)

_IRQ_GET_SECRET

29

הערימה מבקשת סוד Bonding מאוחסן. אם key הוא None, החזירו את הערך המאוחסן ה-indexי מסוג sec_type; אחרת החזירו את הערך המשויך ל-(sec_type, key) הנתון. החזירו None אם לא מאוחסן דבר.

(sec_type, index, key)

_IRQ_SET_SECRET

30

הערימה מבקשת מהיישום לשמור סוד Bonding. החזירו True לאחר השמירה.

(sec_type, key, value)

_IRQ_PASSKEY_ACTION

31

נדרשת פעולת passkey כחלק מה-Pairing. הגיבו באמצעות gap_passkey; ראו את טבלת פעולות ה-passkey שלהלן עבור הפעולות האפשריות.

(conn_handle, action, passkey)

עבור אירוע _IRQ_GATTS_READ_REQUEST, קודי ההחזרה הזמינים הם:

קבוע

ערך

משמעות

_GATTS_NO_ERROR

0x00

קבל את הקריאה.

_GATTS_ERROR_READ_NOT_PERMITTED

0x02

קריאה אינה מותרת.

_GATTS_ERROR_WRITE_NOT_PERMITTED

0x03

כתיבה אינה מותרת.

_GATTS_ERROR_INSUFFICIENT_AUTHENTICATION

0x05

הלקוח אינו מאומת.

_GATTS_ERROR_INSUFFICIENT_AUTHORIZATION

0x08

הלקוח אינו מורשה.

_GATTS_ERROR_INSUFFICIENT_ENCRYPTION

0x0f

הקישור אינו מוצפן.

עבור אירוע _IRQ_PASSKEY_ACTION, הפעולות הזמינות הן:

קבוע

ערך

משמעות

_PASSKEY_ACTION_NONE

0

לא נדרשת פעולה.

_PASSKEY_ACTION_INPUT

2

בקש מהמשתמש להזין את ה-passkey המוצג בהתקן המרוחק.

_PASSKEY_ACTION_DISPLAY

3

הצג passkey בן 6 ספרות שההתקן המרוחק יזין.

_PASSKEY_ACTION_NUMERIC_COMPARISON

4

אשר שה-passkey תואם לזה המוצג בהתקן המרוחק.

כדי לחסוך מקום בקושחה, קבועים אלה אינם כלולים במודול bluetooth. הוסיפו לתוכנית שלכם את אלה שאתם צריכים מהרשימות שלמעלה.

תפקיד Broadcaster (מפרסם)

gap_advertise(interval_us: int | None, adv_data: bytes | None = None, *, resp_data: bytes | None = None, connectable: bool = True) None

מתחילה פרסום (advertising) במרווח שצוין (במיקרו-שניות). מרווח זה יעוגל כלפי מטה ל-625us הקרובים ביותר. כדי להפסיק את הפרסום, קבעו את interval_us ל-None.

adv_data ו-resp_data יכולים להיות כל טיפוס שמממש את פרוטוקול החוצץ (buffer) (למשל bytes, bytearray, str). adv_data נכלל בכל השידורים, ו-resp_data נשלח בתגובה לסריקה פעילה.

הערה: אם adv_data (או resp_data) הוא None, אזי הנתונים שהועברו בקריאה הקודמת ל-gap_advertise יישמשו מחדש. הדבר מאפשר ל-broadcaster לחדש את הפרסום באמצעות gap_advertise(interval_us) בלבד. כדי לנקות את מטען הפרסום העבירו bytes ריק, כלומר b''.

תפקיד Observer (סורק)

gap_scan(duration_ms: int | None, interval_us: int = 1280000, window_us: int = 11250, active: bool = False, /) None

מריצה פעולת סריקה הנמשכת למשך הזמן שצוין (באלפיות שנייה).

כדי לסרוק ללא הגבלת זמן, קבעו את duration_ms ל-0.

כדי להפסיק את הסריקה, קבעו את duration_ms ל-None.

השתמשו ב-interval_us וב-window_us כדי להגדיר אופציונלית את מחזור הפעולה (duty cycle). הסורק יפעל למשך window_us מיקרו-שניות בכל interval_us מיקרו-שניות, לסך הכול של duration_ms אלפיות שנייה. ברירת המחדל של ה-interval וה-window היא 1.28 שניות ו-11.25 אלפיות שנייה בהתאמה (סריקת רקע).

עבור כל תוצאת סריקה יופעל אירוע _IRQ_SCAN_RESULT, עם נתוני אירוע (addr_type, addr, adv_type, rssi, adv_data).

ערכי addr_type מציינים כתובות ציבוריות או אקראיות:

ערך

שם

משמעות

0x00

PUBLIC

כתובת התקן ציבורית.

0x01

RANDOM

כתובת אקראית (סטטית, RPA או NRPA; הסוג מקודד בכתובת עצמה).

ערכי adv_type מתאימים למפרט ה-Bluetooth:

ערך

שם

משמעות

0x00

ADV_IND

פרסום בלתי-מכוון הניתן לחיבור ולסריקה.

0x01

ADV_DIRECT_IND

פרסום מכוון הניתן לחיבור.

0x02

ADV_SCAN_IND

פרסום בלתי-מכוון הניתן לסריקה.

0x03

ADV_NONCONN_IND

פרסום בלתי-מכוון שאינו ניתן לחיבור.

0x04

SCAN_RSP

תגובת סריקה.

ניתן לקבוע את active ל-True אם ברצונכם לקבל תגובות סריקה בתוצאות.

כאשר הסריקה נעצרת (או בשל סיום משך הזמן או בעצירה מפורשת), יופעל אירוע _IRQ_SCAN_DONE.

תפקיד Central

התקן central יכול להתחבר ל-peripherals שהוא גילה באמצעות תפקיד ה-observer (ראו gap_scan) או באמצעות כתובת ידועה.

gap_connect(addr_type: int | None, addr: bytes | None = None, scan_duration_ms: int = 2000, min_conn_interval_us: int | None = None, max_conn_interval_us: int | None = None, /) None

מתחברת ל-peripheral.

ראו gap_scan לפרטים על סוגי כתובות.

כדי לבטל ניסיון חיבור תלוי ועומד מוקדם, קראו ל-gap_connect(None).

בהצלחה, יופעל אירוע _IRQ_PERIPHERAL_CONNECT. בעת ביטול ניסיון חיבור, יופעל אירוע _IRQ_PERIPHERAL_DISCONNECT.

ההתקן ימתין עד scan_duration_ms כדי לקבל מטען פרסום מההתקן.

ניתן להגדיר את מרווח החיבור במיקרו-שניות באמצעות min_conn_interval_us ו/או max_conn_interval_us. אחרת ייבחר מרווח ברירת מחדל, בדרך כלל בין 30000 ל-50000 מיקרו-שניות. מרווח קצר יותר יגדיל את התפוקה, על חשבון צריכת החשמל.

תפקיד Peripheral

מצופה מהתקן peripheral לשלוח פרסומים הניתנים לחיבור (ראו gap_advertise). הוא ישמש בדרך כלל כ-GATT server, לאחר שרשם תחילה שירותים ומאפיינים באמצעות gatts_register_services.

כאשר התקן central מתחבר, יופעל אירוע _IRQ_CENTRAL_CONNECT.

תפקידי Central ו-Peripheral

gap_disconnect(conn_handle: int, /) bool

מנתקת את ידית החיבור שצוינה. זה יכול להיות או התקן central שהתחבר להתקן זה (אם פועל כ-peripheral) או התקן peripheral שהתקן זה התחבר אליו קודם לכן (אם פועל כ-central).

בהצלחה, יופעל אירוע _IRQ_PERIPHERAL_DISCONNECT או _IRQ_CENTRAL_DISCONNECT.

מחזירה False אם ידית החיבור לא הייתה מחוברת, ו-True אחרת.

GATT Server

ל-GATT server יש קבוצה של שירותים רשומים. כל שירות עשוי להכיל מאפיינים, שלכל אחד מהם ערך. מאפיינים יכולים גם להכיל מתארים (descriptors), שלהם עצמם יש ערכים.

ערכים אלה מאוחסנים מקומית, וניגשים אליהם באמצעות ”value handle“ שלהם המחולל במהלך רישום השירות. ניתן גם לקרוא או לכתוב אליהם על ידי התקן לקוח מרוחק. בנוסף, שרת יכול ”להתריע“ (notify) על מאפיין ללקוח מחובר באמצעות ידית חיבור.

התקן בתפקיד central או peripheral יכול לתפקד כ-GATT server, אולם ברוב המקרים יהיה נפוץ יותר שהתקן peripheral יפעל כשרת.

למאפיינים ולמתארים (descriptors) יש גודל מרבי ברירת מחדל של 20 בתים (ברירת המחדל של ATT MTU היא 23 בתים פחות כותרת ATT בת 3 בתים; MTU גדול יותר שהוסכם אינו מעלה כשלעצמו מגבלה זו). כל מה שלקוח יכתוב אליהם יקוצץ לאורך זה. עם זאת, כל כתיבה מקומית תגדיל את הגודל המרבי, כך שאם ברצונכם לאפשר כתיבות גדולות יותר מלקוח למאפיין נתון, השתמשו ב-gatts_write לאחר הרישום. למשל gatts_write(char_handle, bytes(100)).

gatts_register_services(services_definition: Sequence[Sequence], /) Sequence[Sequence[int]]

מגדירה את השרת עם השירותים שצוינו, ומחליפה שירותים קיימים.

services_definition הוא רשימה של services, שבה כל service הוא tuple בן שני איברים המכיל UUID ורשימה של characteristics.

כל characteristic הוא tuple בן שניים או שלושה איברים המכיל UUID, ערך flags, ואופציונלית רשימה של descriptors.

כל descriptor הוא tuple בן שני איברים המכיל UUID וערך flags.

ה-flags הם שילוב bitwise-OR של הדגלים המוגדרים להלן. הם קובעים הן את התנהגות המאפיין (או המתאר) והן את דרישות האבטחה והפרטיות.

ערך ההחזרה הוא רשימה (איבר אחד לכל שירות) של tuples (כל איבר הוא value handle). ידיות המאפיינים והמתארים משוטחות לאותו tuple, בסדר שבו הן מוגדרות.

הדוגמה הבאה רושמת שני שירותים (Heart Rate ו-Nordic UART):

bt = bluetooth.BLE()
bt.active(True)

# Heart Rate service: one Heart Rate Measurement characteristic.
HR_SERVICE = (
    bluetooth.UUID(0x180D),
    (
        (bluetooth.UUID(0x2A37),
         bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY),
    ),
)

# Nordic UART service: a TX characteristic the client subscribes
# to for notifications, and an RX characteristic it writes to.
UART_SERVICE = (
    bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E'),
    (
        (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'),
         bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY),
        (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'),
         bluetooth.FLAG_WRITE),
    ),
)

((hr,), (tx, rx)) = bt.gatts_register_services(
    (HR_SERVICE, UART_SERVICE),
)

שלוש ידיות הערכים (hr, tx, rx) יכולות לשמש עם gatts_read, gatts_write, gatts_notify ו-gatts_indicate.

הערה: יש להפסיק את הפרסום לפני רישום שירותים.

הדגלים הזמינים עבור מאפיינים ומתארים (descriptors) הם:

קבוע

ערך

משמעות

_FLAG_BROADCAST

0x0001

ניתן לשדר את המאפיין.

_FLAG_READ

0x0002

הלקוח רשאי לקרוא את הערך.

_FLAG_WRITE_NO_RESPONSE

0x0004

הלקוח רשאי לכתוב מבלי לצפות לתגובה.

_FLAG_WRITE

0x0008

הלקוח רשאי לכתוב עם תגובת אישור.

_FLAG_NOTIFY

0x0010

השרת רשאי לשלוח התראות (notifications) לא מאושרות.

_FLAG_INDICATE

0x0020

השרת רשאי לשלוח התרעות (indications) מאושרות.

_FLAG_AUTHENTICATED_SIGNED_WRITE

0x0040

הלקוח רשאי לבצע כתיבות חתומות.

_FLAG_AUX_WRITE

0x0100

מאפיינים מורחבים: כתיבות בתור/אמינות מותרות.

_FLAG_READ_ENCRYPTED

0x0200

קריאה דורשת קישור מוצפן.

_FLAG_READ_AUTHENTICATED

0x0400

קריאה דורשת קישור מאומת (מוגן MITM).

_FLAG_READ_AUTHORIZED

0x0800

קריאה דורשת הרשאה ברמת היישום.

_FLAG_WRITE_ENCRYPTED

0x1000

כתיבה דורשת קישור מוצפן.

_FLAG_WRITE_AUTHENTICATED

0x2000

כתיבה דורשת קישור מאומת (מוגן MITM).

_FLAG_WRITE_AUTHORIZED

0x4000

כתיבה דורשת הרשאה ברמת היישום.

כמו עם קבועי האירועים שלמעלה, דגלים אלה אינם מסופקים על ידי מודול bluetooth; העתיקו לתוכנית שלכם את אלה שאתם צריכים.

gatts_read(value_handle: int, /) bytes

קוראת את הערך המקומי עבור ידית זו (שנכתב או על ידי gatts_write או על ידי לקוח מרוחק).

gatts_write(value_handle: int, data: bytes, send_update: bool = False, /) None

כותבת את הערך המקומי עבור ידית זו, שלקוח יכול לקרוא.

אם send_update הוא True, אזי כל לקוח רשום יקבל התראה (notification) (או התרעה, בהתאם למה שהוא רשום אליו ולאילו פעולות המאפיין תומך) על כתיבה זו.

gatts_notify(conn_handle: int, value_handle: int, data: bytes | None = None, /) None

שולחת בקשת התראה (notification) ללקוח מחובר.

אם data הוא None (ברירת המחדל), אזי יישלח הערך המקומי הנוכחי (כפי שנקבע באמצעות gatts_write).

אחרת, אם data אינו None, אזי ערך זה יישלח ללקוח כחלק מההתראה (notification). הערך המקומי לא ישתנה.

הערה: ההתראה (notification) תישלח ללא תלות במצב המינוי (subscription) של הלקוח למאפיין זה.

gatts_indicate(conn_handle: int, value_handle: int, data: bytes | None = None, /) None

שולחת בקשת התרעה (indication) ללקוח מחובר.

אם data הוא None (ברירת המחדל), אזי יישלח הערך המקומי הנוכחי (כפי שנקבע באמצעות gatts_write).

אחרת, אם data אינו None, אזי ערך זה יישלח ללקוח כחלק מההתרעה (indication). הערך המקומי לא ישתנה.

באישור (או בכשל, למשל timeout), יופעל אירוע _IRQ_GATTS_INDICATE_DONE.

הערה: ההתרעה (indication) תישלח ללא תלות במצב המינוי (subscription) של הלקוח למאפיין זה.

gatts_set_buffer(value_handle: int, len: int, append: bool = False, /) None

קובעת את גודל החוצץ (buffer) הפנימי עבור ערך בבתים. הדבר יגביל את הכתיבה הגדולה ביותר האפשרית שניתן לקבל. ברירת המחדל היא 20 בתים (ברירת מחדל של ATT MTU בת 23 פחות כותרת ATT בת 3 בתים).

קביעת append ל-True תגרום לכל הכתיבות המרוחקות להתווסף לערך הנוכחי במקום להחליפו. ניתן לאגור בחוצץ עד len בתים בדרך זו. כאשר אתם משתמשים ב-gatts_read, הערך ינוקה לאחר הקריאה. תכונה זו שימושית בעת מימוש משהו כמו Nordic UART Service.

GATT Client

GATT client יכול לגלות ולקרוא/לכתוב מאפיינים על GATT server מרוחק.

נפוץ יותר שהתקן בתפקיד central יפעל כ-GATT client, אולם ייתכן גם שהתקן peripheral יפעל כלקוח כדי לגלות מידע על ה-central שהתחבר אליו (למשל לקרוא את שם ההתקן משירות פרטי ההתקן).

gattc_discover_services(conn_handle: int, uuid: UUID | None = None, /) None

מבצעת שאילתה לשרת מחובר לקבלת שירותיו.

ציינו אופציונלית uuid של שירות כדי לבצע שאילתה לשירות זה בלבד.

עבור כל שירות שמתגלה, יופעל אירוע _IRQ_GATTC_SERVICE_RESULT, ולאחריו _IRQ_GATTC_SERVICE_DONE בסיום.

gattc_discover_characteristics(conn_handle: int, start_handle: int, end_handle: int, uuid: UUID | None = None, /) None

מבצעת שאילתה לשרת מחובר לקבלת מאפיינים בטווח שצוין.

ציינו אופציונלית uuid של מאפיין כדי לבצע שאילתה למאפיין זה בלבד.

העברת start_handle=1 ו-end_handle=0xffff מכסה את טווח ידיות מאפייני ה-GATT המלא, כך ששילוב זה למעשה מחפש בכל שירות בהתקן המרוחק.

עבור כל מאפיין שמתגלה, יופעל אירוע _IRQ_GATTC_CHARACTERISTIC_RESULT, ולאחריו _IRQ_GATTC_CHARACTERISTIC_DONE בסיום.

gattc_discover_descriptors(conn_handle: int, start_handle: int, end_handle: int, /) None

מבצעת שאילתה לשרת מחובר לקבלת מתארים (descriptors) בטווח שצוין.

עבור כל מתאר (descriptor) שמתגלה, יופעל אירוע _IRQ_GATTC_DESCRIPTOR_RESULT, ולאחריו _IRQ_GATTC_DESCRIPTOR_DONE בסיום.

gattc_read(conn_handle: int, value_handle: int, /) None

מבצעת קריאה מרוחקת לשרת מחובר עבור ידית המאפיין או המתאר (descriptor) שצוינה.

כאשר ערך זמין, יופעל אירוע _IRQ_GATTC_READ_RESULT, ולאחריו _IRQ_GATTC_READ_DONE בסיום.

gattc_write(conn_handle: int, value_handle: int, data: bytes, mode: int = 0, /) None

מבצעת כתיבה מרוחקת לשרת מחובר עבור ידית המאפיין או המתאר (descriptor) שצוינה.

הארגומנט mode מציין את התנהגות הכתיבה, כשהערכים הנתמכים כעת הם:

  • mode=0 (ברירת מחדל) הוא כתיבה-ללא-תגובה: הכתיבה תישלח לשרת המרוחק אך לא תוחזר אישור, ולא יופעל אירוע.

  • mode=1 הוא כתיבה-עם-תגובה: מתבקש מהשרת המרוחק לשלוח תגובה/אישור על כך שקיבל את הנתונים.

אם מתקבלת תגובה מהשרת המרוחק, יופעל אירוע _IRQ_GATTC_WRITE_DONE.

gattc_exchange_mtu(conn_handle: int, /) None

יוזמת חילופי MTU עם שרת מחובר, באמצעות ה-MTU המועדף שנקבע באמצעות BLE.config(mtu=value).

אירוע _IRQ_MTU_EXCHANGED יופעל כאשר חילופי ה-MTU יושלמו.

חילופי MTU מיוזמים בדרך כלל על ידי ה-central; NimBLE תומך בשני התפקידים.

ערוצי L2CAP מוכווני חיבור

תכונה זו מאפשרת חילופי נתונים דמויי socket בין שני התקני BLE. לאחר שההתקנים מחוברים באמצעות GAP, כל אחד מההתקנים יכול להאזין לחיבור של האחר ב-PSM מספרי (Protocol/Service Multiplexer).

רק ערוץ L2CAP אחד יכול להיות פעיל בכל זמן נתון (כלומר, לא ניתן להתחבר בזמן האזנה).

ערוצי L2CAP פעילים מזוהים על ידי ידית החיבור שעליה נוצרו ו-CID (channel ID).

לערוצים מוכווני חיבור יש בקרת זרימה מובנית מבוססת אשראי (credit). בניגוד ל-ATT, שבו ההתקנים מנהלים משא ומתן על MTU משותף, הן ההתקן המאזין והן ההתקן המתחבר קובעים כל אחד MTU עצמאי המגביל את הכמות המרבית של נתונים תלויים ועומדים שההתקן המרוחק יכול לשלוח לפני שהם נצרכים במלואם ב-l2cap_recvinto.

l2cap_listen(psm: int, mtu: int, /) None

מתחילה להאזין לבקשות ערוץ L2CAP נכנסות ב-psm שצוין כאשר ה-MTU המקומי נקבע ל-mtu.

כאשר התקן מרוחק יוזם חיבור, יופעל אירוע _IRQ_L2CAP_ACCEPT, המעניק לשרת המאזין הזדמנות לדחות את החיבור הנכנס (על ידי החזרת מספר שלם לא-אפס).

לאחר שהחיבור מתקבל, יופעל אירוע _IRQ_L2CAP_CONNECT, המאפשר לשרת לקבל את ה-channel ID (CID) ואת ה-MTU המקומי והמרוחק.

הערה: כרגע לא ניתן להפסיק את ההאזנה.

l2cap_connect(conn_handle: int, psm: int, mtu: int, /) None

מתחברת לעמית מאזין ב-psm שצוין כאשר ה-MTU המקומי נקבע ל-mtu.

בחיבור מוצלח, יופעל אירוע _IRQ_L2CAP_CONNECT, המאפשר ללקוח לקבל את ה-CID ואת ה-MTU המקומי והמרוחק (peer).

חיבור לא מוצלח יפעיל את אירוע _IRQ_L2CAP_DISCONNECT עם status לא-אפס.

l2cap_disconnect(conn_handle: int, cid: int, /) None

מנתקת ערוץ L2CAP פעיל עם ה-conn_handle וה-cid שצוינו.

l2cap_send(conn_handle: int, cid: int, buf: bytes, /) bool

שולחת את ה-buf שצוין (שחייב לתמוך בפרוטוקול החוצץ (buffer)) בערוץ ה-L2CAP המזוהה על ידי conn_handle ו-cid.

החוצץ (buffer) חייב לעמוד בשתי המגבלות: אסור לו לחרוג מה-MTU המרוחק (peer), ואסור לו לחרוג מפי שניים מה-MTU המקומי.

פעולה זו תחזיר False אם הערוץ נמצא כעת ב“תקיעה“ (stalled), כלומר אסור לקרוא ל-l2cap_send שוב עד שיתקבל אירוע _IRQ_L2CAP_SEND_READY (שיקרה כאשר ההתקן המרוחק יעניק אשראי (credits) נוסף, בדרך כלל לאחר שקיבל ועיבד את הנתונים).

l2cap_recvinto(conn_handle: int, cid: int, buf: Any | None, /) int

מקבלת נתונים מה-conn_handle וה-cid שצוינו אל תוך ה-buf שסופק (שחייב לתמוך בפרוטוקול החוצץ (buffer), למשל bytearray או memoryview).

מחזירה את מספר הבתים שנקראו מהערוץ.

אם buf הוא None, אזי מחזירה את מספר הבתים הזמינים.

הערה: לאחר קבלת אירוע _IRQ_L2CAP_RECV, על היישום להמשיך לקרוא ל-l2cap_recvinto עד שלא יישארו בתים זמינים בחוצץ הקבלה (בדרך כלל עד גודל ה-MTU המרוחק (peer)).

עד שחוצץ הקבלה ריק, ההתקן המרוחק לא יקבל אשראי (credits) נוסף לערוץ ולא יוכל לשלוח נתונים נוספים.

Pairing ו-Bonding

Pairing מאפשר להצפין ולאמת חיבור באמצעות חילופי סודות (עם הגנת MITM אופציונלית באמצעות אימות passkey).

Bonding הוא תהליך אחסון הסודות הללו באחסון לא-נדיף. כאשר מקושר (bonded), התקן מסוגל לפענח כתובת פרטית הניתנת לפענוח (RPA) מהתקן אחר על בסיס מפתח זיהוי הזהות המאוחסן (IRK). כדי לתמוך ב-Bonding, על היישום לממש את האירועים _IRQ_GET_SECRET ו-_IRQ_SET_SECRET.

gap_pair(conn_handle: int, /) None

יוזמת Pairing עם ההתקן המרוחק.

לפני הקריאה לזה, ודאו שאפשרויות התצורה io, mitm, le_secure ו-bond נקבעו (באמצעות config).

ב-Pairing מוצלח, יופעל אירוע _IRQ_ENCRYPTION_UPDATE.

gap_passkey(conn_handle: int, action: int, passkey: int, /) None

מגיבה לאירוע _IRQ_PASSKEY_ACTION עבור ה-conn_handle וה-action שצוינו. משמעות passkey תלויה ב-action (שבתורו תלוי ביכולת הקלט/פלט שהוגדרה):

פעולה

תגובת passkey נדרשת

_PASSKEY_ACTION_INPUT

ה-passkey שהמשתמש קורא מההתקן המרוחק.

_PASSKEY_ACTION_DISPLAY

passkey אקראי בן 6 ספרות שחולל מקומית ומוצג למשתמש.

_PASSKEY_ACTION_NUMERIC_COMPARISON

1 כדי לקבל את ה-passkey המוצג באירוע _IRQ_PASSKEY_ACTION, או 0 כדי לבטל את ה-Pairing.

class UUID

class bluetooth.UUID(value: int | bytes | str, /)

יוצרת מופע UUID עם ה-value שצוין. Bluetooth משתמש בשלושה רוחבי UUID; UUID מקבל כל אחד מהם:

רוחב UUID

טיפוסי value מקובלים

דוגמה

16-bit

int או חוצץ בן 2 בתים (little-endian)

UUID(0x2908) או UUID(b'\x08\x29')

32-bit

חוצץ בן 4 בתים (little-endian)

UUID(b'\x08\x29\x00\x00')

128-bit

חוצץ בן 16 בתים או מחרוזת עם מקפים

UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')

UUIDs בני 16 ו-32 ביט הם בדרך כלל מזהים שהוקצו על ידי ה-SIG (ראו מספרים מוקצים של Bluetooth); UUIDs בני 128 ביט מוגדרים בדרך כלל על ידי הספק.