昨天咱們已經在 MicroPython 的 bluetooth
模組增加了 CCCD 的中斷事件,那麼使用端為了監聽 CCCD 何時被改變,就必須處理 MP_BLUETOOTH_IRQ_SUBSCRIBE
,且因這對於所有 characteristic 都是一樣的流程,所以應該由一個地方統一處理。以 Indicate 為例,當然就是由 IndicateMixin
負責:
class IndicateMixin:
def __init__(self):
self._ind_enabled = False
def on_cccd(self, conn_handle, notify, indicate):
self._ind_enabled = indicate
def send_data(self, conn_handle: int | None, value_handle: int, arg):
if conn_handle is not None and self._ind_enabled:
data = self._build_indicate_payload(arg)
self._after_build_tx_data()
if data is not None:
ble.stack.indicate(conn_handle, value_handle, data)
return True
return False
IndicateMixin
裡新增一個成員變數 _ind_enabled
。send_data()
裡新增一個判斷條件,確保 CCCD 有正確設定時才運作。on_cccd()
,讓處理 MP_BLUETOOTH_IRQ_SUBSCRIBE
的函數來呼叫,如以下程式片段:class IdsServer(ble.stack.Server):
def _ble_isr(self, event, data):
if event == _IRQ_SUBSCRIBE:
conn_handle, value_handle, reason, notify, indicate = data
for c in self._ids.chars:
if value_handle == c.value_handle:
c.on_cccd(conn_handle, notify, indicate)
return
當然,on_cccd()
傳入了些沒用到的參數,使用者可以自行設計成需要的形式。會這樣設計只是為了省下辨別是 Indicate 或是 Notify 的程式碼。
看官也許會疑惑,為什麼不像 E2ETxMixin
一樣,有自己的 ISR 來處理 BLE 中斷,而是讓其他模組來處理呢?比如:
class IndicateMixin:
def __init__(self):
self._ind_enabled = False
ble.stack.register_irq_handler(self._isr_ind)
def _isr_ind(self, event, data):
if event == _IRQ_SUBSCRIBE:
conn_handle, value_handle, reason, notify, indicate = data
if value_handle == self.value_handle:
self._ind_enabled = indicate
return
是的!咱們完全可以這樣做!但這個寫法的問題在於 self.value_handle
的引用(可能也有看官考慮到了記憶體或執行效能等)。因為 IndicateMixin
並沒有 value_handle
這個成員變數,value_handle
是 Characteristic
類別的成員變數。當然咱們可以在 IndicateMixin
裡這樣做:
class IndicateMixin:
@property
def value_handle(self):
pass
class SomeChar(IndicateMixin) :
def __init__(self, value_handle):
self._value_handle = value_handle
@property
def value_handle(self):
return self._value_handle
這樣他人就能清楚知道,繼承 IndicateMixin
的類別必須提供 value_handle
。因使用 property
會額外花費一些記憶體和損失一點效能,所以可能有人會選擇用註解來標明,然後繼續選擇方案 2 的 IndicateMixin
。無論哪種方法,都有各自的考量。
另外要記得,繼承 IndicateMixin
的類別需要初始化 IndicateMixin
:
class IddStatusChanged(
ble.mixin.IndicateMixin, ble.mixin.ReadMixin, ble.stack.Characteristic
):
def __init__(self, config: config.Config):
ble.stack.Characteristic.__init__(self, 0x2B20, read=True, indicate=True)
ble.mixin.IndicateMixin.__init__(self)
如此,IDS server 就可以偵測到 IDD Status Changed 的 CCCD 的變化了。