商为云打印设备蓝牙 BLE 配网协议
0. 概述与适用范围
本协议描述如何通过蓝牙低功耗(BLE)为商为云打印设备完成网络配置:下发 WiFi 凭据、 设置静态 / 动态 IP,并读取设备当前的联网状态。适用于手机 App、微信小程序、桌面配网工具等 各类宿主应用 —— 协议只依赖标准 GATT 的 写(Write)/ 读(Read)两种操作, 不依赖任何厂商私有 SDK。
设备侧 BLE GATTS 提供两个服务:
| 服务 | UUID(16 位缩写) | 用途 |
|---|---|---|
| 服务一 | 0x00FF | 蓝牙打印数据通道(特征值 FF01),与本配网协议无关,另见蓝牙打印相关文档 |
| 服务二 | 0x00EE | WiFi 配网服务(特征值 EE01)—— 本协议的全部报文都发生在该服务上 |
1. 快速上手:配网全流程
一次典型配网按以下顺序完成,全程约 10 ~ 30 秒:
- 扫描 —— 发现目标设备(可按广播名粗筛,见 2.2);
- 连接 —— 与目标设备建立 BLE 连接;
- 服务发现 —— 枚举服务,确认存在
0x00EE配网服务及其特征值(见 2.1); - 读取当前状态 —— 对
EE01发起 Read,获取设备当前配网与联网状态(见 3.3); - 写入 WiFi 凭据 —— 向
EE01写入type=1JSON(见 3.1); 写入后 BLE 连接保持,可继续写入 IP 设置并轮询状态; - (可选)写入 IP 设置 —— 静态 IP 场景写入
type=2JSON(见 3.2); - 轮询状态 —— 周期性 Read 观察
net_status, 直到变为4(已接入互联网); - 结束 —— 设备联网成功后即可断开 BLE,后续业务经云端 / MQTT 进行。
2.1 服务与特征值
配网服务 0x00EE 下的特征值 EE01 同时具备写与读两种属性,两种操作共用同一特征值:
| 项目 | UUID | 属性 |
|---|---|---|
| 配网服务(Service) | 000000EE-0000-1000-8000-00805F9B34FB | — |
| 配网特征值(Characteristic) | 0000EE01-0000-1000-8000-00805F9B34FB | write / read |
000000EE」+ 特征值 properties 是否包含 write / read 来匹配,
而不是硬编码完整 128 位 UUID 字符串。这样可兼容设备侧不同固件批次对 UUID 表示
(16 位 / 128 位、大小写)的差异 —— 商为官方配网小程序即采用此匹配方式。
2.2 设备广播与发现
设备开机后对外广播,广播名为「产品前缀 + 标识」的形式。应用可按业务需要过滤扫描结果;
最终确认设备是否支持本协议,以服务发现中是否存在 0x00EE 配网服务为准,
广播名仅用于缩小扫描范围。
2.3 数据格式与写入规则
- 应用与设备之间以 JSON 文本组织数据,编码为 UTF-8,支持中文 SSID 等多字节字符;
- 写操作:一次
write承载一条完整 JSON(不要拆分为多次写入); JSON 报文可能超过 BLE 默认 MTU 单帧容量(20 字节),请确保连接后协商了足够的 ATT MTU (iOS 由系统自动协商;Android 建议在写入前调用setBLEMTU请求更大的 MTU, 详见 4.3); - 读操作:对
EE01发起read,设备以 JSON 作为特征值应答返回 (宿主框架的 characteristic 值回调中收取); - 解析宿主应以宽容策略处理状态 JSON:新版本固件可能追加新字段
(如
mac、dualwifi),未识别字段应忽略而非报错。
3.1 写入:WiFi 配置(type=1)
向 EE01 写入如下 JSON,命令设备连接指定 WiFi:
{
"type": 1,
"ssid": "Office-WiFi",
"pwd": "password123"
}
| 参数 | 值类型 | 说明 |
|---|---|---|
type | Number | 固定为 1,标识 WiFi 配置报文 |
ssid | String | WiFi 的 SSID,最长 32 字符,支持中文等多字节字符(UTF-8) |
pwd | String | WiFi 密码,最长 64 字符;开放网络可为空 |
net_status,直到变为 4(已接入互联网)。
若此期间出现连接断开(微信小程序错误码 10006 / 10012 等),
属异常而非预期 —— 多为距离过远、信号干扰或系统回收连接所致,
应提示用户靠近设备并重连后继续轮询。
3.2 写入:IP 设置(type=2)
向 EE01 写入如下 JSON,设置设备的 IP 获取方式。设备默认使用动态 IP(DHCP),
仅在部署环境要求固定地址时才需要本步骤:
{
"type": 2,
"ip_mode": 1,
"ip": "192.168.1.100",
"netmask": "255.255.255.0",
"gateway": "192.168.1.1",
"dns": "8.8.8.8"
}
| 参数 | 值类型 | 说明 |
|---|---|---|
type | Number | 固定为 2,标识 IP 设置报文 |
ip_mode | Number | IP 模式:1 = 静态 IP,0 = 动态 IP(DHCP) |
ip | String | IPv4 地址,如 "192.168.1.100"。静态必填 |
netmask | String | 子网掩码,如 "255.255.255.0"。静态必填 |
gateway | String | 网关,如 "192.168.1.1"。静态必填 |
dns | String | DNS 服务器,如 "8.8.8.8"。静态必填 |
动态 IP 模式(ip_mode=0)下,四个地址字段可省略。
字段名统一为 ip_mode(与状态应答 3.3 中的字段一致);历史资料中出现的
ipmode 写法以本文为准。
3.3 读取:设备网络状态
对 EE01 发起 Read,得到如下状态 JSON:
{
"type": 1,
"net_status": 4,
"link_type": 1,
"ssid": "Office-WiFi",
"ip_mode": 0,
"ip": "192.168.1.23",
"netmask": "255.255.255.0",
"gateway": "192.168.1.1",
"dns": "8.8.8.8",
"dualwifi": true,
"mac": "AABBCCDDEEFF"
}
| 参数 | 值类型 | 说明 |
|---|---|---|
type | Number | 固定为 1,标识网络状态报文 |
net_status | Number | 1 未配网 | 2 已配网未联网 | 3 已获取 IP | 4 已接入互联网(状态机见 4.1) |
link_type | Number | 联网方式:0 以太网 | 1 WiFi | 2 4G |
ssid | String | 已配置的 WiFi SSID;未配置时不携带 |
ip_mode | Number | 0 动态 IP | 1 静态 IP;net_status 为 3、4 时携带 |
ip / netmask / gateway / dns | String | 当前网络参数;net_status 为 3、4 时携带 |
dualwifi | Boolean | 双频能力标志:true 支持 2.4G + 5.8G;部分固件携带 |
mac | String | 设备 MAC 地址;部分固件携带 |
dualwifi 判断设备能力,提前过滤手机当前所连的
5.8G 热点,避免下发必败的凭据。
3.4 扩展:设备信息查询(固件版本与 MAC)
固件版本与 MAC 的读取按以下优先级处理(与商为官方配网小程序一致):
- 首选:直接 Read。对
EE01发起 Read,设备经特征值应答返回 版本与 MAC 信息(收取通道与状态读取相同); - 兜底:AT+INFO? 指令。Read 失败时,向
EE01写入 ASCII 字符串AT+INFO?\r\n,设备随后经特征值应答返回包含版本与 MAC 的应答文本。
应答文本形如 VER:1.2.3 MAC:AABBCCDDEEFF,或等价的 JSON
"version" / "mac" 字段;建议按 VER: / MAC:
前缀或 JSON 字段两种形态宽容匹配(MAC 地址可能含冒号分隔符)。
工程注意(与官方配网小程序一致):发出查询后等待应答或 5 秒超时; 等待期间应暂停常规的状态轮询,避免状态 JSON 被误当作设备信息应答解析; 超时后提示用户靠近设备重试。
4.1 状态机与轮询建议
设备 net_status 沿以下状态机推进(不回跳;配置失效或网络变化时可能回到 2):
1 未配网 ──写入WiFi──▶ 2 已配网未联网 ──关联成功──▶ 3 已获取IP ──云端可达──▶ 4 已接入互联网
轮询节奏建议(与商为官方配网小程序一致):
| 场景 | 建议轮询间隔 |
|---|---|
| 刚下发 WiFi 凭据、等待联网结果 | 1 秒(尽快给出成功 / 失败反馈) |
| net_status = 2(已配网未联网) | 1.5 秒 |
| net_status = 3(已获取 IP) | 2 秒 |
| net_status = 4(已接入互联网) | 停止轮询 |
| net_status = 1(未配网,停留页面) | 3 秒(降频省电) |
- 注意避免并发 Read:建议对读操作加 1 ~ 1.5 秒的在途锁, 上一次未返回前不发起新的 Read;
- Read 应答与写入失败回调在不同平台可能经不同事件收取, 轮询逻辑不要绑定在写入回调上,保持独立定时器驱动。
4.2 安全说明
- 配网操作请在设备近场(3 米以内)完成,避免在远距离或公共场合暴露凭据;
- 配网成功后,如需更高安全性,建议在业务侧引导用户修改该热点的密码;
- 设备不提供远程 BLE 配置能力 —— 攻击者无法经网络触发本协议,攻击面仅限近场物理接触范围内。
4.3 平台适配要点
| 平台 | 要点 |
|---|---|
| 微信小程序 | 使用 wx.openBluetoothAdapter → startBluetoothDevicesDiscovery →
createBLEConnection → getBLEDeviceServices /
getBLEDeviceCharacteristics 标准流程;写入用
wx.writeBLECharacteristicValue。正常流程下设备不会在写入 WiFi 凭据后
主动断开 BLE;若出现 10006 / 10012 属连接异常断开(见 3.1),
应提示用户重连。 |
| Android | 扫描 BLE 必须授予定位权限(部分安卓版本需开启系统定位开关);
默认 ATT MTU 为 23 字节,写入前请调用 requestMtu() 请求更大 MTU,
以容纳完整 JSON 报文。 |
| iOS | MTU 由系统自动协商,无需手动请求;注意 CBCharacteristicWriteWithResponse
方式写入,确保 JSON 完整送达。 |
| HarmonyOS | 使用系统蓝牙 API 按相同 GATT 流程接入即可;定位权限要求与 Android 一致。 |
4.4 微信小程序示例代码
以下为关键步骤片段(节选自商为官方配网小程序的实测实现,可直接复用):
① 服务与特征值发现(按前缀 + 属性匹配)
// 枚举服务:按 UUID 前 8 位匹配,避免硬编码完整 128 位 UUID
wx.getBLEDeviceServices({
deviceId,
success(res) {
const svc = res.services.find(s => s.uuid.slice(0, 8).toUpperCase() === '000000EE')
if (!svc) { /* 设备不支持配网服务 */ return }
wx.getBLEDeviceCharacteristics({
deviceId,
serviceId: svc.uuid,
success(res) {
// 同一特征值 EE01 同时承担 write / read
for (const c of res.characteristics) {
if (c.properties.write) writeId = c.uuid
if (c.properties.read) readId = c.uuid
}
}
})
}
})
② 写入 WiFi 凭据
// 写入 WiFi 凭据(UTF-8 编码,单次写入完整 JSON)
const payload = utf8Encode(JSON.stringify({ type: 1, ssid, pwd }))
wx.writeBLECharacteristicValue({
deviceId, serviceId: svcId, characteristicId: writeId,
value: payload.buffer,
fail(e) {
if (e.errCode === 10006) {
// 连接异常断开(正常流程设备不会主动断开)—— 提示用户靠近设备后重连
}
}
})
③ 轮询状态(含在途锁)
// Read 应答经特征值回调收取(微信小程序所有特征值变化都走这一个回调)
wx.onBLECharacteristicValueChange(res => {
const json = JSON.parse(utf8Decode(res.value)) // 状态 JSON,见 3.3
// json.net_status === 4 即配网成功
})
let readInFlight = false
function pollStatus() {
if (readInFlight) return
readInFlight = true
wx.readBLECharacteristicValue({
deviceId, serviceId: svcId, characteristicId: readId,
complete() { setTimeout(() => readInFlight = false, 1500) }
})
}
// 配网等待期 1s/次;net_status=4 后停止
附录 A:字段速查表
| 字段 | 方向 | type | 类型 | 说明与出现条件 |
|---|---|---|---|---|
type | 双向 | — | Number | 报文类型:写入 1=WiFi 配置、2=IP 设置;状态应答固定为 1 |
ssid | 写入 | 1 | String ≤32 | 目标 WiFi SSID(UTF-8) |
pwd | 写入 | 1 | String ≤64 | WiFi 密码,开放网络可为空 |
ip_mode | 写入 | 2 | Number | 1 静态 / 0 动态 |
ip netmask gateway dns | 写入 | 2 | String | 静态 IP 四项必填;动态模式可省略 |
net_status | 应答 | 1 | Number | 1 未配网 / 2 未联网 / 3 已获 IP / 4 已联网 |
link_type | 应答 | 1 | Number | 0 以太网 / 1 WiFi / 2 4G |
ssid | 应答 | 1 | String | 已配置 SSID;未配置时不携带 |
ip_mode | 应答 | 1 | Number | net_status=3、4 时携带 |
ip netmask gateway dns | 应答 | 1 | String | net_status=3、4 时携带 |
dualwifi | 应答 | 1 | Boolean | 双频能力标志,部分固件携带 |
mac | 应答 | 1 | String | 设备 MAC,部分固件携带 |
附录 B:版本记录
| 版本 | 日期 | 说明 |
|---|---|---|
| V1.0 | 2026-09-28 | 首个公开版本。整理自现网固件实现与商为官方配网小程序实测代码:
双服务(0x00FF 打印 / 0x00EE 配网)与 EE01 写/读两属性定义;
type=1 WiFi 配置、type=2 IP 设置、状态 JSON(含 dualwifi /
mac 扩展字段);设备信息查询(Read 优先,AT+INFO? 兜底);
状态机与轮询节奏建议;平台适配要点与小程序示例代码。 |
字段兼容性承诺:后续版本只增不删、不改变既有字段语义;宿主应用按「忽略未知字段」解析即可向前兼容。
本文档描述商为云打印设备蓝牙 BLE 配网的公开协议。设备固件与本文档持续演进, 实际交付设备的最终行为以合同约定与出厂固件为准;协议扩展与商务合作请联系我们或当地授权代理商。