BLE 数据发送与 MTU 管理
本文档深入解析 JLOTA 微信小程序固件升级 SDK 中 BLE 数据发送链路与 MTU(最大传输单元)管理的完整实现,涵盖 RCSP 数据包封帧、串行发送队列、响应超时重试、粘包解析,以及 sendMtu/receiveMtu 的协商与存储机制。
Purpose and Scope
本页面属于“BLE 数据链路”能力域,覆盖以下内容:
- RCSP 协议数据包的封帧与解帧(包头
FE DC BA、标志位、opcode、长度、包尾EF) - RCSPDataHandler 串行发送机制:发送队列、等待响应缓存、超时重试(最多 3 次)、接收解析队列
- MTU 管理:
DeviceInfoManager中 per-device 的sendMtu/receiveMtu存储、默认协议 MTU(530)、CMD_SETTINGS_COMMUNICATION_MTU(0xD1)协商流程、OTA 流程中的changeReceiveMtu() - SDK 与上层传输的边界:
setOnSendDataCallback/sendDataToDevice抽象,以及认证(Auth)流程如何复用该发送通道
以下内容不在本页面范围内,由其他目录页覆盖:
- 设备扫描、连接与特征值订阅(小程序 BLE 适配层)
- OTA 升级状态机(
OTAImpl的 start/cancel/progress/reconnect 流程)—— 仅在本页说明其与数据通道的交互点 - 认证加密算法(
jl_auth_2.0.0.js中的密码学实现)
说明:仓库中的 SDK 为压缩混淆后的单行文件(
jl_rcsp_ota_2.1.1.js等),本页面基于其导出的类与方法语义进行解读;源码中未包含应用层wx.writeBLECharacteristicValue的具体适配实现,该部分以 SDK 暴露的onSendData回调为边界如实说明。
Overview
在微信小程序 BLE 场景下,小程序与杰理(Jieli)音频设备通过 GATT 特征值交换数据。为了让升级包、认证握手、设备控制命令稳定可靠地通过 BLE 传输,SDK 在 GATT 之上实现了一层 RCSP 应用协议:
- 封帧:所有业务数据(命令 + 参数 / 响应)被封装为带固定头尾的 RCSP 包,便于接收端从字节流中切分帧。
- 串行化:BLE 写操作必须逐个进行,SDK 用发送队列保证同一时刻只发送一个包,避免并发写导致丢包。
- 可靠传输:需要响应的命令在发出后登记到"待响应缓存",启动超时定时器;超时后自动重发(上限 3 次),仍无响应则回调
ERROR_RESPONSE_TIMEOUT。 - 粘包处理:BLE Notify 可能一次性带回多个包或半个包,
RcspParser负责从字节流中按 MTU 上限扫描合法包头,缓存不完整尾部。 - MTU 语义:SDK 中
receiveMtu表示"设备能接收的最大包长"(决定本端发送上限),sendMtu表示"设备发送的最大包长"(决定本端解析上限)。二者由设备在连接初始化时上报(ResponseTargetInfotype 13),也可通过CMD_SETTINGS_COMMUNICATION_MTU动态协商。
这套设计的目标是:把不可靠、无流控的 BLE 传输抽象成类似串口的可靠命令通道,让上层(OTA、Auth、RCSP 业务命令)只关心业务语义,不必处理分帧、重传与背压。
Architecture
下图展示了 BLE 数据发送与 MTU 管理在 SDK 内的分层结构:
flowchart TD
subgraph sg_App["应用层 (Mini Program)"]
App["页面 / 业务代码"]
IOAdapter["onSendData 回调<br/>(wx.writeBLECharacteristicValue 适配)"]
end
subgraph sg_SDK["RCSP 协议层 (jl_rcsp_ota_2.1.1.js)"]
RcspImpl["RcspImpl (Ut)"]
SNGen["CmdSnGenerator<br/>命令序号 0-255"]
DataHandler["RCSPDataHandler (R)"]
SendQueue["发送队列 H[]"]
RecvQueue["接收解析队列 F[]"]
PendingCache["待响应缓存 N[]"]
TimeoutMap["超时定时器 Map G"]
Parser["RcspParser (k)"]
MtuMgr["DeviceInfoManager (At)"]
end
subgraph sg_Proto["协议/设备模型"]
Packet["RcspPacket 封帧<br/>FE DC BA + flags + opcode + len + EF"]
DevInfo["DeviceInfo<br/>sendMtu / receiveMtu"]
end
subgraph sg_Consumers["SDK 消费者"]
OTA["OTAImpl (jl_ota_2.1.1.js)"]
Auth["Auth (jl_auth_2.0.0.js)"]
end
App --> OTA
App --> Auth
OTA -->|"sendRCSPCommand / receiveFileBlock"| RcspImpl
Auth -->|"onSendData(deviceId, data)"| IOAdapter
RcspImpl --> SNGen
RcspImpl --> DataHandler
DataHandler --> SendQueue
DataHandler --> PendingCache
DataHandler --> TimeoutMap
DataHandler --> RecvQueue
DataHandler --> Parser
DataHandler --> MtuMgr
Parser --> Packet
MtuMgr --> DevInfo
DataHandler -->|"sendDataToDevice (IO 代理)"| IOAdapter
IOAdapter -->|"BLE 写特征值"| App
各组件职责:
| 组件 | 类/对象 | 职责 |
|---|---|---|
RcspImpl | Ut(导出为 RcspImpl) | RCSP 操作门面:维护当前设备、命令序号生成、回调分发;sendRCSPCommand() 入队发送,transmitDeviceData() 入队解析 |
RCSPDataHandler | R | 核心数据通道:串行发送队列、待响应缓存、超时重发、接收解析队列;发送上限取 getReceiveMtu(),解析上限取 getSendMtu() |
RcspParser | k | 从接收字节流中按包头/包尾/长度切分 RCSP 帧,支持粘包与半包缓存 |
DeviceInfoManager | At | 按 deviceId 存储 DeviceInfo,提供 getReceiveMtu / getSendMtu |
RcspPacket | w | RCSP 帧结构:[FE DC BA][flags][opcode][len_hi][len_lo][payload][EF] |
| IO 适配 | 应用注入 | 通过 setOnSendDataCallback 注入,sendDataToDevice(device, bytes) 内部调用 wx.writeBLECharacteristicValue |
设计意图:RCSPDataHandler 是唯一的字节收发出口,无论 OTA 大块固件数据、Auth 认证包还是普通命令,都必须经过它的队列与 MTU 校验,从机制上保证了 BLE 链路上的串行写与长度安全;MTU 值统一收敛在 DeviceInfoManager,发送与解析两侧共享同一数据源,避免各模块自行假设 MTU 导致不一致。
核心实现:数据发送链路
1. 命令发送入口
上层所有命令最终都汇聚到 RcspImpl.sendRCSPCommand(device, command, timeoutMs, callback):
sendRCSPCommand(t, s, e, r) {
if (!this.kt(t, r)) return; // 设备必须等于当前使用设备
if (null == s) { /* ERROR_INVALID_PARAM */ return; }
s.isCommand() && s.setSn(this.mCmdSnGenerator.getRcspCmdSeq(t)); // 分配 0-255 序号
const n = new g(t, s, e, r); // 包装为 DataInfo(含 timeoutMs/callback)
this.mRCSPDataHandler.W(n) // 入发送队列
}
Source: jl_rcsp_ota_2.1.1.js
要点:
- 每个命令由
CmdSnGenerator分配 0–255 的序号(按设备独立递增,(sn+1) % 256),用于匹配响应。 DataInfo(类g)持有command、timeoutMs、callback、重发计数_与时间戳O。- 发送前先做
kt()校验:命令目标必须是当前mTargetDevice,否则直接报ERROR_DEVICE_OFFLINE。
2. RCSPDataHandler 串行发送与重试
发送入队(W)与处理(X)的关键逻辑:
W(t) { // 入队发送
if (this.B(t)) { // 校验非空 & 通道已启动
if (this.H.push(t), this.H.length > 1)
return void n("RCSPDataHandler: 放入数据缓冲区,等待发送");
const s = { complete: () => { this.X(this.H, s) } };
this.X(this.H, s) // 队列为空则立即处理
}
}
X(t, s) { // 串行发送循环
const e = t.shift();
if (null == e) { /* ... */ }
if (!this.Z(e)) return this.$(e, ERROR_IO_EXCEPTION, "send data failed."), ...;
if (e.command.isNeedResponse()) {
-1 == this.N.indexOf(e) && this.N.push(e); // 登记待响应缓存
let timer = setTimeout((t) => {
// 超时:从缓存移除;重发计数 < 3 则重发,否则 ERROR_RESPONSE_TIMEOUT
if (e._ < this.T) { e._ = e._ + 1; this.W(e) }
else this.$(e, ERROR_RESPONSE_TIMEOUT, "Command[...]");
}, e.timeoutMs, e);
this.G.set(this.tt(e), timer); // key = opcode<<16 | sn
} else this.st(e); // 无需响应:直接回调 onCmdResponse
s.success?.(), s.complete?.()
}
Source: jl_rcsp_ota_2.1.1.js
设计意图:
- 队列即背压:
H队列保证"同一时刻只有一个包在写"。BLE 的writeBLECharacteristicValue是异步且无流控的,并发写会导致系统层失败或覆盖;串行化把速率控制交给"收到响应后再发下一个"的天然握手。 - 超时即重发:
N缓存 +G定时器构成 request-response 匹配。超时后先移除缓存再重发,避免重复响应被二次匹配。 - 重发上限:
T = 3(常量在RCSPDataHandler构造函数中)。超过上限回调ERROR_RESPONSE_TIMEOUT(-64)并通知全局listener.onError。
3. 发送前的封帧与长度校验
真正把字节交给 IO 层的是 Z(t):
Z(t) {
const s = t.command.v(); // 封帧:RcspPacket.v() 生成完整 RCSP 包
if (null == s || 0 == s.byteLength) return false; // 数据为空
if (s.byteLength > this.et(t.device)) // 超过设备 receiveMtu → 拒绝发送
return h("RCSPDataHandler: sendData : data over limit. RCSP mtu = " + ...), false;
let e = false;
for (let r = 0; r < 3 && (e = this.ioProxy.sendDataToDevice(t.device, s), !e); r++);
return e // 系统写失败时立即重试 3 次
}
et(t) { // 发送长度上限 = 设备的 receiveMtu(设备能收多大)
let s = this.deviceMtuManager.getReceiveMtu(t);
return null != s && s > 0 ? s : p.DEFAULT_PROTOCOL_MTU // 默认 530
}
Source: jl_rcsp_ota_2.1.1.js
RCSP 帧结构(RcspPacket.v() 生成,RcspPacket.l() 解析):
| 偏移 | 内容 | 说明 |
|---|---|---|
| 0–2 | FE DC BA | 固定包头 RCSP_HEAD |
| 3 | flags | bit7=command(0x80),bit6=needResponse(0x40),bit0-5=reserve |
| 4 | opcode | 命令码(如 CMD_SETTINGS_COMMUNICATION_MTU=0xD1、OTA 命令 0xE1–0xE8) |
| 5–6 | length | payload 长度(大端 2 字节) |
| 7..7+len | payload | 命令参数或响应体 |
| 末尾 | EF | 固定包尾 RCSP_END |
其中 command 包在 payload 首字节携带 SN(序号),响应包则在第 0 字节为 status、第 1 字节为 SN。
4. 接收链路:粘包解析与响应匹配
BLE Notify 数据到达后经 transmitDeviceData() 进入 RCSPDataHandler.K(),再交给 j() 解析:
j(t, s) { // 接收解析循环
const e = t.shift();
const r = this.V.rt(this.nt(e.device), e.data); // 按 sendMtu 上限切分 RCSP 帧
r.forEach((t) => {
if (t.isCommand()) { // 设备发来的命令
const s = this.V.t(t);
null == s ? /* ERROR_NONE_PARSER */ : this.listener.onRcspCommand(e.device, s);
} else { // 设备发来的响应
const s = this.it(this.N, t); // 在待响应缓存中按 opcode+sn 匹配
if (null == s) return; // 找不到对应命令(超时已移除)
const r = this.tt(s), i = this.G.get(r); // 清除超时定时器
null != i && (clearTimeout(i), this.G.delete(r));
const a = s.command;
const r2 = a.getResponse().l(t.h()); // 解析响应体
r2 >= 0 ? (this.listener.onRcspResponse(e.device, a), this.st(s))
: this.$(s, ERROR_DATA_FORMAT, ...);
}
});
}
nt(t) { // 接收解析上限 = 设备的 sendMtu(设备能发多大)
let s = this.deviceMtuManager.getSendMtu(t);
return null != s && s > 0 ? s : p.DEFAULT_PROTOCOL_MTU
}
Source: jl_rcsp_ota_2.1.1.js
RcspParser(类 k)的 rt(mtu, data) 实现"先合并半包缓存 → 扫描包头 → 校验长度 ≤ mtu 且包尾为 EF → 切帧",无法凑齐的尾部数据通过 wt() 缓存等待下一包;长度超过 MTU 的"脏帧"直接丢弃并继续向后扫描,体现了对非可靠传输的健壮性设计。
5. 发送-响应时序总览
sequenceDiagram
participant App as 应用层
participant Rcsp as RcspImpl
participant H as RCSPDataHandler
participant IO as onSendData 回调 (BLE 写)
participant Dev as BLE 设备
App->>Rcsp: sendRCSPCommand(cmd, timeout, cb)
Rcsp->>Rcsp: 分配 SN (0-255)
Rcsp->>H: W(DataInfo) 入队
H->>H: Z(): 封帧 + 校验长度 <= receiveMtu
H->>IO: sendDataToDevice(device, packet)
IO->>Dev: wx.writeBLECharacteristicValue
H->>H: 登记 N[] 缓存 + G 启动超时定时器
Dev-->>IO: Notify 返回数据
IO->>H: K(DataInfo) 入队
H->>H: Parser 切帧 (上限 sendMtu)
H->>H: 匹配 N[] (opcode+sn) → 清除定时器
H->>Rcsp: onRcspResponse → 上层回调 onResult
Note over H: 超时未响应 → 重发 (最多 3 次)<br/>仍无响应 → ERROR_RESPONSE_TIMEOUT
核心实现:MTU 管理
1. 默认值与存储
class p {}
p.DEFAULT_SEND_CMD_TIMEOUT = 3e3 // 默认命令超时 3000ms
p.DEFAULT_PROTOCOL_MTU = 530 // 默认协议 MTU 530 字节
DeviceInfoManager(类 At)以 deviceId 为键维护 DeviceInfo,其中 sendMtu / receiveMtu 初始默认均为 DEFAULT_PROTOCOL_MTU(530),设备信息同步后由实际值覆盖:
class At extends U {
constructor() { super(...arguments); this.deviceInfoMap = new Map }
getReceiveMtu(t) { const s = this.getDeviceInfo(t); return null != s && s.receiveMtu > 0 ? s.receiveMtu : null }
getSendMtu(t) { const s = this.getDeviceInfo(t); return null != s && s.sendMtu > 0 ? s.sendMtu : null }
updateDeviceInfo(t, s) { this.deviceInfoMap.set(t.deviceId, s) }
}
Source: jl_rcsp_ota_2.1.1.js
方向性语义(务必区分):RCSPDataHandler.et()(发送上限)读的是 receiveMtu,nt()(接收解析上限)读的是 sendMtu。命名以"设备视角"为准:receiveMtu = 设备接收能力 = 手机可发送的最大包长;sendMtu = 设备发送能力 = 手机可接收的最大包长。
2. MTU 信息来源一:连接初始化同步
设备连接成功(Connection.CONNECTION_CONNECTED)后,RcspImpl.St() 调用 syncDeviceInfo() 下发 CmdGetTargetInfo,设备返回 ResponseTargetInfo。其中 type 13 字段携带 MTU:
case 13:
s.length >= 4 ? (this.sendMtu = (255 & s[0]) << 8 | s[1],
this.receiveMtu = (255 & s[2]) << 8 | s[3])
: 2 == s.length && (this.sendMtu = (255 & s[0]) << 8 | s[1],
this.receiveMtu = this.sendMtu)
Source: jl_rcsp_ota_2.1.1.js
3. MTU 信息来源二:动态协商(CMD 0xD1)
设备可通过 CMD_SETTINGS_COMMUNICATION_MTU(opcode 0xD1)主动上报/协商 MTU,RcspImpl.gt() 处理该命令:把参数中的 protocolMtu 写入当前设备的 receiveMtu 并回包(ResponseMtu.realProtocolMtu);响应返回后 At() 再用 realProtocolMtu 更新 receiveMtu。这使设备在固件升级等场景中能主动把接收窗口放大,让手机端发送更大的数据块。
4. OTA 场景的 MTU 抬升
在 jl_ota_2.1.1.js 的 OTA 状态机中,当设备 isNeedBootLoader 时,进入更新模式前会调用:
changeReceiveMtu() {
if (!this.isDeviceConnected()) return;
const e = this.Ut.getDeviceInfo(this._t);
null != e && e.receiveMtu < t.RcspConstant.DEFAULT_PROTOCOL_MTU &&
(e.receiveMtu = t.RcspConstant.DEFAULT_PROTOCOL_MTU, // 抬升到 530
this.Ut.getDeviceInfoManager().updateDeviceInfo(this._t, e))
}
Source: jl_ota_2.1.1.js
设计意图:OTA 固件传输是吞吐敏感路径,BootLoader 场景下设备侧接收能力通常受限;在发送固件块(receiveFileBlock)之前先强制把 receiveMtu 抬到 530,使每个 CmdReadFileBlock 能携带最大数据块,减少 BLE 包数量、缩短升级耗时。这是"以发送端 MTU 驱动升级速率"的典型工程取舍。
使用示例
注意:仓库内 SDK 文件为压缩单行源码,以下示例为从源码中提取的片段并做语义化排版,标识符保持原样。
示例一:注册 BLE 传输回调(SDK 与小程序传输层的边界)
RcspImpl 通过 setOnSendDataCallback 注入实际的 BLE 写实现。SDK 内所有数据(命令、OTA 固件块、认证包)最终都走这一出口:
// RcspImpl 内的转发逻辑
sendDataToDevice(t, s) {
return null == this.mOnSendDataCallback
? (h("RcspImpl: OnSendDataCallback is null,so sendDataToDevice failed"), !1)
: this.mOnSendDataCallback.sendDataToDevice(t, s)
}
Source: jl_rcsp_ota_2.1.1.js
应用层需要提供一个 { sendDataToDevice(device, arrayBuffer) } 对象,内部调用 wx.writeBLECharacteristicValue,并把 wx.onBLECharacteristicValueChange 收到的数据通过 transmitDeviceData(device, buffer) 回灌给 SDK。仓库中未包含该适配器的具体实现(源码缺失),接入时需在业务代码中补齐。
示例二:认证流程复用发送通道
jl_auth_2.0.0.js 的 Auth 类不直接写 BLE,而是回调应用注入的 onSendData,并配合 5 秒超时任务实现握手机制:
_writeAuthData(t) {
this.authDeviceId && (this.callback?.onSendData(this.authDeviceId, t), this._startTimeoutTask())
}
_startTimeoutTask() {
this.timeoutTaskId = setTimeout((() => { this._onAuthFailed(this.authDeviceId) }), 5e3)
}
Source: jl_auth_2.0.0.js
可见"发送 + 超时重试"是 SDK 各模块共用的通用模式:Auth 自行管理 5s 超时,RCSPDataHandler 管理命令级超时,二者共享同一个字节出口,但责任边界清晰。
示例三:字节与十六进制转换工具
util.js 提供 BLE 调试常用的转换函数(非压缩源码,可直接引用):
export function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
}
export function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
Source: util.js
Configuration Options
SDK 内的"配置"以常量形式硬编码(无外部配置文件),列示如下:
| 常量 | 值 | 所属类 | 说明 |
|---|---|---|---|
DEFAULT_PROTOCOL_MTU | 530 | RcspConstant (p) | 协议层默认 MTU;设备未上报或上报值非法时回退值 |
DEFAULT_SEND_CMD_TIMEOUT | 3000 ms | RcspConstant (p) | syncDeviceInfo 等内部命令的默认超时 |
RCSPDataHandler.T | 3 | RCSPDataHandler (R) | 命令响应超时的最大重发次数 |
Auth._startTimeoutTask | 5000 ms | Auth | 认证握手单步超时 |
RCSP_HEAD / RCSP_END | [254,220,186] / 239 | RcspPacket (w) | 帧头帧尾,切帧依据 |
Connection | 0/1/2 | exports | DISCONNECT / CONNECTING / CONNECTED 状态枚举 |
日志可通过 setLogger / setLogGrade(0–5,对应 verbose→error)注入与调节。
API Reference(关键接口)
RcspImpl.sendRCSPCommand(device, command, timeoutMs, callback)
- 描述:发送一条 RCSP 命令。自动分配 SN、封装为
DataInfo并进入RCSPDataHandler发送队列。 - 参数:
device(Device):目标设备,必须等于getUsingDevice(),否则报ERROR_DEVICE_OFFLINE。command(Command):命令对象(如CmdGetTargetInfo、CmdReadFileBlock),isCommand()为 true 时自动赋 SN。timeoutMs(number):响应超时阈值。callback({onResult, onError}):结果回调。
- 回调:成功走
onCmdResponse→callback.onResult;失败走onError(errorCode, desc)。
RCSPDataHandler.W(DataInfo) / K(DataInfo)
W:入发送队列;队列空则立即串行发送,非空则等待前序完成。K:入接收解析队列;由RcspParser按sendMtu切帧后分发给onRcspCommand/onRcspResponse。- 前置条件:通道已启动(
L(),连接建立时调用);未启动时回调ERROR_IO_EXCEPTION。
DeviceInfoManager.getReceiveMtu(device) / getSendMtu(device)
- 返回设备上报的接收/发送 MTU;不存在或 ≤0 时返回
null,调用侧回退到DEFAULT_PROTOCOL_MTU(530)。 - 写入入口:
syncDeviceInfo结果(type 13)、CMD_SETTINGS_COMMUNICATION_MTU命令与响应、OTAchangeReceiveMtu()。
RcspParser.rt(sendMtu, data) → RcspPacket[]
- 将原始字节流切分为完整 RCSP 帧数组;半包缓存进
ht,超长帧(长度 > sendMtu)丢弃并继续扫描。
Failure Modes, Edge Cases & Concurrency
失败模式与错误码
| 场景 | 错误码 | 触发点 |
|---|---|---|
发送包超过设备 receiveMtu | 发送直接失败(无错误码回调) | Z() 中长度校验,随后 $() 报 ERROR_IO_EXCEPTION(-35) |
| 命令超时且重发 3 次仍无响应 | ERROR_RESPONSE_TIMEOUT (-64) | X() 中 G 定时器触发 |
| 响应体解析失败 | ERROR_DATA_FORMAT (-3) | j() 中 getResponse().l() 返回值 < 0 |
| 收到无法识别的命令 opcode | ERROR_NONE_PARSER (-67) | j() 中 V.t(t) 返回 null |
| 设备非当前使用设备/离线 | ERROR_DEVICE_OFFLINE (-33) | kt() 校验 |
| 通道未启动(未连接) | ERROR_IO_EXCEPTION (-35) | B() 校验 M 标志 |
| 重复连接 | ERROR_REPEAT_STATUS (-36) | vt() 中 CONNECTED 时已有 mTargetDevice |
并发与一致性
- 发送串行化:
H队列 +M运行标志保证单写者;X()每次只shift()一个包处理。 - 响应匹配:
N缓存与G定时器以opcode<<16 | sn为键;同一命令重发后,旧定时器已被清除,不会出现双定时器。 - 断线清理:
J()会遍历N回调ERROR_IO_EXCEPTION、清空所有定时器与队列,防止断线后悬挂的 Promise/回调泄漏。 - OTP 序号回绕:SN 按 256 取模递增,
it()按 opcode+sn 双条件匹配,短周期内同命令同序号的重叠由minSameCmdE5Time(OTA 层 50ms 去重)缓解。
边界情况
- 单包恰好等于 MTU 上限时允许发送(
>才拒绝)。 - 半包(
byteLength < 8)进入缓存;跨多帧粘包时循环切帧直到缓冲区耗尽。 - 设备上报 MTU 为 0/非法时回退 530,保证老设备兼容。
Performance & Operational Considerations
- 吞吐瓶颈在 MTU 与往返:每块 OTA 数据(
CmdReadFileBlock)需等待设备CmdReadFileOffset请求-响应循环,实际速率 ≈ MTU / 往返时延;因此changeReceiveMtu()抬升到 530 是升级性能的关键开关。 - 写失败即时重试:
Z()对sendDataToDevice失败最多立即重试 3 次(不占用命令级重发配额)。 - 日志分级:
setLogGrade(1..5)控制 verbose→error;setLogger可接小程序console,用于线上排查 MTU 协商与超时问题。 - 内存:发送/接收队列上限无显式限制,但受"单命令单响应"约束,正常并发数等于未完成命令数;大文件升级靠
receiveFileBlock逐块推进,不会整包驻留。
Extension Points
- 传输层替换:
setOnSendDataCallback({sendDataToDevice})是唯一字节出口,可替换为 SPP、USB 或其他通道实现,实现OTAConfig.COMMUNICATION_WAY_*(0=BLE/1=SPP/2=USB)对应的多通道传输。 - 自定义命令:
CmdCustom(0xF0)/CmdExtraCustom(0xFF)允许业务方定义私有命令,自动享受队列、SN、超时重发与 MTU 校验。 - 监听回调:
addOnRcspCallback(OnRcspCallback)可注册多路监听器(如 OTA 与业务层共存),RcspCallbackManager负责广播。 - MTU 协商扩展:设备侧可通过
CmdSetMtu主动协商,应用侧无需改动即可感知(RcspImpl.gt()自动更新receiveMtu)。
Related Links
- jl_rcsp_ota_2.1.1.js(RCSP 协议与数据通道实现)
- jl_ota_2.1.1.js(OTA 升级状态机,消费数据通道)
- jl_auth_2.0.0.js(认证握手,复用 onSendData 通道)
- util.js(ab2hex / hexToBytes 等 BLE 调试工具)
- 相关目录页:BLE 连接与特征值管理、OTA 升级流程、设备认证(见目录结构 3.x 章节)