bluetooth --- Bluetooth ระดับต่ำ¶
โมดูลนี้มอบอินเทอร์เฟซสำหรับควบคุม Bluetooth บนบอร์ด รองรับ Bluetooth Low Energy (BLE) ในบทบาท Central, Peripheral, Broadcaster และ Observer รวมถึง GATT Server, GATT Client และช่องทาง L2CAP แบบ connection-oriented อุปกรณ์หนึ่งเครื่องสามารถทำงานได้หลายบทบาทพร้อมกัน และยังรองรับการจับคู่ (Pairing) และการผูกอุปกรณ์ (Bonding) อีกด้วย
API นี้ออกแบบให้สอดคล้องกับโปรโตคอล Bluetooth ระดับต่ำ และทำหน้าที่เป็นส่วนประกอบพื้นฐานสำหรับสร้างนามธรรมในระดับที่สูงขึ้น เช่น ประเภทอุปกรณ์เฉพาะทาง
Tip
สำหรับแอปพลิเคชันส่วนใหญ่ แนะนำให้ใช้ไลบรารี aioble ระดับสูงกว่า ซึ่งให้ wrapper แบบ asyncio รอบโมดูลนี้ ดูรายละเอียดได้ที่ aioble --- Async BLE
class BLE¶
- class bluetooth.BLE¶
คืนค่าออบเจกต์ BLE เดี่ยว (singleton)
การกำหนดค่า
- active(active: bool | None = None, /) bool¶
เปลี่ยนสถานะการทำงานของวิทยุ BLE (ไม่บังคับ) และคืนค่าสถานะปัจจุบัน
ต้องเปิดใช้งานวิทยุก่อนจึงจะเรียกใช้เมธอดอื่น ๆ ในคลาสนี้ได้
- config(param: str, /) Any¶
- config(*, **kwargs: Any) None
อ่านหรือตั้งค่าคอนฟิกของอินเทอร์เฟซ BLE หากต้องการอ่านค่า ให้ระบุชื่อพารามิเตอร์เป็นสตริง และสอบถามได้ครั้งละหนึ่งพารามิเตอร์เท่านั้น หากต้องการตั้งค่า ให้ใช้ syntax แบบ keyword และสามารถตั้งค่าได้หลายพารามิเตอร์พร้อมกัน
ค่าที่รองรับในปัจจุบัน ได้แก่:
'mac': ที่อยู่ปัจจุบันที่ใช้งานอยู่ ขึ้นอยู่กับโหมดที่อยู่ปัจจุบัน จะคืนค่าเป็น tuple(addr_type, addr)ดูรายละเอียดเกี่ยวกับประเภทที่อยู่ได้ที่
gap_scanสามารถสอบถามค่านี้ได้เฉพาะเมื่ออินเทอร์เฟซกำลังทำงานอยู่เท่านั้น
'addr_mode': ตั้งค่าโหมดที่อยู่ ค่าที่รองรับ ได้แก่:ค่า
ชื่อ
พฤติกรรม
0x00PUBLIC
ใช้ที่อยู่สาธารณะของตัวควบคุม
0x01RANDOM
ใช้ที่อยู่สถิตที่สร้างขึ้น
0x02RPA
ใช้ที่อยู่ส่วนตัวแบบแก้ไขได้ (Resolvable Private Address)
0x03NRPA
ใช้ที่อยู่ส่วนตัวแบบแก้ไขไม่ได้ (Non-Resolvable Private Address)
โดยค่าเริ่มต้น อินเทอร์เฟซจะใช้ที่อยู่ PUBLIC หากมี มิฉะนั้นจะใช้ที่อยู่ RANDOM
'gap_name': อ่าน/ตั้งค่าชื่ออุปกรณ์ GAP ที่ใช้โดยบริการ Generic Access (UUID0x1800) คุณลักษณะ Device Name (UUID0x2a00) สามารถตั้งค่าได้ตลอดเวลาและเปลี่ยนแปลงได้หลายครั้ง'rxbuf': อ่าน/ตั้งค่าขนาดเป็นไบต์ของบัฟเฟอร์ภายในที่ใช้เก็บอีเวนต์ที่รับเข้ามา บัฟเฟอร์นี้ใช้ร่วมกันทั้งไดรเวอร์ BLE และรองรับข้อมูลที่รับเข้าจากทุกอีเวนต์ รวมถึงทุก characteristic การเพิ่มขนาดจะช่วยรองรับข้อมูลที่รับเข้าแบบพุ่งกระชากได้ดีขึ้น (เช่น ผลการสแกน) และรองรับค่า characteristic ขนาดใหญ่ขึ้นได้'mtu': อ่าน/ตั้งค่า MTU ที่จะใช้ในการแลกเปลี่ยน ATT MTU ค่า MTU ที่ได้จะเป็นค่าที่น้อยกว่าระหว่างค่านี้กับค่า MTU ของอุปกรณ์ระยะไกล การแลกเปลี่ยน ATT MTU จะไม่เกิดขึ้นโดยอัตโนมัติ (เว้นแต่อุปกรณ์ระยะไกลจะเป็นผู้เริ่มต้น) และต้องเริ่มด้วยตนเองผ่านgattc_exchange_mtuใช้อีเวนต์_IRQ_MTU_EXCHANGEDเพื่อดูค่า MTU สำหรับการเชื่อมต่อที่กำหนด'bond': ตั้งค่าว่าจะเปิดใช้งาน bonding ในระหว่างการจับคู่หรือไม่ เมื่อเปิดใช้งาน คำขอจับคู่จะตั้งค่าแฟล็ก "bond" และกุญแจจะถูกจัดเก็บโดยอุปกรณ์ทั้งสอง'mitm': ตั้งค่าว่าต้องการการป้องกัน MITM สำหรับการจับคู่หรือไม่'io': ตั้งค่าความสามารถ I/O ของอุปกรณ์นี้ตัวเลือกที่รองรับ ได้แก่:
ค่าคงที่
ค่า
ความสามารถ
_IO_CAPABILITY_DISPLAY_ONLY0
แสดงผลเท่านั้น
_IO_CAPABILITY_DISPLAY_YESNO1
แสดงผลพร้อมรับข้อมูล yes/no
_IO_CAPABILITY_KEYBOARD_ONLY2
แป้นพิมพ์เท่านั้น
_IO_CAPABILITY_NO_INPUT_OUTPUT3
ไม่มีอินพุตหรือเอาต์พุต
_IO_CAPABILITY_KEYBOARD_DISPLAY4
แป้นพิมพ์และจอแสดงผล
'le_secure': ตั้งค่าว่าต้องการการจับคู่แบบ "LE Secure" หรือไม่ ค่าเริ่มต้นคือ false (อนุญาต "Legacy Pairing")
การจัดการอีเวนต์
- irq(handler: Callable[[int, Tuple], Any | None], /) None¶
ลงทะเบียน callback สำหรับอีเวนต์จาก BLE stack โดย handler รับสองอาร์กิวเมนต์ คือ
event(ซึ่งจะเป็นหนึ่งในโค้ดด้านล่าง) และdata(ซึ่งเป็น tuple ของค่าที่เฉพาะเจาะจงสำหรับแต่ละอีเวนต์)หมายเหตุ: เพื่อเพิ่มประสิทธิภาพและลดการจัดสรรหน่วยความจำที่ไม่จำเป็น รายการ
addr,adv_data,char_data,notify_dataและuuidใน tuple จะเป็น instance ของ memoryview แบบอ่านอย่างเดียวที่ชี้ไปยัง ringbuffer ภายในของbluetoothและใช้ได้เฉพาะในระหว่างการเรียกใช้ฟังก์ชัน IRQ handler เท่านั้น หากโปรแกรมต้องการบันทึกค่าใดค่าหนึ่งเพื่อเข้าถึงหลังจาก IRQ handler คืนค่าแล้ว (เช่น โดยการบันทึกลงใน instance ของคลาสหรือตัวแปร global) จำเป็นต้องคัดลอกข้อมูลโดยใช้bytes()หรือbluetooth.UUID()ดังนี้:connected_addr = bytes(addr) # equivalently: adv_data, char_data, or notify_data matched_uuid = bluetooth.UUID(uuid)
ตัวอย่างเช่น IRQ handler สำหรับผลการสแกนอาจตรวจสอบ
adv_dataเพื่อตัดสินว่าเป็นอุปกรณ์ที่ถูกต้องหรือไม่ แล้วจึงคัดลอกข้อมูลที่อยู่เพื่อนำไปใช้งานที่อื่นในโปรแกรม และในการพิมพ์ข้อมูลจาก IRQ handler จำเป็นต้องใช้print(bytes(addr))handler มักจะกระจายการทำงานตามโค้ดอีเวนต์และแกะ tuple ข้อมูลของแต่ละอีเวนต์:
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 ...
รายการโค้ดอีเวนต์ทุกตัว payload ที่ส่งมา และคำอธิบายสั้น ๆ แสดงไว้ด้านล่าง สำหรับอีเวนต์ที่กล่าวถึงฟิลด์
statusนั้นstatusจะเป็น0เมื่อสำเร็จ และเป็นค่าที่ไม่เป็นศูนย์ที่เฉพาะเจาะจงกับ implementation เมื่อเกิดความผิดพลาดค่าคงที่
ค่า
อีเวนต์
Payload tuple
_IRQ_CENTRAL_CONNECT1
Central ได้เชื่อมต่อกับ peripheral นี้แล้ว
(conn_handle, addr_type, addr)_IRQ_CENTRAL_DISCONNECT2
Central ได้ตัดการเชื่อมต่อจาก peripheral นี้แล้ว
(conn_handle, addr_type, addr)_IRQ_GATTS_WRITE3
Client ที่เชื่อมต่ออยู่ได้เขียนข้อมูลลงใน characteristic หรือ descriptor ในเครื่อง ใช้
gatts_readเพื่ออ่านค่าใหม่(conn_handle, attr_handle)_IRQ_GATTS_READ_REQUEST4
Client ที่เชื่อมต่ออยู่ได้ออก read request ส่งคืนโค้ดข้อผิดพลาดที่ไม่ใช่ศูนย์จากตารางด้านล่างเพื่อปฏิเสธการอ่าน หรือ
0/Noneเพื่อยอมรับ(conn_handle, attr_handle)_IRQ_SCAN_RESULT5
ได้รับ advertising packet เดี่ยวในระหว่างการสแกนแบบ active
(addr_type, addr, adv_type, rssi, adv_data)_IRQ_SCAN_DONE6
การสแกนปัจจุบันสิ้นสุดแล้ว ไม่ว่าจะเป็นเพราะระยะเวลาที่กำหนดหมดลง หรือเพราะมีการเรียก
gap_scan(None)()_IRQ_PERIPHERAL_CONNECT7
gap_connectที่ออกไปก่อนหน้านี้สำเร็จแล้ว(conn_handle, addr_type, addr)_IRQ_PERIPHERAL_DISCONNECT8
Peripheral ที่เชื่อมต่ออยู่ได้ตัดการเชื่อมต่อแล้ว
(conn_handle, addr_type, addr)_IRQ_GATTC_SERVICE_RESULT9
พบบริการหนึ่งรายการโดย
gattc_discover_services(conn_handle, start_handle, end_handle, uuid)_IRQ_GATTC_SERVICE_DONE10
การค้นหาบริการเสร็จสิ้นแล้ว
(conn_handle, status)_IRQ_GATTC_CHARACTERISTIC_RESULT11
พบ characteristic หนึ่งรายการโดย
gattc_discover_characteristics(conn_handle, end_handle, value_handle, properties, uuid)_IRQ_GATTC_CHARACTERISTIC_DONE12
การค้นหา characteristic เสร็จสิ้นแล้ว
(conn_handle, status)_IRQ_GATTC_DESCRIPTOR_RESULT13
พบ descriptor หนึ่งรายการโดย
gattc_discover_descriptors(conn_handle, dsc_handle, uuid)_IRQ_GATTC_DESCRIPTOR_DONE14
การค้นหา descriptor เสร็จสิ้นแล้ว
(conn_handle, status)_IRQ_GATTC_READ_RESULT15
gattc_readที่ออกไปก่อนหน้านี้ได้คืนค่าข้อมูลแล้ว(conn_handle, value_handle, char_data)_IRQ_GATTC_READ_DONE16
gattc_readที่ออกไปก่อนหน้านี้เสร็จสิ้นแล้ว(conn_handle, value_handle, status)_IRQ_GATTC_WRITE_DONE17
gattc_writeที่ออกไปก่อนหน้านี้ได้รับการยืนยันแล้ว(conn_handle, value_handle, status)_IRQ_GATTC_NOTIFY18
เซิร์ฟเวอร์ระยะไกลได้ส่ง notification (แบบไม่มีการยืนยัน)
(conn_handle, value_handle, notify_data)_IRQ_GATTC_INDICATE19
เซิร์ฟเวอร์ระยะไกลได้ส่ง indication (แบบมีการยืนยัน)
(conn_handle, value_handle, notify_data)_IRQ_GATTS_INDICATE_DONE20
indication ที่ส่งออกไปก่อนหน้านี้ได้รับการยืนยันจาก client แล้ว (หรือหมดเวลา)
(conn_handle, value_handle, status)_IRQ_MTU_EXCHANGED21
การแลกเปลี่ยน ATT MTU เสร็จสมบูรณ์แล้ว (เริ่มต้นโดยฝ่ายใดก็ได้)
(conn_handle, mtu)_IRQ_L2CAP_ACCEPT22
อุปกรณ์ระยะไกลได้ร้องขอการเชื่อมต่อ L2CAP บน PSM ที่อุปกรณ์นี้กำลังฟังอยู่ ส่งคืนค่าจำนวนเต็มที่ไม่ใช่ศูนย์เพื่อปฏิเสธ หรือ
0/Noneเพื่อยอมรับ(conn_handle, cid, psm, our_mtu, peer_mtu)_IRQ_L2CAP_CONNECT23
ช่อง L2CAP ได้ถูกสร้างขึ้นแล้ว ไม่ว่าจะเป็นการยอมรับคำขอขาเข้าหรือการเสร็จสิ้น
l2cap_connectแบบขาออก(conn_handle, cid, psm, our_mtu, peer_mtu)_IRQ_L2CAP_DISCONNECT24
ช่อง L2CAP ถูกตัดการเชื่อมต่อแล้ว
statusเป็น0สำหรับการตัดการเชื่อมต่อปกติ หรือไม่ใช่ศูนย์หากความพยายามเชื่อมต่อขาออกล้มเหลว(conn_handle, cid, psm, status)_IRQ_L2CAP_RECV25
ข้อมูลมาถึงช่อง L2CAP แล้ว เรียก
l2cap_recvintoเพื่ออ่านข้อมูล(conn_handle, cid)_IRQ_L2CAP_SEND_READY26
l2cap_sendก่อนหน้าที่คืนค่าFalseได้ระบายข้อมูลแล้วและช่องพร้อมใช้งานอีกครั้งstatusที่ไม่ใช่ศูนย์หมายความว่า transmit buffer ล้น และแอปพลิเคชันต้องส่งข้อมูลใหม่อีกครั้ง(conn_handle, cid, status)_IRQ_CONNECTION_UPDATE27
อุปกรณ์ระยะไกลได้อัปเดตพารามิเตอร์การเชื่อมต่อ (interval, latency, supervision timeout)
(conn_handle, conn_interval, conn_latency, supervision_timeout, status)_IRQ_ENCRYPTION_UPDATE28
สถานะการเข้ารหัสของการเชื่อมต่อได้เปลี่ยนแปลง โดยทั่วไปหลังจากการจับคู่หรือ bonding เสร็จสิ้น
(conn_handle, encrypted, authenticated, bonded, key_size)_IRQ_GET_SECRET29
stack กำลังร้องขอความลับ bonding ที่จัดเก็บไว้ หาก
keyเป็นNoneให้คืนค่าที่จัดเก็บไว้ลำดับที่indexของsec_typeมิฉะนั้นคืนค่าที่เกี่ยวข้องกับ(sec_type, key)ที่กำหนด หากไม่มีข้อมูลจัดเก็บให้คืนค่าNone(sec_type, index, key)_IRQ_SET_SECRET30
stack กำลังขอให้แอปพลิเคชันบันทึกความลับ bonding ส่งคืน
Trueเมื่อบันทึกแล้ว(sec_type, key, value)_IRQ_PASSKEY_ACTION31
ต้องดำเนินการ passkey เป็นส่วนหนึ่งของการจับคู่ ตอบสนองโดยใช้
gap_passkeyดูตาราง passkey-action ด้านล่างสำหรับการดำเนินการที่เป็นไปได้(conn_handle, action, passkey)สำหรับอีเวนต์
_IRQ_GATTS_READ_REQUESTโค้ดตอบกลับที่รองรับ ได้แก่:ค่าคงที่
ค่า
ความหมาย
_GATTS_NO_ERROR0x00ยอมรับการอ่าน
_GATTS_ERROR_READ_NOT_PERMITTED0x02ไม่อนุญาตให้อ่าน
_GATTS_ERROR_WRITE_NOT_PERMITTED0x03ไม่อนุญาตให้เขียน
_GATTS_ERROR_INSUFFICIENT_AUTHENTICATION0x05Client ยังไม่ได้รับการยืนยันตัวตน
_GATTS_ERROR_INSUFFICIENT_AUTHORIZATION0x08Client ยังไม่ได้รับการอนุญาต
_GATTS_ERROR_INSUFFICIENT_ENCRYPTION0x0fการเชื่อมต่อยังไม่ได้เข้ารหัส
สำหรับอีเวนต์
_IRQ_PASSKEY_ACTIONการดำเนินการที่รองรับ ได้แก่:ค่าคงที่
ค่า
ความหมาย
_PASSKEY_ACTION_NONE0
ไม่ต้องดำเนินการใด
_PASSKEY_ACTION_INPUT2
แจ้งให้ผู้ใช้ป้อน passkey ที่แสดงบนอุปกรณ์ระยะไกล
_PASSKEY_ACTION_DISPLAY3
แสดง passkey 6 หลักเพื่อให้อุปกรณ์ระยะไกลป้อน
_PASSKEY_ACTION_NUMERIC_COMPARISON4
ยืนยันว่า passkey ตรงกับที่แสดงบนอุปกรณ์ระยะไกล
เพื่อประหยัดพื้นที่ใน firmware ค่าคงที่เหล่านี้ไม่ได้รวมอยู่ในโมดูล
bluetoothให้เพิ่มเฉพาะค่าที่ต้องการจากรายการด้านบนลงในโปรแกรมของคุณ
บทบาท Broadcaster (ผู้โฆษณา)
- gap_advertise(interval_us: int | None, adv_data: bytes | None = None, *, resp_data: bytes | None = None, connectable: bool = True) None¶
เริ่มต้นการโฆษณาด้วย interval ที่กำหนด (เป็นไมโครวินาที) โดย interval นี้จะถูกปัดลงเป็นทวีคูณของ 625us หยุดการโฆษณาโดยตั้งค่า interval_us เป็น
Noneadv_data และ resp_data สามารถเป็นประเภทใดก็ได้ที่รองรับ buffer protocol (เช่น
bytes,bytearray,str) โดย adv_data จะรวมอยู่ใน broadcast ทุกครั้ง และ resp_data จะถูกส่งเป็นการตอบสนองต่อการสแกนแบบ activeหมายเหตุ: หาก adv_data (หรือ resp_data) เป็น
Noneข้อมูลที่ส่งให้กับการเรียกgap_advertiseครั้งก่อนจะถูกนำมาใช้ซ้ำ ซึ่งช่วยให้ broadcaster สามารถกลับมาโฆษณาด้วยgap_advertise(interval_us)เพียงอย่างเดียว หากต้องการล้าง advertising payload ให้ส่ง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ระบุที่อยู่แบบ public หรือ random:ค่า
ชื่อ
ความหมาย
0x00PUBLIC
ที่อยู่อุปกรณ์แบบ public
0x01RANDOM
ที่อยู่แบบ random (ไม่ว่าจะเป็นแบบ static, RPA หรือ NRPA โดยประเภทจะถูกเข้ารหัสในที่อยู่เอง)
ค่า
adv_typeสอดคล้องกับ Bluetooth Specification:ค่า
ชื่อ
ความหมาย
0x00ADV_IND
การโฆษณาแบบ connectable และ scannable undirected
0x01ADV_DIRECT_IND
การโฆษณาแบบ connectable directed
0x02ADV_SCAN_IND
การโฆษณาแบบ scannable undirected
0x03ADV_NONCONN_IND
การโฆษณาแบบ non-connectable undirected
0x04SCAN_RSP
Scan response
สามารถตั้งค่า
activeเป็นTrueหากต้องการรับ scan response ในผลลัพธ์เมื่อการสแกนหยุดลง (ไม่ว่าจะเป็นเพราะระยะเวลาสิ้นสุดหรือหยุดอย่างชัดเจน) อีเวนต์
_IRQ_SCAN_DONEจะถูกเรียกขึ้น
บทบาท Central
อุปกรณ์ Central สามารถเชื่อมต่อกับ peripheral ที่ค้นพบผ่านบทบาท 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จะถูกเรียกขึ้นอุปกรณ์จะรอรับ advertising payload จากอุปกรณ์นานถึง scan_duration_ms
สามารถกำหนดค่า connection interval เป็นไมโครวินาทีโดยใช้ min_conn_interval_us และ/หรือ max_conn_interval_us มิฉะนั้นจะเลือก interval เริ่มต้น โดยทั่วไปอยู่ระหว่าง 30000 ถึง 50000 ไมโครวินาที Interval ที่สั้นลงจะเพิ่ม throughput แต่ใช้พลังงานมากขึ้น
บทบาท Peripheral
อุปกรณ์ peripheral คาดว่าจะส่ง connectable advertisement (ดู
gap_advertise) โดยปกติจะทำหน้าที่เป็น GATT server โดยลงทะเบียนบริการและ characteristic ก่อนโดยใช้gatts_register_servicesเมื่อ central เชื่อมต่อ อีเวนต์
_IRQ_CENTRAL_CONNECTจะถูกเรียกขึ้นบทบาท Central & Peripheral
- gap_disconnect(conn_handle: int, /) bool¶
ตัดการเชื่อมต่อ connection handle ที่กำหนด ซึ่งอาจเป็น central ที่เชื่อมต่อกับอุปกรณ์นี้ (เมื่อทำหน้าที่เป็น peripheral) หรือ peripheral ที่อุปกรณ์นี้เชื่อมต่อก่อนหน้า (เมื่อทำหน้าที่เป็น central)
เมื่อสำเร็จ อีเวนต์
_IRQ_PERIPHERAL_DISCONNECTหรือ_IRQ_CENTRAL_DISCONNECTจะถูกเรียกขึ้นคืนค่า
Falseหาก connection handle ไม่ได้เชื่อมต่ออยู่ และTrueในกรณีอื่น
GATT Server
GATT server มีชุดบริการที่ลงทะเบียนไว้ แต่ละบริการอาจประกอบด้วย characteristic ซึ่งแต่ละตัวมีค่า characteristic ยังสามารถมี descriptor ซึ่งก็มีค่าเช่นกัน
ค่าเหล่านี้ถูกจัดเก็บไว้ในเครื่อง และเข้าถึงได้ผ่าน "value handle" ที่สร้างขึ้นระหว่างการลงทะเบียนบริการ นอกจากนี้ยังสามารถอ่านหรือเขียนโดยอุปกรณ์ client ระยะไกลได้ และ server ยังสามารถ "notify" characteristic ไปยัง client ที่เชื่อมต่ออยู่ผ่าน connection handle ได้อีกด้วย
อุปกรณ์ในบทบาท central หรือ peripheral ก็สามารถทำหน้าที่เป็น GATT server ได้ อย่างไรก็ตามในกรณีส่วนใหญ่อุปกรณ์ peripheral มักทำหน้าที่เป็น server มากกว่า
Characteristic และ descriptor มีขนาดสูงสุดเริ่มต้นที่ 20 ไบต์ (ATT MTU เริ่มต้น 23 ไบต์ลบด้วย ATT header 3 ไบต์ การเจรจา MTU ที่ใหญ่ขึ้นไม่ได้เพิ่มขีดจำกัดนี้โดยอัตโนมัติ) ข้อมูลใดที่ client เขียนจะถูกตัดให้อยู่ในความยาวนี้ อย่างไรก็ตาม การเขียนในเครื่องจะเพิ่มขนาดสูงสุด ดังนั้นหากต้องการอนุญาตให้ client เขียนข้อมูลขนาดใหญ่กว่านี้ไปยัง characteristic ที่กำหนด ให้ใช้
gatts_writeหลังจากลงทะเบียน เช่นgatts_write(char_handle, bytes(100))- gatts_register_services(services_definition: Sequence[Sequence], /) Sequence[Sequence[int]]¶
กำหนดค่า server ด้วยบริการที่ระบุ แทนที่บริการที่มีอยู่ทั้งหมด
services_definition คือรายการของ บริการ โดยแต่ละ บริการ เป็น tuple สองส่วนที่ประกอบด้วย UUID และรายการ characteristic
แต่ละ characteristic เป็น tuple สองหรือสามส่วนที่ประกอบด้วย UUID ค่า flags และรายการ descriptor (ไม่บังคับ)
แต่ละ descriptor เป็น tuple สองส่วนที่ประกอบด้วย UUID และค่า flags
flags คือการรวมกันแบบ bitwise-OR ของแฟล็กที่กำหนดด้านล่าง โดยกำหนดทั้งพฤติกรรมของ characteristic (หรือ descriptor) รวมถึงข้อกำหนดด้านความปลอดภัยและความเป็นส่วนตัว
ค่าที่คืนมาคือรายการ (หนึ่งส่วนต่อบริการ) ของ tuple (แต่ละส่วนเป็น value handle) โดย handle ของ characteristic และ descriptor จะถูกรวมลงใน 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), )
value handle สามทัว (
hr,tx,rx) สามารถใช้กับgatts_read,gatts_write,gatts_notifyและgatts_indicateหมายเหตุ: ต้องหยุด advertising ก่อนลงทะเบียนบริการ
แฟล็กที่รองรับสำหรับ characteristic และ descriptor ได้แก่:
ค่าคงที่
ค่า
ความหมาย
_FLAG_BROADCAST0x0001Characteristic อาจถูก broadcast ได้
_FLAG_READ0x0002Client อาจอ่านค่าได้
_FLAG_WRITE_NO_RESPONSE0x0004Client อาจเขียนโดยไม่ต้องรอการตอบกลับ
_FLAG_WRITE0x0008Client อาจเขียนพร้อมการตอบกลับที่มีการยืนยัน
_FLAG_NOTIFY0x0010Server อาจส่ง notification (แบบไม่มีการยืนยัน)
_FLAG_INDICATE0x0020Server อาจส่ง indication (แบบมีการยืนยัน)
_FLAG_AUTHENTICATED_SIGNED_WRITE0x0040Client อาจออก signed write ได้
_FLAG_AUX_WRITE0x0100Extended properties: อนุญาตให้ใช้ queued/reliable write ได้
_FLAG_READ_ENCRYPTED0x0200การอ่านต้องใช้การเชื่อมต่อที่เข้ารหัส
_FLAG_READ_AUTHENTICATED0x0400การอ่านต้องใช้การเชื่อมต่อที่ยืนยันตัวตนแล้ว (ป้องกัน MITM)
_FLAG_READ_AUTHORIZED0x0800การอ่านต้องได้รับอนุญาตในระดับแอปพลิเคชัน
_FLAG_WRITE_ENCRYPTED0x1000การเขียนต้องใช้การเชื่อมต่อที่เข้ารหัส
_FLAG_WRITE_AUTHENTICATED0x2000การเขียนต้องใช้การเชื่อมต่อที่ยืนยันตัวตนแล้ว (ป้องกัน MITM)
_FLAG_WRITE_AUTHORIZED0x4000การเขียนต้องได้รับอนุญาตในระดับแอปพลิเคชัน
เช่นเดียวกับค่าคงที่ของอีเวนต์ด้านบน แฟล็กเหล่านี้ไม่ได้ถูกจัดเตรียมโดยโมดูล
bluetoothให้คัดลอกเฉพาะที่ต้องการใช้ลงในโปรแกรมของคุณ
- gatts_read(value_handle: int, /) bytes¶
อ่านค่าในเครื่องสำหรับ handle นี้ (ซึ่งถูกเขียนโดย
gatts_writeหรือโดย client ระยะไกล)
- gatts_write(value_handle: int, data: bytes, send_update: bool = False, /) None¶
เขียนค่าในเครื่องสำหรับ handle นี้ ซึ่ง client สามารถอ่านได้
หาก send_update เป็น
Trueclient ที่สมัครรับข้อมูลอยู่จะได้รับแจ้ง (หรือ indication ขึ้นอยู่กับสิ่งที่ client สมัครรับและการดำเนินการที่ characteristic รองรับ) เกี่ยวกับการเขียนนี้
- gatts_notify(conn_handle: int, value_handle: int, data: bytes | None = None, /) None¶
ส่งคำขอ notification ไปยัง client ที่เชื่อมต่ออยู่
หาก data เป็น
None(ค่าเริ่มต้น) ค่าในเครื่องปัจจุบัน (ที่ตั้งค่าโดยgatts_write) จะถูกส่งหาก data ไม่ใช่
Noneค่านั้นจะถูกส่งไปยัง client เป็นส่วนหนึ่งของ notification โดยค่าในเครื่องจะไม่ถูกแก้ไขหมายเหตุ: notification จะถูกส่งโดยไม่คำนึงถึงสถานะการสมัครรับ characteristic ของ client
- gatts_indicate(conn_handle: int, value_handle: int, data: bytes | None = None, /) None¶
ส่งคำขอ indication ไปยัง client ที่เชื่อมต่ออยู่
หาก data เป็น
None(ค่าเริ่มต้น) ค่าในเครื่องปัจจุบัน (ที่ตั้งค่าโดยgatts_write) จะถูกส่งหาก data ไม่ใช่
Noneค่านั้นจะถูกส่งไปยัง client เป็นส่วนหนึ่งของ indication โดยค่าในเครื่องจะไม่ถูกแก้ไขเมื่อได้รับการยืนยัน (หรือเกิดความล้มเหลว เช่น timeout) อีเวนต์
_IRQ_GATTS_INDICATE_DONEจะถูกเรียกขึ้นหมายเหตุ: indication จะถูกส่งโดยไม่คำนึงถึงสถานะการสมัครรับ characteristic ของ client
- gatts_set_buffer(value_handle: int, len: int, append: bool = False, /) None¶
ตั้งค่าขนาดบัฟเฟอร์ภายในสำหรับค่าหนึ่งเป็นไบต์ ซึ่งจะจำกัดขนาดการเขียนสูงสุดที่รับได้ ค่าเริ่มต้นคือ 20 ไบต์ (ATT MTU เริ่มต้น 23 ลบด้วย ATT header 3 ไบต์)
การตั้งค่า append เป็น
Trueจะทำให้การเขียนทั้งหมดจากระยะไกลต่อท้ายแทนที่จะแทนที่ค่าปัจจุบัน โดยบัฟเฟอร์ได้สูงสุด len ไบต์ด้วยวิธีนี้ เมื่อใช้gatts_readค่าจะถูกล้างหลังจากอ่าน ฟีเจอร์นี้มีประโยชน์เมื่อ implement บางอย่าง เช่น Nordic UART Service
GATT Client
GATT client สามารถค้นพบและอ่าน/เขียน characteristic บน GATT server ระยะไกลได้
โดยทั่วไปอุปกรณ์ที่มีบทบาท central จะทำหน้าที่เป็น GATT client อย่างไรก็ตาม peripheral ก็สามารถทำหน้าที่เป็น client ได้เช่นกัน เพื่อค้นหาข้อมูลเกี่ยวกับ central ที่เชื่อมต่ออยู่ (เช่น การอ่านชื่ออุปกรณ์จากบริการข้อมูลอุปกรณ์)
- gattc_discover_services(conn_handle: int, uuid: UUID | None = None, /) None¶
สอบถาม server ที่เชื่อมต่ออยู่เพื่อค้นหาบริการ
ระบุ 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¶
สอบถาม server ที่เชื่อมต่ออยู่เพื่อค้นหา characteristic ในช่วงที่กำหนด
ระบุ uuid ของ characteristic เพื่อสอบถามเฉพาะ characteristic นั้นได้ (ไม่บังคับ)
การส่ง
start_handle=1และend_handle=0xffffครอบคลุมช่วง GATT attribute-handle ทั้งหมด ดังนั้นการรวมกันนี้จะค้นหาทุกบริการบนอุปกรณ์ระยะไกลอย่างมีประสิทธิภาพสำหรับแต่ละ characteristic ที่ค้นพบ อีเวนต์
_IRQ_GATTC_CHARACTERISTIC_RESULTจะถูกเรียกขึ้น ตามด้วย_IRQ_GATTC_CHARACTERISTIC_DONEเมื่อเสร็จสมบูรณ์
- gattc_discover_descriptors(conn_handle: int, start_handle: int, end_handle: int, /) None¶
สอบถาม server ที่เชื่อมต่ออยู่เพื่อค้นหา descriptor ในช่วงที่กำหนด
สำหรับแต่ละ descriptor ที่ค้นพบ อีเวนต์
_IRQ_GATTC_DESCRIPTOR_RESULTจะถูกเรียกขึ้น ตามด้วย_IRQ_GATTC_DESCRIPTOR_DONEเมื่อเสร็จสมบูรณ์
- gattc_read(conn_handle: int, value_handle: int, /) None¶
ออก remote read ไปยัง server ที่เชื่อมต่ออยู่สำหรับ characteristic หรือ descriptor handle ที่กำหนด
เมื่อค่าพร้อมใช้งาน อีเวนต์
_IRQ_GATTC_READ_RESULTจะถูกเรียกขึ้น ตามด้วย_IRQ_GATTC_READ_DONEเมื่อเสร็จสมบูรณ์
- gattc_write(conn_handle: int, value_handle: int, data: bytes, mode: int = 0, /) None¶
ออก remote write ไปยัง server ที่เชื่อมต่ออยู่สำหรับ characteristic หรือ descriptor handle ที่กำหนด
อาร์กิวเมนต์ mode ระบุพฤติกรรมการเขียน โดยค่าที่รองรับในปัจจุบัน ได้แก่:
mode=0(ค่าเริ่มต้น) คือ write-without-response: การเขียนจะถูกส่งไปยัง server ระยะไกลแต่จะไม่มีการยืนยันส่งกลับมา และจะไม่มีการเรียกอีเวนต์mode=1คือ write-with-response: server ระยะไกลจะถูกขอให้ส่งการตอบกลับ/การยืนยันว่าได้รับข้อมูลแล้ว
หาก server ระยะไกลส่งการตอบกลับมา อีเวนต์
_IRQ_GATTC_WRITE_DONEจะถูกเรียกขึ้น
- gattc_exchange_mtu(conn_handle: int, /) None¶
เริ่มต้นการแลกเปลี่ยน MTU กับ server ที่เชื่อมต่ออยู่ โดยใช้ค่า MTU ที่ต้องการซึ่งตั้งค่าด้วย
BLE.config(mtu=value)อีเวนต์
_IRQ_MTU_EXCHANGEDจะถูกเรียกขึ้นเมื่อการแลกเปลี่ยน MTU เสร็จสมบูรณ์โดยทั่วไป central จะเป็นผู้เริ่มต้นการแลกเปลี่ยน MTU โดย NimBLE รองรับทั้งสองบทบาท
ช่อง L2CAP แบบ Connection-Oriented
ฟีเจอร์นี้ช่วยให้แลกเปลี่ยนข้อมูลแบบ socket ระหว่างอุปกรณ์ BLE สองเครื่องได้ เมื่ออุปกรณ์เชื่อมต่อกันผ่าน GAP แล้ว อุปกรณ์ใดก็ได้สามารถฟังอีกฝ่ายเพื่อเชื่อมต่อบน PSM (Protocol/Service Multiplexer) ที่กำหนด
สามารถใช้งาน L2CAP channel ได้เพียงหนึ่งช่องในเวลาหนึ่ง (ไม่สามารถเชื่อมต่อขณะกำลังฟังอยู่)
L2CAP channel ที่ใช้งานอยู่จะถูกระบุโดย connection handle ที่ถูกสร้างขึ้นและ CID (channel ID)
ช่องทาง connection-oriented มีการควบคุมการไหลแบบ credit-based ในตัว ต่างจาก ATT ที่อุปกรณ์เจรจา MTU ร่วมกัน อุปกรณ์ฟังและอุปกรณ์เชื่อมต่อต่างตั้งค่า MTU อิสระของตนเอง ซึ่งจำกัดจำนวนข้อมูลค้างส่งสูงสุดที่อุปกรณ์ระยะไกลสามารถส่งก่อนที่จะถูกบริโภคใน
l2cap_recvinto- l2cap_listen(psm: int, mtu: int, /) None¶
เริ่มฟังคำขอ L2CAP channel ขาเข้าบน psm ที่กำหนด โดยตั้งค่า local MTU เป็น mtu
เมื่ออุปกรณ์ระยะไกลเริ่มต้นการเชื่อมต่อ อีเวนต์
_IRQ_L2CAP_ACCEPTจะถูกเรียกขึ้น ซึ่งให้โอกาส server ที่กำลังฟังปฏิเสธการเชื่อมต่อขาเข้า (โดยคืนค่าจำนวนเต็มที่ไม่ใช่ศูนย์)เมื่อการเชื่อมต่อได้รับการยอมรับ อีเวนต์
_IRQ_L2CAP_CONNECTจะถูกเรียกขึ้น ซึ่งให้ server ได้รับ CID และค่า local และ remote MTUหมายเหตุ: ขณะนี้ยังไม่สามารถหยุดการฟังได้
- l2cap_connect(conn_handle: int, psm: int, mtu: int, /) None¶
เชื่อมต่อกับ peer ที่กำลังฟังบน psm ที่กำหนด โดยตั้งค่า local MTU เป็น mtu
เมื่อเชื่อมต่อสำเร็จ อีเวนต์
_IRQ_L2CAP_CONNECTจะถูกเรียกขึ้น ให้ client ได้รับ CID และค่า local และ remote (peer) MTUการเชื่อมต่อที่ไม่สำเร็จจะเรียกอีเวนต์
_IRQ_L2CAP_DISCONNECTพร้อม status ที่ไม่ใช่ศูนย์
- l2cap_disconnect(conn_handle: int, cid: int, /) None¶
ตัดการเชื่อมต่อ L2CAP channel ที่ใช้งานอยู่ด้วย conn_handle และ cid ที่กำหนด
- l2cap_send(conn_handle: int, cid: int, buf: bytes, /) bool¶
ส่ง buf ที่กำหนด (ซึ่งต้องรองรับ buffer protocol) บน L2CAP channel ที่ระบุโดย conn_handle และ cid
บัฟเฟอร์ต้องปฏิบัติตามข้อจำกัดทั้งสอง: ต้องไม่เกิน remote (peer) MTU และต้องไม่เกินสองเท่าของ local MTU
จะคืนค่า
Falseหากช่อง "stalled" อยู่ ซึ่งหมายความว่าต้องไม่เรียกl2cap_sendอีกจนกว่าจะได้รับอีเวนต์_IRQ_L2CAP_SEND_READY(ซึ่งจะเกิดขึ้นเมื่ออุปกรณ์ระยะไกลให้ credit เพิ่มเติม โดยทั่วไปหลังจากได้รับและประมวลผลข้อมูล)
- l2cap_recvinto(conn_handle: int, cid: int, buf: Any | None, /) int¶
รับข้อมูลจาก conn_handle และ cid ที่กำหนดลงใน buf ที่ให้มา (ซึ่งต้องรองรับ buffer protocol เช่น bytearray หรือ memoryview)
คืนค่าจำนวนไบต์ที่อ่านจาก channel
หาก buf เป็น
Noneจะคืนค่าจำนวนไบต์ที่พร้อมใช้งานหมายเหตุ: หลังจากได้รับอีเวนต์
_IRQ_L2CAP_RECVแอปพลิเคชันควรเรียกl2cap_recvintoต่อไปจนกว่าไม่มีไบต์เหลืออยู่ใน receive buffer (โดยทั่วไปไม่เกินขนาดของ remote (peer) MTU)จนกว่า receive buffer จะว่างเปล่า อุปกรณ์ระยะไกลจะไม่ได้รับ channel credit เพิ่มและไม่สามารถส่งข้อมูลเพิ่มเติมได้
การจับคู่และการผูกอุปกรณ์
การจับคู่ช่วยให้การเชื่อมต่อถูกเข้ารหัสและยืนยันตัวตนผ่านการแลกเปลี่ยนความลับ (พร้อมการป้องกัน MITM ผ่านการยืนยัน passkey ตามต้องการ)
Bonding คือกระบวนการจัดเก็บความลับเหล่านั้นลงใน non-volatile storage เมื่อ bonded แล้ว อุปกรณ์สามารถแก้ไข resolvable private address (RPA) จากอุปกรณ์อื่นได้โดยอ้างอิงจาก identity resolving key (IRK) ที่จัดเก็บไว้ เพื่อรองรับ bonding แอปพลิเคชันต้อง implement อีเวนต์
_IRQ_GET_SECRETและ_IRQ_SET_SECRET- gap_pair(conn_handle: int, /) None¶
เริ่มต้นการจับคู่กับอุปกรณ์ระยะไกล
ก่อนเรียกสิ่งนี้ ให้ตรวจสอบว่าตัวเลือกการกำหนดค่า
io,mitm,le_secureและbondถูกตั้งค่าแล้ว (ผ่านconfig)เมื่อจับคู่สำเร็จ อีเวนต์
_IRQ_ENCRYPTION_UPDATEจะถูกเรียกขึ้น
- gap_passkey(conn_handle: int, action: int, passkey: int, /) None¶
ตอบสนองต่ออีเวนต์
_IRQ_PASSKEY_ACTIONสำหรับ conn_handle และ action ที่กำหนด ความหมายของ passkey ขึ้นอยู่กับ action (ซึ่งขึ้นอยู่กับความสามารถ I/O ที่กำหนดค่า):การดำเนินการ
การตอบสนอง passkey ที่ต้องการ
_PASSKEY_ACTION_INPUTpasskey ที่ผู้ใช้อ่านจากอุปกรณ์ระยะไกล
_PASSKEY_ACTION_DISPLAYpasskey 6 หลักแบบสุ่มที่สร้างในเครื่องและแสดงให้ผู้ใช้เห็น
_PASSKEY_ACTION_NUMERIC_COMPARISON1เพื่อยอมรับ passkey ที่แสดงในอีเวนต์_IRQ_PASSKEY_ACTIONหรือ0เพื่อยกเลิกการจับคู่
class UUID¶
- class bluetooth.UUID(value: int | bytes | str, /)¶
สร้าง instance ของ UUID ด้วย
valueที่กำหนด Bluetooth ใช้ UUID สามขนาด โดยUUIDรองรับทั้งหมด:ความกว้าง UUID
ประเภท
valueที่รองรับตัวอย่าง
16 บิต
intหรือบัฟเฟอร์ 2 ไบต์ (little-endian)UUID(0x2908)หรือUUID(b'\x08\x29')32 บิต
บัฟเฟอร์ 4 ไบต์ (little-endian)
UUID(b'\x08\x29\x00\x00')128 บิต
บัฟเฟอร์ 16 ไบต์หรือสตริงแบบมีเครื่องหมายยัติภังค์
UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')UUID แบบ 16 บิตและ 32 บิตมักเป็นตัวระบุที่ SIG จัดสรร (ดู Bluetooth assigned numbers) ส่วน UUID แบบ 128 บิตมักกำหนดโดยผู้ผลิต