目录
一 安卓程序设计
二 创建文件选择器
三 安卓遇上Ymodem
四 一些坑和注意点汇总
一 安卓程序设计
根据【嵌入式】基于串口的IAP在线升级详解与实战1----IAP功能设计中的说明,已经完成了MCU-HC08-超级终端的固件烧写流程,距离设想的使用手机APP实现MCU固件烧写只有一步之遥。剩下来的工作就是将1K-Ymodem协议移植到手机APP中,再结合手机蓝牙,将需要烧写的固件发送到MCU的IAP程序中。
APP中要实现的功能大致分为以下几点:
(1)设计一个文件选择器,可以加载手机本地文件(固件bin文件),并返回文件名和文件路径;
(2)移植Ymodem协议,并嵌入蓝牙读写;
(3)烧写进度显示以及一些其他的功能;
二 创建文件选择器
此处参考android 调用系统文件管理器,可以通过调用手机自带的文件管理器访问文件,点击之后响应回调,返回文件名和文件路径。
三 安卓遇上Ymodem
这边首先提供两篇参考资料:
- 当Android BLE遇上YModem
- 【安卓相关】蓝牙基于Ymodem协议发送bin文件,对硬件设备进行升级
还有两个相关的github开源库:
- LeonXtp / YModemForAndroid
- ArdWang / YModemlib_Android
这两个库里面的实现基本上是差不多的,我这边使用的是第二个库,支持经典蓝牙socket通讯 和BLE。
【1】在项目中添加该库:
方法一:Gradle使用(在build.gradle中添加下列依赖,但是这样导入的库是只读的,不能修改):
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.ArdWang:YModemLib:2.0.0'
}
方法二:AndroidStudio中导入module
【2】实例化Ymodem类:
文件选择好之后,调用startYmodem以开启Ymodem传输:
java">private void startYmodem(){
yModem = new YModem.Builder()
.with(this)
.filePath(Path_new) //存放到手机的文件路径 stroge/0/.../xx.bin 这种路径
.fileName(Name)
.checkMd5("") //Md5可以写可以不写 看自己的通讯协议
.sendSize(1024) //可以修改成你需要的大小
.callback(new YModemListener() {
@Override
public void onDataReady(byte[] data) {
MainActivity.writeData(data);
}
@Override
public void onProgress(int currentSent, int total) {
//进度条处理
}
@Override
public void onSuccess() {
//成功的显示
}
@Override
public void onFailed(String reason) {
}
}).build();
yModem.start();
}
这边主要关注一下filePath、fileName、sendSize(我这边用的是1K Ymodem,所以该属性赋值1024)。
【3】蓝牙数据发送:
同样是startYmodem中,在onDataReady方法里加入当前使用的蓝牙发送数据接口,我这边是封装的writeData()接口,底层是蓝牙原生的数据发送接口:
java">charaWrite.setValue(data);
mBluetoothGatt.writeCharacteristic(charaWrite);
java">public static void writeData(byte[] data){
BluetoothGattService service=mBluetoothGatt.getService(write_UUID_service);
charaWrite=service.getCharacteristic(write_UUID_chara);
if (data.length>20)
{//数据大于个字节 分批次写入
Log.e(TAG, "writeData: length="+data.length);
int num=0;
if (data.length%20!=0){
num=data.length/20+1;
}else{
num=data.length/20;
}
for (int i=0;i<num;i++){
if (i==num-1){
tempArr=new byte[data.length-i*20];
System.arraycopy(data,i*20,tempArr,0,data.length-i*20);
}else{
tempArr=new byte[20];
System.arraycopy(data,i*20,tempArr,0,20);
}
charaWrite.setValue(tempArr);
mBluetoothGatt.writeCharacteristic(charaWrite);
Log.e("YMODEM", "[BT]tempArr.length="+tempArr.length);
Log.e("YMODEM", "[BT]tempArr="+ Arrays.toString(tempArr));
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
else
{
charaWrite.setValue(data);
mBluetoothGatt.writeCharacteristic(charaWrite);
}
}
【4】蓝牙数据接收:
onDataReceivedFromBLE方法用于接收数据,将其嵌入到蓝牙接收模块中,每当手机蓝牙模块收到外部返回的数据,则将该数据用于Ymodem协议的返回判断中:
java">//用于接受到你蓝牙设备给你反馈的蓝牙信息
public static void onDataReceivedFromBLE(byte[] data) {
yModem.onReceiveData(data);
}
java">
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.e(TAG,"onCharacteristicChanged()"+characteristic.getValue());
data=characteristic.getValue();
s = new String(data);
Log.e(TAG,s);
runOnUiThread(new Runnable() {
@Override
public void run() {
UpDate.onDataReceivedFromBLE(data);
Log.e("YMODEM", "[RCV]data="+ Arrays.toString(data));
}
}
});
综上,便将Ymodem移植到了自己的安卓APP中,可以通过按钮进入文件管理器,选择到需要的固件bin文件之后,将该文件的名称和路径返回给Yomodem作为数据发送的依据,同时Ymodem调用蓝牙底层的收发逻辑实施数据交换。
至此,手机APP可以完成与超级终端一样的功能,即文件选择与发送。具体的操作步骤可以参考超级终端那一块的内容。
四 一些坑和注意点汇总
实际的研发过程中遇到了不少的坑,这边做一个统一的汇总说明:
【1】握手阶段--蓝牙发包最大每包20字节
【问题定位】:蓝牙BLE发包的MTU是20字节是其劣根性,可以通过Android 修改ble蓝牙20字节限制中的方法修改其MTU:ble蓝牙BluetoothGattCallback:onCharacteristicChanged接收数据时,被限制只能接收20字节(实际为23字节,其中3字节为ATT占用),要突破20字节需要在BluetoothGattCallback:onConnectionStateChange连接成功时加入一下设置:
int mut = 512
bluetoothGatt.requestMtu(mut)
但是修改了MTU之后,其可靠性得不到保证,故这边对蓝牙底层发包逻辑做一下上层的封装,超过20字节的包,分批次发送。新的发包函数writeData()前文有所提及,这边不再赘述。
【2】收发阶段--首包正确发完之后发送数据包,但是数据包一般正确发送几个之后,就会断掉:
【问题定位】:这个问题的原因在于,我们选用的蓝牙模块存在劣根性,发送大量数据中间需要人为增加间隔,否则就会出现问题:
针对这个问题,在发送逻辑中加上延时20ms逻辑,即正常:
【3】挥手阶段--中间的数据包全部发完之后,板子应该回ACK,如下所示:
但是实际情况是,下板会在这一段时间内,多回个C。
【问题定位】目前这个问题原因不明,采取的规避方法是在MCU中做规避,在发送中间数据包的过程中,不发C,测试OK(MCU程序参考【嵌入式】基于串口的IAP在线升级详解与实战2----移植Ymodem协议)。