iT邦幫忙

2025 iThome 鐵人賽

DAY 17
0

昨天咱們已經在 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_handleCharacteristic 類別的成員變數。當然咱們可以在 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 的變化了。


上一篇
Day 16 - CCCD Monitor - 為 MicroPython 藍牙新增功能吧!
下一篇
Day 18 - Event Bus - 讓 IDD Status Changed 優雅地執行吧
系列文
以MicroPython在ESP32上實作Insulin Delivery Service31
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言