首先,进行一下科普:
1.BLE(Bluetooth Low Energy),蓝牙4.0核心profile,主要特点是快速搜索,快速连接,超低功耗保持连接和数据传输,缺点:数据传输速率低,由于其具有低功耗特点,故而经常用在可穿戴设备之中。Android4.3才开始支持BLE api。
2.关于BLE数据传输:
a.profile可以理解为一种规范,一个标准的通信协议,其存在于手机中,蓝牙组织规定了一些标准的profile:HID OVER GATT ,防丢器等,每个profile中包含了多个service。
b.service 可以理解为一个服务,在BLE从机中有多个服务,电量信息,系统服务信息等,每一个service中包含了多个characteristic特征值,每一个具体的characteristic特征值才是BLE通信的主题。
举个例子吧:从机当前的电量是80%,从机会借由电量的characteristic特征值将这个信息储存于从机的profile中,主机可以通过该characteristic来读取80%这个数据。
c.characteristic特征值:BLE主机从机通信均是通过characteristic进行,可以将其理解为一个标签,通过该标签可以读取或写入相关信息。
d. UUID(统一标识码):service和characteristic均需要这个唯一的UUID进行标识
/****************************************************************************************/
科普结束,首先上效果图:
这个就是实现后的效果,我用的BLE模块是:MicrodunioBT4.0
/*********************************************************分隔符********************************************************************************/
BLE设备和Android应用进行通信,首先要做的就是让Android应用找到BLE设备(代码分析部分,只对关键点进行注释)
package com.TANK.temperature.BT; import java.util.List; import com.TANK.temperature.R; import com.TANK.temperature.BT.BluetoothLeClass.OnDataAvailableListener; import com.TANK.temperature.BT.BluetoothLeClass.OnServiceDiscoverListener; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Typeface; import android.net.wifi.WifiConfiguration.Status; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.widget.TextView; import android.widget.Toast; public class DeviceScanActivity extends Activity { private final static String TAG = DeviceScanActivity.class.getSimpleName(); private final static String UUID_KEY_DATA = “0000fff6-0000-1000-8000-00805f9b34fb”; private Handler mHandler; private static final long SCAN_PERIOD = 10000; /** 搜索BLE终端 */
private BluetoothAdapter mBluetoothAdapter; private BluetoothLeClass mBLE; private boolean mScanning; private BluetoothDevice BTtoLink = null; private String BTtoFind = “Microduino”; private TextView BT_find, BT_info, BT_link;//三个textview分别表示:设备是否找到,BLE模块传输的信息,连接状态
private String S_BT_info = “null”, info = “111”;
@Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.temperature);
mHandler \= new Handler();
BT\_find \= (TextView) findViewById(R.id.BT\_find);
BT\_info \= (TextView) findViewById(R.id.BT\_info);
BT\_link \= (TextView) findViewById(R.id.BT\_link);
Typeface typeface = Typeface.createFromAsset(getAssets(), “fonts/maozedong.ttf”);//设置显示字体
BT_find.setTypeface(typeface);
BT_info.setTypeface(typeface);
BT_link.setTypeface(typeface);
BT\_find.setText("查找中");
BT\_info.setText("null");
BT\_link.setText("未连接"); if (!getPackageManager().hasSystemFeature( //判断主机是否支BLE牙设备
PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, “BLE is not supported”, Toast.LENGTH_SHORT)
.show();
finish();
} final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH\_SERVICE);//获得Android设备中的bluetoothmanager
mBluetoothAdapter = bluetoothManager.getAdapter();//获得bluetoothadapter
if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth not supported", Toast.LENGTH\_SHORT)
.show();
finish(); return;
}
mBluetoothAdapter.enable(); //强制使能Bluetoothadapter,打开Android设备蓝牙
mBLE = new BluetoothLeClass(this); //BLuetoothLeClass类
if (!mBLE.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
} // 发现BLE终端的Service时回调
mBLE.setOnServiceDiscoverListener(mOnServiceDiscover); // 收到BLE终端数据交互的事件
mBLE.setOnDataAvailableListener(mOnDataAvailable);
}
@Override protected void onResume() { // TODO Auto-generated method stub
super.onResume();
scanLeDevice(true);//搜索BLE设备
}
@Override protected void onPause() { // TODO Auto-generated method stub
super.onPause();
scanLeDevice(false);
mBLE.disconnect();
}
@Override protected void onStop() { // TODO Auto-generated method stub
super.onStop();
mBLE.close();
} private void scanLeDevice(final boolean enable) { // TODO Auto-generated method stub
if (enable) {
mHandler.postDelayed(new Runnable() {
@Override public void run() { // TODO Auto-generated method stub
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN\_PERIOD);//在搜索时间内,关闭搜索标志,不对搜索回调函数进行响应
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning \= false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
} /\*\* \* 搜索到BLE终端服务的事件 \*/
private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener() {
@Override public void onServiceDiscover(BluetoothGatt gatt) {
displayGattServices(mBLE.getSupportedGattService());
}
}; /\*\* \* 收到BLE终端数据交互的事件 \*/
private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener() { /\*\* \* BLE终端数据写入的事件 \*/ @Override public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT\_SUCCESS)
Log.e(TAG, "onCharRead "
+ gatt.getDevice().getName() \+ " read "
+ characteristic.getUuid().toString() \+ " -> "
+ Utils.bytesToHexString(characteristic
.getValue()));
} /\*\* \* 对BLE终端读取数据 \*/ @Override public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
info \= new String(characteristic.getValue());//对得到的byte数组进行解码,构造新的string
Log.e(TAG, "onCharWrite " + gatt.getDevice().getName() + " write "
+ characteristic.getUuid().toString() + " -> " + info); if (!S\_BT\_info.equals(info)) {//判断读取的数据是否发生变化,如果变化,更新UI
DeviceScanActivity.this.runOnUiThread(new Runnable() {
@Override public void run() { // TODO Auto-generated method stub
BT\_link.setText("已连接");
StringBuilder sb \= new StringBuilder();//详情参见:http://blog.csdn.net/rmn190/article/details/1492013
sb.append(info);
sb.append(“度”);
BT_info.setText(sb.toString());
sb = null;
}
});
}
}
}; private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {//搜索回调函数:
String BT\_name = null;
@Override public void onLeScan(final BluetoothDevice device, int rssi, byte\[\] scanRecord) { // TODO Auto-generated method stub
runOnUiThread(new Runnable() {
@Override public void run() { // TODO Auto-generated method stub
BT\_name = device.getName(); if (BT\_name.equals(BTtoFind)) { //如果是要找的设备,更新UI上信息,设置搜索标志,停止响应搜索回调函数,连接BLE设备
/\*\* 连接事件 \*/ BT\_find.setText("已找到设备!");
mScanning \= false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mBLE.connect(device.getAddress());
}
}
});
}
}; private void displayGattServices(BluetoothGattService bluetoothGattService) { if (bluetoothGattService == null) return; // -----Service的字段信息-----//
int type = bluetoothGattService.getType();
Log.e(TAG, "-->service type:" + Utils.getServiceType(type));
Log.e(TAG, "-->includedServices size:"
+ bluetoothGattService.getIncludedServices().size());
Log.e(TAG, "-->service uuid:" + bluetoothGattService.getUuid()); // -----Characteristics的字段信息-----//
List<BluetoothGattCharacteristic> gattCharacteristics = bluetoothGattService
.getCharacteristics(); for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
Log.e(TAG, "---->char uuid:" + gattCharacteristic.getUuid()); int permission = gattCharacteristic.getPermissions();
Log.e(TAG, "---->char permission:"
+ Utils.getCharPermission(permission)); int property = gattCharacteristic.getProperties();
Log.e(TAG, "---->char property:" + Utils.getCharPropertie(property)); byte\[\] data = gattCharacteristic.getValue(); if (data != null && data.length > 0) {
Log.e(TAG, "---->char value:" + new String(data));
} // UUID\_KEY\_DATA是可以跟蓝牙模块串口通信的Characteristic
if (gattCharacteristic.getUuid().toString().equals(UUID\_KEY\_DATA)) { // 测试读取当前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead()
mHandler.postDelayed(new Runnable() {
@Override public void run() {
mBLE.readCharacteristic(gattCharacteristic);
}
}, 500); // 接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
mBLE.setCharacteristicNotification(gattCharacteristic, true); // 设置数据内容
gattCharacteristic.setValue("send data->"); // 往蓝牙模块写入数据
mBLE.writeCharacteristic(gattCharacteristic);
} // —–Descriptors的字段信息—–//
List
.getDescriptors(); for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) {
Log.e(TAG, “——–>desc uuid:” + gattDescriptor.getUuid()); int descPermission = gattDescriptor.getPermissions();
Log.e(TAG, “——–>desc permission:”
+ Utils.getDescPermission(descPermission)); byte[] desData = gattDescriptor.getValue(); if (desData != null && desData.length > 0) {
Log.e(TAG, “——–>desc value:” + new String(desData));
}
}
}
}
}
package com.TANK.temperature.BT; import java.util.UUID; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.util.Log; public class BluetoothLeClass{ private final static String TAG = BluetoothLeClass.class.getSimpleName(); private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; public interface OnConnectListener { public void onConnect(BluetoothGatt gatt);
} public interface OnDisconnectListener { public void onDisconnect(BluetoothGatt gatt);
} public interface OnServiceDiscoverListener { public void onServiceDiscover(BluetoothGatt gatt);
} public interface OnDataAvailableListener { public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status); public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic);
} private OnConnectListener mOnConnectListener; private OnDisconnectListener mOnDisconnectListener; private OnServiceDiscoverListener mOnServiceDiscoverListener; private OnDataAvailableListener mOnDataAvailableListener; private Context mContext; public void setOnConnectListener(OnConnectListener l){
mOnConnectListener = l;
} public void setOnDisconnectListener(OnDisconnectListener l){
mOnDisconnectListener = l;
} public void setOnServiceDiscoverListener(OnServiceDiscoverListener l){
mOnServiceDiscoverListener = l;
} public void setOnDataAvailableListener(OnDataAvailableListener l){
mOnDataAvailableListener = l;
} public BluetoothLeClass(Context c){
mContext = c;
} // Implements callback methods for GATT events that the app cares about. For example, // connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { if(mOnConnectListener!=null)
mOnConnectListener.onConnect(gatt);
Log.i(TAG, “Connected to GATT server.”); // Attempts to discover services after successful connection.
Log.i(TAG, “Attempting to start service discovery:” + mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE\_DISCONNECTED) { if(mOnDisconnectListener!=null)
mOnDisconnectListener.onDisconnect(gatt);
Log.i(TAG, "Disconnected from GATT server.");
}
}
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT\_SUCCESS && mOnServiceDiscoverListener!=null) {
mOnServiceDiscoverListener.onServiceDiscover(gatt);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) { if (mOnDataAvailableListener!=null)
mOnDataAvailableListener.onCharacteristicRead(gatt, characteristic, status);
}
@Override public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) { if (mOnDataAvailableListener!=null)
mOnDataAvailableListener.onCharacteristicWrite(gatt, characteristic);
}
}; /\*\* \* Initializes a reference to the local Bluetooth adapter.
\*
\* @return Return true if the initialization is successful. \*/
public boolean initialize() { // For API level 18 and above, get a reference to BluetoothAdapter through // BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager \= (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH\_SERVICE); if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager."); return false;
}
}
mBluetoothAdapter \= mBluetoothManager.getAdapter(); if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter."); return false;
} return true;
} /\*\* \* Connects to the GATT server hosted on the Bluetooth LE device.
\*
\* @param address The device address of the destination device.
\*
\* @return Return true if the connection is initiated successfully. The connection result
\* is reported asynchronously through the
\* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
\* callback. \*/
public boolean connect(final String address) { if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false;
} // Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); if (mBluetoothGatt.connect()) { return true;
} else { return false;
}
} final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) {
Log.w(TAG, "Device not found. Unable to connect."); return false;
} // We want to directly connect to the device, so we are setting the autoConnect // parameter to false.
mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
Log.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress \= address; return true;
} /\*\* \* Disconnects an existing connection or cancel a pending connection. The disconnection result
\* is reported asynchronously through the
\* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
\* callback. \*/
public void disconnect() { if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized"); return;
}
mBluetoothGatt.disconnect();
} /\*\* \* After using a given BLE device, the app must call this method to ensure resources are
\* released properly. \*/
public void close() { if (mBluetoothGatt == null) { return;
}
mBluetoothGatt.close();
mBluetoothGatt \= null;
} /\*\* \* Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
\* asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
\* callback.
\*
\* @param characteristic The characteristic to read from. \*/
public void readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized"); return;
}
mBluetoothGatt.readCharacteristic(characteristic);
} /\*\* \* Enables or disables notification on a give characteristic.
\*
\* @param characteristic Characteristic to act on.
\* @param enabled If true, enable notification. False otherwise. \*/
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,//便于更新数据
boolean enabled) { if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized"); return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
} public void writeCharacteristic(BluetoothGattCharacteristic characteristic){
mBluetoothGatt.writeCharacteristic(characteristic);
} /\*\* \* Retrieves a list of supported GATT services on the connected device. This should be
\* invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
\*
\* @return A {@code List} of supported services. \*/
public BluetoothGattService getSupportedGattService() {//根据service的UUID来获取service
if (mBluetoothGatt == null) return null; return mBluetoothGatt.getService(UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb"));
}
}
public class Utils { private static HashMap<Integer, String> serviceTypes = new HashMap(); static { // Sample Services.
serviceTypes.put(BluetoothGattService.SERVICE_TYPE_PRIMARY, “PRIMARY”);
serviceTypes.put(BluetoothGattService.SERVICE_TYPE_SECONDARY, “SECONDARY”);
} public static String getServiceType(int type){ return serviceTypes.get(type);
} //-——————————————
private static HashMap<Integer, String> charPermissions = new HashMap(); static {
charPermissions.put(0, “UNKNOW”);
charPermissions.put(BluetoothGattCharacteristic.PERMISSION_READ, “READ”);
charPermissions.put(BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED, “READ_ENCRYPTED”);
charPermissions.put(BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED_MITM, “READ_ENCRYPTED_MITM”);
charPermissions.put(BluetoothGattCharacteristic.PERMISSION_WRITE, “WRITE”);
charPermissions.put(BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED, “WRITE_ENCRYPTED”);
charPermissions.put(BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED_MITM, “WRITE_ENCRYPTED_MITM”);
charPermissions.put(BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED, “WRITE_SIGNED”);
charPermissions.put(BluetoothGattCharacteristic.PERMISSION_WRITE_SIGNED_MITM, “WRITE_SIGNED_MITM”);
} public static String getCharPermission(int permission){ return getHashMapValue(charPermissions,permission);
} //-——————————————
private static HashMap<Integer, String> charProperties = new HashMap(); static {
charProperties.put(BluetoothGattCharacteristic.PROPERTY\_BROADCAST, "BROADCAST");
charProperties.put(BluetoothGattCharacteristic.PROPERTY\_EXTENDED\_PROPS, "EXTENDED\_PROPS");
charProperties.put(BluetoothGattCharacteristic.PROPERTY\_INDICATE, "INDICATE");
charProperties.put(BluetoothGattCharacteristic.PROPERTY\_NOTIFY, "NOTIFY");
charProperties.put(BluetoothGattCharacteristic.PROPERTY\_READ, "READ");
charProperties.put(BluetoothGattCharacteristic.PROPERTY\_SIGNED\_WRITE, "SIGNED\_WRITE");
charProperties.put(BluetoothGattCharacteristic.PROPERTY\_WRITE, "WRITE");
charProperties.put(BluetoothGattCharacteristic.PROPERTY\_WRITE\_NO\_RESPONSE, "WRITE\_NO\_RESPONSE");
} public static String getCharPropertie(int property){ return getHashMapValue(charProperties,property);
} //\--------------------------------------------------------------------------
private static HashMap<Integer, String> descPermissions = new HashMap(); static {
descPermissions.put(0, "UNKNOW");
descPermissions.put(BluetoothGattDescriptor.PERMISSION\_READ, "READ");
descPermissions.put(BluetoothGattDescriptor.PERMISSION\_READ\_ENCRYPTED, "READ\_ENCRYPTED");
descPermissions.put(BluetoothGattDescriptor.PERMISSION\_READ\_ENCRYPTED\_MITM, "READ\_ENCRYPTED\_MITM");
descPermissions.put(BluetoothGattDescriptor.PERMISSION\_WRITE, "WRITE");
descPermissions.put(BluetoothGattDescriptor.PERMISSION\_WRITE\_ENCRYPTED, "WRITE\_ENCRYPTED");
descPermissions.put(BluetoothGattDescriptor.PERMISSION\_WRITE\_ENCRYPTED\_MITM, "WRITE\_ENCRYPTED\_MITM");
descPermissions.put(BluetoothGattDescriptor.PERMISSION\_WRITE\_SIGNED, "WRITE\_SIGNED");
descPermissions.put(BluetoothGattDescriptor.PERMISSION\_WRITE\_SIGNED\_MITM, "WRITE\_SIGNED\_MITM");
} public static String getDescPermission(int property){ return getHashMapValue(descPermissions,property);
} //这段代码没看明白,欢迎大神指教
private static String getHashMapValue(HashMap<Integer, String> hashMap,int number){
String result \=hashMap.get(number); if(TextUtils.isEmpty(result)){
List<Integer> numbers = getElement(number);
result\=""; for(int i=0;i<numbers.size();i++){
result+=hashMap.get(numbers.get(i))+"|";
}
} return result;
} /\*\* \* 位运算结果的反推函数10 -> 2 | 8; \*/
static private List<Integer> getElement(int number){
List<Integer> result = new ArrayList<Integer>(); for (int i = 0; i < 32; i++){ int b = 1 << i; if ((number & b) > 0)
result.add(b);
} return result;
} public static String bytesToHexString(byte\[\] src){
StringBuilder stringBuilder \= new StringBuilder(""); if (src == null || src.length <= 0) { return null;
} for (int i = 0; i < src.length; i++) { int v = src\[i\] & 0xFF;
String hv \= Integer.toHexString(v); if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
} return stringBuilder.toString();
}
}