(一)参考文献
【安卓相关】蓝牙基于Ymodem协议发送bin文件,对硬件设备进行升级。 - 简书
当Android BLE遇上YModem - 简书
(二)收发机制
基于我们具体的需求,在原有的基础上加了一下前后的处理。
* MY YMODEM IMPLEMTATION
* *SENDER: ANDROID APP *------------------------------------------* RECEIVER: BLE DEVICE*
* HELLO BOOTLOADER ---------------------------------------------->*
* <---------------------------------------------------------------* C
* SOH 00 FF filename0fileSizeInByte0MD5[90] ZERO[38] CRC CRC----->*
* <---------------------------------------------------------------* ACK C
* STX 01 FE data[1024] CRC CRC ---------------------------------->*
* <---------------------------------------------------------------* ACK
* STX 02 FF data[1024] CRC CRC ---------------------------------->*
* <---------------------------------------------------------------* ACK
* ...
* ...
* <p>
* STX 08 F7 data[1000] CPMEOF[24] CRC CRC ----------------------->*
* <---------------------------------------------------------------* ACK
* EOT ----------------------------------------------------------->*
* <---------------------------------------------------------------* ACK
* SOH 00 FF ZERO[128] ------------------------------------------->*
* <---------------------------------------------------------------* ACK
* <---------------------------------------------------------------* MD5_OK
(三)核心代码模块
首先梳理一下它应该具有哪些模块:
- 协议的核心实现
主要是负责数据传输过程中有关协议的部分,如在数据包上加入头,CRC,验证返回的正确性以及超时重发等。- 一个协议工具类,封装包数据的提供
- 一个文件数据的读取模块:它是耗时任务,应该在子线程进行。
- 各种执行状态的监听
3.1协议的核心实现
/**
* Created by JimXu on 2017/9/16.
* Modified by JimXu on 2017/9/16
*/
public class Ymodem implements FileStreamThread.DataRaderListener {
private static final int STEP_HELLO = 0x00;
private static final int STEP_FILE_NAME = 0x01;
private static final int STEP_FILE_BODY = 0x02;
private static final int STEP_EOT = 0x03;
private static final int STEP_END = 0x04;
private static int CURR_STEP = STEP_HELLO;
private static final byte ACK = 0x06; /* ACKnowlege */
private static final byte NAK = 0x15; /* Negative AcKnowlege */
private static final byte CAN = 0x18; /* CANcel character */
private static final byte ST_C = 'C';
private static final String MD5_OK = "MD5_OK";
private static final String MD5_ERR = "MD5_ERR";
private Context mContext;
private String filePath;
private String fileNameString = "LPK001_Android";
private String fileMd5String = "63e7bb6eed1de3cece411a7e3e8e763b";
private YModemListener listener;
private TimeOutHelper timerHelper = new TimeOutHelper();
private FileStreamThread streamThread;
//bytes has been sent of this transmission
private int bytesSent = 0;
//package data of current sending, used for int case of fail
private byte[] currSending = null;
private int packageErrorTimes = 0;
private static final int MAX_PACKAGE_SEND_ERROR_TIMES = 5;
//the timeout interval for a single package
private static final int PACKAGE_TIME_OUT = 6000;
/**
* Construct of the YModemBLE,you may don't need the fileMD5 checking,remove it
*
* @param filePath absolute path of the file
* @param fileNameString file name for sending to the terminal
* @param fileMd5String md5 for terminal checking after transmission finished
* @param listener
*/
public Ymodem(Context context, String filePath,
String fileNameString, String fileMd5String,
YModemListener listener) {
this.filePath = filePath;
this.fileNameString = fileNameString;
this.fileMd5String = fileMd5String;
this.mContext = context;
this.listener = listener;
}
/**
* Start the transmission
*/
public void start() {
sayHello();
}
/**
* Stop the transmission when you don't need it or shut it down in accident
*/
public void stop() {
bytesSent = 0;
currSending = null;
packageErrorTimes = 0;
if (streamThread != null) {
streamThread.release();
}
timerHelper.stopTimer();
}
/**
* Method for the outer caller when received data from the terminal
*/
public void onReceiveData(byte[] respData) {
//Stop the package timer
timerHelper.stopTimer();
if (respData != null && respData.length > 0) {
switch (CURR_STEP) {
case STEP_HELLO:
handleHello(respData);
break;
case STEP_FILE_NAME:
handleFileName(respData);
break;
case STEP_FILE_BODY:
handleFileBody(respData[0]);
break;
case STEP_EOT:
handleEOT(respData);
break;
case STEP_END:
handleEnd(respData);
break;
default:
break;
}
} else {
L.f("The terminal do responsed something, but received nothing??");