Androidiot蓝牙配网简单封装

342 阅读7分钟

Androidiot蓝牙配网简单封装

1.蓝牙配网流程图:

image.png

2.蓝牙配网时序图:

image.png

Iot_ BLE配网流程:

  • APP扫描BLE设备,过滤名称前缀IPC_XX_
  • 进入配网页,关闭tange、ali其他配网分支
  • APP调用preAdd,云端生成会话,拿到addToken
  • BLE下发wifisetup报文(SSID+密码)
  • 设备BLE返回0x04(WiFi配置报文已收到)
  • BLE下发bindinfo(addToken/userId/deviceMac/homeId等)
  • 设备返回0x10成功 /0x11失败(bindinfo接收应答)
  • 设备连接WiFi,携带addToken主动向IOT云端注册绑定
  • APP轮询pollAddResult(addToken)查询绑定结果
  • 云端返回device_id,配网成功

3.iot配网管理整体架构:

image.png

4.实现步骤:

用户点击开始配网   
     ↓
进入Iot BLE Flow(暂停 Tuya 旧 BLE)   
     ↓
BLE 连接设备   
     ↓
发送 wifisetup(78 字节)   
     ↓
收到 notify ACK = 0x04   
     ↓
【Bind Phase 开始】   
     ↓
调用 preAdd → 获取 add_token   
     ↓
发送 bindinfo(包含 add_token / uid / deviceId / homeId)   
     ↓
收到 notify ACK = 0x10 / 0x11   
     ↓
开始 pollAddResult(add_token)   
     ↓
轮询成功 → 返回 device_id   
     ↓
UI 显示“添加成功”•

5.iot设备搜索:

5.1 设备搜索流程图:

image.png

5.2 核心通信流程图:

CoolKit 设备通信流程

flowchart TD
A([connectGatt<br/>ssid, psd, deviceId]) --> B[HTTP 获取 secretKey<br/>GET /v2/device/fdbaseinfo]
B --> C[connectGatt<br/>autoConnect=false<br/>TRANSPORT_LE + PHY_LE_2M]
C --> D{onConnectionStateChange}
D -->|STATE_CONNECTED| E[gatt.discoverServices]
D -->|STATE_DISCONNECTED| F{reConnectTime < 3<br/>且未成功?}
F -->|是| C
F -->|否| G[bleConnStatus = false<br/>结束]
E --> H[onServicesDiscovered<br/>requestMtu512]
H --> I{遍历特征值}
I -->|UUID_WRITE<br/>0000bbb0| J[缓存 writeCharacteristic<br/>延迟2s写入数据]
I -->|UUID_READ<br/>0000bbb1| K[缓存 readCharacteristic<br/>延迟1s开启 Notify]
​
J --> L[写入 AES加密UUID<br/>握手请求]
K --> M[等待设备上报]
​
M -->|0x01| N[解析 deviceCode<br/>回传 AESUUID_deviceCode]
N --> M
M -->|0x03| O{data[4] == 0x00?}
O -->|是| P[回写 {4,0,3,0,1,0,5}<br/>启动 WiFi 扫描轮询]
O -->|否| Q[握手失败<br/>bleConnStatus=false]P --> R[每5s下发扫描指令<br/>直至收到 0x06]
M -->|0x05| S[解析 SSID/BSSID/RSSI<br/>匹配目标 WiFi]
S --> T[构建 WiFi 连接请求包<br/>密码 AES 加密]
M -->|0x06| U[扫描结束<br/>发送 connWifiData]
​
M -->|0x08| V{WiFi 连接状态}
V -->|0x00| W[连接成功]
V -->|0x02| X[密码错误<br/>bleConnStatus=false]
V -->|其他| Y[连接失败<br/>bleConnStatus=false]
​
W --> Z[0x0A 上报设备信息<br/>deviceId / apikey / chipId]
Z --> AA[addDevice 注册到 CoolKit 云]
AA --> AB[下发调度服务器地址<br/>0x0B 指令]
AB --> AC[0x0C 配网完成<br/>status=0x00 成功]
AC --> AD[bleConnStatus = true<br/>close GATT]
​
style AA fill:#d4edda
style G fill:#f8d7da
style X fill:#f8d7da
style Y fill:#f8d7da

TanGe / IoT 设备通信流程

image.png

5.3 GATT 服务与特征值:

设备类型方向UUID属性说明
CoolKitApp → 设备0000bbb0-0000-1000-8000-00805f9b34fbWrite指令下发
CoolKit设备 → App0000bbb1-0000-1000-8000-00805f9b34fbRead / Notify状态与数据上报
TanGe/IoTApp → 设备00009999-0000-1000-8000-00805f9b34fbWriteWiFi 包 / 绑定信息写入
TanGe/IoT设备 → App00008888-0000-1000-8000-00805f9b34fbRead / NotifyACK / JSON 数据上报

5.5 扫描广播包解析(CoolKit PN 值)

偏移(相对 AD Type 段)长度内容字节序
+2 ~ +32 bytesPN 值小端(需反转读取)
+41 byte保留值-
+51 byte产品类型-

5.5 CoolKit 设备通信协议

通用包结构(设备上报与 App 下发均遵循):

[0]      cmd_id      // 指令标识
[1]      0x00        // 保留
[2]      len_low     // 后续数据长度(低字节)
[3]      len_high    // 后续数据长度(高字节)
[4..N]   payload     // 数据载荷
指令集
cmd_id方向指令Payload 说明
0x01设备→App握手响应[len(2B), AES(deviceCode)]
0x03设备→App握手确认[status: 0x00=成功]
0x05设备→AppWiFi 扫描结果[ssidLen, ssid..., bssid[6], rssi, isConnect]
0x06设备→AppWiFi 扫描结束无 payload
0x08设备→AppWiFi 连接状态[status] 0成功 1失败 2密码错 3无AP 4超时
0x0A设备→App设备身份信息[deviceIdLen, deviceId..., apikeyLen, apikey..., chipIdLen, chipId...]
0x0C设备→App配网完成状态[status: 0x00=成功]
0x0E设备→AppIP 获取结果[status: 0x00=成功]

5.6 App 下发关键指令示例

场景字节流说明
初始握手{0,0,(byte)appCodeLen,0} + AES(UUID)UUID 为随机生成,AES 加密
握手回传{2,0,(byte)secretDataLen,0} + AES(UUID_deviceCode)设备返回 deviceCode 后回传
触发 WiFi 扫描{4,0,3,0,1,0,5}固定指令,每 5s 轮询发送
WiFi 连接请求{7,0,dataLen,0, bssid[6], isConnect, timeout, pwdLen} + AES(pwd)匹配到目标 SSID 后发送
下发调度地址{11,0,(byte)(urlLen+2),0,0,(byte)urlLen} + urlBytesCoolKit 云注册成功后下发
AES 加密参数
参数
算法AES/CBC/PKCS7Padding
密钥secretKey(Hex 解码为 16 字节)
IV1111111111111111(16 字节 ASCII)这里只是举例
Base64Base64.NO_WRAP

5.7 TanGe / IoT 设备通信协议

78 字节 WiFi 配置包(App → 设备)

通过 00009999 特征写入,固定 78 字节:

字节偏移长度内容填充
010x01(帧头)-
1 ~ 1212"wifisetup"(UTF-8)不足补 0x00
13 ~ 4432SSID(UTF-8)不足补 0x00
45 ~ 7632Password(UTF-8)不足补 0x00
7710x04(帧尾)-
设备响应(设备 → App,通过 8888 Notify)
数据格式内容App 处理
单字节 0x01与下发包格式相同忽略
单字节 0x02WiFi 连接失败bleConnStatus = false
单字节 0x03WiFi 密码错误bleConnStatus = false
JSON 字符串设备信息(DeviceInfoTanGeBean解析后上报云端,关闭 GATT,标记成功

6.关键机制说明:

机制实现细节
连接重试非探鸽设备断开时自动重连,最多 3 次reConnectTime 计数)
探鸽超时独立 3 分钟(180s)超时定时器,超时未成功则 bleConnStatus = false
总超时180 秒 countdownTask 心跳超时,强制结束配网
WiFi 扫描轮询5 秒 通过 scanWifiTask 发送一次 0x05 扫描触发指令,直到收到 0x06 扫描结束
资源清理resetData() 统一释放:停止扫描、关闭 GATT、清空设备列表、取消所有 Disposable、重置状态位
数据写入所有下发数据统一走 setWriteCharacteristicData(byte[]),强制 WRITE_TYPE_NO_RESPONSE

7.Iot设备绑定:

7.1 绑定成功:

/**
 * 绑定成功
 */
private void showBindSuccess() {
        closeCountDown(); // 立即停止倒计时
        mDataBinding.ivFaild.setVisibility(View.GONE);
        mDataBinding.tvProgress.setVisibility(View.VISIBLE);
        mDataBinding.vProgress.setVisibility(View.VISIBLE);
        mDataBinding.btnProblem.setVisibility(View.GONE);
        mDataBinding.btnRebind.setVisibility(View.GONE);
        mDataBinding.tvBindingStatus.setText(UIUtils.getString(R.string.ty_bind_email_success));
        mDataBinding.tvBindingDesc.setText("Iot设备绑定成功");
        mDataBinding.titleview.setTitle(UIUtils.getString(R.string.ty_bind_email_success));
        mDataBinding.titleview.showLeftImage(false);
    }
    
/**
 * 进入下一步,绑定成功显示界面然后跳转到成功界面
 */
public void gotoNext() {
    if (mData != null) {
        SharedPrefs.getInstance().setBindDevicePic(mData.getPic());
        DeviceBindSuccessActivity.startActivity(mContext, mDevId, mData.getHome_device_id(), mProductId);
    } else {
        LogUploadUtils.d("bind device viewModel gotoNext mData is null");
        bindResult.setValue(false);
    }
}

7.2 绑定失败:

/**
 * 绑定失败ui
 */
private void showBindFailed() {
        if (isBindSuccess) return;
        mDownCount = 0;
        setClickText();
        mDataBinding.titleview.setTitle(UIUtils.getString(R.string.text_bind_faild));
        if (TextUtils.isEmpty(mRobotViewModel.bindErrorMsg)) {
            mDataBinding.tvErrorBind.setVisibility(View.GONE);
        } else {
            mDataBinding.tvErrorBind.setText(mRobotViewModel.bindErrorMsg);
            mDataBinding.tvErrorBind.setVisibility(View.VISIBLE);
        }
        mDataBinding.tvErrorBind.setVisibility(View.VISIBLE);
        mDataBinding.ivFaild.setVisibility(View.VISIBLE);
        mDataBinding.tvProgress.setVisibility(View.INVISIBLE);
        mDataBinding.vProgress.setVisibility(View.INVISIBLE);
        mDataBinding.btnProblem.setVisibility(View.VISIBLE);
        mDataBinding.btnRebind.setVisibility(View.VISIBLE);
        mDataBinding.llFailTipsNew.setVisibility(View.VISIBLE);
        mDataBinding.tvBindingStatus.setVisibility(View.GONE);
        mDataBinding.tvBindingDesc.setVisibility(View.GONE);
        mDataBinding.titleview.showLeftImage(false);
        mDataBinding.btnRebind.setOnClickListener(v -> {
            mRobotViewModel.onDestroyed();
            ProductListActivity3.startActivity(mContext, false, false);
        });
    }

8.总结

  • CoolKit 设备采用 "AES 加密握手 + 分阶段指令交互" 的私有协议,通过 bbb0/bbb1 完成身份校验、WiFi 扫描、连接、云端注册的全流程。
  • TanGe/IoT 设备采用 "78 字节定长 WiFi 包 + 单字节 ACK / JSON 响应" 的简化协议,通过 9999/8888 完成配网,成功后以 JSON 上报设备信息。
  • 两者均依赖 BluetoothLeScanner 进行设备发现,通过名称前缀区分设备类型,并在 BleScanUtils 内部以静态状态机管理连接生命周期。
  • 每个iot项目的配网协议不一样,但是核心差不多,根据自己的项目进行封装.