MicroPython中提供了ubluetooth模組作為藍芽BLE(Bluetooth Low Energy)的操作介面,其主要提供了藍芽控制器層面的操作, 匹配低階藍芽BLE協議,但未整合各種特定裝置型別配置檔案(Profile),僅提供基礎實現模組以供高階層抽象,故稱作低階藍芽BLE介面。
在藍芽BLE中,一個裝置可能需要以多個角色進行工作。當前,其能夠支援中心(Central)、外設(Peripheral)、廣播者(Broadcaster)和觀察者(Observer)角色,還能夠支援GATT伺服器、GATT客戶端和L2CAP面向連線通道的工作模式。目前,僅有部分MicroPython移植版本能夠支援配對和繫結操作。
注意:該模組仍在開發中,其涉及的類、函式、方法和常量定義均有可能發生變化。
class BLE
建構函式
class ubluetooth.BLE
返回BLE類的單例物件。
配置
BLE.active([active,] /)
啟用藍芽BLE射頻元件,並返回其當前狀態。在使用該類下面各方法之前,必須首先進行該操作以啟用射頻元件。
BLE.config('param', /)BLE.config(*, param=value, ...)
查詢或者設定藍芽BLE相應配置引數值。查詢引數值時,引數名需以字串形式傳入,每次只能查詢一個值。設定引數值時,應使用關鍵字引數的語法規則傳入引數名和其對應值,每次可設定多個引數值。
當前支援的配置引數值有:
‘mac’:當前在用的地址,其與當前地址模式有關,查詢時返回(addr_type,addr)形式的元組。具體地址型別在gatts_write部分有詳細描述。
‘addr_mode’:設定地址模式。可能取值如下:
0x00-PUBLIC 使用控制器公共地址;0x01-RANDOM 使用設定的靜態偽隨機地址;0x02-RPA 使用可自解析的私有地址;0x03-NRPA 使用不可自解析的私有地址。
預設情況下,如果PUBLIC地址可用,優先使用PUBLIC地址,否則使用RANDOM地址。
‘gap_name’:查詢或設定通用存取規範GAP(Generic Access Profile)裝置名,該裝置名可用服務控制代碼0x1800和特性控制代碼0x2a00標識。該配置值可以在任意時間進行任意次修改。
‘rxbuf’:查詢或者設定內部用於儲存事件資訊的緩衝區大小。該緩衝區對於藍芽BLE內部驅動全域性有效,能夠處理所有事件,包括所有特性的接收資料。增大該配置值有助於更好地處理類似掃描結果返回的突發資料,提升接收更大的特性資料值的能力。
‘mtu’:查詢或設定在屬性MTU交換期間所使用的MTU值(MTU:Maximum transmission unit,指在一個協議資料單元中能夠傳輸的最大資料量)。程式最終使用的MTU取該配置值和對方裝置MTU值中的較小值。屬性MTU交換過程不會自動進行,其要麼由對端裝置主動發起,要麼由本裝置透過呼叫gattc_exchange_mtu手動發起。使用_IRQ_MTU_EXCHANGED事件可以獲取特定藍芽連線的最終MTU值。
‘bond’:用於設定是否需要在配對期間進行繫結操作。設定該引數值後,配對請求過程中會置位’bond’標誌,並且儲存兩方裝置的金鑰。
‘mitm’:設定配對時,是否需要特意防止MITM(中間人攻擊)。
‘io’:設定裝置的輸入/輸出能力,該能力體現於在配對期間裝置能夠以哪種方式進行金鑰交換。可選項如下:
_IO_CAPABILITY_DISPLAY_ONLY = const(0)_IO_CAPABILITY_DISPLAY_YESNO = const(1)_IO_CAPABILITY_KEYBOARD_ONLY = const(2)_IO_CAPABILITY_NO_INPUT_OUTPUT = const(3)_IO_CAPABILITY_KEYBOARD_DISPLAY = const(4)
‘le_secure’:設定是否需要”LE Secure”式配對,預設為False。(LE Secure和 LE Legacy為藍芽BLE支援的兩種配對方式,預設為後者)
事件處理
BLE.irq(handler, /)
為藍芽BLE協議棧註冊事件回撥函式。該回調函式具有兩個引數,event(如下事件程式碼之一)和data(事件特定的元組值)。
注意:為避免進行非必要的記憶體分配,data元組中的addr,adv_data,char_data,notify_data和uuid變數均為ubluetooth模組內部緩衝區中記憶體例項的只讀映像,僅在IRQ回撥函式被呼叫期間有效。如果應用程式需要儲存這些值(比如儲存在類例項中或者全域性變數中)以便IRQ回撥函式返回後能夠使用,需要以bytes()或者bluetooth.UUID()形式複製所需資料。比如下面形式:
connected_addr=bytes(addr) #若為adv_data、char_data或notify_data,亦需此形式matched_uuid=bluetooth.UUID(uuid)
舉例來講,IRQ回撥函式處理裝置掃描的結果時,需要解析adv_data以查驗其是否為正確的裝置,然後再複製儲存該地址資料以在程式其它地方使用。另外,如果需要在IRQ回撥函式中列印資料,可以使用print(bytes(addr))。
如下事件回撥函式展示了所有可能的事件:
def bt_irq(event, data): if event == _IRQ_CENTRAL_CONNECT: #中心裝置已連線到本外設裝置上 conn_handle, addr_type, addr = data elif event == _IRQ_CENTRAL_DISCONNECT: #中心裝置已與本外設裝置斷開 conn_handle, addr_type, addr = data elif event == _IRQ_GATTS_WRITE: #客戶端已經對特性值或描述符值進行了寫入操作 conn_handle, attr_handle = data elif event == _IRQ_GATTS_READ_REQUEST: #客戶端發起了讀取請求(僅支援STM32平臺) #返回非零值表示拒絕請求,零值表示接受請求。 conn_handle, attr_handle = data elif event == _IRQ_SCAN_RESULT: #掃描結果 addr_type, addr, adv_type, rssi, adv_data = data elif event == _IRQ_SCAN_DONE: #掃描週期結束,或手動結束掃描 pass elif event == _IRQ_PERIPHERAL_CONNECT: #gap_connect()成功呼叫 conn_handle, addr_type, addr = data elif event == _IRQ_PERIPHERAL_DISCONNECT: #所連線的外設裝置已斷開 conn_handle, addr_type, addr = data elif event == _IRQ_GATTC_SERVICE_RESULT: #呼叫gattc_discover_services()時每次發現服務後被觸發 conn_handle, start_handle, end_handle, uuid = data elif event == _IRQ_GATTC_SERVICE_DONE: #服務發現完成(成功時狀態值為零,否則,不同平臺依據具體程式碼實現而不同) conn_handle, status = data elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: #呼叫gattc_discover_characteristics()時每次發現特性後被觸發 conn_handle, def_handle, value_handle, properties, uuid = data elif event == _IRQ_GATTC_CHARACTERISTIC_DONE: #特性發現完成(成功時狀態值為零,否則,不同平臺依據具體程式碼實現而不同) conn_handle, status = data elif event == _IRQ_GATTC_DESCRIPTOR_RESULT: #呼叫gattc_discover_descriptors()時每次發現描述符後被觸發 conn_handle, dsc_handle, uuid = data elif event == _IRQ_GATTC_DESCRIPTOR_DONE: #描述符發現完成(成功時狀態值為零,否則,不同平臺依據具體程式碼實現而不同) conn_handle, status = data elif event == _IRQ_GATTC_READ_RESULT: #gattc_read()返回結果 conn_handle, value_handle, char_data = data elif event == _IRQ_GATTC_READ_DONE: #gattc_read()呼叫完成(成功時狀態值為零,否則,不同平臺依據具體程式碼實現而不同) conn_handle, value_handle, status = data elif event == _IRQ_GATTC_WRITE_DONE: #gattc_write()呼叫完成(成功時狀態值為零,否則,不同平臺依據具體程式碼實現而不同) conn_handle, value_handle, status = data elif event == _IRQ_GATTC_NOTIFY: #伺服器傳送了上報(Notification)請求 conn_handle, value_handle, notify_data = data elif event == _IRQ_GATTC_INDICATE: #伺服器傳送了指示(Indication)請求 conn_handle, value_handle, notify_data = data elif event == _IRQ_GATTS_INDICATE_DONE: #客戶端確認了指示(Indication)請求(成功確認時狀態值為零,否則,不同平臺依據具體程式碼實現而不同) conn_handle, value_handle, status = data elif event == _IRQ_MTU_EXCHANGED: #屬性MTU交換完成(該交換可能由自身或者對方裝置發起) conn_handle, mtu = data elif event == _IRQ_L2CAP_ACCEPT: #接受/拒絕新的通道連線(返回非零值表示拒絕連線,零值或者None表示接受連線) conn_handle, cid, psm, our_mtu, peer_mtu = data elif event == _IRQ_L2CAP_CONNECT: #新通道連線已建立(可能為主動連線的返回結果,也可能為被動接受連線的狀態指示). conn_handle, cid, psm, our_mtu, peer_mtu = data elif event == _IRQ_L2CAP_DISCONNECT: #已建立的通道被斷開(狀態值為零),或者主動連線嘗試失敗(狀態值非零) conn_handle, cid, psm, status = data elif event == _IRQ_L2CAP_RECV: #通道接收到資料,用使用l2cap_recvinto去讀取資料 conn_handle, cid = data elif event == _IRQ_L2CAP_SEND_READY: #前次l2cap_send已經失敗返回,此時該通道可以用於再次傳送(如果狀態值非零,傳輸緩衝區溢位,應用程式應該重新發送資料) conn_handle, cid, status = data elif event == _IRQ_CONNECTION_UPDATE: #對方裝置更新了連線引數 conn_handle, conn_interval, conn_latency, supervision_timeout, status = data elif event == _IRQ_ENCRYPTION_UPDATE: #加密狀態已改變(可能由於配對或繫結). conn_handle, encrypted, authenticated, bonded, key_size = data elif event == _IRQ_GET_SECRET: #返回已經儲存完畢的金鑰。(如果key為None,應返回sec_type的第index個值,否則,返回sec_type和key的對應值) sec_type, index, key = data return value elif event == _IRQ_SET_SECRET: #儲存sec_type和key對應的金鑰 sec_type, key, value = data return True elif event == _IRQ_PASSKEY_ACTION: #在此處響應配對期間的配對金鑰請求(passkey request),可檢視gap_passkey()部分獲取詳情。 #此處進行的操作應對應於”io”引數配置的能力,如果操作為數字比對(numric comparison),passkey應為非零值 conn_handle, action, passkey = data
上面涉及的事件程式碼有如下這些:
from micropython import const _IRQ_CENTRAL_CONNECT = const(1) _IRQ_CENTRAL_DISCONNECT = const(2) _IRQ_GATTS_WRITE = const(3) _IRQ_GATTS_READ_REQUEST = const(4) _IRQ_SCAN_RESULT = const(5) _IRQ_SCAN_DONE = const(6) _IRQ_PERIPHERAL_CONNECT = const(7) _IRQ_PERIPHERAL_DISCONNECT = const(8) _IRQ_GATTC_SERVICE_RESULT = const(9) _IRQ_GATTC_SERVICE_DONE = const(10) _IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) _IRQ_GATTC_CHARACTERISTIC_DONE = const(12) _IRQ_GATTC_DESCRIPTOR_RESULT = const(13) _IRQ_GATTC_DESCRIPTOR_DONE = const(14) _IRQ_GATTC_READ_RESULT = const(15) _IRQ_GATTC_READ_DONE = const(16) _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) _IRQ_GATTS_INDICATE_DONE = const(20) _IRQ_MTU_EXCHANGED = const(21) _IRQ_L2CAP_ACCEPT = const(22) _IRQ_L2CAP_CONNECT = const(23) _IRQ_L2CAP_DISCONNECT = const(24) _IRQ_L2CAP_RECV = const(25) _IRQ_L2CAP_SEND_READY = const(26) _IRQ_CONNECTION_UPDATE = const(27) _IRQ_ENCRYPTION_UPDATE = const(28) _IRQ_GET_SECRET = const(29) _IRQ_SET_SECRET = const(30)
對於_IRQ_GATTS_READ_REQUEST事件,可能的返回值如下:
_GATTS_NO_ERROR = const(0x00)_GATTS_ERROR_READ_NOT_PERMITTED = const(0x02)_GATTS_ERROR_WRITE_NOT_PERMITTED = const(0x03)_GATTS_ERROR_INSUFFICIENT_AUTHENTICATION = const(0x05)_GATTS_ERROR_INSUFFICIENT_AUTHORIZATION = const(0x08)_GATTS_ERROR_INSUFFICIENT_ENCRYPTION = const(0x0f)
對於_IRQ_PASSKEY_ACTION 事件,可能的action取值如下:
_PASSKEY_ACTION_NONE = const(0)_PASSKEY_ACTION_INPUT = const(2)_PASSKEY_ACTION_DISPLAY = const(3)_PASSKEY_ACTION_NUMERIC_COMPARISON = const(4)
為了節省韌體程式碼的空間佔用,這些常量定義沒有包含在ubluetooth模組中,若程式中需要使用,可以自行新增相應定義。
廣播者角色
BLE.gap_advertise(interval_us,adv_data=None,*, resp_data=None, connectable=True)
開始以特定時間間隔(毫秒級)廣播。該時間間隔會取整到625us的整數倍。若要停止廣播,只需將interval_us設為None。
adv_data和resp_data可以是有緩衝意義的任何資料型別(比如bytes,bytearray,str等等)。adv_data將包含在所有的廣播資料中,resp_data用於對活動掃描的回覆報文。
注意:若adv_data或resp_data引數為None,其會重用前次呼叫所傳入的引數值。這樣,廣播者能夠透過gap_advertise(interval_us)形式的呼叫就能恢復廣播。若要清空廣播的應用層負載,可以傳入一個空的bytes,比如b’’。
觀察者角色
BLE.gap_scan(duration_ms, interval_us=1280000, window_us=11250, active=False, /)
持續進行特定時間(微妙級)的的掃描操作。若要一直掃描下去,需把duration_ms設為0,若要停止掃描,需把duration_ms設為None。
使用interval_us和window_us可以配置掃描佔空比:掃描器每隔interval_ms時間間隔掃描一次,每次持續duration_ms時間,該時間內真正工作window_us時間。預設的後臺掃描時間間隔和視窗時間分別為1.28秒和11.25毫秒。
每次掃描到結果後,_IRQ_SCAN_RESULT事件會被觸發,並帶有如下事件資料:(addr_type,addr,adv_type,rssi,adv_data)。
addr_type值表明為公共地址還是隨機地址:
0x00-PUBLIC0x01-隨機地址(可以為靜態隨機、自解析私有地址RPA或者非自解析私有地址NRPA,其中具體型別編碼於地址本身中)
adv_type取值對應於藍牙規範中的規定:
0x00-ADV_IND 可連線、可掃描非定向廣播0x01-ADV_DIRECT_IND 可連線定向廣播0x02-ADV_SCAN_IND 可掃描非定向廣播0x03-ADV_NONCONN_IND 不可連線非定向廣播0x04-SCAN_RSP 掃描響應
active:若要在結果中收到掃描響應資訊,應把該值設為True。
無論由於掃描持續時間到期還是由於使用者顯式停止掃描,掃描結束時_IRQ_SCAN_DONE事件均會被觸發。
中心角色
觀察者角色裝置決定主動連線特定廣播者時,其角色轉換為中心裝置。其既可以主動連線所掃描到的外設裝置,也可以指定連線已知地址的外設裝置。
BLE.gap_connect(addr_type, addr, scan_duration_ms=2000, /)
連線到特定外設裝置(可以檢視gap_scan()部分以瞭解具體地址型別)。成功後,_IRQ_PERIPHERAL_CONNECT事件會被觸發。
外設角色
外設裝置用於傳送可連線廣播資訊(詳情可檢視gap_advertise()部分)。其通常作為GATT伺服器使用,使用gatts_register_services()註冊服務和特性。
當連線中心裝置後,_IRQ_CENTRAL_CONNECT事件會被觸發。
中心和外設角色
BLE.gap_disconnect(conn_handle, /)
斷開特定的連線。其既可能被已接受連線的中心裝置呼叫,也能被髮起連線的外設裝置呼叫。成功後,_IRQ_PERIPHERAL_DISCONNECT事件或者 _IRQ_CENTRAL_DISCONNECT事件會被觸發。如果conn_handle所指定的連線並未真正連線上,該函式會返回False,否則返回True。