2. 开发说明

2.1 认证库使用

Warning

每次蓝牙连接只可以与设备进行一次认证,重复认证会导致认证失败。若设备端打开认证,小程序端未认证就与设备进行RCSP命令通讯(包含OTA),设备不会回应命令。若设备端不打开认证,则不需要进行认证流程。

//初始化认证
let auth = new Auth()
//设备回复数据callback
let bleDataCallback: BleDataCallback = {
  onReceiveData(res: WechatMiniprogram.OnBLECharacteristicValueChangeCallbackResult) {
    //数据传输给认证库
    auth.handlerAuth(res.deviceId, res.value)
  }
}
//认证结果监听
let authListener: AuthListener = {
    //发送认证数据回调
  onSendData: (deviceId: string, data: ArrayBuffer) => {
    //调用发数接口
    this._SendData(deviceId, new Uint8Array(data))
  },
  //认证成功回调
  onAuthSuccess: () => {
    //移除设备回复数据callback
    BleDataHandler.removeCallbacks(bleDataCallback)
    //通知RCSP_SDK连接成功
    this.rcspOp.transmitDeviceStatus(new Device(device.deviceId), Connection.CONNECTION_CONNECTED)
  },
  //认证失败回调
  onAuthFailed: () => {
    //移除设备回复数据callback
    BleDataHandler.removeCallbacks(bleDataCallback)
    this.bleConnect.disconnect()
  },
}
//添加设备回复数据callback
BleDataHandler.addCallbacks(bleDataCallback)
//开始认证
auth.startAuth(device.deviceId, authListener)

2.2 RCSP库使用

RCSP命令架构图:

../_images/RCSP%E5%91%BD%E4%BB%A4%E6%9E%B6%E6%9E%84%E5%9B%BE.png

Note

目前这个渠道的RCSP库只支持OTA命令

SDK初始化:

//初始化
let rcspOp = new RcspOpImpl();
//设置RCSP发送数据回调
rcspOp.setOnSendDataCallback({
  sendDataToDevice: (device: Device, data: Uint8Array): boolean => {
    //调用发数接口
    return this._SendData(device.mac, data)
  }
})
//设备回复数据callback
const bleDataCallback = {
  onReceiveData(res: WechatMiniprogram.OnBLECharacteristicValueChangeCallbackResult) {
    //数据传输给RCSP库
    rcspOp.transmitDeviceData(new Device(res.deviceId), new Uint8Array(res.value))
  }
}
//添加设备回复数据callback
BleDataHandler.addCallbacks(bleDataCallback)
//RCSP事件回调
const onRcspCallback: OnRcspCallback = {
    /**
    * Rcsp协议初始化回调
    *
    * @param device 已连接设备
    * @param isInit 初始化结果
    */
    onRcspInit(device?: Device | null, isInit?: boolean){},
    /**
    * 设备主动发送的rcsp命令回调
    *
    * @param device  已连接设备
    * @param command RCSP命令
    */
    onRcspCommand(device: Device | null, command: CommandBase){},
    /**
    * 设备主动发送的数据命令回调
    *
    * @param device  已连接设备
    * @param dataCmd 数据命令
    */
    onRcspDataCmd(device: Device | null, dataCmd: CommandBase){},
    /**
    * RCSP错误事件回调
    *
    * @param device  设备对象
    * @param error   错误码 (参考{@link com.jieli.rcsp.data.constant.ErrorCode})
    * @param message 错误描述
    */
    onRcspError(device: Device | null, error: number, message: string){},
    /**
    * 需要强制升级回调
    *
    * @param device 需要强制升级的设备
    */
    onMandatoryUpgrade(device: Device | null){},
    /**
    * 设备连接状态
    *
    * @param device 蓝牙设备
    * @param status 连接状态
    */
    onConnectStateChange(device: Device | null, status: Connection){}
  }
//添加RCSP事件回调
rcspOp.addOnRcspCallback(onRcspCallback)

如何传输设备的连接状态:

Important

当传输设备状态之后,SDK就会开始与设备进行RCSP协议初始化(获取设备相关信息等)。初始化结果从 OnRcspCallback.onRcspInit() 回调

Warning

打开认证时,先认证成功,再传输连接状态

//设备连接已连接(打开认证时,先认证成功,再传输连接状态)
this.rcspOp.transmitDeviceStatus(new Device(result.deviceId), Connection.CONNECTION_CONNECTED)


//设备连接已断开
this.rcspOp.transmitDeviceStatus(new Device(result.deviceId), Connection.CONNECTION_DISCONNECT)

如何发送命令给设备:

//创建一条命令(例如:获取固件特征信息)
let devcie: Device | null = this.rcspOp.getUsingDevice();
//构建命令
const param = new ParamTargetInfo(0xffffffff, 2);//android平台:0,iOS:1,小程序:2
let command = new CmdGetTargetInfo(param);
//构建命令回复回调
const commandCallback: CommandCallback ={
    onCmdResponse(device: Device, command: CommandBase) {
        //命令状态是否成功
        if (command.getStatus() != ResponseBase.STATUS_SUCCESS) {
            const code = ErrorCode.ERROR_REPLY_BAD_STATUS;
            return
        }
        const response = command.response
        //命令是否有回复
        if (null == response) {
            return;
        }
        //强制转换成对应的Response
        const responseTargetInfo = response as ResponseTargetInfo
        //获取命令回复解析后的属性(举例:设备的版本名称)
        responseTargetInfo.versionName;
    },
    onError(device: Device, code: number, message: string) {
        //命令异常
    }
}
//发送命令
if(device!=null){
    //命令超时时间
    const timeoutMs = RcspConstant.DEFAULT_SEND_CMD_TIMEOUT;
    this.rcspOp.sendRCSPCommand(device,command,timeoutMs,commandCallback);
}

2.3 OTA库使用

Note

v2.1.1版本修改了OTA库的使用方式。增加了otaWrapper类,对OTA库进行进一层封装,方便使用。

Note

v2.1.1版本修改了OTA库的打包结构,将ota-rcsp.ts文件也打包进了SDK文件内,但是export保留了 RcspOTAManagerOTAImpl。不影响v2.1.0之前的使用方式。

2.3.1 使用OTAWrapper进行OTA

通常情况下,我们只需要通过使用OTAWrapper就可以完成OTA功能。不需要实现和使用OTA库的接口。

第一步:初始化OTAWrapper

//OTAWrapper 初始化
const otaWrapperOption: OTAWrapperOption = {
    /**是否需要认证。 在上层已经认证过,就不需要认证。**/
    isUseAuth: () => {
        return this._BluetoothConfigure.isUseAuth
    },
    /**是否需要回连。 在上层进行回连,就不需要内部回连。**/
    isInnerReconnect: () => {
        return true
    },
    /**扫描设备**/
    sanDevice: () => {
        //todo 实现蓝牙扫描操作
    },
    /**连接设备**/
    connectDevice: (device: BluetoothDevice) => {
        //todo 实现蓝牙连接操作
    },
    /**断开设备**/
    disconnectDevice: (device: BluetoothDevice) => {
        //todo 实现蓝牙断开操作
    },
    /**发送数据(非必须实现),
    * 必须实现的情况:
    * - 1.内部创建并管理RCSPImpl, 即OTAWrapperOption.getRCSPImpl未实现
    * - 2.需要进行认证, 即OTAWrapperOption.isUseAuth返回false
    * **/
    sendData: (device: BluetoothDevice, data: Uint8Array) => {
       //todo 实现蓝牙发数操作
    }
    /**获取RCSPImpl(非必须实现)。
    * - 上层管理RCSPImpl则需要实现,如使用jl-rcsp-op时需要实现。
    * - 上层不管理RCSPImpl则不需要实现,由内部创建并管理RCSPImpl
    *  **/
    //getRCSPImpl?(device: BluetoothDevice): RCSPProtocol.RcspImpl | undefined
}
this._OTAWrapper = new OTAWrapper(otaWrapperOption)

第二步:监听蓝牙连接状态并同步OTAWrapper

this._bluetoothInstance.addConnectCallback({
    onMTUChange: (dev: any, mtu) => {
        BleSendDataHandler.setMtu(dev.deviceId, mtu)
        this._onConnectStateMTUChange(dev, mtu)
    }, onConnectSuccess: (dev: any) => {
        // 通知 OTAWrapper 蓝牙连接成功
        this._OTAWrapper.onConnectStateSuccess(dev)
        this._onConnectStateSuccess(dev)
    }, onConnectFailed: (dev: any, _err) => {
        // 通知 OTAWrapper 蓝牙连接失败
        this._OTAWrapper.onConnectStateFailed(dev)
        this._onConnectStateFailed(dev)
    }, onConnectDisconnect: (dev: any) => {
        // 通知 OTAWrapper 蓝牙连接断开
        this._OTAWrapper.onConnectStateDisconnect(dev)
        this._onConnectStateDisconnect(dev)
    }
})

第三步:监听蓝牙扫描状态并同步OTAWrapper

this._bluetoothInstance.addScanCallback({
    onFound: (devs: BTBean.BluetoothDevice[]) => {
        // 通知 OTAWrapper 发现设备
        this._OTAWrapper.onScanFound(devs)
        this._onScanFound(devs)
    }, onScanStart: () => {
        this._onScanStart()
    }, onScanFailed: (err: BTBean.BluetoothError) => {
        this._onScanFailed(err)
    }, onScanFinish: () => {
        // 通知 OTAWrapper 扫描设备停止
        this._OTAWrapper.onSanDeviceStop()
        this._onScanFinish()
    }
})

第四步:监听蓝牙数据推送并同步OTAWrapper

const bleDataCallback: BleDataCallback = {
    onReceiveData: (res: WechatMiniprogram.OnBLECharacteristicValueChangeCallbackResult) => {
        // 通知 OTAWrapper 收到数据
        this._OTAWrapper.onReceiveData(this._toDevice(res.deviceId), res.value)
    }
}
BleDataHandler.addCallbacks(bleDataCallback)

第五步:连接设备

连接设备时,会进行RCSP的认证和RCSP的初始化。当认证失败或者初始化失败时,SDK会主动断开设备(OTAWrapperOption.disconnectDevice)。

若需要监听设备的初始化状态。可在OTAWrapper注册RCSP回调(OTAWrapper.registerRcspCallback)。

若需要判断设备是否初始化成功,可调用IOTAWrapper.isRCSPInit方法。

第六步:开始OTA

/*--- 开始执行OTA升级 ---*/
//创建OTA配置项
const otaConfig: OTAConfig = new OTAConfig()
//是否支持新的回连方式
otaConfig.isSupportNewRebootWay = true
//固件升级文件数据
otaConfig.updateFileData = this.upgradeData
//升级目标设备
const device = connectedDevices[0]
const onUgradeCallback: OnUpgradeCallback = {
    onStartOTA: () => {
    // 开始升级
    },
    onNeedReconnect: (reConnectMsg: ReConnectMsg) => {
    // 正在回连
    },
    onProgress: (type: UpgradeType, progress: number) => {
    // 升级进度回调
     if (type == UpgradeType.UPGRADE_TYPE_CHECK_FILE) {
      // 校验文件(传输BootLoader)
      } else if (type == UpgradeType.UPGRADE_TYPE_FIRMWARE) {
      // 传输升级内容
      }
    },
    onStopOTA: () => {
    // 升级结束
    },
    onCancelOTA: () => {
    // 升级取消
    },
    onError: (error: number, message: string) => {
    // 升级失败
    },
}
this._OTAWrapper.startOTA(device, otaConfig, onUgradeCallback)

关于如何获取设备的固件版本信息:

Important

获取设备的固件版本信息,可用以下几种方式进行:
1.初始化设备成功后,上层可获取设备信息( OTAWrapper.getDeviceInfo() ),设备信息中包含了固件的版本信息( deviceInfo.versionNamedeviceInfo.versionCode )
2.自定义命令交互

2.4 单备份升级注意事项

关于单备份的强制升级有以下几点注意事项:

Important

1. 设备单备份升级在传输Loader系统完成后,假如出现升级失败,设备会进入强制升级模式(既此刻设备会一直跑Loader系统,非正常系统),我们只需要对强制升级设备进行升级,升级成功后就可以正常使用。
2. 设备进入Loader系统后,通常情况下,是不支持固件端其余功能,包括自定义的命令通讯交互。需特殊处理,请跟固件开发人员沟通。
3. 如果应用开发时,对蓝牙连接有特殊处理(举例:连接应答的命令机制,超时断开),需要特殊考虑强制升级设备的情况,进行处理。
举例处理:应用需求-蓝牙连接时,App向设备发送连接命令,设备若超时没有回复连接命令 ,App端主动断开设备。
当设备是强制升级设备且是新回连广播包时,我们可通过广播包判断出该设备是强制升级设备,直接跳过连接应答机制。
当设备是强制升级设备且是旧回连广播包时,我们需要先连接上设备,通过RCSP-SDK获取到设备信息,才可判断设备是不是强制升级设备,视情况跳过连接应答机制。
4. 若打开认证,进入强制升级模式后(包括升级过程中的回连),连接设备也需要先进行认证,才能进行RCSP通讯。

关于单备份如何检查设备是否需要强制升级:

Important

检验设备是否需要强制升级,可用以下几种方式进行:
1.连接上设备后,传输连接状态给RCSP-SDK( RcspOpImpl.transmitDeviceStatus() )。当OnRcspCallback 回调初始化成功后( OnRcspCallback.onRcspInit() ),上层可获取设备信息( RcspOpImpl.getDeviceInfo() ),设备信息中包含了是否需要强制升级( deviceInfo.mandatoryUpgradeFlag==1 )
2.连接上设备后,传输连接状态给RCSP-SDK( RcspOpImpl.transmitDeviceStatus() )。当OnRcspCallback 回调需要强制升级时( OnRcspCallback.onMandatoryUpgrade() ) ,则表示该设备需要强制升级。不需要强制升级的设备是不会回调的。
3.当设备端使用的是升级新回连广播包时,只要收到该广播包就可以认为该设备需要强制升级。
4.在Loader系统内自定义命令交互(不推荐,理论可行)

2.5 单备份升级的新回连广播包

该功能可选用:在设备端或小程序端之一选择不使用新回连,设备就会使用旧的广播包。小程序端配置 OTAConfig.isSupportNewRebootWay

部分设备和部分手机单备份升级时,在回连过程中,系统会直接回连上设备,导致SDK回连设备超时。故此在切换到Loader系统时,设备换了一个新的蓝牙地址,App端通过设备的广播包去判断设备是不是当前升级设备。

新回连广播包格式:

Byte0-4

Byte5

Byte6-11

Byte12-13

Byte14-15

Byte16

Byte17

Byte18-26

JLOTA(标识)

0

原BLE地址

JLOTA(标识)

1

原BLE地址

UID

PID

Bit7-4:Type Bit3-0:Version

电量

保留位

2.6 注意事项

  1. 为了防止单备份升级失败导致固件变”砖”, 因此固件单备份升级失败后会进入强制升级模式

  2. 因为未连接设备时, 不知道设备状态, 所以库初始化成功后需要查询设备升级状态()

  3. 当连接上设备(若打开认证,需要先认证成功)后,可以将设备连接状态传输给RcspOpImpl

  4. 用户SDK或APP应该具备回连上一次连接的蓝牙设备的功能, 用于强制升级时自动回连设备, 再通过OTA升级更新固件

  5. 单备份方案,下载boot完成后,会先断开已连接的蓝牙(SPP或BLE), 进入uboot后,然后回连BLE

  6. 双备份方案, 不需要回连过程,直接开始升级流程