Skip to content

序言 ​

  • 藍牙通信的編程非常貼近物理層面,越是接近物理世界,就越遠離那些完美的抽象編程概念。 處理這些藍牙指令確實非常繁瑣,而所謂的 SesameSDK,正是將這些複雜的操作良好實現,並封裝成一個開源庫以便於使用。
  • 這整套藍牙協議是 CANDY HOUSE 三代工程師超過十年(2014〜2016、2017〜2019、2020〜)辛勤工作和智慧的結晶,它的免費開源並不意味著"便宜無好貨",相反,我們希望這個我們認為接近完美的作品能作為一套「活」的藍牙協議,得以在世界範圍內傳承中心思想和發揚光大。
  • 藍牙操作涉及兩個主要部分:藍牙廣播(BLE Advertising) 和 藍牙數據傳(BLE Data Transmission),接下來將進行詳細介紹。

藍牙廣播(BLE Advertising) ​

第一行:属于蓝牙规范,还是厂家定义

第二行:序号

第三行:十六进制值

第四行:内容描述

  • Bluetooth LE 4.0 advertising 長度最長為 31 Bytes.
  • 黃底或黃底標記表示 CANDY HOUSE 定義的協議。
  • 其餘白底皆是依 Bluetooth SIG 規範要求的義務宣告。

Bluetooth SIG 官方 IDs ​

DecimalHex
BLE Service UUID648970xFD81
Company ID13700x055A

機種 Product Model ​

  • 藍芽廣播裡的第 8th 9th Bytes 表達機種
8th 9th Bytes機種
0Sesame 3
1WiFi Module 2
2Sesame Bot 1
3Sesame Bike 1
4Sesame 4
5Sesame 5
6Sesame Bike 2
7Sesame 5 Pro
8Open Sensor
9Sesame Touch 1 Pro
10Sesame Touch 1
11Ble Connector
13Hub 3
14Sesame Remote
15Remote Nano
16Sesame 5 Usa
17Sesame Bot 2
18F2
19F1
20Bluetooth半導體光繼電器

設備狀態 Device Status ​

  • 藍芽廣播裡的第 10th Byte 切分成 8 個 bits, 每個 bit 表達設備的各種狀態
第10th Byte 的 8 個 bits設備狀態
01
1st bitUnregisteredRegistered
2nd bit
3rd bit
4th bit
5th bit
6th bit
7th bit
8th bit

遵守  Accessory Design Guidelines for Apple Devices ​

鼓勵用 nRF Connect app 熟悉 Sesame BLE adv ​

nrfConnect_Demo_SesameOS3_adv.svg


藍牙數據透傳(BLE Data Transmission) ​

单个SEG的定义:

  • 受到 TCP/IP 模型的啟發, 我們將藍芽通信分成 4 層。
  • 明文的任意格式之信息 經過 加密、切割後 透過藍芽GATT收發。4 個狀態分別稱 Application Layer, Security Layer, Transport Layer, Gatt Bearer Layer.
  • 這套切割信息的作法, 可以傳輸無限長度的任意格式之信息。

傳輸協議 Request, Response and Publish ​

  • 受到 RESTful API 增刪查改數據庫 POST 方法的啟發, 我們基於 BLE write without response 封裝了 1 命令方法:
    ・Request
  • 受到 RESTful API async/sync 的啟發, 我們基於BLE notify封裝成 2 個收 Callback 方法:
    ・Response
    ・Publish
  • Response方法與 BLE write without response 同名卻完全無關, 造成困擾我們十分抱歉。
  • 每個 Request 都會立即得到一次 Response反饋。需要執行時間才能得知執行結果的信息以 Publish反饋。

只用到 2 個 BLE Characteristics 收發藍芽信息 ​

  • 透過 Request, Response, Publish 方法收發藍芽信息時, 只會用到 Sesame BLE Service(0xFD81) 的 2 個 BLE Characteristics.
  • UUID 16860002-a5ae-9856-b6d3-dbb4c676993e 的 Characteristics 是用來 write BLE commands without response
  • UUID 16860003-a5ae-9856-b6d3-dbb4c676993e 的 Characteristics 是用來 收 BLE Notify

nrfConnect_Demo_Sesame_BLE_Characteristics.svg

Application Layer 裡的業務內容 ​

1. Sesame 「註冊」活動圖 ​

2. Sesame「註冊」循序圖 ​

3. Sesame「登录」循序圖 ​

4. Sesame「发送命令」循序圖 ​

5. 對外封裝後的手機 SesameSDK 狀態機 State Machine ​

6. Sesame 時序圖 ​

手機藍牙跟 Sesame 兩種溝通方式 ​

  • 第一種是 Publish,Sesame 主動將訊息傳送給手機
  • 第二種是 Request,手機要 Sesame 幹活,Sesame 回覆 Response應答

1. Publish【Sesame->手机】 ​

当 NotifyEnable = True 的時候啓用,Sesame 主動將訂閱訊息推送給手機,如「應答指令对照表」 81_機械狀態。

字节索引012 ~ N
Datatypeitem_codepayload
說明推送類型指令編號送給手機的資料
c

typedef struct {
    uint8_t type;               /// 推送類型 : publish
    uint8_t item_code;          /// 指令 item_code
    union ss5_payload payload;  /// 送給手機的資料
} ss5_publish;

2. Request【手机->Sesame】 ​

  • Request即是手機要 Sesame 幹活,Sesame 回覆接收狀態,如「請求指令對照表」 82_關鎖
  • 爲了避免混亂,需要一個保證順序的队列,支持同步串行发送,BLE 作為下位機的時候,就是讀寫各一個特徵值Characteristics,讀寫的操作方法是发送协议的內容
字节索引01 ~ N
Dataitem_codepayload
說明指令編號送給 Sesame 的資料
c

typedef struct {
    uint8_t item_code;              /// 指令(詳見 item_code)
    union ss5_payload payload;      /// 送給 Sesame5 的資料
} ss5_request;
c
typedef enum {
    ITEM_CODE_NONE = 0,
    ITEM_CODE_REGISTRATION = 1,
    ITEM_CODE_LOGIN = 2,
    ITEM_CODE_HISTORY = 4,
    ITEM_CODE_VERSION_DETAIL = 5,
    ITEM_CODE_TIME = 8,
    ITEM_CODE_AUTOLOCK = 11,
    ITEM_CODE_INITIAL = 14,
    ITEM_CODE_IRER = 15,
    ITEM_CODE_MAGNET = 17,
    ITEM_CODE_MECH_SETTING = 80,
    ITEM_CODE_MECH_STATUS = 81,
    ITEM_CODE_LOCK = 82,
    ITEM_CODE_UNLOCK = 83,

	......
	
    SSM3_ITEM_RESET = 104
} item_code_e;

2.1. Response 應答指令資料格式 ​

字节索引0123 ~ N
Datatypeitem_coderespayload
說明推送類型指令編號命令處裡狀態送給手機的資料
c
typedef struct {
    uint8_t type;                   /// 推送類型:Response
    uint8_t item_code;              /// 指令(item_code)
    uint8_t res;                    /// 命令處理狀態 (都回 success)
    union ss5_payload payload;      /// 送給手機的資料
} ss5_response;

Result 處理狀態 ​

c
typedef enum {
    CMD_RESULT_SUCCESS = 0,         /// 手機发送命令给 Sesame5 會直接回 success
    CMD_RESULT_INVALID_ACTION = 9,  /// 已經註冊還下註冊命令
} cmd_result_e;

2.2 Type 推送類型 ​

標記符爲0x07時爲普通應答response,0x08爲主動推送publish

c
typedef enum {
    OP_CODE_RESPONSE = 0x07,
    OP_CODE_PUBLISH = 0x08,
} op_code_e;

請求指令對照表 ​

字节索引01 ~ NN+
Dataitem_codepayloadothers
說明指令編號送給 Sesame 的資料其它資料
注册11-64【publicKeyA】65-68【timestamp】
登入21-4【ccmkeyA】
历史41【is peek】ispeek=true 不删除
版本5none
更新时间81-4【timestamp】
自动上锁111-2【秒数】
角度校正17none
機械設定801-5【autolock+开关锁角度】
機械狀態81none无响应
關鎖821-7【历史标签】
開鎖831-7【历史标签】
開門90none
關門91none
自動上鎖92none
重置104none
添加芝麻「touch」1011-16【Device Name】17-32【secretKey】
删除芝麻「touch」1031-16【Device Name】
更新卡片「touch」1070【Card ID Len】1-id_len【CardID】CardIDLen+1【CardNameLen】etc
删除卡片「touch」1081-16【Card ID】
获取卡片「touch」113none
卡片模式114none
指纹更新1150【FingerIDLen】1-Len【FingerID】】
指纹删除1161【FingerID】
指纹获取117none
指纹模式121none
指纹模式设置122none
密码更新1230【PwIDlen】1-pwLen【Password】】
密码删除124none
密码获取125none
获取密码模式129none
密码模式设置1300 验证 1 新增

應答指令对照表 ​

字节索引0123 ~ NN+N++
Datatypeitem_coderespayloadpayloadpayload
說明推送類型指令編號命令處裡狀態送給手機的資料 1送給手機的資料 2送給手機的資料 3
初始化81400-3【random code】none
注册7100-6【mechStatus】7-12【mechSetting】】13~76【publicKeyS】】
登入7200-3【timestamp】
历史7400-3【id】】4【type】5-8【timestamp】
版本7500-11【version】
機械設定88000-1【lock】2-3【unlock】】4-5【autolock_second】
機械狀態88100-3【random code】
推送芝麻钥匙810200-21【ssm0-name】22【ssm0-status】23-44【ssm1_name】etc
获取芝麻钥匙710900-3【CardID】
获取指纹711700-3【FingerID】
指纹模式获取812100-3【random code】

安全层 Security Layer ​

安全層 Security Layer 是 手機藍牙 与 Sesame5 間資料透傳實作的加密傳輸協定。

1. 4 字节 Random code ​

搜尋到 芝麻設備的藍牙 並連線開啟特徵值爲0xFD81的notify,這時 芝麻設備 主動 publish 發送 4Bytes Random Code/Session Token。(詳見 14_初始化)

2. 设备密钥 Device secret ​

註冊時,APP 及 Sesame5 雙方都會透過 secp256r1 產生 public key 及 private key,交換生成 Device secret。

  • publicKey : 64 Bytes
  • privateKey : 32 Bytes

secret : 32 Bytes

步驟 : ​

    1. sesame5 使用 secp256r1 產生 publicKeyS 及 privateKeyS
    1. APP 使用 secp256r1 產生 publicKeyA 及 privateKeyA
    1. 雙方交換 publicKey
    1. sesame5 使用 privateKeyS 及 publicKeyA 產生 secret S
    1. APP 使用 privateKeyA 及 publicKeyS 產生 secret A
    1. secret S 跟 secret A 相等
    1. 雙方將 secret 前 16 Bytes 作為 Device secret。

Token 用途 ​

    1. 登入驗證 (詳見 : 2_登入)
    1. 數據透傳時透過 aes_ccm 加密資料,使用 Token 作為加密時用到的 key。

產生方式 ​

以 AES_CMAC 演算法,使用 Device secret 對 4Bytes Random code 簽章,就能產生 Token。 device_secret :16 Bytes random_code :4 Bytes Token :16 Bytes

c

AES_CMAC(key:device_secret, input:random_code, input_size:4, output:Token);

3. 資料加密与解密 ​

加密参数:初始化向量(CCM_IV) ​

    1. count :8 个字节,開機初始化為 0,每做一次加密 count 計數加一
    1. nouse :1 个字节 默认值為 0
    1. random_code :4 个字节,跟設備連線後更新 random_code
c

typedef struct {
    int64_t count;///8 byte
    uint8_t nouse;///1 byte = 0
    uint8_t random_code[4];///4 byte
} CCM_IV;

aes_ccm 參數 ​

輸入:

    1. key :加密密钥,用于 AES-CCM 操作
    1. iv :初始化向量(CCM_IV)
    1. iv length :AES-CCM 使用 13 字节(104 位)的 IV
    1. add :附加认证数据
    1. add length :附加认证数据长度
    1. input :要加密的输入数据
    1. input length :要加密的数据长度
    1. tag length :用于存储认证标签(MAC)的缓冲区和标签的长度

輸出:

    1. tag : ccm_tag
    1. output : ccm_ecrrypt_data 加密後的資料

C 語言 加密範例 ​

c
aes_ccm_encrypt_and_tag(
                        key:ccm_key,
                        iv:CCM_IV,
                        iv_length:13,
                        add:0x00,
                        add_length:1,
                        input:p_data,
                        add_length:length,
                        output:ccm_ecrrypt_data,
                        tag:ccm_tag,
                        tag_length:4
                        );
CCM_IV.count ++;

AES 爲對稱加密算法,方法名稱爲 aes_ccm_auth_decrypt 參數和加密相互對應

C 語言 解密範例 ​

c
aes_ccm_auth_decrypt(
                    key:ccm_key,
                    iv:CCM_IV,
                    iv_length:13,
                    add:0x00,
                    add_length:1,
                    input:ccm_ecrrypt_data,
                    add_length:length,
                    output:ans_data,
                    tag:ccm_tag, tag_length:4
                    );
CCM_IV.count ++;

注意

資料加解密使用的 CCM_IV 內有個 count 紀錄加密次數,若是加密與解密端的 count 計數不匹配會導致解密失敗。

Segment Layer ​

Segment Layer 要做兩件事情。

    1. Sesame5 與 手機通訊時,限制封包最大長度 20Bytes,送長訊息需要對資料進行拆解,傳到接收端再組合。
    1. 標記傳送的資料是否有加密,三種長度的明文密文封包方式。

1. 標記對照表 ​

Segment Layer 在封包頭中的標記此封包是否被切割或加密,接收端可以按照標記做相應的處理,將封包還原成原始資料。以下為 Segment Layer 標記對照表。

Segment Layer 標記有 8bits,其中 bit 7 ~ 1 表示是否為結束封包及資料是否加密,bit 0 表示該封包是否為一筆資料的起始封包。

bit 7 ~ 1結束bit開始
b0000000沒結束0非開始
b0000000沒結束1開始
b0000001明文結束0非開始
b0000001明文結束1開始
b0000010加密結束0非開始
b0000010加密結束1開始

標記以 16 進位解讀 ​

0 表示該封包不是開始封包也不是結束封包

1 表示該封包是開始封包不是結束封包

2 表示該封包不是開始封包是結束封包,並且組合起來的資料是明文

3 表示該封包是開始封包也是結束封包,並且資料是明文,資料沒有被切割,不需要組合

4 表示該封包不是開始封包是結束封包,並且組合起來的資料是密文

5 表示該封包是開始封包也是結束封包,並且資料是密文,資料沒有被切割,不需要組合

標記開始結束
0x00非開始沒結束
0x01開始沒結束
0x02非開始明文結束
0x03開始明文結束
0x04非開始加密結束
0x05開始加密結束

範例 ​

假設一個封包能放 4bytes 的資料,超過 4bytes 就需要進行分割(實際為 20bytes)。

2. 三種長度的明文密文封包方式 ​

2.1 短明文封包 ​

資料: A A B B

發送:

1. 收到資料 : A A B B
2. 資料 : A A B B
3. 加上封包頭 : 3 A A B B

接收:

4. 收到 : 3 A A B B
5. 去除封包頭還原資料 : A A B B

2.2 中明文封包 ​

資料: A A A A B B B B

發送:

1. 收到資料 : A A A A B B B B
2. 拆解資料 : A A A A
3. 加上封包頭 : 1 A A A A
6. 拆解資料 : B B B B
7. 加上封包頭 : 2 B B B B

接收:

4. 收到 : 1 A A A A
5. 判斷資料還沒送完,等下一個封包
8. 收到 : 2 B B B B
9. 資料接收完成,組合封包
10. 去除封包頭還原資料 : A A A A B B B B

2.3 長明文封包 ​

資料 : A A A A B B B B C C C C

發送:

1. 收到資料 : A A A A B B B B C C C C
2. 拆解資料 : A A A A
3. 加上封包頭 : 1 A A A A
6. 拆解資料 : B B B B
7. 加上封包頭 : 0 B B B B
10. 拆解資料 : C C C C
11. 加上封包頭 : 2 C C C C

接收:

4. 收到 : 1 A A A A
5. 判斷資料還沒送完,等下一個封包
8. 收到 : 0 B B B B
9. 判斷資料還沒送完,等下一個封包
12. 收到 : 2 C C C C
13. 資料接收完成,組合封包
14. 去除封包頭還原資料 : A A A A B B B B C C C C

2.4 短密文封包 ​

資料 : A A B B

資料加密後 : X X Y Y

發送:

1. 收到資料 : A A B B
2. 資料加密後 : X X Y Y
3. 加上封包頭 : 5 X X Y Y

接收:

4. 收到 : 5 X X Y Y
5. 去除封包頭還原資料 : X X Y Y
6. 解密 : A A B B

2.5 中密文封包 ​

資料: A A A A B B B B

資料加密後 : X X X X Y Y Y Y

發送:

1. 收到資料 : A A A A B B B B
2. 資料加密後 : X X X X Y Y Y Y
3. 拆解資料 : X X X X
4. 加上封包頭 : 1 X X X X
7. 拆解資料 : Y Y Y Y
8. 加上封包頭 : 4 Y Y Y Y

接收:

5. 收到 : 1 X X X X
6. 判斷資料還沒送完,等下一個封包
9. 收到 : 4 Y Y Y Y
10. 資料接收完成,組合封包
11. 去除封包頭還原資料 : X X X X Y Y Y Y
12. 資料解密 : A A A A B B B B

2.6 長密文封包 ​

資料 : A A A A B B B B C C C C

資料加密後 : X X X X Y Y Y Y Z Z Z Z

發送:

1. 收到資料 : A A A A B B B B C C C C
2. 資料加密後 : X X X X Y Y Y Y Z Z Z Z
3. 拆解資料 : X X X X
4. 加上封包頭 : 1 X X X X
7. 拆解資料 : Y Y Y Y
8. 加上封包頭 : 0 Y Y Y Y
11. 拆解資料 : Z Z Z Z
12. 加上封包頭 : 4 Z Z Z Z

接收:

5. 收到 : 1 X X X X
6. 判斷資料還沒送完,等下一個封包
9. 收到 : 0 Y Y Y Y
10. 判斷資料還沒送完,等下一個封包
13. 收到 : 4 Z Z Z Z
14. 資料接收完成,組合封包
15. 去除封包頭還原資料 : X X X X Y Y Y Y Z Z Z Z
16. 資料解密 : A A A A B B B B C C C C

注意

資料經過 AES_CCM 加密後除了密文外還會產生 4Bytes 的 ccm_tag 用於解密,密文及 ccm_tag 需要一起傳給接收端,因此加密後的資料會增加 4Bytes,詳見 security_Layer 說明。