zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android提高之Android手机与BLE终端通信

Android手机通信 提高 终端 BLE
2023-06-13 09:15:41 时间

最近穿戴设备发展得很火,把相关技术也带旺了,其中一项是BLE(BluetoothLowEnergy)。BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。Android4.3才开始支持BLEAPI,所以请各位客官把本文代码运行在蓝牙4.0和Android4.3及其以上的系统,另外本文所用的BLE终端是一个蓝牙4.0的串口蓝牙模块。

注:笔者的i9100刷了4.4系统后,竟然也能跟BLE蓝牙模块通信。

BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多个Descriptor,一个Descriptor包含一个Value。一般来说,Characteristic是手机与BLE终端交换数据的关键,Characteristic有较多的跟权限相关的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE蓝牙模块竟然没有标准的Characteristic的PERMISSION。Characteristic的PROPERTY可以通过位运算符组合来设置读写属性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此读取PROPERTY后要分解成所用的组合(本文代码已含此分解方法)。

接下来贴出本文运行的结果,首先是连接BLE设备后,枚举出设备所有Service、Characteristic、Descriptor,并且手机会往Characteristicuuid=0000ffe1-0000-1000-8000-00805f9b34fb写入“senddata->”字符串,BLE终端收到数据通过串口传到PC串口助手:
04-2118:28:25.465:E/DeviceScanActivity(12254):-->servicetype:PRIMARY
04-2118:28:25.465:E/DeviceScanActivity(12254):-->includedServicessize:0
04-2118:28:25.465:E/DeviceScanActivity(12254):-->serviceuuid:00001800-0000-1000-8000-00805f9b34fb
04-2118:28:25.465:E/DeviceScanActivity(12254):---->charuuid:00002a00-0000-1000-8000-00805f9b34fb
04-2118:28:25.465:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.465:E/DeviceScanActivity(12254):---->charproperty:READ
04-2118:28:25.465:E/DeviceScanActivity(12254):---->charuuid:00002a01-0000-1000-8000-00805f9b34fb
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charproperty:READ
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charuuid:00002a02-0000-1000-8000-00805f9b34fb
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charproperty:READ|WRITE|
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charuuid:00002a03-0000-1000-8000-00805f9b34fb
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.475:E/DeviceScanActivity(12254):---->charproperty:READ|WRITE|
04-2118:28:25.475:E/DeviceScanActivity(12254):---->charuuid:00002a04-0000-1000-8000-00805f9b34fb
04-2118:28:25.475:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.475:E/DeviceScanActivity(12254):---->charproperty:READ
04-2118:28:25.475:E/DeviceScanActivity(12254):-->servicetype:PRIMARY
04-2118:28:25.475:E/DeviceScanActivity(12254):-->includedServicessize:0
04-2118:28:25.475:E/DeviceScanActivity(12254):-->serviceuuid:00001801-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charuuid:00002a05-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charproperty:INDICATE
04-2118:28:25.480:E/DeviceScanActivity(12254):-------->descuuid:00002902-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):-------->descpermission:UNKNOW
04-2118:28:25.480:E/DeviceScanActivity(12254):-->servicetype:PRIMARY
04-2118:28:25.480:E/DeviceScanActivity(12254):-->includedServicessize:0
04-2118:28:25.480:E/DeviceScanActivity(12254):-->serviceuuid:0000ffe0-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charuuid:0000ffe1-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charproperty:READ|WRITE_NO_RESPONSE|NOTIFY|
04-2118:28:25.490:E/DeviceScanActivity(12254):-------->descuuid:00002902-0000-1000-8000-00805f9b34fb
04-2118:28:25.490:E/DeviceScanActivity(12254):-------->descpermission:UNKNOW
04-2118:28:25.490:E/DeviceScanActivity(12254):-------->descuuid:00002901-0000-1000-8000-00805f9b34fb
04-2118:28:25.490:E/DeviceScanActivity(12254):-------->descpermission:UNKNOW
04-2118:28:26.025:E/DeviceScanActivity(12254):onCharReadBLEDEVICEread0000ffe1-0000-1000-8000-00805f9b34fb->00
这里红字是由BluetoothGattCallback的onCharacteristicRead()回调而打出Log

以下Log是PC上的串口工具通过BLE模块发送过来,由BluetoothGattCallback的onCharacteristicChanged()打出Log
04-2118:30:18.260:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:18.745:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:19.085:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:19.350:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:19.605:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:19.835:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:20.055:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:20.320:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:20.510:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:20.735:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:21.000:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone

接下来贴出本文核心代码:

publicclassDeviceScanActivityextendsListActivity{
privatefinalstaticStringTAG=DeviceScanActivity.class.getSimpleName();
privatefinalstaticStringUUID_KEY_DATA="0000ffe1-0000-1000-8000-00805f9b34fb";

privateLeDeviceListAdaptermLeDeviceListAdapter;
/**搜索BLE终端*/
privateBluetoothAdaptermBluetoothAdapter;
/**读写BLE终端*/
privateBluetoothLeClassmBLE;
privatebooleanmScanning;
privateHandlermHandler;

//Stopsscanningafter10seconds.
privatestaticfinallongSCAN_PERIOD=10000;

@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
getActionBar().setTitle(R.string.title_devices);
mHandler=newHandler();
//UsethischecktodeterminewhetherBLEissupportedonthedevice.Thenyoucan
//selectivelydisableBLE-relatedfeatures.
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
Toast.makeText(this,R.string.ble_not_supported,Toast.LENGTH_SHORT).show();
finish();
}
//InitializesaBluetoothadapter.ForAPIlevel18andabove,getareferenceto
//BluetoothAdapterthroughBluetoothManager.
finalBluetoothManagerbluetoothManager=
(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter=bluetoothManager.getAdapter();
//ChecksifBluetoothissupportedonthedevice.
if(mBluetoothAdapter==null){
Toast.makeText(this,R.string.error_bluetooth_not_supported,Toast.LENGTH_SHORT).show();
finish();
return;
}
//开启蓝牙
mBluetoothAdapter.enable();
mBLE=newBluetoothLeClass(this);
if(!mBLE.initialize()){
Log.e(TAG,"UnabletoinitializeBluetooth");
finish();
}
//发现BLE终端的Service时回调
mBLE.setOnServiceDiscoverListener(mOnServiceDiscover);
//收到BLE终端数据交互的事件
mBLE.setOnDataAvailableListener(mOnDataAvailable);
}
@Override
protectedvoidonResume(){
super.onResume();

//Initializeslistviewadapter.
mLeDeviceListAdapter=newLeDeviceListAdapter(this);
setListAdapter(mLeDeviceListAdapter);
scanLeDevice(true);
}
@Override
protectedvoidonPause(){
super.onPause();
scanLeDevice(false);
mLeDeviceListAdapter.clear();
mBLE.disconnect();
}
@Override
protectedvoidonStop(){
super.onStop();
mBLE.close();
}
@Override
protectedvoidonListItemClick(ListViewl,Viewv,intposition,longid){
finalBluetoothDevicedevice=mLeDeviceListAdapter.getDevice(position);
if(device==null)return;
if(mScanning){
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mScanning=false;
}
mBLE.connect(device.getAddress());
}
privatevoidscanLeDevice(finalbooleanenable){
if(enable){
//Stopsscanningafterapre-definedscanperiod.
mHandler.postDelayed(newRunnable(){
@Override
publicvoidrun(){
mScanning=false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
invalidateOptionsMenu();
}
},SCAN_PERIOD);
mScanning=true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
}else{
mScanning=false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
/**
*搜索到BLE终端服务的事件
*/
privateBluetoothLeClass.OnServiceDiscoverListenermOnServiceDiscover=newOnServiceDiscoverListener(){

@Override
publicvoidonServiceDiscover(BluetoothGattgatt){
displayGattServices(mBLE.getSupportedGattServices());
}
};
/**
*收到BLE终端数据交互的事件
*/
privateBluetoothLeClass.OnDataAvailableListenermOnDataAvailable=newOnDataAvailableListener(){
/**
*BLE终端数据被读的事件
*/
@Override
publicvoidonCharacteristicRead(BluetoothGattgatt,
BluetoothGattCharacteristiccharacteristic,intstatus){
if(status==BluetoothGatt.GATT_SUCCESS)
Log.e(TAG,"onCharRead"+gatt.getDevice().getName()
+"read"
+characteristic.getUuid().toString()
+"->"
+Utils.bytesToHexString(characteristic.getValue()));
}
/**
*收到BLE终端写入数据回调
*/
@Override
publicvoidonCharacteristicWrite(BluetoothGattgatt,
BluetoothGattCharacteristiccharacteristic){
Log.e(TAG,"onCharWrite"+gatt.getDevice().getName()
+"write"
+characteristic.getUuid().toString()
+"->"
+newString(characteristic.getValue()));
}
};
//Devicescancallback.
privateBluetoothAdapter.LeScanCallbackmLeScanCallback=
newBluetoothAdapter.LeScanCallback(){
@Override
publicvoidonLeScan(finalBluetoothDevicedevice,intrssi,byte[]scanRecord){
runOnUiThread(newRunnable(){
@Override
publicvoidrun(){
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
privatevoiddisplayGattServices(List<BluetoothGattService>gattServices){
if(gattServices==null)return;
for(BluetoothGattServicegattService:gattServices){
//-----Service的字段信息-----//
inttype=gattService.getType();
Log.e(TAG,"-->servicetype:"+Utils.getServiceType(type));
Log.e(TAG,"-->includedServicessize:"+gattService.getIncludedServices().size());
Log.e(TAG,"-->serviceuuid:"+gattService.getUuid());

//-----Characteristics的字段信息-----//
List<BluetoothGattCharacteristic>gattCharacteristics=gattService.getCharacteristics();
for(finalBluetoothGattCharacteristicgattCharacteristic:gattCharacteristics){
Log.e(TAG,"---->charuuid:"+gattCharacteristic.getUuid());
intpermission=gattCharacteristic.getPermissions();
Log.e(TAG,"---->charpermission:"+Utils.getCharPermission(permission));
intproperty=gattCharacteristic.getProperties();
Log.e(TAG,"---->charproperty:"+Utils.getCharPropertie(property));
byte[]data=gattCharacteristic.getValue();
if(data!=null&&data.length>0){
Log.e(TAG,"---->charvalue:"+newString(data));
}
//UUID_KEY_DATA是可以跟蓝牙模块串口通信的Characteristic
if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){
//测试读取当前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead()
mHandler.postDelayed(newRunnable(){
@Override
publicvoidrun(){
mBLE.readCharacteristic(gattCharacteristic);
}
},500);
//接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
mBLE.setCharacteristicNotification(gattCharacteristic,true);
//设置数据内容
gattCharacteristic.setValue("senddata->");
//往蓝牙模块写入数据
mBLE.writeCharacteristic(gattCharacteristic);
}
//-----Descriptors的字段信息-----//
List<BluetoothGattDescriptor>gattDescriptors=gattCharacteristic.getDescriptors();
for(BluetoothGattDescriptorgattDescriptor:gattDescriptors){
Log.e(TAG,"-------->descuuid:"+gattDescriptor.getUuid());
intdescPermission=gattDescriptor.getPermissions();
Log.e(TAG,"-------->descpermission:"+Utils.getDescPermission(descPermission));
byte[]desData=gattDescriptor.getValue();
if(desData!=null&&desData.length>0){
Log.e(TAG,"-------->descvalue:"+newString(desData));
}
}
}
}//
}
}

感兴趣的读者可以动手测试一下代码的运行情况,希望能对大家的Android项目开发有所帮助。