由于公司前段時間有一個項目要用到藍牙BLE技術,才開始研究Ble技術,在網上也找了很多文章查看,基本的藍牙連接通訊都有,就是出現的問題解答比較少,在這里說說個人遇到的問題。
129錯誤
133錯誤
分包發(fā)送數據
自定義數據交互格式
首次連接速度慢
藍牙防丟器原理
Ble連接數據交互整個流程
在網上找了許多,基本都沒有說明129錯誤在BLE中代表什么。個人測試后,猜測129類似于連接藍牙,獲取服務超時的返回值,一般存在于藍牙信號比較弱或者藍牙附近存在電池,變壓器等設備影響到藍牙信號時
'''
代碼塊
//連接藍牙成功后,獲取服務
mBluetoothGatt.discoverServices()
// 發(fā)現新服務
? ? ? ? @Override
? ? ? ? public void onServicesDiscovered(BluetoothGatt gatt, int status) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? if (status == BluetoothGatt.GATT_SUCCESS
? ? ? ? ? ? ? ? ? ? ? ? && mOnServiceDiscoverListener != null) {
? ? ? ? ? ? ? ? ? ? mOnServiceDiscoverListener.onServiceDiscover(gatt);
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? LogUtil.i(TAG, System.currentTimeMillis() + "發(fā)現新服務端失敗"
? ? ? ? ? ? ? ? ? ? ? ? ? ? + status);
? ? ? ? ? ? ? ? ? ? close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
'''
133錯誤
跟129錯誤類似,表示獲取服務失敗的返回值
//連接藍牙成功后,獲取服務
mBluetoothGatt.discoverServices()
'''
// 發(fā)現新服務
? ? ? ? @Override
? ? ? ? public void onServicesDiscovered(BluetoothGatt gatt, int status) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? if (status == BluetoothGatt.GATT_SUCCESS
? ? ? ? ? ? ? ? ? ? ? ? && mOnServiceDiscoverListener != null) {
? ? ? ? ? ? ? ? ? ? mOnServiceDiscoverListener.onServiceDiscover(gatt);
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? LogUtil.i(TAG, System.currentTimeMillis() + "發(fā)現新服務端失敗"
? ? ? ? ? ? ? ? ? ? ? ? ? ? + status);
? ? ? ? ? ? ? ? ? ? close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
'''
分包發(fā)送數據
藍牙BLE支持的數據發(fā)送每包只能20字節(jié),所以想要發(fā)送比較大的數據包只能通過分包發(fā)送。注意:如果你在要發(fā)送數據的地方直接一個for循環(huán)把所有分包的數據發(fā)完,藍牙設備那邊也只會收到一包數據而已,再此有兩種方法解決:
1、在for循環(huán)中每發(fā)一包數據加一個20毫秒的休眠時間,缺點:時間不可控,容易導致丟包。
2、每發(fā)完一包數據,成功發(fā)送后系統(tǒng)會進入onCharacteristicWrite回調里面,在這里進行發(fā)送第二包和第二包之后的數據(代碼如下)。
'''
// 發(fā)送大于20字節(jié)的包(第一包數據)
? ? public void sendPackFromOut20(BluetoothGatt gatt) {
? ? ? ? sendLength = con.length;
? ? ? ? sendtabLength = 20;
? ? ? ? byte[] c = new byte[20];
? ? ? ? for (int i = 0; i < 20; i++) {
? ? ? ? ? ? c[i] = con[i];
? ? ? ? }
? ? ? ? BluetoothGattService RxService = gatt.getService(UUID
? ? ? ? ? ? ? ? .fromString(BluetoothLeClass.UUID_KEY_Data));
? ? ? ? BluetoothGattCharacteristic TxChar = RxService.getCharacteristic(UUID
? ? ? ? ? ? ? ? .fromString(BluetoothLeClass.UUID_KEY_Write));
? ? ? ? TxChar.setValue(c);
? ? ? ? mBLE.writeCharacteristic(TxChar);
? ? }
? ? //超過第一包的數據在上一包數據發(fā)送成功后系統(tǒng)回調里面發(fā)
? ? /**
? ? ? * 寫入BLE終端數據成功回調
? ? ? */
? ? ? ? @Override
? ? ? ? public void onCharacteristicWrite(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattCharacteristic characteristic) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? LogUtil.i(TAG, new String(characteristic.getValue()));
? ? ? ? ? ? ? ? ? ? int s = sendLength - sendtabLength;
? ? ? ? ? ? ? ? ? ? if (s > 0) {
? ? ? ? ? ? ? ? ? ? ? ? // 發(fā)送con未發(fā)送完的數據
? ? ? ? ? ? ? ? ? ? ? ? byte[] c;
? ? ? ? ? ? ? ? ? ? ? ? if (s > 20) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? c = new byte[20];
? ? ? ? ? ? ? ? ? ? ? ? ? ? for (int i = 0; i < 20; i++) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? c[i] = con[i + sendtabLength];
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? sendtabLength += 20;
? ? ? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? c = new byte[s];
? ? ? ? ? ? ? ? ? ? ? ? ? ? for (int i = 0; i < s; i++) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? c[i] = con[i + sendtabLength];
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? sendtabLength += s;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? BluetoothGattService RxService = gatt.getService(UUID
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .fromString(BluetoothLeClass.UUID_KEY_Data));
? ? ? ? ? ? ? ? ? ? ? ? BluetoothGattCharacteristic TxChar = RxService
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .getCharacteristic(UUID
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .fromString(BluetoothLeClass.UUID_KEY_Write));
? ? ? ? ? ? ? ? ? ? ? ? TxChar.setValue(c);
? ? ? ? ? ? ? ? ? ? ? ? mBLE.writeCharacteristic(TxChar);
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? sendLength = -1;
? ? ? ? ? ? ? ? ? ? ? ? sendtabLength = -1;
? ? ? ? ? ? ? ? ? ? ? ? LogUtil.i(TAG, "所有包已發(fā)完!");
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
'''
自定義數據交互格式
這個類似于android跟http數據交互的格式,無非就是定義一些樣式讓android和藍牙模塊兩邊都能識別,為了安全起見可以對數據進行加密處理。由于我這邊是跟藍牙單片機進行交互的,藍牙單片機不支持String,只能通過byte[]數組按位去組裝數據交互。(結構如下)
數據格式:[起始碼][長度碼] [命令碼] [數據碼] [校驗碼]
起始碼 :固定為 0XAA
長度碼 :命令碼+數據碼+校驗碼
命令碼,數據碼(自定義數據接口)
校驗碼 :一個字節(jié),前面所有數據值相加之和
加密:self加密和DES加密對整個數據碼進行加密
由于藍牙首次連接要配置緩沖所有廣播的服務,所以一般第一次連接都比較慢,如果想要加快第一次連接速度,只能讓藍牙模塊關閉部分不必要的廣播。
藍牙防丟器的原理其實很簡單,就是通過手機app定時去掃描你配置好的藍牙單片機,如果掃描到該藍牙單片機返回的rssi值(藍牙的信號值)超出app設定的值,則app將會報警;或者掃不到該單片機app也會報警。(代碼如下)
'''
代碼塊
// 初始化藍牙
private void initBLE() {
? ? ? ? // TODO Auto-generated method stub
? ? ? ? ? ? // 檢查當前手機是否支持ble 藍牙,如果不支持退出程序
? ? ? ? ? ? if (!mContext.getPackageManager().hasSystemFeature(
? ? ? ? ? ? ? ? ? ? PackageManager.FEATURE_BLUETOOTH_LE)) {
? ? ? ? ? ? ? ? Toast.makeText(mContext, "手機不支持ble藍牙", Toast.LENGTH_SHORT)
? ? ? ? ? ? ? ? ? ? ? ? .show();
? ? ? ? ? ? }
? ? ? ? ? ? // 初始化 Bluetooth adapter,
? ? ? ? ? ? // 通過藍牙管理器得到一個參考藍牙適配器(API必須在以上android4.3或以上和版本)
? ? ? ? ? ? final BluetoothManager bluetoothManager = (BluetoothManager) mContext
? ? ? ? ? ? ? ? ? ? .getSystemService(Context.BLUETOOTH_SERVICE);
? ? ? ? ? ? mBluetoothAdapter = bluetoothManager.getAdapter();
? ? ? ? ? ? // 檢查設備上是否支持藍牙
? ? ? ? ? ? if (mBluetoothAdapter == null) {
? ? ? ? ? ? ? ? Toast.makeText(mContext, "手機不支持藍牙", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? // 打開藍牙
? ? ? ? ? ? if (!mBluetoothAdapter.isEnabled()) {
? ? ? ? ? ? ? ? mBluetoothAdapter.enable();
? ? ? ? ? ? }
? ? ? ? ? ? // 開始掃描
? ? ? ? ? ? if (dAddress.size() > 0) {
? ? ? ? ? ? ? ? dAddress.clear();
? ? ? ? ? ? }
? ? ? ? ? ? if (rssiDistance.size() > 0) {
? ? ? ? ? ? ? ? rssiDistance.clear();
? ? ? ? ? ? }
? ? ? ? ? ? if (!mScanning) {
? ? ? ? ? ? ? ? mScanning = true;
? ? ? ? ? ? ? ? scanLeDevice(true);
? ? ? ? ? ? }
? ? }
// 掃描
private void scanLeDevice(final boolean enable) {
? ? ? ? if (enable) {
? ? ? ? ? ? // Stops scanning after a pre-defined scan period.
? ? ? ? ? ? mHandler.postDelayed(new Runnable() {
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? mScanning = false;
? ? ? ? ? ? ? ? ? ? mBluetoothAdapter.stopLeScan(mLeScanCallback);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }, SCAN_PERIOD);
? ? ? ? ? ? mScanning = true;
? ? ? ? ? ? boolean scan = mBluetoothAdapter.startLeScan(mLeScanCallback);
? ? ? ? } else {
? ? ? ? ? ? mScanning = false;
? ? ? ? ? ? mBluetoothAdapter.stopLeScan(mLeScanCallback);
? ? ? ? }
? ? }
// 設備掃描
? ? private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
? ? ? ? @Override
? ? ? ? public void onLeScan(final BluetoothDevice device, int rssi,
? ? ? ? ? ? ? ? byte[] scanRecord) {
? ? ? ? ? ? LogUtil.i(
? ? ? ? ? ? ? ? ? ? TAG,
? ? ? ? ? ? ? ? ? ? device.getAddress() + ","
? ? ? ? ? ? ? ? ? ? ? ? ? ? + (int) BLEUtil.RssiToDistance(rssi));
? ? ? ? ? ? if (!dAddress.contains(device.getAddress())) {
? ? ? ? ? ? //對比獲取到的設備的mac地址和rssi值進行處理
? ? ? ? ? ? ? ? dAddress.add(device.getAddress());
? ? ? ? ? ? ? ? rssiDistance.put(device.getAddress(),
? ? ? ? ? ? ? ? ? ? ? ? (int) BLEUtil.RssiToDistance(rssi));
? ? ? ? ? ? }
? ? ? ? }
? ? };
'''
Ble連接數據交互整個流程
藍牙Ble連接跟藍牙防丟器的原理其實是一樣的,先啟動藍牙,然后掃面附近的藍牙(只有android5.0+的手機的藍牙才會被掃描到,或支持ble的藍牙單片機),拿到所有掃描的藍牙進行篩選匹配到你想要連接的藍牙,然后連接。(代碼如下)
'''
代碼塊
// 初始化藍牙
private void initBLE() {
? ? ? ? // TODO Auto-generated method stub
? ? ? ? ? ? // 檢查當前手機是否支持ble 藍牙,如果不支持退出程序
? ? ? ? ? ? if (!mContext.getPackageManager().hasSystemFeature(
? ? ? ? ? ? ? ? ? ? PackageManager.FEATURE_BLUETOOTH_LE)) {
? ? ? ? ? ? ? ? Toast.makeText(mContext, "手機不支持ble藍牙", Toast.LENGTH_SHORT)
? ? ? ? ? ? ? ? ? ? ? ? .show();
? ? ? ? ? ? }
? ? ? ? ? ? // 初始化 Bluetooth adapter,
? ? ? ? ? ? // 通過藍牙管理器得到一個參考藍牙適配器(API必須在以上android4.3或以上和版本)
? ? ? ? ? ? final BluetoothManager bluetoothManager = (BluetoothManager) mContext
? ? ? ? ? ? ? ? ? ? .getSystemService(Context.BLUETOOTH_SERVICE);
? ? ? ? ? ? mBluetoothAdapter = bluetoothManager.getAdapter();
? ? ? ? ? ? // 檢查設備上是否支持藍牙
? ? ? ? ? ? if (mBluetoothAdapter == null) {
? ? ? ? ? ? ? ? Toast.makeText(mContext, "手機不支持藍牙", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? // 打開藍牙
? ? ? ? ? ? if (!mBluetoothAdapter.isEnabled()) {
? ? ? ? ? ? ? ? mBluetoothAdapter.enable();
? ? ? ? ? ? }
? ? ? ? ? ? // 開始掃描
? ? ? ? ? ? if (dAddress.size() > 0) {
? ? ? ? ? ? ? ? dAddress.clear();
? ? ? ? ? ? }
? ? ? ? ? ? if (rssiDistance.size() > 0) {
? ? ? ? ? ? ? ? rssiDistance.clear();
? ? ? ? ? ? }
? ? ? ? ? ? if (!mScanning) {
? ? ? ? ? ? ? ? mScanning = true;
? ? ? ? ? ? ? ? scanLeDevice(true);
? ? ? ? ? ? }
? ? }
// 掃描
private void scanLeDevice(final boolean enable) {
? ? ? ? if (enable) {
? ? ? ? ? ? // Stops scanning after a pre-defined scan period.
? ? ? ? ? ? mHandler.postDelayed(new Runnable() {
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? mScanning = false;
? ? ? ? ? ? mBluetoothAdapter.stopLeScan(mLeScanCallback);
? ? ? ? ? ? ? ? ? ? // 開始連接,篩選后的藍牙的mac地址
? ? ? ? ? ? ? ? ? ? mBLE.connect(mac)
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }, SCAN_PERIOD);
? ? ? ? ? ? mScanning = true;
? ? ? ? ? ? boolean scan = mBluetoothAdapter.startLeScan(mLeScanCallback);
? ? ? ? } else {
? ? ? ? ? ? mScanning = false;
? ? ? ? ? ? mBluetoothAdapter.stopLeScan(mLeScanCallback);
? ? ? ? }
? ? }
// 設備掃描
? ? private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
? ? ? ? @Override
? ? ? ? public void onLeScan(final BluetoothDevice device, int rssi,
? ? ? ? ? ? ? ? byte[] scanRecord) {
? ? ? ? ? ? LogUtil.i(
? ? ? ? ? ? ? ? ? ? TAG,
? ? ? ? ? ? ? ? ? ? device.getAddress() + ","
? ? ? ? ? ? ? ? ? ? ? ? ? ? + (int) BLEUtil.RssiToDistance(rssi));
? ? ? ? ? ? if (!dAddress.contains(device.getAddress())) {
? ? ? ? ? ? //對比獲取到的設備的mac地址和rssi值進行處理
? ? ? ? ? ? ? ? dAddress.add(device.getAddress());
? ? ? ? ? ? ? ? rssiDistance.put(device.getAddress(),
? ? ? ? ? ? ? ? ? ? ? ? (int) BLEUtil.RssiToDistance(rssi));
? ? ? ? ? ? }
? ? ? ? }
? ? };
'''
附上封裝好的BluetoothLeClass,主要封裝好:連接,收發(fā)數據的回調,斷開藍牙。調用mBLE.connect(mac)連接藍牙后進入public
boolean connect(final String address)
{}方法,根據你是否是二次連接進行處理,如果是首次連接將創(chuàng)建mGattCallback ,方法private final
BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
{}里面封裝了藍牙狀態(tài)改變(連接/斷開)回調onConnectionStateChange;連接成功后調mBluetoothGatt.discoverServices()獲取收發(fā)數據的服務,在onServicesDiscovered中獲取到服務,我這邊在前臺mOnServiceDiscoverListener.onServiceDiscover(gatt);回調中進行數據收發(fā)(代碼如下)
'''
package com.etclients.util;
import java.util.List;
import java.util.UUID;
import com.etclients.activity.MainActivity;
import com.etclients.receiver.AlarmRecevier;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
/**
* Service for managing connection and data communication with a GATT server
* hosted on a given Bluetooth LE device.
*/
public class BluetoothLeClass { private final static String TAG = BluetoothLeClass.class.getSimpleName(); private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; // BLE回調得到的藍牙RSSI值 private static int BLERSSI = 0; public final static String UUID_KEY_Write = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";// 手機發(fā)送數據給設備 public final static String UUID_KEY_Read = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";// 設備發(fā)送數據給手機 public final static String UUID_KEY_Data = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";// public static final UUID CCCD = UUID .fromString("00002902-0000-1000-8000-00805f9b34fb"); private byte[] c; private boolean is = false; 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 onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor); } 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); LogUtil.i(TAG, "已連接 to GATT server." + System.currentTimeMillis()); // 成功連接后發(fā)現服務的嘗試。 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } LogUtil.i(TAG, System.currentTimeMillis() / 1000 + "發(fā)現服務的嘗試" + mBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { try { // 當設備無法連接 if (mOnDisconnectListener != null) mOnDisconnectListener.onDisconnect(gatt); LogUtil.i(TAG, "斷開 GATT server."); close2(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); close(); } } } // 發(fā)現新服務 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { try { if (status == BluetoothGatt.GATT_SUCCESS && mOnServiceDiscoverListener != null) { mOnServiceDiscoverListener.onServiceDiscover(gatt); } else { LogUtil.i(TAG, System.currentTimeMillis() + "發(fā)現新服務端失敗" + status); close(); Intent intent = new Intent(); intent.setAction(MainActivity.ACTION_UPDATEUI); Bundle bundle = new Bundle(); bundle.putString("msg", "發(fā)現服務129失敗,請重新開門!"); intent.putExtras(bundle); mContext.sendBroadcast(intent); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 讀取特征值 @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { LogUtil.i(TAG, "onCharacteristicRead"); if (mOnDataAvailableListener != null) mOnDataAvailableListener.onCharacteristicRead(gatt, characteristic, status); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { LogUtil.i(TAG, "onCharacteristicChanged"); if (mOnDataAvailableListener != null) mOnDataAvailableListener.onCharacteristicChanged(gatt, characteristic); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { // TODO Auto-generated method stub super.onCharacteristicWrite(gatt, characteristic, status); LogUtil.i(TAG, "onCharacteristicWrite"); if (status == BluetoothGatt.GATT_SUCCESS && mOnDataAvailableListener != null) mOnDataAvailableListener.onCharacteristicWrite(gatt, characteristic); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { // TODO Auto-generated method stub super.onDescriptorWrite(gatt, descriptor, status); if (status == BluetoothGatt.GATT_SUCCESS && mOnDataAvailableListener != null) { mOnDataAvailableListener.onDescriptorWrite(gatt, descriptor); LogUtil.i(TAG, "onDescriptorWrite"); } else { // 重新連接 if (mBluetoothGatt != null) { // disconnect(); close(); } System.out .println("onDescriptorWrite重新連接mBluetoothGatt== null"); mBluetoothGatt = mBluetoothAdapter.getRemoteDevice( AlarmRecevier.mac).connectGatt(mContext, false, mGattCallback); mBluetoothDeviceAddress = AlarmRecevier.mac; } } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { // TODO Auto-generated method stub super.onReadRemoteRssi(gatt, rssi, status); // 將回調的RSSI值賦值 BLERSSI = rssi; LogUtil.i(TAG, "rssi = " + rssi); } }; /** * 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; } /** * 開始連接藍牙 * * @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) { LogUtil.i(TAG, "你沒有初始化或未指定的地址。"); return false; } // Previously connected device. Try to reconnect. if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { LogUtil.i(TAG, "mBluetoothGatt連接已存在"); close(); } final BluetoothDevice device = mBluetoothAdapter .getRemoteDevice(address); if (device == null) { return false; } //創(chuàng)建一個BluetoothGattCallback(藍牙連接狀態(tài),收發(fā)數據的回調) mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback); LogUtil.i(TAG, "試圖創(chuàng)建一個新的連接。"); 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) { return; } mBluetoothGatt.disconnect(); try { Thread.sleep(20); close(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 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; LogUtil.i(TAG, "釋放資源!"); } public void close2() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; try { DialogUtil.dismissAnimationDialog(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } LogUtil.i(TAG, "釋放資源,關閉加載動畫!"); } /** * 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; } boolean is = mBluetoothGatt.readCharacteristic(characteristic); LogUtil.i(TAG, "開始回調!" + is); } /** * 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) { LogUtil.i(TAG, "BluetoothAdapter not initialized"); return; } try { LogUtil.i(TAG, characteristic + "," + enabled); boolean is = mBluetoothGatt.setCharacteristicNotification( characteristic, enabled); LogUtil.i(TAG, "setCharacteristicNotification = " + is); BluetoothGattDescriptor descriptor = characteristic .getDescriptor(CCCD); descriptor .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void writeCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothGatt != null) { boolean is = mBluetoothGatt.writeCharacteristic(characteristic); LogUtil.i(TAG, "writeCharacteristic = " + is); } } /** * 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 List<BluetoothGattService> getSupportedGattServices() { if (mBluetoothGatt == null) return null; return mBluetoothGatt.getServices(); } // 獲取已經得到的RSSI值 public static int getBLERSSI() { return BLERSSI; } // 是都能讀取到已連接設備的RSSI值 // 執(zhí)行該方法一次,獲得藍牙回調onReadRemoteRssi()一次 /** * Read the RSSI for a connected remote device. * */ public boolean getRssiVal() { if (mBluetoothGatt == null) return false; return mBluetoothGatt.readRemoteRssi(); } }
'''
前臺private BluetoothLeClass.OnServiceDiscoverListener
mOnServiceDiscover = new OnServiceDiscoverListener()
{}中通過獲取的服務綁定數據改變后發(fā)送通知 mBLE.setCharacteristicNotification(RxChar,
true);然后在調mBluetoothGatt.writeDescriptor(descriptor);在onDescriptorWrite(連接首次發(fā)送數據)回調中開始向BLE終端寫入數據,藍牙單片機收到數據后向手機返回數據,數據從onCharacteristicChanged方法回調到前端,前端收到數據后進行二次發(fā)送數據,具體數據發(fā)送流程見代碼
(代碼如下)
'''
/**
? ? * 搜索到BLE終端服務的事件
? ? */
? ? private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener() {
? ? ? ? @Override
? ? ? ? public void onServiceDiscover(BluetoothGatt gatt) {
? ? ? ? ? ? if (gatt != null) {
? ? ? ? ? ? ? ? // 設置UUID_KEY_Read字服務數據改變監(jiān)聽
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? LogUtil.i(TAG, System.currentTimeMillis() / 1000
? ? ? ? ? ? ? ? ? ? ? ? ? ? + "onServiceDiscover");
? ? ? ? ? ? ? ? ? ? BluetoothGattService RxService = gatt.getService(UUID
? ? ? ? ? ? ? ? ? ? ? ? ? ? .fromString(BluetoothLeClass.UUID_KEY_Data));
? ? ? ? ? ? ? ? ? ? if (RxService != null) {
? ? ? ? ? ? ? ? ? ? ? ? BluetoothGattCharacteristic RxChar = RxService
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .getCharacteristic(UUID
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .fromString(BluetoothLeClass.UUID_KEY_Read));
? ? ? ? ? ? ? ? ? ? ? ? if (RxChar != null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //綁定數據改變后發(fā)送通知
? ? ? ? ? ? ? ? ? ? ? ? ? ? mBLE.setCharacteristicNotification(RxChar, true);
? ? ? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //獲取服務失敗,關閉藍牙
? ? ? ? ? ? ? ? ? ? ? ? ? ? mBLE.close();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? //獲取服務失敗,關閉藍牙
? ? ? ? ? ? ? ? ? ? ? ? mBLE.close();? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? };
? ? /**
? ? * 收到BLE終端數據交互的事件
? ? */
? ? private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener() {
? ? ? ? /**
? ? ? ? * 讀取BLE終端數據回調
? ? ? ? */
? ? ? ? @Override
? ? ? ? public void onCharacteristicRead(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattCharacteristic characteristic, int status) {
? ? ? ? ? ? if (status == BluetoothGatt.GATT_SUCCESS)
? ? ? ? ? ? ? ? LogUtil.i(TAG, "讀取BLE終端數據回調");
? ? ? ? }
? ? ? ? /**
? ? ? ? * BLE終端數據改變通知回調
? ? ? ? */
? ? ? ? @Override
? ? ? ? public void onCharacteristicChanged(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattCharacteristic characteristic) {
? ? ? ? ? ? byte[] value = characteristic.getValue();
? ? ? ? ? ? LogUtil.i(TAG, "BLE終端數據改變通知回調" + new String(value));
? ? ? ? ? ? //在這里接受藍牙單片機的數據
? ? ? ? ? ? //進行處理后
? ? ? ? ? ? //發(fā)送二次數據
? ? ? ? ? ? byte[] content = BLEParams.sendReturnCode(commandcode);//封裝好的數據
? ? ? ? ? ? BluetoothGattService RxService = gatt.getService(UUID
? ? ? ? ? ? ? ? ? ? .fromString(BluetoothLeClass.UUID_KEY_Data));
? ? ? ? ? ? BluetoothGattCharacteristic TxChar = RxService
? ? ? ? ? ? ? ? ? ? .getCharacteristic(UUID
? ? ? ? ? ? ? ? ? ? ? ? ? ? .fromString(BluetoothLeClass.UUID_KEY_Write));
? ? ? ? ? ? TxChar.setValue(content);
? ? ? ? ? ? mBLE.writeCharacteristic(TxChar);? ? ? ? ?
? ? ? ? }
? ? ? ? /**
? ? ? ? * 寫入BLE終端數據成功回調
? ? ? ? */
? ? ? ? @Override
? ? ? ? public void onCharacteristicWrite(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattCharacteristic characteristic) {
? ? ? ? ? ? //二次發(fā)送數據成功后在此返回發(fā)送的數據
? ? ? ? ? ? ? ? LogUtil.i(TAG, new String(characteristic.getValue()));
? ? ? ? ? ? //可以在此進行分包發(fā)送數據
? ? ? ? }
? ? ? ? /**
? ? ? ? * 寫入BLE終端通知數據成功回調
? ? ? ? */
? ? ? ? @Override
? ? ? ? public void onDescriptorWrite(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattDescriptor descriptor) {
? ? ? ? ? ? // TODO Auto-generated method stub
? ? ? ? ? ? // 開始向BLE終端寫入數據(連接首次發(fā)送數據)? ? ? ? ?
? ? ? ? ? ? ? ? // 查詢版本號
? ? ? ? ? ? ? ? sendGetConnectId(0x01, gatt);
? ? ? ? }
? ? };
? ? // 倒數
? ? /* 定義一個倒計時的內部類 */
? ? class TimeCount extends CountDownTimer { public TimeCount(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval);// 參數依次為總時長,和計時的時間間隔 } @Override public void onFinish() {// 計時完畢時觸發(fā) if (isTimeout) { // 超時 mBLE.close(); } } @Override public void onTick(long millisUntilFinished) {// 計時過程顯示 } }
'''