杰理 SDK 文档中心
首页
首页
  • 项目概览与入门

    • 项目概述
    • 快速开始
  • OTA SDK 核心库

    • RCSP 认证库(jl_auth)
    • OTA 流程库(jl_ota)
    • RCSP-OTA 协议库(jl_rcsp_ota)
    • OTAWrapper 高层封装
  • 蓝牙通信与设备管理

    • 蓝牙连接生命周期管理
    • BLE 数据发送与 MTU 管理
    • 自动回连机制
  • 参考 Demo 小程序

    • 应用入口与页面导航
    • 设备连接页(pageConnect)
    • 固件升级页(pageUpdate)
    • 设置与调试页(pageSetting)
    • 自定义 UI 组件
    • 固件文件解析工具(upgradeFileUtil)
    • 日志系统

RCSP-OTA 协议库(jl_rcsp_ota)

RCSP-OTA 协议库(jl_rcsp_ota_2.1.1.js / jl_rcsp_ota_2.1.1.d.ts)是杰理(Jieli)微信小程序 OTA 方案中的核心协议层:它实现 RCSP(Remote Control Serial Protocol)数据包编解码、命令/回复路由、发送超时重发、设备状态管理与 RCSP 数据处理器(RCSPDataHandler),并向上层 OTA 流程库(jl_ota)提供 RcspImpl 级别的设备操作能力。

Purpose and Scope

本页面向读者完整解释 jl_rcsp_ota 协议库的职责边界、内部结构与工作机理,包括:

  • RCSP 数据包(RcspPacket)的字节结构、命令(Command)/参数(ParamBase)/回复(ResponseBase)三类基类;
  • 数据透传代理 IOProxy 与微信小程序蓝牙通道之间的适配方式;
  • RCSPDataHandler 发送队列、超时重发与回复匹配机制;
  • RCSP-OTA 专用命令集(CmdReadFileOffset、CmdRequestUpdate、CmdEnterUpdateMode、CmdReadFileBlock、CmdQueryUpdateResult、CmdRebootDevice 等)的协议交互流程;
  • 库的导出 API(RcspImpl 相关导出、日志接口、ab2hex 工具等)与配置入口。

本页不覆盖以下内容(属于相邻目录项):

  • OTA 流程库(jl_ota):OTAImpl / RcspOTAManager 的状态机、断线重连、进度回调等高层流程编排。本页仅在说明协议库如何被其消费时引用,详见「OTA 流程库」目录页。
  • RCSP 认证库(jl_auth):设备鉴权与密钥协商流程。
  • 小程序蓝牙接入层:bluetoothOTAManager.ts / otaWrapper.ts 中的 wx.* BLE API 封装与页面 UI。
  • Demo 页面:pageCustomCmd 等页面级自定义命令演示。

Overview

背景与定位

在微信小程序 OTA 方案中,库依赖分三层(详见 README.md):

库文件职责
jl_auth_x.x.xRCSP 认证库:设备鉴权
jl_rcsp_ota_x.x.xRCSP-OTA 协议库:数据包编解码、命令收发、回复匹配、错误处理
jl_ota_x.x.xOTA 流程库:升级流程编排(读取文件标志、查询可升级、传输固件块、查询结果、重启)

jl_rcsp_ota 是承上启下的协议基础:上层流程库不直接操作蓝牙字节流,而是通过本库的命令对象与回调机制完成一次有状态的 RCSP 会话。它同时向应用层暴露 RcspImpl、Device、CommandBase、RCSPDataHandler 等类型,使 Demo 工程(如 pageCustomCmd 页面)可以直接构造自定义 RCSP 命令并发送。

关键设计意图

  1. 协议与传输解耦:协议库只负责 RCSP 包的组包/解包与收发逻辑,真正的 BLE 写特征值操作通过 IOProxy.sendDataToDevice 由调用方实现(声明文件注释明确:该方法运行在子线程、允许阻塞、回调完整一包 RCSP 数据、调用方需按 MTU 分包)。这让同一套协议库可以复用于 BLE / SPP / USB 三种通信方式(OTAConfig.COMMUNICATION_WAY_BLE=0、SPP=1、USB=2)。
  2. 同步请求-异步回复模型:所有命令通过 CommandCallback 回调结果,RCSPDataHandler 内部用 SendDataInfo 队列 + 定时器实现超时重发(SEND_AGAIN_LIMIT),并依据 sn(序号)将设备回复路由回对应的待处理命令。
  3. 状态机前置检查:命令发送前统一走 RcspImpl 的连接状态校验(设备在线、命令对象有效),无效时直接通过 CommandCallback.onError 返回错误码,避免向离线设备发送无意义数据。

Architecture

flowchart TD
    subgraph sg_App["应用层(Demo / 业务页面)"]
        App["app.ts / 页面"]
        BLEMgr["bluetoothOTAManager.ts"]
        OTAWrapper["otaWrapper.ts"]
    end

    subgraph sg_OtaLib["OTA 流程库 jl_ota"]
        OTAImpl["OTAImpl"]
        RcspOTAMgr["RcspOTAManager"]
    end

    subgraph sg_RcspLib["RCSP-OTA 协议库 jl_rcsp_ota"]
        Handler["RCSPDataHandler"]
        RcspImpl["RcspImpl(设备操作门面)"]
        Cmd["命令体系<br/>Command / ParamBase / ResponseBase"]
        Parser["BaseCmdParser / RcspPacket"]
        DeviceMgr["DeviceMtuManager / DeviceInfo"]
    end

    subgraph sg_Ble["微信小程序 BLE 通道"]
        IOProxy["IOProxy(调用方实现)"]
        WXBle["wx.writeBLECharacteristicValue"]
    end

    App -->|"setLogger / 自定义命令"| RcspImpl
    BLEMgr -->|"RCSPProtocol.*"| RcspImpl
    OTAWrapper --> RcspImpl
    OTAImpl -->|"sendRCSPCommand"| RcspImpl
    RcspOTAMgr --> OTAImpl
    RcspImpl --> Handler
    Handler --> Cmd
    Cmd --> Parser
    Parser -->|"toData / parseData"| IOProxy
    IOProxy -->|"sendDataToDevice"| WXBle
    IOProxy -->|"transmitDeviceData / transmitDeviceStatus"| Handler

架构说明:

  • RcspImpl 是协议库对外暴露的设备操作门面:sendRCSPCommand、isDeviceConnected、getUsingDevice、getDeviceInfo、getDeviceInfoManager、addOnRcspCallback / removeOnRcspCallback 等操作都汇聚到 RCSPDataHandler 完成实际收发。
  • RCSPDataHandler 是协议核心引擎:维护发送队列(_sendInfoArray)、接收队列(_receiveInfoArray)、超时定时器表(_sendTimeOutIDMap)与解析器(_rcspParser),将 IOProxy 传入的原始字节流解析为命令/回复对象,再分发给监听者。
  • 命令体系(CommandBase/Command/ParamBase/ResponseBase)将每个 RCSP 操作建模为可序列化对象:toData() 组包、parseData() 解包,上层流程库直接实例化具体命令类(如 CmdReadFileOffset)并通过 sendRCSPCommand 发送。
  • IOProxy 是协议库与小程序 BLE 之间的唯一桥梁:协议库不知道 wx.* 的存在,bluetoothOTAManager.ts 等文件实现 IOProxy 并把设备上报的字节流/连接状态喂给协议库。

核心类型与数据包结构

RCSP 数据包(RcspPacket)

RcspPacket 是协议库最底层的数据载体,声明见 jl_rcsp_ota_2.1.1.d.ts:

/** Command-RCSP基础数据类型 */
declare class RcspPacket {
    static RCSP_HEAD: number[];
    static RCSP_END: number;
    private _isCommand;
    private _isNeedResponse;
    private _reserve;
    private _opCode;
    private _payload?;
    private _sn;
    isCommand(): boolean;
    setCommand(command: boolean): this;
    isNeedResponse(): boolean;
    setNeedResponse(needResponse: boolean): RcspPacket;
    getReserve(): number;
    setReserve(reserve: number): RcspPacket;
    getOpCode(): number;
    setOpCode(opCode: number): RcspPacket;
    getSn(): number;
    getPayload(): Uint8Array | undefined;
    setPayload(payload: Uint8Array): void;
    toData(): Uint8Array | null;
    parseData(data: Uint8Array): number;
}

来源:jl_rcsp_ota_2.1.1.d.ts

一个 RCSP 包的核心字段:帧头 RCSP_HEAD(固定头)、帧尾 RCSP_END、isCommand(命令包 / 回复包)、isNeedResponse(是否需要设备回复)、opCode(操作码,决定命令类型)、sn(序号,用于命令与回复配对)、payload(载荷)。toData() 负责组包为 Uint8Array,parseData() 负责从字节流解析并返回消费长度——这是 RCSPDataHandler 实现粘包/半包处理的基础。

命令三基类

/** Command-基础命令 */
declare class Command<P extends ParamBase, R extends ResponseBase> extends RcspPacket {
    private _param;
    private _response;
    constructor(opCode: number, param: P, response: R | null);
    getParam(): P;
    getResponse(): R | null;
    getSn(): number;
    setParam(param: P): void;
    setResponse(response: R | null): void;
    setSn(sn: number): void;
    setStatus(status: number): void;
    getStatus(): number;
    toData(): Uint8Array | null;
}

/** Command-参数基类 */
declare class ParamBase {
    private _sn;
    private _basePayload?;
    setSn(sn: number): void;
    getSn(): number;
    getData(): Uint8Array | undefined;
    setData(data: Uint8Array): void;
    toData(): Uint8Array;
    parseData(data: Uint8Array): number;
}

/** Command-回复数据基类 */
declare class ResponseBase {
    static STATUS_UNKNOWN: number;
    static STATUS_SUCCESS: number;
    static STATUS_FAILED: number;
    static STATUS_UNKNOWN_CMD: number;
    static STATUS_BUSY: number;
    static STATUS_NONE_RESOURCE: number;
    static STATUS_CRC_ERROR: number;
    static STATUS_ALL_DATA_CRC_ERROR: number;
    static STATUS_INVALID_PARAM: number;
    static STATUS_RESPONSE_DATA_OVER_LIMIT: number;
    private _status;
    private _sn;
    private _payload?;
    getStatus(): number;
    getSn(): number;
    getPayload(): Uint8Array | undefined;
    setStatus(status: number): void;
    setSn(sn: number): void;
    setPayload(payload: Uint8Array): void;
    toData(): Uint8Array;
    parseData(data: Uint8Array): number;
}

/** Command-回复结果码 */
declare class ResponseResult extends ResponseBase {
    static RESULT_OK: number;
    static RESULT_FAIL: number;
    result: number;
    parseData(data: Uint8Array): number;
    toData(): Uint8Array;
}

来源:jl_rcsp_ota_2.1.1.d.ts

设计意图:

  • Command<P,R> 将「请求参数」与「预期回复」封装为泛型对,getResponse() 在收到设备回复后被填充——上层(如 jl_ota 中的 RcspOTAManager)正是通过 command.getResponse()?.result / getStatus() 判断设备侧执行结果。
  • ResponseBase.STATUS_* 是传输层状态码(成功、失败、未知命令、忙、无资源、CRC 错误、参数非法、回复超限等),ResponseResult 在其上追加 result(RESULT_OK / RESULT_FAIL)作为业务结果码。
  • sn 在 ParamBase / ResponseBase / RcspPacket 三处都存在且相互同步:发送时由 RCSPDataHandler 分配,回复时按 sn 匹配。

回调与数据信息

/** Command-命令回调 */
interface CommandCallback<T extends CommandBase> {
    onCmdResponse(device: Device, command: T): void;
    onError(device: Device, code: number, message: string): void;
}

/** 数据处理-发送数据信息 */
declare class SendDataInfo extends BaseDataInfo {
    command: CommandBase;
    timeoutMs: number;
    callback: CommandCallback<CommandBase> | null;
    reSendCount: number;
    sendTimestamp: number;
    constructor(device: Device, command: CommandBase, timeoutMs: number, callback: CommandCallback<CommandBase> | null);
}

/** 数据处理-接收数据信息 */
declare class ReceiveDataInfo extends BaseDataInfo {
    data: Uint8Array;
    constructor(device: Device, data: Uint8Array);
}

来源:jl_rcsp_ota_2.1.1.d.ts

SendDataInfo 记录了每次发送的完整上下文(命令、超时、回调、已重发次数、发送时间戳),是 RCSPDataHandler 实现「超时重发 + 回复去重」的关键数据结构。

设备与连接状态

declare enum Connection {
    CONNECTION_DISCONNECT = 0,
    CONNECTION_CONNECTING = 1,
    CONNECTION_CONNECTED = 2
}

/** 设备 */
declare class Device {
    deviceId: string;
    name?: string;
    constructor(deviceId: string, name?: string);
    equals(o: Device | null): boolean;
}

来源:jl_rcsp_ota_2.1.1.d.ts

Device 以 deviceId 标识一台设备,equals() 用于 OTA 流程中「回连后确认还是同一台设备」(jl_ota 中 RcspOTAManager 用它过滤非目标设备的 onRcspInit 事件)。

(文档续写见后续 AppendDoc 调用)

RCSPDataHandler:协议收发引擎

RCSPDataHandler 是协议库的内部引擎,声明见 jl_rcsp_ota_2.1.1.d.ts(声明文件在该行之后继续展开其余成员):

/** 数据处理-RCSP数据处理器 */
declare class RCSPDataHandler {
    private SEND_AGAIN_LIMIT;
    protected ioProxy: IOProxy;
    listener: OnRcspDataListener;
    deviceMtuManager: DeviceMtuManager;
    private _dataInfoCache;
    private _sendInfoArray;
    private _receiveInfoArray;
    private _rcspParser;
    private _isCanHandler;
    private _sendTimeOutIDMap;
    // ...(sendRCSPCommand / onReceiveDeviceData / 超时重发等成员)
}

来源:jl_rcsp_ota_2.1.1.d.ts

内部职责

成员作用
ioProxy数据透传代理(调用方实现),sendDataToDevice 完成真实字节发送
listenerOnRcspDataListener:onRcspCommand / onRcspResponse / onError 分发回调
deviceMtuManagerMTU 管理(配合 RcspConstant.DEFAULT_PROTOCOL_MTU 调整设备接收能力)
_sendInfoArray待处理发送队列(SendDataInfo),等待设备回复
_receiveInfoArray接收缓冲,处理粘包/半包
_sendTimeOutIDMap每个在途命令的超时定时器句柄
SEND_AGAIN_LIMIT单条命令的最大重发次数,超限后回调超时错误

工作机理

  1. 发送:上层(RcspImpl.sendRCSPCommand)把 CommandBase、超时时间、CommandCallback 封装成 SendDataInfo 入队;分配 sn 后调用 ioProxy.sendDataToDevice(device, command.toData());同时启动超时定时器(默认超时见 RcspConstant.DEFAULT_SEND_CMD_TIMEOUT,OTA 流程中常用 OTAImpl.WAITING_CMD_TIMEOUT = 20s)。
  2. 接收:ioProxy.transmitDeviceData 喂入原始字节 → _receiveInfoArray 缓冲 → _rcspParser(BaseCmdParser 体系)按 RCSP_HEAD / RCSP_END 切包 → parseData 得到 CommandBase 或回复包。
  3. 路由:解析结果若是命令包(isCommand() 为真),分发给 listener.onRcspCommand(OTA 流程中 CmdReadFileBlock / CmdNotifyUpdateFileSize / CmdNotifyADVInfo 正是通过该回调被 RcspOTAManager 拦截处理);若是回复包,按 sn 在 _sendInfoArray 中匹配 SendDataInfo,取消定时器并回调 onCmdResponse。
  4. 超时重发:定时器到期后,若 reSendCount < SEND_AGAIN_LIMIT 则重发并重置定时器;否则以 ErrorCode.ERROR_RESPONSE_TIMEOUT 回调 onError。

这套「队列 + 定时器 + sn 匹配」的设计保证了一次 RCSP 会话中多个命令可以交错发送而不互相串扰,同时为上层提供了统一的重试与超时语义。

RCSP-OTA 命令集

协议库为 OTA 场景定义了整套命令类,它们在 jl_ota 流程库中被按序实例化与发送(证据见 jl_ota_2.1.1.js 中 RcspOTAManager 的实现):

命令类方向作用关键参数/回复
CmdGetTargetInfo设备→主机上报设备信息(是否双备份、是否需 BootLoader、是否强制升级、bleAddr、receiveMtu)FLAG_MANDATORY_UPGRADE
CmdReadFileOffset主机→设备读取升级文件偏移(readUpgradeFileFlag)回复 FileOffset{offset, len}
CmdRequestUpdate主机→设备查询设备是否可升级(inquiryDeviceCanOTA)ParamRequestUpdate,回复 result
CmdEnterUpdateMode主机→设备进入升级模式(enterUpdateMode)回复 result
CmdReadFileBlock设备→主机设备请求固件数据块(gainFileBlock)ParamReadFileBlock{offset, len}
CmdNotifyUpdateFileSize设备→主机设备通知文件总大小/已写大小(进度)totalSize / currentSize
CmdNotifyADVInfo设备→主机设备上报回连广播信息触发 CmdControlADVStream 关闭广播
CmdControlADVStream主机→设备控制广播流(CTRL_OP_CLOSE 关闭)CTRL_OP_CLOSE
CmdChangeCommunicationWay主机→设备切换通信方式(BLE/SPP/USB)与重启方式ParamCommunicationWay
CmdQueryUpdateResult主机→设备查询升级结果(queryUpdateResult)回复 result(见结果码映射)
CmdRebootDevice主机→设备重启设备(rebootDevice)ParamRebootDevice.OP_REBOOT
CmdExitUpdateMode主机→设备退出升级模式(用于取消升级)回复 result

其中 CmdNotifyUpdateFileSize 的回复会由 RcspOTAManager 主动回填 ResponseBase.STATUS_SUCCESS 并原样返回设备(setCommand(false) 后 sendRCSPCommand),这是对设备通知类命令的 ACK 机制。

升级结果码映射(CmdQueryUpdateResult)

jl_ota 中 queryUpdateResult 的 result 会被映射为 OTAError 错误码(证据见 jl_ota_2.1.1.js):

设备 result含义映射错误码
0升级成功触发 rebootDevice → 成功后 onStopOTA
128需要重新升级(设备仍在升级态)重走 readyToReconnectDevice
1数据校验错误ERROR_OTA_DATA_CHECK_ERROR (-102)
2升级失败ERROR_OTA_FAIL (-103)
3加密密钥不匹配ERROR_OTA_ENCRYPTED_KEY_NOT_MATCH (-104)
4升级文件损坏ERROR_OTA_UPGRADE_FILE_ERROR (-105)
5升级类型错误ERROR_OTA_UPGRADE_TYPE_ERROR (-106)
6长度错误ERROR_OTA_LENGTH_OVER (-107)
7Flash 读写错误ERROR_OTA_FLASH_IO_EXCEPTION (-108)
8设备端命令超时ERROR_OTA_CMD_TIMEOUT (-109)
9相同升级文件ERROR_OTA_SAME_FILE (-114)

可升级性检查映射(CmdRequestUpdate)

inquiryDeviceCanOTA 的 result 映射(jl_ota 内常量 T):0=可升级;1=ERROR_OTA_LOW_POWER (-97)(低电量);2=ERROR_OTA_UPDATE_FILE (-98)(升级文件信息错误);3=ERROR_OTA_FIRMWARE_VERSION_NO_CHANGE (-99)(固件版本未变化);4=ERROR_OTA_TWS_NOT_CONNECT (-100)(TWS 未连接);5=ERROR_OTA_HEADSET_NOT_IN_CHARGING_BIN (-101)(耳机未在充电仓内)。

库导出与常量

协议库顶层导出(exports,见 jl_rcsp_ota_2.1.1.js 及 jl_ota_2.1.1.js 的消费方式):

  • 核心门面:RcspImpl(协议操作实现,RcspOTAManager 构造时接收)、Device、Connection(CONNECTION_DISCONNECT=0、CONNECTING=1、CONNECTED=2)。
  • 命令体系:CommandBase、Command、ParamBase、ResponseBase、ResponseResult、BaseCmdParser、RcspPacket,以及全部 Cmd* / Param* 命令类(CmdGetTargetInfo、CmdReadFileOffset、CmdRequestUpdate、CmdEnterUpdateMode、CmdExitUpdateMode、CmdQueryUpdateResult、CmdRebootDevice、CmdChangeCommunicationWay、CmdControlADVStream、CmdReadFileBlock、CmdNotifyUpdateFileSize、CmdNotifyADVInfo 等)。
  • 处理器与监听:RCSPDataHandler、IOProxy、OnRcspDataListener、OnRcspCommandListener、OnRcspResponseListener、DeviceMtuManager、SendDataInfo、ReceiveDataInfo。
  • 常量与错误:RcspConstant(DEFAULT_SEND_CMD_TIMEOUT、DEFAULT_PROTOCOL_MTU)、ErrorCode(getErrorDesc1 / getErrorDesc2)。
  • 工具:ab2hex(ArrayBuffer 转十六进制字符串)、OTAError(getErrorDesc)。
  • 日志:setLogger(logger) / setLogGrade(grade) 与 logv/logd/logi/logw/loge 五级日志函数(jl_ota 库与 app.ts 都调用 setLogger / setLogGrade 注入统一日志)。

核心流程:一次 RCSP-OTA 升级的协议交互

下面的时序图展示 jl_rcsp_ota 在完整 OTA 升级中的协议交互(主机侧流程编排由 jl_ota 的 OTAImpl 完成,协议命令收发全部经由本库;证据来自 jl_ota_2.1.1.js 中 RcspOTAManager 对命令的按序调用):

sequenceDiagram
    participant App as 小程序应用
    participant Rcsp as jl_rcsp_ota (RcspImpl/RCSPDataHandler)
    participant OTA as jl_ota (OTAImpl)
    participant Dev as 蓝牙设备

    App->>Rcsp: setLogger / setLogGrade
    App->>OTA: startOTA(OTAConfig, listener)
    OTA->>Rcsp: 注册 OnRcspDataListener(addOnRcspCallback)
    Dev-->>Rcsp: CmdGetTargetInfo(onRcspInit 透传)
    Rcsp-->>OTA: onRcspInit(device, info)
    OTA->>Rcsp: sendRCSPCommand(CmdReadFileOffset)
    Rcsp->>Dev: RCSP 包(读文件偏移)
    Dev-->>Rcsp: FileOffset{offset, len} 回复
    Rcsp-->>OTA: onCmdResponse(sn 匹配)
    OTA->>Rcsp: sendRCSPCommand(CmdRequestUpdate)
    Dev-->>Rcsp: result(0=可升级 / 错误码)
    OTA->>Rcsp: sendRCSPCommand(CmdEnterUpdateMode)
    Dev-->>Rcsp: result(0=成功)
    Dev-->>Rcsp: CmdReadFileBlock{offset, len}(设备主动请求固件块)
    Rcsp-->>OTA: onRcspCommand → gainFileBlock
    OTA->>Rcsp: sendRCSPCommand(receiveFileBlock: 带 block 数据的回复包)
    Rcsp->>Dev: 固件数据块(按 sn 回填 ResponseBase)
    Dev-->>Rcsp: CmdNotifyUpdateFileSize{totalSize, currentSize}
    Rcsp-->>OTA: notifyUpgradeSize → onProgress
    OTA->>Rcsp: sendRCSPCommand(CmdQueryUpdateResult)
    Dev-->>Rcsp: result(0=成功)
    OTA->>Rcsp: sendRCSPCommand(CmdRebootDevice)
    Rcsp->>Dev: 重启命令
    OTA-->>App: onStopOTA / onError

关键协议交互点

  1. 设备主动上行命令:RCSP 协议下,设备可以主动下发命令(如 CmdGetTargetInfo、CmdReadFileBlock、CmdNotifyUpdateFileSize)。RCSPDataHandler 解析到命令包后经 listener.onRcspCommand 通知 OTAImpl,后者根据命令类型做出响应:
    • CmdReadFileBlock:从本地固件文件切片(FileOffset{offset, len})并构造回复包(setCommand(false)、回填 sn、携带 block 数据、setStatus(STATUS_SUCCESS))发回设备;
    • CmdNotifyUpdateFileSize:更新进度(100 * currentSize / totalSize,封顶 99.9)并回 ACK;
    • CmdNotifyADVInfo:主机回复 CmdControlADVStream(CTRL_OP_CLOSE) 关闭广播流,防止回连阶段干扰。
  2. sn 配对:RCSPDataHandler 为每个下行命令分配 sn,设备回复携带相同 sn,从而在乱序/多命令场景下正确路由回调。
  3. 进度推进:OTAImpl 通过 W(type, percent) 映射升级类型(UPGRADE_TYPE_CHECK_FILE=0 / UPGRADE_TYPE_FIRMWARE=1)后回调 onProgress(type, percent),最终展示到小程序 UI。

使用示例

1. 注入日志(应用入口)

app.ts 在应用启动时统一注入 RCSP 协议库与 OTA 流程库的日志器与日志级别:

import { setLogger as setOTALogger, setLogGrade as setOTALoggerGrade } from "./lib/jl_lib/jl_ota_2.1.1";
import { setLogger as setRCSPLogger, setLogGrade as setRCSPLoggerGrade } from "./lib/jl_lib/jl_rcsp_ota_2.1.1";
import { setLogger as setAppLogger, setLogGrade as setAppLoggerGrade } from "./lib/log";

来源:app.ts

设计意图:协议库与流程库的日志走同一套五级日志接口(setLogger + setLogGrade),应用层可集中控制日志输出等级,便于 OTA 排障。

2. 在蓝牙管理器中引用协议库

bluetoothOTAManager.ts 通过 import * as RCSPProtocol 获得协议库全部导出,并以此构造 IOProxy 与设备连接管理:

import { OTAWrapper, OTAWrapperOption, OTAWrapperListenner, BluetoothDevice } from "./otaWrapper"
import * as RCSPProtocol from "./jl_lib/jl_rcsp_ota_2.1.1";

来源:bluetoothOTAManager.ts

otaWrapper.ts 同样引入协议库(见 otaWrapper.ts),说明协议库同时被「蓝牙适配层」与「OTA 包装层」两层消费:适配层实现 IOProxy 完成字节收发,包装层把 RcspImpl 桥接给 jl_ota 的 RcspOTAManager。

3. 页面级自定义命令

Demo 的「自定义命令」页面直接使用协议库构造 RCSP 命令:

import * as RCSPProtocol from "../../../lib/jl_lib/jl_rcsp_ota_2.1.1"
import { formatTime, string2buffer, byteArrayToHex } from "../../../utils/util";
import { loge } from "../../../lib/log";

来源:pageCustomCmd.ts

这展示了协议库的扩展性:业务页面可基于 CommandBase / ParamBase / CommandCallback 构造任意自定义 RCSP 命令,经由 RcspImpl.sendRCSPCommand 与设备交互,无需改动协议库本身。

配置选项

配置项类型默认值说明
RcspConstant.DEFAULT_SEND_CMD_TIMEOUTnumber由库定义协议层默认命令发送超时(毫秒)
RcspConstant.DEFAULT_PROTOCOL_MTUnumber由库定义协议层默认 MTU;jl_ota 中 changeReceiveMtu 会将设备 receiveMtu 提升到该值再开始固件传输
setLogGrade(grade)number1(库内默认 n=1)日志输出级别阈值:logv(1) / logd(2) / logi(3) / logw(4) / loge(5),级别数值大于 n 的日志不输出
setLogger(logger)objectnull注入日志实现(logv/logd/logi/logw/loge 方法),未注入则日志静默丢弃
RCSPDataHandler.SEND_AGAIN_LIMITnumber库内定义单条命令最大重发次数
OTAImpl.WAITING_CMD_TIMEOUTnumber20000OTA 流程单命令等待超时(毫秒),供上层配置参考
OTAImpl.WAITING_DEVICE_OFFLINE_TIMEOUTnumber6000等待设备断线超时(毫秒)
OTAImpl.RECONNECT_DEVICE_TIMEOUTnumber80000回连设备超时(毫秒)
OTAConfig.COMMUNICATION_WAY_BLE / SPP / USBnumber0(BLE)通信方式枚举,CmdChangeCommunicationWay 的取值来源
UpgradeType.UPGRADE_TYPE_CHECK_FILE / FIRMWARE / UNKNOWNenum-1/0/1升级类型(文件校验 / 固件升级 / 未知),用于 onProgress 类型参数

注:WAITING_* / RECONNECT_* 常量属于 jl_ota 流程库(jl_ota_2.1.1.js),此处列出是因为它们直接决定协议层命令超时窗口的取值。

API 参考(协议库关键接口)

IOProxy(数据透传代理,调用方实现)

interface IOProxy {
    transmitDeviceStatus: (device: Device, status: Connection) => void;
    transmitDeviceData: (device: Device, data: Uint8Array) => void;
    sendDataToDevice: (device: Device, data: Uint8Array) => boolean;
}

来源:jl_rcsp_ota_2.1.1.d.ts

参数与语义:

  • transmitDeviceStatus(device, status):向协议库上报设备连接状态变化,status 取值 Connection 枚举(断开/连接中/已连接)。
  • transmitDeviceData(device, data):把设备侧收到的原始字节喂给协议库(通常来自 wx.onBLECharacteristicValueChange)。
  • sendDataToDevice(device, data):发送一包完整 RCSP 数据;返回 boolean 表示是否发送成功。按声明注释,该方法运行在子线程、允许阻塞、由调用方按实际 MTU 分包。

RcspImpl(协议操作门面,由协议库实现)

RcspImpl 是 jl_ota 流程库依赖的核心抽象(jl_ota_2.1.1.js 开头即 require("./jl_rcsp_ota_2.1.1.js"),RcspOTAManager 构造参数即 RcspImpl)。其主要方法(由 jl_ota_2.1.1.js 中的调用点归纳):

方法说明
sendRCSPCommand(device, command, timeoutMs, callback)发送一条 RCSP 命令,异步经 CommandCallback.onCmdResponse / onError 回调
isDeviceConnected()当前设备是否在线(离线时命令直接以 ERROR_DEVICE_OFFLINE 失败)
getUsingDevice()获取当前使用中的 Device
getDeviceInfo(device)获取 DeviceInfo(isSupportDoubleBackup、isNeedBootLoader、mandatoryUpgradeFlag、bleAddr、receiveMtu)
getDeviceInfoManager()获取设备信息管理器(updateDeviceInfo 可修改 receiveMtu 等)
addOnRcspCallback(listener) / removeOnRcspCallback(listener)注册/注销 OnRcspDataListener(onRcspInit、onRcspCommand、onRcspDataCmd、onConnectStateChange、onRcspError、onMandatoryUpgrade、onRcspResponse)
exitUpdateMode(callback)退出升级模式(取消双备份升级时使用)

OnRcspDataListener(协议事件监听)

interface OnRcspDataListener {
    onRcspCommand: (device: Device, command: CommandBase) => void;
    onRcspResponse: (device: Device, command: CommandBase) => void;
    onError: (device: Device | null, code: number, message: string) => void;
}

来源:jl_rcsp_ota_2.1.1.d.ts

onRcspCommand 收到设备上行命令(如 CmdReadFileBlock),onRcspResponse 收到下行命令的回复。jl_ota 的 RcspOTAManager 同时实现了这些回调,并在 onRcspCommand 中完成对 CmdReadFileBlock(回传固件块)、CmdNotifyUpdateFileSize(ACK)、CmdNotifyADVInfo(关闭广播)的处理。

ErrorCode 与 OTAError(错误模型)

declare class ErrorCode {
    static ERROR_UNKNOWN: number;
    static ERROR_NONE: number;
    static ERROR_INVALID_PARAM: number;
    static ERROR_DATA_FORMAT: number;
    static ERROR_NOT_FOUND_RESOURCE: number;
    static ERROR_UNKNOWN_DEVICE: number;
    static ERROR_DEVICE_OFFLINE: number;
    static ERROR_IO_EXCEPTION: number;
    static ERROR_REPEAT_STATUS: number;
    static ERROR_RESPONSE_TIMEOUT: number;
    static ERROR_REPLY_BAD_STATUS: number;
    static ERROR_REPLY_BAD_RESULT: number;
    static ERROR_NONE_PARSER: number;
    static getErrorDesc1(errorCode: number): string;
    static getErrorDesc2(errorCode: number, explain: string): string;
}

来源:jl_rcsp_ota_2.1.1.d.ts

核心错误码取值(协议层): ERROR_UNKNOWN=-1、ERROR_INVALID_PARAM=-2、ERROR_DATA_FORMAT=-3、ERROR_UNKNOWN_DEVICE=-32、ERROR_DEVICE_OFFLINE=-33、ERROR_IO_EXCEPTION=-35、ERROR_REPEAT_STATUS=-36、ERROR_RESPONSE_TIMEOUT=-64、ERROR_REPLY_BAD_STATUS=-65、ERROR_REPLY_BAD_RESULT=-66、ERROR_NONE_PARSER=-67。

OTA 域错误码(OTAError,-97 ~ -114): ERROR_OTA_LOW_POWER=-97、ERROR_OTA_UPDATE_FILE=-98、ERROR_OTA_FIRMWARE_VERSION_NO_CHANGE=-99、ERROR_OTA_TWS_NOT_CONNECT=-100、ERROR_OTA_HEADSET_NOT_IN_CHARGING_BIN=-101、ERROR_OTA_DATA_CHECK_ERROR=-102、ERROR_OTA_FAIL=-103、ERROR_OTA_ENCRYPTED_KEY_NOT_MATCH=-104、ERROR_OTA_UPGRADE_FILE_ERROR=-105、ERROR_OTA_UPGRADE_TYPE_ERROR=-106、ERROR_OTA_LENGTH_OVER=-107、ERROR_OTA_FLASH_IO_EXCEPTION=-108、ERROR_OTA_CMD_TIMEOUT=-109、ERROR_OTA_IN_PROGRESS=-110、ERROR_OTA_COMMAND_TIMEOUT=-111、ERROR_OTA_RECONNECT_DEVICE_TIMEOUT=-112、ERROR_OTA_USE_CANCEL=-113、ERROR_OTA_SAME_FILE=-114。

错误回调统一为 (device, code, message) 三元组,getErrorDesc2(code, explain) 拼接出带上下文的描述(jl_ota 中所有 onError 都通过它格式化错误信息)。

失败模式、边界情况与并发

失败模式

  • 设备离线:RcspImpl 命令入口先校验 isDeviceConnected(),离线直接 ERROR_DEVICE_OFFLINE,不产生无线流量。
  • 回复状态异常:设备回复 status != STATUS_SUCCESS 时,CommandCallback.onError 收到 ERROR_REPLY_BAD_STATUS;result != RESULT_OK 时收到 ERROR_REPLY_BAD_RESULT(jl_ota 的 IHandleResult 回调封装实现此判定)。
  • 超时:命令等待超时(协议层 DEFAULT_SEND_CMD_TIMEOUT,OTA 流程中 WAITING_CMD_TIMEOUT=20s)且重发次数用尽 → ERROR_RESPONSE_TIMEOUT;OTA 各阶段另有独立超时(等待断线 6s、回连 80s、命令 20s)。
  • 解析失败:无匹配解析器时返回 ERROR_NONE_PARSER,数据格式错误返回 ERROR_DATA_FORMAT,保证异常字节流不会崩溃协议栈。

边界情况

  • 固件文件为空/越界:OTAImpl.startOTA 校验 updateFileData 为空时直接 ERROR_INVALID_PARAM;readUpgradeFileFlag 读取超过文件长度时返回 ERROR_INVALID_PARAM("Read Data over Limit")。
  • 重复升级:isOTA() 为真时再次 startOTA 返回 ERROR_OTA_IN_PROGRESS。
  • 单备份固件不可中断:设备不支持双备份(isSupportDoubleBackup=false)时 cancelOTA() 直接返回 false 并提示 "device is single flash ota, so ota progress cannot be interrupted"——这是由 Flash 单备份物理特性决定的硬限制。
  • 设备主动下发重复命令:RcspOTAManager 对 CmdReadFileBlock 按 sn + 时间窗(minSameCmdE5Time=50ms)去重,防止设备重传导致重复切块。

并发与一致性

  • 命令交错:RCSPDataHandler 以 sn 区分在途命令,支持多条命令并发在途;CmdReadFileBlock 请求被暂存到 vt 数组,receiveFileBlock 按 offset/len 精确匹配并出队,防止错位回传。
  • 进度一致性:notifyUpgradeSize 维护 totalSize/currentSize,进度计算 100*currentSize/totalSize 封顶 99.9(100% 留给 onStopOTA),避免 UI 提前显示完成。
  • 定时器生命周期:每次命令/阶段切换都会 clearTimeout 旧定时器(F()/V()/M() 三组清理),防止 OTA 结束后残留回调触发误报。

性能与运维注意

  • MTU 协商:jl_ota 在需要 BootLoader 时先 changeReceiveMtu() 把设备 receiveMtu 提到 RcspConstant.DEFAULT_PROTOCOL_MTU,再进入固件块传输,减少分包次数、提升吞吐。协议层 sendDataToDevice 的回调数据是完整 RCSP 包,实际 BLE 分包由调用方(bluetoothOTAManager)完成。
  • 日志开销:协议库提供 logv/logd/logi/logw/loge 五级日志且带等级门控(setLogGrade),生产环境建议调高等级(如 n=3,仅输出 logi 及以上),避免 BLE 高频数据(固件块传输)刷屏拖慢小程序。
  • 固件数据缓存:OTAImpl 持有整个 updateFileData(Uint8Array),readUpgradeFileFlag 按 FileOffset 切片发送;大固件文件会占用较多小程序内存,建议按官方指导控制单次升级文件大小。

扩展点

  1. 自定义 RCSP 命令:基于 Command<P,R> / ParamBase / ResponseBase 派生新命令类并注册解析器(BaseCmdParser 体系),即可扩展协议命令集——Demo 的 pageCustomCmd 页面正是这一能力的展示。
  2. 通信方式扩展:IOProxy.sendDataToDevice 完全由调用方实现,同一协议库可对接 BLE(COMMUNICATION_WAY_BLE=0)、SPP(=1)、USB(=2),切换时通过 CmdChangeCommunicationWay 通知设备。
  3. 协议事件订阅:addOnRcspCallback 允许注册多个监听者;jl_ota 的 RcspOTAManager 与业务层可同时监听,各自处理关心的命令(OTA 相关 vs 自定义命令)。
  4. 日志接入:setLogger 可注入任意日志实现,便于对接小程序日志上报或远程排障系统。

Related Links

  • README.md(库清单与快速开始)
  • RCSP-OTA 协议库声明文件 jl_rcsp_ota_2.1.1.d.ts
  • RCSP-OTA 协议库实现 jl_rcsp_ota_2.1.1.js
  • OTA 流程库 jl_ota_2.1.1.js(协议库的消费方,含 RcspOTAManager)
  • OTA 流程库声明 jl_ota_2.1.1.d.ts
  • 应用入口 app.ts(setLogger 注入示例)
  • 蓝牙管理器 bluetoothOTAManager.ts(IOProxy 适配示例)
  • OTA 包装层 otaWrapper.ts
  • 自定义命令页面 pageCustomCmd.ts(自定义命令示例)

相邻主题请参见:OTA 流程库(jl_ota)、RCSP 认证库(jl_auth)、蓝牙连接管理(bluetoothOTAManager) 等目录页。

Prev
OTA 流程库(jl_ota)
Next
OTAWrapper 高层封装