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项目的配网协议不一样,但是核心差不多,搭建根据自己的进行封装.
相关推荐
千里马学框架6 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台6 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone6 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc6 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo6 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077006 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼6 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone6 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen6 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone6 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui