Androidiot蓝牙配网简单封装

Androidiot蓝牙配网简单封装

1.蓝牙配网流程图:

2.蓝牙配网时序图:

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配网管理整体架构:

4.实现步骤:

ini 复制代码
用户点击开始配网   
     ↓
进入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 设备搜索流程图:

5.2 核心通信流程图:

CoolKit 设备通信流程

css 复制代码
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 设备通信流程

5.3 GATT 服务与特征值:

设备类型 方向 UUID 属性 说明
CoolKit App → 设备 0000bbb0-0000-1000-8000-00805f9b34fb Write 指令下发
CoolKit 设备 → App 0000bbb1-0000-1000-8000-00805f9b34fb Read / Notify 状态与数据上报
TanGe/IoT App → 设备 00009999-0000-1000-8000-00805f9b34fb Write WiFi 包 / 绑定信息写入
TanGe/IoT 设备 → App 00008888-0000-1000-8000-00805f9b34fb Read / Notify ACK / JSON 数据上报

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

偏移(相对 AD Type 段) 长度 内容 字节序
+2 ~ +3 2 bytes PN 值 小端(需反转读取)
+4 1 byte 保留值 -
+5 1 byte 产品类型 -

5.5 CoolKit 设备通信协议

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

csharp 复制代码
[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 设备→App WiFi 扫描结果 [ssidLen, ssid..., bssid[6], rssi, isConnect]
0x06 设备→App WiFi 扫描结束 无 payload
0x08 设备→App WiFi 连接状态 [status] 0成功 1失败 2密码错 3无AP 4超时
0x0A 设备→App 设备身份信息 [deviceIdLen, deviceId..., apikeyLen, apikey..., chipIdLen, chipId...]
0x0C 设备→App 配网完成状态 [status: 0x00=成功]
0x0E 设备→App IP 获取结果 [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} + urlBytes CoolKit 云注册成功后下发
AES 加密参数
参数
算法 AES/CBC/PKCS7Padding
密钥 secretKey(Hex 解码为 16 字节)
IV 1111111111111111(16 字节 ASCII)这里只是举例
Base64 Base64.NO_WRAP

5.7 TanGe / IoT 设备通信协议

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

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

字节偏移 长度 内容 填充
0 1 0x01(帧头) -
1 ~ 12 12 "wifisetup"(UTF-8) 不足补 0x00
13 ~ 44 32 SSID(UTF-8) 不足补 0x00
45 ~ 76 32 Password(UTF-8) 不足补 0x00
77 1 0x04(帧尾) -
设备响应(设备 → App,通过 8888 Notify)
数据格式 内容 App 处理
单字节 0x01 与下发包格式相同 忽略
单字节 0x02 WiFi 连接失败 bleConnStatus = false
单字节 0x03 WiFi 密码错误 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 绑定成功:

scss 复制代码
​
/**
 * 绑定成功
 */
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 绑定失败:

scss 复制代码
/**
 * 绑定失败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项目的配网协议不一样,但是核心差不多,搭建根据自己的进行封装.
相关推荐
愚公搬代码4 小时前
【愚公系列】《Android应用案例开发大全》016-LBS类应用掌上杭州(辅助工具类的开发)
android·前端
g10565591395 小时前
LAMP博客平台Wordpress实战
android·mysql·nginx·php
hunterandroid6 小时前
[Android 从零到一] Compose LazyColumn 性能优化:key、稳定性与重组治理
android·前端
pengyu6 小时前
【Kotlin 协程修仙录 · 元婴境 · 中阶】 | 操作符真解:Flow 中间操作符与数据流变幻术
android·kotlin
pengyu7 小时前
【Kotlin 协程修仙录 · 元婴境 · 初阶】 | 冷流初啼:Flow 的基础与响应式编程的入门
android·kotlin
wawo007 小时前
Kuikly实践-从Android compose迁移到kuikly compose
android
Carson带你学Android9 小时前
Android 版 MCP 正式登场:AppFunctions
android·ai编程·jetbrains
古法安卓12 小时前
Android-Direct Boot 阶段与 getFilesDir() 路径变更问题深度分析
android·java·android studio
阿巴斯甜12 小时前
Android Studio Detekt 使用
android