蓝牙固件升级模块:OAT升级又称空中升级、DFU 升级,
这里使用的是Nordic Semiconductor公司开源提供的第三方升级库:https://github.com/NordicSemiconductor/Android-DFU-Library
参考的其他博主的博客:https://www.jianshu.com/p/4017e7389804
OAT升级的流程:
1、使硬件设备进入DFU升级模式,进入升级模式的方式有很多,(每个项目可能都不一样,需要与硬件进行协商确定)
例如目前的项目:
1.设置设备的Indication
public boolean enableIndication(boolean enable) {
BluetoothGattService RxService = mBluetoothGatt.getService(UUID.fromString("0000fe59-0000-1000-8000-00805f9b34fb"));
if (RxService == null) {
showMessage("Rx service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
}
BluetoothGattCharacteristic characteristic = RxService.getCharacteristic(UUID.fromString("8ec90003-f315-4f60-9fb8-838830daea50"));
if (characteristic == null) {
showMessage("Tx charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
}
if (mBluetoothGatt == null || characteristic == null)
return false;
if (!mBluetoothGatt.setCharacteristicNotification(characteristic,
enable))
return false;
BluetoothGattDescriptor clientConfig = characteristic
.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (clientConfig == null)
return false;
if (enable) {
clientConfig
.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
} else {
clientConfig
.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
return mBluetoothGatt.writeDescriptor(clientConfig);
}
2.发送进入设备DFU模式的指令
byte[] value = new byte[1];
value[0] =0x01;
mService.writeDFUCharacteristic(value);
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void writeDFUCharacteristic(byte[] value)
{
// Log.e("蓝牙写入查看工作状态的命令", "写入的命令:" +value.toString());
if (mBluetoothGatt == null) {
showMessage("mBluetoothGatt null" + mBluetoothGatt);
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
BluetoothGattService RxService = mBluetoothGatt.getService(UUID.fromString("0000fe59-0000-1000-8000-00805f9b34fb"));
showMessage("mBluetoothGatt null"+ mBluetoothGatt);
if (RxService == null) {
showMessage("Rx service not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(UUID.fromString("8ec90003-f315-4f60-9fb8-838830daea50"));
if (RxChar == null) {
showMessage("Rx charateristic not found!");
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
RxChar.setValue(value);
boolean status = mBluetoothGatt.writeCharacteristic(RxChar);
Log.d(TAG, "write TXchar - status=" + status);
}
2、重新扫描,获取DFU模式下的MAC地址
重新进行扫描操作,因为当进入到DFU模式的时候,蓝牙的名称和MAC地址都会发生改变,获取DFU模式下的MAC地址。
3、使用Android-DFU-Library,发送升级包,进行DFU升级
使用Android-DFU-Library的流程:
- 添加依赖
implementation 'no.nordicsemi.android:dfu:1.10.3'
- 新建服务:DfuService ,并注册
package com.gzp.smartclothing.page.equipment.service; import android.app.Activity; import android.app.Service; import android.content.Intent; import android.os.IBinder; import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import com.gzp.smartclothing.BuildConfig; import com.gzp.smartclothing.page.equipment.view.NotificationActivity; import no.nordicsemi.android.dfu.DfuBaseService; public class DfuService extends DfuBaseService { @Override protected Class<? extends Activity> getNotificationTarget() { return NotificationActivity.class; } @Override protected boolean isDebug() { // Here return true if you want the service to print more logs in LogCat. // Library's BuildConfig in current version of Android Studio is always set to DEBUG=false, so // make sure you return true or your.app.BuildConfig.DEBUG here. return BuildConfig.DEBUG; } @Override protected void updateForegroundNotification(@NonNull final NotificationCompat.Builder builder) { // Customize the foreground service notification here. } }
- 通知Activity设置
package com.gzp.smartclothing.page.equipment.view; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; /** * @author lk */ public class NotificationActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // If this activity is the root activity of the task, the app is not running if (isTaskRoot()) { // Start the app before finishing final Intent intent = new Intent(this, ProductSetUpActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtras(getIntent().getExtras()); // copy all extras startActivity(intent); } // Now finish, which will drop you to the activity at which you were at the top of the task stack finish(); } }
- 在需要使用固件升级的Activity中创建一个 DfuProgressListener 并进行注册和反注册
private final DfuProgressListener mDfuProgressListener = new DfuProgressListener() { @Override public void onDeviceConnecting(String deviceAddress) { Log.i("dfu", "onDeviceConnecting"); } @Override public void onDeviceConnected(String deviceAddress) { Log.i("dfu", "onDeviceConnected"); } @Override public void onDfuProcessStarting(String deviceAddress) { Log.i("dfu", "onDfuProcessStarting"); } @Override public void onDfuProcessStarted(String deviceAddress) { Log.i("dfu", "onDfuProcessStarted"); } @Override public void onEnablingDfuMode(String deviceAddress) { Log.i("dfu", "onEnablingDfuMode"); } @Override public void onProgressChanged(String deviceAddress, int percent, float speed, float avgSpeed, int currentPart, int partsTotal) { Log.i("dfu", "onProgressChanged"); Log.i("dfu", "onProgressChanged" + percent); } @Override public void onFirmwareValidating(String deviceAddress) { Log.i("dfu", "onFirmwareValidating"); } @Override public void onDeviceDisconnecting(String deviceAddress) { Log.i("dfu", "onDeviceDisconnecting"); } @Override public void onDeviceDisconnected(String deviceAddress) { Log.i("dfu", "onDeviceDisconnected"); mState = UART_PROFILE_DISCONNECTED; } @Override public void onDfuCompleted(String deviceAddress) { Log.i("dfu", "onDfuCompleted"); //升级成功,重新连接设备 mSexualMuscleScanHandler.postDelayed(mSexualMuscleScanRunnable, 100);//延时100毫秒 } @Override public void onDfuAborted(String deviceAddress) { Log.i("dfu", "onDfuAborted"); //升级流产,失败 Toast.makeText(mContext, "升级流产,失败", Toast.LENGTH_SHORT).show(); } @Override public void onError(String deviceAddress, int error, int errorType, String message) { Log.i("dfu", "onError"); Toast.makeText(mContext, "升级失败。", Toast.LENGTH_SHORT).show(); } };
@Override protected void onResume() { DfuServiceListenerHelper.registerProgressListener(this, mDfuProgressListener); super.onResume(); } @Override protected void onPause() { DfuServiceListenerHelper.unregisterProgressListener(this, mDfuProgressListener); super.onPause(); }
- 开启 DfuService 进行升级
writeRXCharacteristicHandler = new Handler(Looper.getMainLooper()); writeRXCharacteristicHandler.postDelayed(new Runnable() { @Override public void run() { if (!curDfuTargMac.equals(dfuTargMac)){ curDfuTargMac = dfuTargMac; new Thread(new Runnable() { @Override public void run() { DfuServiceInitiator dfuServiceInitiator = new DfuServiceInitiator(curDfuTargMac); dfuServiceInitiator.createDfuNotificationChannel(ProductSetUpActivity.this); dfuServiceInitiator.setDisableNotification(false) .setZip(R.raw.test_file) .start((getBaseContext()), DfuService.class); } }).start(); } } }, 1500);
- 坐等升级完成
升级完成后,蓝牙会自动断开,若需使用,重新进行连接。
PS:在开启设备的Indication和进行升级操作的时候在可以的情况下,尽量的一些延迟(在升级的过程中会出现各种意外、升级失败、或者无法开启升级的情况)。