ESP32使用MicroPython:藍牙

前面的MicroPython版本有個不小的缺陷,就是無法使用藍牙模塊。ESP32的一個巨大優勢就是將藍牙和WIFI集成到了一塊芯片上,沒有藍牙功能無疑是個巨大的損失。現在已經有一個版本已經支持藍牙開發了,這裏簡單介紹一下如何使用藍牙模塊。

最新的版本:

bluetooth — 低級藍牙

該模塊爲板上的藍牙控制器提供接口。目前,這支持中央、外設、廣播和觀察者角色的低功耗藍牙 (BLE),以及 GATT 服務器和客戶端以及 L2CAP 面向連接的通道。一個設備可以同時扮演多個角色。某些端口支持配對(和綁定)。

此 API 旨在匹配低級藍牙協議,併爲更高級別的抽象(例如特定設備類型)提供構建塊。

筆記

該模塊仍在開發中,其類、函數、方法和常量可能會發生變化。

低功耗等級

構造函數

class bluetooth.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 地址,否則將使用 RANDOM 地址。

  • 'gap_name': 獲取/設置服務0x1800使用的GAP設備名稱,特徵0x2a00。這可以隨時設置並多次更改。

  • 'rxbuf': 獲取/設置用於存儲傳入事件的內部緩衝區的大小(以字節爲單位)。該緩衝區對整個 BLE 驅動程序是全局的,因此處理所有事件的傳入數據,包括所有特徵。增加此值可以更好地處理突發傳入數據(例如掃描結果)和接收更大特徵值的能力。

  • 'mtu':獲取/設置將在 ATT MTU 交換期間使用的 MTU。生成的 MTU 將是該 MTU 和遠程設備的 MTU 中的最小值。ATT MTU 交換不會自動發生(除非遠程設備啓動它),必須手動啓動 gattc_exchange_mtu。使用該_IRQ_MTU_EXCHANGED 事件來發現給定連接的 MTU。

  • 'bond': 設置配對時是否啓用綁定。啓用後,配對請求將設置“綁定”標誌,並且密鑰將由兩個設備存儲。

  • 'mitm':設置配對是否需要 MITM 保護。

  • 'io': 設置此設備的 I/O 功能。

    可用選項有:

    _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(即允許“傳統配對”)。

事件處理

BLE.irq(handler, /)

爲來自 BLE 堆棧的事件註冊回調。的處理程序採用兩個參數,event(這將在下面的代碼中的一個)和data (其是值的特定事件元組)。

注:作爲一種優化,以防止不必要的配發,addr, adv_data, char_data, notify_data, 和 uuid在元組的條目是隻讀的指向memoryview實例 bluetooth的內部ringbuffer,並且僅在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))

顯示所有可能事件的事件處理程序:

def bt_irq(event, data):
    if event == _IRQ_CENTRAL_CONNECT:
        # A central has connected to this peripheral.
        conn_handle, addr_type, addr = data
    elif event == _IRQ_CENTRAL_DISCONNECT:
        # A central has disconnected from this peripheral.
        conn_handle, addr_type, addr = data
    elif event == _IRQ_GATTS_WRITE:
        # A client has written to this characteristic or descriptor.
        conn_handle, attr_handle = data
    elif event == _IRQ_GATTS_READ_REQUEST:
        # A client has issued a read. Note: this is only supported on STM32.
        # Return a non-zero integer to deny the read (see below), or zero (or None)
        # to accept the read.
        conn_handle, attr_handle = data
    elif event == _IRQ_SCAN_RESULT:
        # A single scan result.
        addr_type, addr, adv_type, rssi, adv_data = data
    elif event == _IRQ_SCAN_DONE:
        # Scan duration finished or manually stopped.
        pass
    elif event == _IRQ_PERIPHERAL_CONNECT:
        # A successful gap_connect().
        conn_handle, addr_type, addr = data
    elif event == _IRQ_PERIPHERAL_DISCONNECT:
        # Connected peripheral has disconnected.
        conn_handle, addr_type, addr = data
    elif event == _IRQ_GATTC_SERVICE_RESULT:
        # Called for each service found by gattc_discover_services().
        conn_handle, start_handle, end_handle, uuid = data
    elif event == _IRQ_GATTC_SERVICE_DONE:
        # Called once service discovery is complete.
        # Note: Status will be zero on success, implementation-specific value otherwise.
        conn_handle, status = data
    elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
        # Called for each characteristic found by gattc_discover_services().
        conn_handle, def_handle, value_handle, properties, uuid = data
    elif event == _IRQ_GATTC_CHARACTERISTIC_DONE:
        # Called once service discovery is complete.
        # Note: Status will be zero on success, implementation-specific value otherwise.
        conn_handle, status = data
    elif event == _IRQ_GATTC_DESCRIPTOR_RESULT:
        # Called for each descriptor found by gattc_discover_descriptors().
        conn_handle, dsc_handle, uuid = data
    elif event == _IRQ_GATTC_DESCRIPTOR_DONE:
        # Called once service discovery is complete.
        # Note: Status will be zero on success, implementation-specific value otherwise.
        conn_handle, status = data
    elif event == _IRQ_GATTC_READ_RESULT:
        # A gattc_read() has completed.
        conn_handle, value_handle, char_data = data
    elif event == _IRQ_GATTC_READ_DONE:
        # A gattc_read() has completed.
        # Note: The value_handle will be zero on btstack (but present on NimBLE).
        # Note: Status will be zero on success, implementation-specific value otherwise.
        conn_handle, value_handle, status = data
    elif event == _IRQ_GATTC_WRITE_DONE:
        # A gattc_write() has completed.
        # Note: The value_handle will be zero on btstack (but present on NimBLE).
        # Note: Status will be zero on success, implementation-specific value otherwise.
        conn_handle, value_handle, status = data
    elif event == _IRQ_GATTC_NOTIFY:
        # A server has sent a notify request.
        conn_handle, value_handle, notify_data = data
    elif event == _IRQ_GATTC_INDICATE:
        # A server has sent an indicate request.
        conn_handle, value_handle, notify_data = data
    elif event == _IRQ_GATTS_INDICATE_DONE:
        # A client has acknowledged the indication.
        # Note: Status will be zero on successful acknowledgment, implementation-specific value otherwise.
        conn_handle, value_handle, status = data
    elif event == _IRQ_MTU_EXCHANGED:
        # ATT MTU exchange complete (either initiated by us or the remote device).
        conn_handle, mtu = data
    elif event == _IRQ_L2CAP_ACCEPT:
        # A new channel has been accepted.
        # Return a non-zero integer to reject the connection, or zero (or None) to accept.
        conn_handle, cid, psm, our_mtu, peer_mtu = data
    elif event == _IRQ_L2CAP_CONNECT:
        # A new channel is now connected (either as a result of connecting or accepting).
        conn_handle, cid, psm, our_mtu, peer_mtu = data
    elif event == _IRQ_L2CAP_DISCONNECT:
        # Existing channel has disconnected (status is zero), or a connection attempt failed (non-zero status).
        conn_handle, cid, psm, status = data
    elif event == _IRQ_L2CAP_RECV:
        # New data is available on the channel. Use l2cap_recvinto to read.
        conn_handle, cid = data
    elif event == _IRQ_L2CAP_SEND_READY:
        # A previous l2cap_send that returned False has now completed and the channel is ready to send again.
        # If status is non-zero, then the transmit buffer overflowed and the application should re-send the data.
        conn_handle, cid, status = data
    elif event == _IRQ_CONNECTION_UPDATE:
        # The remote device has updated connection parameters.
        conn_handle, conn_interval, conn_latency, supervision_timeout, status = data
    elif event == _IRQ_ENCRYPTION_UPDATE:
        # The encryption state has changed (likely as a result of pairing or bonding).
        conn_handle, encrypted, authenticated, bonded, key_size = data
    elif event == _IRQ_GET_SECRET:
        # Return a stored secret.
        # If key is None, return the index'th value of this sec_type.
        # Otherwise return the corresponding value for this sec_type and key.
        sec_type, index, key = data
        return value
    elif event == _IRQ_SET_SECRET:
        # Save a secret to the store for this sec_type and key.
        sec_type, key, value = data
        return True
    elif event == _IRQ_PASSKEY_ACTION:
        # Respond to a passkey request during pairing.
        # See gap_passkey() for details.
        # action will be an action that is compatible with the configured "io" config.
        # passkey will be non-zero if action is "numeric comparison".
        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事件,可用的操作是:

_PASSKEY_ACTION_NONE = const(0)
_PASSKEY_ACTION_INPUT = const(2)
_PASSKEY_ACTION_DISPLAY = const(3)
_PASSKEY_ACTION_NUMERIC_COMPARISON = const(4)

爲了節省固件空間,這些常量不包含在 bluetooth模塊中。將上面列表中您需要的那些添加到您的程序中。

廣播員角色(廣告商)

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將被重新使用。這允許廣播公司僅使用 來恢復廣告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_us微秒運行window_us 微秒,總共持續duration_ms毫秒。默認間隔和窗口分別爲 1.28 秒和 11.25 毫秒(後臺掃描)。

對於每個掃描結果,_IRQ_SCAN_RESULT將引發事件,並帶有事件數據。(addr_type, addr, adv_type, rssi, adv_data).

addr_type 值表示公共或隨機地址:

  • 0x00 - 公共

  • 0x01 - RANDOM (靜態、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將引發該事件。

核心角色

中央設備可以使用觀察者角色(參見 參考資料 gap_scan)或已知地址連接到它發現的外圍設備。

BLE.gap_connect(addr_type, addr, scan_duration_ms=2000, /)

連接到外圍設備。

See 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 事件。

False如果連接句柄未連接,True 則返回,否則返回。

關貿總協定服務器

GATT 服務器有一組註冊服務。每個服務可能包含特性,每個特性都有一個值。特徵還可以包含描述符,描述符本身具有值。

這些值存儲在本地,並由服務註冊期間生成的“值句柄”訪問。它們也可以從遠程客戶端設備讀取或寫入。此外,服務器可以通過連接句柄向連接的客戶端“通知”特徵。

處於中央或外圍角色的設備可以充當 GATT 服務器,但在大多數情況下,外圍設備充當服務器更爲常見。

特徵和描述符的默認最大大小爲 20 字節。客戶寫給他們的任何東西都將被截斷到這個長度。但是,任何本地寫入都會增加最大大小,因此如果您希望允許從客戶端對給定特性進行更大的寫入,請 gatts_write在註冊後使用 。例如 。 gatts_write(char_handle, bytes(100)).

BLE.gatts_register_services(services_definition, /)

使用指定的服務配置服務器,替換任何現有服務。

services_definition是列表服務,其中每個服務是包含UUID和列表的兩個元素的元組的特性。

每個特性都是一個包含 UUID、標誌值和可選的描述符列表的二或三元素元組 。

每個描述符都是一個包含 UUID 和標誌 值的兩元素元組。

這些標誌是下面定義的標誌的按位或組合。這些設置了特徵(或描述符)的行爲以及安全和隱私要求。

返回值是一個元組列表(每個服務一個元素)(每個元素是一個值句柄)。特徵和描述符句柄按照定義的順序平展爲同一個元組。

以下示例註冊了兩個服務(Heart Rate 和 Nordic UART):

HR_UUID = bluetooth.UUID(0x180D)
HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
HR_SERVICE = (HR_UUID, (HR_CHAR,),)
UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
SERVICES = (HR_SERVICE, UART_SERVICE,)
( (hr,), (tx, rx,), ) = bt.gatts_register_services(SERVICES)

這三個值手柄(hr, tx, rx)可與使用 gatts_read, gatts_write, gatts_notifygatts_indicate

注意: 在註冊服務之前必須停止廣告。

特徵和描述符的可用標誌是:

from micropython import const
_FLAG_BROADCAST = const(0x0001)
_FLAG_READ = const(0x0002)
_FLAG_WRITE_NO_RESPONSE = const(0x0004)
_FLAG_WRITE = const(0x0008)
_FLAG_NOTIFY = const(0x0010)
_FLAG_INDICATE = const(0x0020)
_FLAG_AUTHENTICATED_SIGNED_WRITE = const(0x0040)

_FLAG_AUX_WRITE = const(0x0100)
_FLAG_READ_ENCRYPTED = const(0x0200)
_FLAG_READ_AUTHENTICATED = const(0x0400)
_FLAG_READ_AUTHORIZED = const(0x0800)
_FLAG_WRITE_ENCRYPTED = const(0x1000)
_FLAG_WRITE_AUTHENTICATED = const(0x2000)
_FLAG_WRITE_AUTHORIZED = const(0x4000)

至於上面的 IRQ,任何需要的常量都應該添加到你的 Python 代碼中。

BLE.gatts_read(value_handle, /)

讀取此句柄的本地值(已由 gatts_write 遠程客戶端寫入或由遠程客戶端寫入 )。

BLE.gatts_write(value_handle, data, send_update=False, /)

寫入此句柄的本地值,客戶端可以讀取該值。

如果send_update是True,則任何訂閱的客戶端都將收到有關此寫入的通知(或指示,具體取決於他們訂閱的內容和特徵支持的操作)。

BLE.gatts_notify(conn_handle, value_handle, data=None, /)

向連接的客戶端發送通知請求。

如果data不是None,則該值將作爲通知的一部分發送給客戶端。本地值不會被修改。

否則,如果data是None,則將發送當前本地值(使用 設置gatts_write) 。

注意: 無論客戶端對此特性的訂閱狀態如何,都會發送通知。

BLE.gatts_indicate(conn_handle, value_handle, /)

向連接的客戶端發送包含特徵當前值的指示請求。

在確認(或失敗,例如超時)時, _IRQ_GATTS_INDICATE_DONE將引發該 事件。

注意: 無論客戶端對此特性的訂閱狀態如何,都會發送指示。

BLE.gatts_set_buffer(value_handle, len, append=False, /)

以字節爲單位設置值的內部緩衝區大小。這將限制可以接收的最大可能寫入。默認值爲 20。

設置append toTrue 將使所有遠程寫入附加到當前值,而不是替換當前值。在大多數LEN字節可以用這種方式進行緩衝。當您使用 時gatts_read,讀取後該值將被清除。在實現類似 Nordic UART 服務之類的功能時,此功能很有用。

關貿總協定客戶端

GATT 客戶端可以發現和讀取/寫入遠程 GATT 服務器上的特性。

中央角色設備充當 GATT 客戶端更爲常見,但是外圍設備也可以充當客戶端,以發現有關已連接到它的中央角色的信息(例如,從設備信息服務)。

BLE.gattc_discover_services(conn_handle, uuid=None, /)

查詢已連接服務器的服務。

(可選)指定服務uuid以僅查詢該服務。

對於發現的每個服務, _IRQ_GATTC_SERVICE_RESULT 將引發事件,然後是_IRQ_GATTC_SERVICE_DONE 完成。

BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle, uuid=None, /)

查詢連接的服務器以獲取指定範圍內的特徵。

可選)指定一個特徵uuid以僅查詢該特徵。

您可以使用start_handle=1, end_handle=0xffff 在任何服務中搜索特徵。

對於發現的每個特徵, _IRQ_GATTC_CHARACTERISTIC_RESULT 將引發事件,然後是 _IRQ_GATTC_CHARACTERISTIC_DONE 完成。

BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle, /)

向連接的服務器查詢指定範圍內的描述符。

對於發現的每個描述符,_IRQ_GATTC_DESCRIPTOR_RESULT將引發事件,然後_IRQ_GATTC_DESCRIPTOR_DONE在完成時引發。

BLE.gattc_read(conn_handle, value_handle, /)

爲指定的特徵或描述符句柄向連接的服務器發出遠程讀取。

當值可用時,_IRQ_GATTC_READ_RESULT 將引發該事件。此外,_IRQ_GATTC_READ_DONE 將提高。

BLE.gattc_write(conn_handle, value_handle, data, mode=0, /)

爲指定的特徵或描述符句柄向連接的服務器發出遠程寫入。

參數mode指定寫入行爲,當前支持的值爲:

  • mode=0(默認)是無響應寫入:寫入將發送到遠程服務器,但不會返回確認,也不會引發任何事件。

  • mode=1是 write-with-response:請求遠程服務器發送它收到數據的響應/確認。

如果收到來自遠程服務器的響應, _IRQ_GATTC_WRITE_DONE則將引發該 事件。

BLE.gattc_exchange_mtu(conn_handle, /)

使用首選 MTU 集啓動與連接的服務器的 MTU 交換BLE.config(mtu=value).

_IRQ_MTU_EXCHANGED 當 MTU 交換完成時將引發該事件。

注意: MTU 交換通常由中央發起。在中心角色中使用 BlueKitchen 堆棧時,它不支持啓動 MTU 交換的遠程外圍設備。NimBLE 適用於這兩個角色。

L2CAP 面向連接的通道

此功能允許在兩個 BLE 設備之間進行類似套接字的數據交換。一旦設備通過 GAP 連接,任何一個設備都可以偵聽另一個設備連接到數字 PSM(協議/服務多路複用器)。

注意: 目前僅在 STM32 和 Unix(非 ESP32)上使用 NimBLE 堆棧時才支持此功能。在給定的時間可能只有一個 L2CAP 通道處於活動狀態(即您在收聽時無法連接)。

活動 L2CAP 通道由建立它們的連接句柄和 CID(通道 ID)標識。

面向連接的通道具有內置的基於信用的流量控制。與設備協商共享 MTU 的 ATT 不同,偵聽設備和連接設備都設置了一個獨立的 MTU,該 MTU 限制了遠程設備在完全消耗之前可以發送的最大未完成數據量l2cap_recvinto

BLE.l2cap_listen(psm, mtu, /)

在本地 MTU 設置爲mtu 的情況下,開始偵聽指定psm上的傳入 L2CAP 通道請求。

當遠程設備發起連接時, _IRQ_L2CAP_ACCEPT 將引發該事件,這使偵聽服務器有機會拒絕傳入連接(通過返回非零整數)。

一旦連接被接受,_IRQ_L2CAP_CONNECT 將引發該事件,允許服務器獲取通道 ID (CID) 以及本地和遠程 MTU。

注意:目前無法停止收聽。

BLE.l2cap_connect(conn_handle, psm, mtu, /)

連接到指定 psm上的偵聽對等方,並將本地 MTU 設置爲mtu

成功連接後,_IRQ_L2CAP_CONNECT將引發該事件,允許客戶端獲取 CID 以及本地和遠程(對等)MTU。

不成功的連接將引發 _IRQ_L2CAP_DISCONNECT具有非零狀態的事件。

BLE.l2cap_disconnect(conn_handle, cid, /)

使用指定的conn_handle和 cid斷開活動的 L2CAP 通道。

BLE.l2cap_send(conn_handle, cid, buf, /)

在由conn_handle和cid標識的 L2CAP 通道上發送指定的buf(必須支持緩衝區協議)。

指定的緩衝區不能大於遠程(對等)MTU,並且不能超過本地 MTU 大小的兩倍。

False 如果通道現在“停滯”,這將返回,這意味着 l2cap_send_IRQ_L2CAP_SEND_READY 接收到事件之前不得再次調用 它(這將在遠程設備授予更多信用時發生,通常是在它接收並處理數據之後)。

BLE.l2cap_recvinto(conn_handle, cid, buf, /)

從指定的conn_handle和cid接收數據到提供的 buf(必須支持緩衝區協議,例如 bytearray 或 memoryview)。

返回從通道讀取的字節數。

如果buf爲 None,則返回可用字節數。

注意: 收到 _IRQ_L2CAP_RECV事件後,應用程序應繼續調用,l2cap_recvinto 直到接收緩衝區中沒有可用字節爲止(通常達到遠程(對等)MTU 的大小)。

在接收緩衝區爲空之前,遠程設備將不會被授予更多的信道信​​用,並且將無法再發送任何數據。

配對和綁定

配對允許通過祕密交換對連接進行加密和身份驗證(通過密鑰身份驗證提供可選的 MITM 保護)。

綁定是將這些祕密存儲到非易失性存儲中的過程。綁定後,設備能夠根據存儲的身份解析密鑰 (IRK) 解析來自另一設備的可解析私有地址 (RPA)。要支持綁定,應用程序必須實現_IRQ_GET_SECRET_IRQ_SET_SECRET事件。

注意: 目前僅在 STM32 和 Unix(非 ESP32)上使用 NimBLE 堆棧時才支持此功能。

BLE.gap_pair(conn_handle, /)

啓動與遠程設備的配對。

調用此之前,請確保 io, mitm, le_secure, 和 bond 配置選項(通過設置config)。

成功配對後, _IRQ_ENCRYPTION_UPDATE 將引發該事件。

BLE.gap_passkey(conn_handle, action, passkey, /)

響應_IRQ_PASSKEY_ACTION 指定的conn_handle 和action的事件。

該密鑰是一個數值,將取決於在 行動(這將取決於什麼樣的I / O能力已經設置):

  • 當操作爲 時_PASSKEY_ACTION_INPUT,應用程序應提示用戶輸入遠程設備上顯示的密碼。

  • 當操作爲 時_PASSKEY_ACTION_DISPLAY,應用程序應生成一個隨機的 6 位密碼並將其顯示給用戶。

  • 當操作爲 時_PASSKEY_ACTION_NUMERIC_COMPARISON,應用程序應顯示_IRQ_PASSKEY_ACTION事件中提供的密鑰,然後以 0 (取消配對)或 1(接受配對)響應。

類 UUID

構造函數

class bluetooth.UUID(value, /)

創建具有指定值的 UUID 實例。

該值可以是:

  • 一個 16 位整數。例如 0x2908.

  • 一個 128 位的 UUID 字符串。例如'6E400001-B5A3-F393-E0A9-E50E24DCCA9E'.

以下爲以前版本:

一、固件更新

從下面這個鏈接可以下載到最新的MicroPython的版本:

更新過程請參考專欄中《EPS32 MicroPython 開發入門》這篇文章
https://zhuanlan.zhihu.com/p/55366938
下面這個鏈接是GitHub上MicroPython Bluetooth分支的連接。在這裏你可以看到源碼,如果你會自己編譯的話,可以通過源碼編譯得到帶有藍牙功能的固件。
https://github.com/icetomcat/micropython/tree/bluetooth

二、例程

這裏放一個簡單藍牙收發的例程。

from machine import Pin
import os
import bluetooth
bt = bluetooth.Bluetooth()
bt.active(1)
bt.advertise(100, 'ESP32_BLE_01')

LED=Pin(21, Pin.OUT) #port 21 is used for led
LED.value(0)

tx = bluetooth.Characteristic('6E400002-B5A3-F393-E0A9-E50E24DCCA9E', bluetooth.FLAG_READ|bluetooth.FLAG_NOTIFY)
rx = bluetooth.Characteristic('6E400003-B5A3-F393-E0A9-E50E24DCCA9E', bluetooth.FLAG_WRITE)
s = bt.add_service('6E400001-B5A3-F393-E0A9-E50E24DCCA9E', [tx, rx])

def callback(char, data):
    print("Get command:",data)
    if(data == b'0'):      
      LED.value(0)
      print("LED OFF")
      tx.write("LED is OFF Now!\n\r")
    if(data == b'1'):
      LED.value(1)
      print("LED OFF")
      tx.write("LED is ON Now!\n\r")
    
rx.on_update(callback)


整個例程中創建了一個藍牙的service,包含一個監聽讀取的Characteristic和一個寫入的Characteristic。當收到字節‘1’的時候點亮LED,收到‘0’的時候關閉LED,並向控制檯返回LED的狀態。
當藍牙程序運行後,可以在手機上使用藍牙調試軟件連接名爲“ESP32_BLE_01”的藍牙設備,在發送框裏輸入0或者1。我使用是FreeBlue,在appstore上面直接下載。調試界面如下:
 

調試界面

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章