android ota block file,bxble_sdk_doc/android_ota.rst at master · bluexmicro/bxble_sdk_doc · GitHub

BX2400 OTA on Android

第一阶段

连接设备。

读取设备服务列表。

发现服务(gatt.discoverServices())。

发现相关特征值(ctrlChar ,dataChar)。

写入ctrlChar request描述符。

设备响应ctrlChar response描述符。

手机发起OTA请求(wirite characteristic)。

设备响应OTA请求(到此OTA 开始进入准备升级状态)。

以上是OTA准备状态。

第一阶段流程图如下所示

BXOTAAndroid_READY.png

第二阶段流程图如下所示

f05362e93df5934618e3f55f62778418.png

OTA 升级的三种情况流程图

OTA.png

cf5086634437ec05b0abc6463cb5246c.png

OTA_with_crc32_signature.png

OTA opcode 说明

ctrlChar>控制特征值

opcode:00 ->BXOTA_CTRL_PKT_START_REQ (开始请求升级)

opcode:01

opcode:02 -> BXOTA_CTRL_PKT_NEW_BLOCK_CMD (image transport)

opcode: 03 ->BXOTA_CTRL_PKT_IMAGE_TRANSFER_FINISH_CMD (app 已经将image 全部写入成功)

opcode:04 -> BXOTA_CTRL_PKT_SIGN_DATA_SEND(写入签名文件字节流,一共64bytes,一次写入16字节,一共写入四次 )

opcode:05

CRC32 ftp://bluexsh.22ip.net/misc/OTA/crc32/

App 效果图

Android_App_UI1.png

android 代码执行步骤仅供参考

#.请查看参考代码中 setp1~11 的执行步骤

android OTA 参考代码

package com.lianrui.mesh_ota;

import android.bluetooth.BluetoothGatt;

import android.bluetooth.BluetoothGattCharacteristic;

import android.bluetooth.BluetoothGattDescriptor;

import android.bluetooth.BluetoothGattService;

import android.content.Context;

import android.os.Build;

import android.support.annotation.NonNull;

import android.util.Log;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.util.Arrays;

import java.util.Deque;

import java.util.LinkedList;

import java.util.UUID;

import no.nordicsemi.android.ble.BleManager;

import no.nordicsemi.android.ble.Request;

import no.nordicsemi.android.ble.utils.ParserUtils;

import static com.lianrui.mesh_ota.Crc32.CRC32_INIT_VAL;

public class MeshOTAManager extends BleManager {

public final static String ERROR_CONNECTION_STATE_CHANGE = "Error on connection state change";

public final static String ERROR_DISCOVERY_SERVICE = "Error on discovering services";

public final static String ERROR_AUTH_ERROR_WHILE_BONDED = "Phone has lost bonding information";

public final static String ERROR_READ_CHARACTERISTIC = "Error on reading characteristic";

public final static String ERROR_WRITE_CHARACTERISTIC = "Error on writing characteristic";

public final static String ERROR_READ_DESCRIPTOR = "Error on reading descriptor";

public final static String ERROR_WRITE_DESCRIPTOR = "Error on writing descriptor";

public final static String ERROR_MTU_REQUEST = "Error on mtu request";

public final static String ERROR_CONNECTION_PRIORITY_REQUEST = "Error on connection priority request";

public final static String ERROR_READ_RSSI = "Error on RSSI read";

public final static String ERROR_READ_PHY = "Error on PHY read";

public final static String ERROR_PHY_UPDATE = "Error on PHY update";

public final static String ERROR_RELIABLE_WRITE = "Error on Execute Reliable Write";

/**

* The maximum packet size is 20 bytes.

*/

private static final int MAX_PACKET_SIZE = 20;

public static final int MTU_SIZE_MIN = 23;

private static final int MTU_SIZE_MAX = 40;

/**

* Mesh provisioning data in characteristic UUID

*/

/**

* Mesh OTA service UUID

*/

public final static UUID MESH_OTA_UUID = UUID.fromString("00002600-0000-1000-8000-00805F9B34FB");

private final static UUID MESH_OTA_CHAR_CTRL_UUID = UUID.fromString("00007000-0000-1000-8000-00805F9B34FB");

private final static UUID MESH_OTA_CHAR_DATA_UUID = UUID.fromString("00007001-0000-1000-8000-00805F9B34FB");

public static final UUID CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

private final String TAG = MeshOTAManager.class.getSimpleName();

private BluetoothGattCharacteristic ctrlChar;

private BluetoothGattCharacteristic dataChar;

private BluetoothGatt mBluetoothGatt;

public final static int BXOTA_CTRL_PKT_START_REQ = 0;

public final static int BXOTA_CTRL_PKT_START_RSP = 1;

public final static int BXOTA_CTRL_PKT_NEW_BLOCK_CMD = 2;

public final static int BXOTA_CTRL_PKT_IMAGE_TRANSFER_FINISH_CMD = 3;

public final static int BXOTA_CTRL_PKT_SIGN_DATA_SEND = 4;

public final static int BXOTA_CTRL_PKT_SIGN_DATA_RSP = 5;

private byte[] OTAData;

private boolean[] currentAck;

private int blockNum;

private int maxSegmentNumInBlock;

private int lastBlockSegmentNum;

private short currentBlock;

private short currentSegment;

private short maxSegmentDataSize = 19;

private final short blockHeaderSize = 1;

private final static int MAX_SIGNATURE_SEGMENT_COUNT = 4 - 1;//index from 0 so max segindex=size-1

public void setNeedCheckSign(boolean mNeedCheckSign) {

this.mNeedCheckSign = mNeedCheckSign;

}

private boolean mNeedCheckSign;

private boolean mNeedCrc32 = false;

private static final int MAX_SIGN_SEG_SIZE = 16;

public void setNeedCrc32(boolean mNeedCrc32) {

this.mNeedCrc32 = mNeedCrc32;

}

public void setSignData(byte[] signData) {

this.signData = signData;

}

private byte[] signData;

public MeshOTAManager(Context context, byte[] otaData) {

super(context);

this.OTAData = otaData;

}

BleManagerGattCallback bleManagerGattCallback = new BleManagerGattCallback() {

@Override

protected Deque initGatt(@NonNull BluetoothGatt gatt) {

isOtaAReady = false;

final LinkedList requests = new LinkedList<>();

mBluetoothGatt = gatt;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

mBluetoothGatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);

}

ctrlChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);

mBluetoothGatt.setCharacteristicNotification(ctrlChar, true);

dataChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);

mBluetoothGatt.setCharacteristicNotification(dataChar, true);

BluetoothGattDescriptor ctrlDesc = ctrlChar.getDescriptor(CCCD);

ctrlDesc.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);

mBluetoothGatt.writeDescriptor(ctrlDesc);

return null;

}

@Override

protected boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) {

for (BluetoothGattService service : gatt.getServices()) {

if (service.getUuid().toString().equals(MESH_OTA_UUID.toString())) {

log(Log.DEBUG, "Service:" + service.getUuid().toString());

for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {

log(Log.DEBUG, "characteristic:" + characteristic.getUuid().toString());

}

} else {

}

}

boolean writeRequest;

BluetoothGattService meshService = gatt.getService(MESH_OTA_UUID);

if (meshService != null) {

log(Log.DEBUG, "found OTA services ");

ctrlChar = meshService.getCharacteristic(MESH_OTA_CHAR_CTRL_UUID);

dataChar = meshService.getCharacteristic(MESH_OTA_CHAR_DATA_UUID);

writeRequest = false;

if (dataChar != null) {

final int rxProperties = dataChar.getProperties();

writeRequest = (rxProperties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0;

}

return (ctrlChar != null && dataChar != null && writeRequest);

}

log(Log.DEBUG, "OTA service not support");

return false;

}

@Override

protected void onDeviceDisconnected() {

}

@Override

protected void onCharacteristicIndicated(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {

super.onCharacteristicIndicated(gatt, characteristic);

log(Log.DEBUG, "onCharacteristicIndicated: " + characteristic.getUuid().toString());

if (characteristic.getUuid().compareTo(MESH_OTA_CHAR_CTRL_UUID) == 0) {

ctrlPktIndicationRX(characteristic.getValue());

}

}

@Override

public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {

if (status == 0) {

super.onCharacteristicWrite(gatt, characteristic, status);

} else {

if (!isOtaAReady) {

startOtaRequest();

}

}

}

@Override

protected void onCharacteristicWrite(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic) {

super.onCharacteristicWrite(gatt, characteristic);

if (characteristic.getUuid().compareTo(MESH_OTA_CHAR_CTRL_UUID) == 0) {

byte[] txBytes = characteristic.getValue();

ctrlPktSent(txBytes);

} else if (characteristic.getUuid().compareTo(MESH_OTA_CHAR_DATA_UUID) == 0) {

// step 8 每调用一次 segmentTX 就会回调这个函数

OTATransferContinue(false);

}

}

@Override

public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {

// super.onDescriptorWrite(gatt, descriptor, status);

log(Log.INFO, "onDescriptorWrite:" + descriptor.getUuid().toString());

if (descriptor.getUuid().compareTo(CCCD) == 0) {

startOtaRequest();

}

}

@Override

protected void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic) {

super.onCharacteristicRead(gatt, characteristic);

final byte[] data = characteristic.getValue();

log(Log.DEBUG, "dataReceived: " + ParserUtils.parse(data));

if (characteristic.getUuid().compareTo(MESH_OTA_CHAR_DATA_UUID) == 0) {

onAckRead(characteristic.getValue());

}

}

@Override

public void onCharacteristicNotified(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {

super.onCharacteristicNotified(gatt, characteristic);

}

@Override

protected void onMtuChanged(@NonNull int mtu) {

super.onMtuChanged(mtu);

maxSegmentDataSize = (short) (mtu - 3 - blockHeaderSize);

log(Log.DEBUG, "onMtuChanged: " + maxSegmentDataSize);

}

};

@Override

public void log(int priority, @NonNull String message) {

super.log(priority, message);

if (priority==Log.DEBUG)

mCallbacks.print(message);

Log.d(TAG, message);

}

@NonNull

@Override

protected BleManagerGattCallback getGattCallback() {

return bleManagerGattCallback;

}

private void segmentTX() {

//step 7 传输image

int length = getSegmentLength(currentBlock, currentSegment);

byte[] data = new byte[blockHeaderSize + length];

data[0] = (byte) currentSegment;

data[1] = (byte) (currentSegment >> 8);

System.arraycopy(OTAData, (currentBlock * maxSegmentNumInBlock + currentSegment) * maxSegmentDataSize

, data, blockHeaderSize, length);

dataChar.setValue(data);

writeCharacteristic(dataChar, data);

}

private int getSegmentLength(int blockID, int segmentID) {

if (blockID == blockNum - 1 && segmentID == lastBlockSegmentNum - 1) {

return OTAData.length - maxSegmentDataSize *

((blockNum - 1) * maxSegmentNumInBlock + (lastBlockSegmentNum - 1));

} else {

return maxSegmentDataSize;

}

}

public void readAck() {

mBluetoothGatt.readCharacteristic(dataChar);

}

public void writeDescriptor() {

}

void OTATransferContinue(boolean newBlock) {

int segmentNum = getSegmentNumOfCurrentBlock();

if (newBlock) {

// 当开启新的block 会执行这里

currentSegment = 0;

log(Log.DEBUG, "newBlock start..");

} else {

// 写入一次就++

++currentSegment;

}

while (currentSegment < segmentNum && currentAck[currentSegment]) {

++currentSegment;

}

//step 9 判断当前block 是否写入完毕

if (currentSegment == segmentNum) {

log(Log.DEBUG, "Seg 1 ~ seg " + segmentNum + " write complete then read ack");

//step 10 手机请求读取当前block 写入状态

readAck();

} else {

segmentTX();

}

}

private void ctrlPktTX(int type, byte[] param) {

byte[] ctrl;

if (param != null) {

ctrl = new byte[param.length + 1];

System.arraycopy(param, 0, ctrl, 1, param.length);

} else {

ctrl = new byte[1];

}

ctrl[0] = (byte) type;

//ctrlChar.setValue(ctrl);

if (type == BXOTA_CTRL_PKT_START_REQ) {

log(Log.DEBUG, "send start reuest:" + Arrays.toString(ctrl));

} else if (type == BXOTA_CTRL_PKT_NEW_BLOCK_CMD) {

log(Log.DEBUG, "send new block cmd:" + Arrays.toString(ctrl));

} else if (type == BXOTA_CTRL_PKT_IMAGE_TRANSFER_FINISH_CMD) {

log(Log.DEBUG, "send transfer comolete cmd:" + Arrays.toString(ctrl));

} else {

}

writeCharacteristic(ctrlChar, ctrl);

}

private void newBlockCmd() {

//step 5 传输前先用ctrlChar(控制特征值)发送新的block 开始cmd

mCallbacks.print("currentBlock:" + currentBlock);

byte[] blockIDArray = new byte[]{(byte) currentBlock, (byte) (currentBlock >> 8)};

ctrlPktTX(BXOTA_CTRL_PKT_NEW_BLOCK_CMD, blockIDArray);

}

private void imageTXFinishCmd() {

ctrlPktTX(BXOTA_CTRL_PKT_IMAGE_TRANSFER_FINISH_CMD, null);

}

private boolean isOtaAReady;

void ctrlPktSent(byte[] txData) {

switch (txData[0]) {

case BXOTA_CTRL_PKT_START_REQ:

isOtaAReady = true;

log(Log.DEBUG, "ota ready: ");

break;

case BXOTA_CTRL_PKT_NEW_BLOCK_CMD:

Arrays.fill(currentAck, false);

//step 6

OTATransferContinue(true);

break;

case BXOTA_CTRL_PKT_IMAGE_TRANSFER_FINISH_CMD:

log(Log.DEBUG, "ota complete....: ");

break;

case BXOTA_CTRL_PKT_SIGN_DATA_SEND:

mCallbacks.print(String.format("sign data:%d of 4 send complete", +txData[1]));

//step 3-2 // 这里会连续回调4次 也就是写入四次

if (txData[1] < MAX_SIGNATURE_SEGMENT_COUNT) {

transSignDataCmd(txData[1] + 1);

} else {

startOTATransfer();

}

break;

default:

break;

}

}

void transSignDataCmd(int index) {

int type = BXOTA_CTRL_PKT_SIGN_DATA_SEND;

int nextIndex = index;

byte[] data = new byte[17];

data[0] = (byte) index;

System.arraycopy(signData, nextIndex * MAX_SIGN_SEG_SIZE, data, 1, MAX_SIGN_SEG_SIZE);

log(Log.DEBUG, ParserUtils.parse(data));

ctrlPktTX(type, data);

}

private void startOtaRequest() {

//step 1

mCallbacks.onOTARequestStart();

int lenth = mNeedCrc32 ? 10 : 2;

ByteBuffer buffer = ByteBuffer.allocate(lenth).order(ByteOrder.LITTLE_ENDIAN);

byte[] maxBlockDataSizeArray = new byte[]{(byte) maxSegmentDataSize, (byte) (maxSegmentDataSize >> 8)};

buffer.put(maxBlockDataSizeArray);

if (mNeedCrc32) {

buffer.putInt(crc32());

buffer.putInt(OTAData.length);

}

log(Log.DEBUG, "startOtaRequest: " + maxSegmentDataSize);

ctrlPktTX(BXOTA_CTRL_PKT_START_REQ, buffer.array());

}

private int crc32() {

Crc32 crc32 = new Crc32();

long crc = crc32.crc32_calc(CRC32_INIT_VAL, OTAData, OTAData.length);

return (int) crc;

}

void ctrlPktIndicationRX(byte[] rxData) {

int status = rxData[1];

switch (rxData[0]) {

case BXOTA_CTRL_PKT_START_RSP:

//step 2

log(Log.DEBUG, "received OTA start Resp");

maxSegmentNumInBlock = rxData[2] * 8;

log(Log.DEBUG, "total segments size(): " + maxSegmentNumInBlock);

currentAck = new boolean[maxSegmentNumInBlock];

if (status == 0) {

//step 3 判断是否需要验证Signature data

if (mNeedCheckSign) {

//step 3-1 传输signature.bin 中的0~16 bytes

transSignDataCmd(0);

} else {

startOTATransfer();

}

}

break;

case BXOTA_CTRL_PKT_SIGN_DATA_RSP:

String mOtaStatus = status == 1 ? "OTA Status:" + "success" : "failed";

log(Log.DEBUG, mOtaStatus);

break;

default:

break;

}

}

private void startOTATransfer() {

//step 4 开始传输image

mCallbacks.onOTAStart();

new Thread(new Runnable() {

@Override

public void run() {

blockNum = (int) Math.ceil((double) OTAData.length / (maxSegmentDataSize * maxSegmentNumInBlock));

int lastBlockDataLength = OTAData.length % (maxSegmentDataSize * maxSegmentNumInBlock);

lastBlockSegmentNum = (int) Math.ceil((double) lastBlockDataLength / maxSegmentDataSize);

currentBlock = 0;

newBlockCmd();

}

}).start();

}

private int getSegmentNumOfCurrentBlock() {

if (currentBlock == blockNum - 1) {

return lastBlockSegmentNum;

} else {

return maxSegmentNumInBlock;

}

}

void onAckRead(byte[] rxBytes) {

// step 11 接收到设备返回的block 写入状态

boolean allAcked = true;

int segmentNum = getSegmentNumOfCurrentBlock();

for (int i = 0; i < segmentNum; ++i) {

//check data send

if ((rxBytes[i / 8] & (1 << i % 8)) != 0) {

currentAck[i] = true;

} else {

// transfor filed

allAcked = false;

currentAck[i] = false;

}

}

if (allAcked) {

float progress = (float) (currentBlock + 1) / blockNum;

mCallbacks.onProgress(progress);

//判断所有的block 是否全部写入完毕

if (++currentBlock == blockNum) {

log(Log.DEBUG, "OTA Complete");

//发送结束命令

imageTXFinishCmd();

} else {

log(Log.DEBUG, " next block:" + currentBlock);

// 下一个block 传输

newBlockCmd();

}

} else {

//tranfor failed >>continue transfor

//走到这里说明传输过程丢失了一部分数据,那么将当前block 重新传输一次

OTATransferContinue(true);

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值