iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0

在實作 IDD Features 時,咱們在 Config 類別裡新增一個成員變數 is_e2e_protection_supported,它表示 IDS 伺服器是否支援 E2E-Protection,今天本喵就來說明它是什麼。

1. 用途

E2E-Protection (End-to-End) 是利用計數器和 CRC 來保證資料的完整性和明確性,由 IDD Characteristic 的 Flags 欄位的 bit 0 來指示系統是否支援。E2E-Protection 基本是「全有或全無」的設定,除非特別說明,否則一啟用,所有 characteristics 都必須支援;若關閉,所有 characteristics 也必須移除其相關欄位。

2. E2E-Counter

E2E-Counter 的特性如下:

  • 資料型態為 uint8
  • 若 E2E-Protection 啟用,其有效值是 1~255;否則為 0
  • 分為傳送 (Read、Notify、Indicate) 和接收 (Write) 兩種
  • 每次連線,傳送的 E2E-Counter 都由 1 開始
  • 每次連線,接收的 E2E-Counter 都由 255 開始
  • 每次傳送資料與接收資料後,相應的 E2E-Counter 須遞增 1
  • 當 E2E-Counter 到達 255 後,下一次的 E2E-Counter 須為 1
  • 除非特別說明,否則所有 characteristics 都有各自的 E2E-Counter
  • 滿足
    目前接收的 E2E-Counter - (上次接收的 E2E-Counter mod 255) = 1

由以上描述,咱們應該可以很清楚如何實作:

class Counter:
    def __init__(self, init_value):
        self.value = init_value
        self._init_value = init_value

    def reset(self):
        self.value = self._init_value

    def inc_counter(self):
        counter = self.value + 1
        self.value = (counter >> 8) + (counter & 0xFF)

class TxCounter(Counter):
    def __init__(self):
        super().__init__(1)

class RxCounter(Counter):
    def __init__(self):
        super().__init__(255)

    def check(self, received_counter: int) -> bool:
        return received_counter - (self.value % 255) == 1

3. E2E-CRC

E2E-CRC 是使用 CRC-CCITT,其生成多項式是 g(D) = D^16 + D^12 + D^5 + 1,即 0x1021,CRC 初始值是 0xFFFF。依照此定義所實作的程式如下:

def add_int8(self, data: int):
    for bit in range(8):
        # 目前要處理的位元
        in_bit = (data >> bit) & 1

        msb = (self.crc >> 15) & 1
        self.crc <<= 1
        if in_bit ^ msb:
            self.crc ^= 0x1021

    self.crc &= 0xFFFF

不過這還不完整,因為 IDS 規格書要求算出的 CRC 必須將位元由高到低顛倒,也就是:

  • 算出的 CRC: b15, b14, b13, ... b2, b1, b0
  • 需要的 CRC: b0, b1, b2, ... b13, b14, b15

為了省下顛倒的動作,咱們將生成多項式先顛倒:

def add_int8(self, data: int):
    for bit in range(8):
        # 目前要處理的位元
        in_bit = (data >> bit) & 1
        
        msb = self.crc & 1
        self.crc >>= 1

        if in_bit ^ msb:
            # 0x8408 是將 0x1021 顛倒後的值
            self.crc ^= 0x8408

有 CRC 相關程式經驗的看官說不定會說:「既然那麼在乎效率,怎不使用查表法,還要一個一個位元計算呢?」
看官說的極是啊!
其實本喵一開始也是使用查表法的,但好景不長 ...
不久就遇到了記憶體不足、碎片化造成的怪現象,本喵只好想方設法省記憶體,最後屠刀就動到 CRC 上了
。゚ヽ(゚´Д)ノ゚。`

這裡額外說一下查表法大致會消耗多少記憶體,若咱們用 tuple 來儲存:

  • 一個 tuple 需要 40 bytes
  • 每個項目需要 8 bytes,共有 256 個項目
  • 總共要花費:40 + 8*256 = 2088 bytes

要超過 2 KB 的空間來儲存表格 ... 這 ... 實在太多了 ... 本喵投降
(。-_-。)


欲知如何與 IDD Features 整合,且聽下回分曉~


上一篇
Day 10 - 藍牙低功耗環境下的浮點數 SFLOAT
下一篇
Day 12 - 為 IDD Features 加上 E2E-Protection
系列文
以MicroPython在ESP32上實作Insulin Delivery Service31
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言