由于公司前段時(shí)間有一個(gè)項(xiàng)目要用到藍(lán)牙BLE技術(shù)电湘,才開始研究Ble技術(shù)隔节,在網(wǎng)上也找了很多文章查看鹅经,基本的藍(lán)牙連接通訊都有,就是出現(xiàn)的問題解答比較少怎诫,在這里說說個(gè)人遇到的問題瘾晃。
129錯(cuò)誤
133錯(cuò)誤
分包發(fā)送數(shù)據(jù)
自定義數(shù)據(jù)交互格式
首次連接速度慢
藍(lán)牙防丟器原理
Ble連接數(shù)據(jù)交互整個(gè)流程
在網(wǎng)上找了許多,基本都沒有說明129錯(cuò)誤在BLE中代表什么幻妓。個(gè)人測(cè)試后蹦误,猜測(cè)129類似于連接藍(lán)牙,獲取服務(wù)超時(shí)的返回值肉津,一般存在于藍(lán)牙信號(hào)比較弱或者藍(lán)牙附近存在電池强胰,變壓器等設(shè)備影響到藍(lán)牙信號(hào)時(shí)
'''
代碼塊
//連接藍(lán)牙成功后,獲取服務(wù)
mBluetoothGatt.discoverServices()
// 發(fā)現(xiàn)新服務(wù)
? ? ? ? @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ā)現(xiàn)新服務(wù)端失敗"
? ? ? ? ? ? ? ? ? ? ? ? ? ? + status);
? ? ? ? ? ? ? ? ? ? close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
'''
133錯(cuò)誤
跟129錯(cuò)誤類似妹沙,表示獲取服務(wù)失敗的返回值
//連接藍(lán)牙成功后偶洋,獲取服務(wù)
mBluetoothGatt.discoverServices()
'''
// 發(fā)現(xiàn)新服務(wù)
? ? ? ? @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ā)現(xiàn)新服務(wù)端失敗"
? ? ? ? ? ? ? ? ? ? ? ? ? ? + status);
? ? ? ? ? ? ? ? ? ? close();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
'''
分包發(fā)送數(shù)據(jù)
藍(lán)牙BLE支持的數(shù)據(jù)發(fā)送每包只能20字節(jié),所以想要發(fā)送比較大的數(shù)據(jù)包只能通過分包發(fā)送距糖。注意:如果你在要發(fā)送數(shù)據(jù)的地方直接一個(gè)for循環(huán)把所有分包的數(shù)據(jù)發(fā)完玄窝,藍(lán)牙設(shè)備那邊也只會(huì)收到一包數(shù)據(jù)而已牵寺,再此有兩種方法解決:
1、在for循環(huán)中每發(fā)一包數(shù)據(jù)加一個(gè)20毫秒的休眠時(shí)間恩脂,缺點(diǎn):時(shí)間不可控帽氓,容易導(dǎo)致丟包。
2俩块、每發(fā)完一包數(shù)據(jù)黎休,成功發(fā)送后系統(tǒng)會(huì)進(jìn)入onCharacteristicWrite回調(diào)里面,在這里進(jìn)行發(fā)送第二包和第二包之后的數(shù)據(jù)(代碼如下)典阵。
'''
// 發(fā)送大于20字節(jié)的包(第一包數(shù)據(jù))
? ? 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);
? ? }
? ? //超過第一包的數(shù)據(jù)在上一包數(shù)據(jù)發(fā)送成功后系統(tǒng)回調(diào)里面發(fā)
? ? /**
? ? ? * 寫入BLE終端數(shù)據(jù)成功回調(diào)
? ? ? */
? ? ? ? @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ā)送完的數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? ? 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();
? ? ? ? ? ? }
? ? ? ? }
'''
自定義數(shù)據(jù)交互格式
這個(gè)類似于android跟http數(shù)據(jù)交互的格式,無(wú)非就是定義一些樣式讓android和藍(lán)牙模塊兩邊都能識(shí)別壮啊,為了安全起見可以對(duì)數(shù)據(jù)進(jìn)行加密處理嫉鲸。由于我這邊是跟藍(lán)牙單片機(jī)進(jìn)行交互的,藍(lán)牙單片機(jī)不支持String,只能通過byte[]數(shù)組按位去組裝數(shù)據(jù)交互歹啼。(結(jié)構(gòu)如下)
數(shù)據(jù)格式:[起始碼][長(zhǎng)度碼] [命令碼] [數(shù)據(jù)碼] [校驗(yàn)碼]
起始碼 :固定為 0XAA
長(zhǎng)度碼 :命令碼+數(shù)據(jù)碼+校驗(yàn)碼
命令碼玄渗,數(shù)據(jù)碼(自定義數(shù)據(jù)接口)
校驗(yàn)碼 :一個(gè)字節(jié),前面所有數(shù)據(jù)值相加之和
加密:self加密和DES加密對(duì)整個(gè)數(shù)據(jù)碼進(jìn)行加密
由于藍(lán)牙首次連接要配置緩沖所有廣播的服務(wù)狸眼,所以一般第一次連接都比較慢藤树,如果想要加快第一次連接速度,只能讓藍(lán)牙模塊關(guān)閉部分不必要的廣播拓萌。
藍(lán)牙防丟器的原理其實(shí)很簡(jiǎn)單岁钓,就是通過手機(jī)app定時(shí)去掃描你配置好的藍(lán)牙單片機(jī),如果掃描到該藍(lán)牙單片機(jī)返回的rssi值(藍(lán)牙的信號(hào)值)超出app設(shè)定的值微王,則app將會(huì)報(bào)警屡限;或者掃不到該單片機(jī)app也會(huì)報(bào)警。(代碼如下)
'''
代碼塊
// 初始化藍(lán)牙
private void initBLE() {
? ? ? ? // TODO Auto-generated method stub
? ? ? ? ? ? // 檢查當(dāng)前手機(jī)是否支持ble 藍(lán)牙,如果不支持退出程序
? ? ? ? ? ? if (!mContext.getPackageManager().hasSystemFeature(
? ? ? ? ? ? ? ? ? ? PackageManager.FEATURE_BLUETOOTH_LE)) {
? ? ? ? ? ? ? ? Toast.makeText(mContext, "手機(jī)不支持ble藍(lán)牙", Toast.LENGTH_SHORT)
? ? ? ? ? ? ? ? ? ? ? ? .show();
? ? ? ? ? ? }
? ? ? ? ? ? // 初始化 Bluetooth adapter,
? ? ? ? ? ? // 通過藍(lán)牙管理器得到一個(gè)參考藍(lán)牙適配器(API必須在以上android4.3或以上和版本)
? ? ? ? ? ? final BluetoothManager bluetoothManager = (BluetoothManager) mContext
? ? ? ? ? ? ? ? ? ? .getSystemService(Context.BLUETOOTH_SERVICE);
? ? ? ? ? ? mBluetoothAdapter = bluetoothManager.getAdapter();
? ? ? ? ? ? // 檢查設(shè)備上是否支持藍(lán)牙
? ? ? ? ? ? if (mBluetoothAdapter == null) {
? ? ? ? ? ? ? ? Toast.makeText(mContext, "手機(jī)不支持藍(lán)牙", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? // 打開藍(lán)牙
? ? ? ? ? ? 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);
? ? ? ? }
? ? }
// 設(shè)備掃描
? ? 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())) {
? ? ? ? ? ? //對(duì)比獲取到的設(shè)備的mac地址和rssi值進(jìn)行處理
? ? ? ? ? ? ? ? dAddress.add(device.getAddress());
? ? ? ? ? ? ? ? rssiDistance.put(device.getAddress(),
? ? ? ? ? ? ? ? ? ? ? ? (int) BLEUtil.RssiToDistance(rssi));
? ? ? ? ? ? }
? ? ? ? }
? ? };
'''
Ble連接數(shù)據(jù)交互整個(gè)流程
藍(lán)牙Ble連接跟藍(lán)牙防丟器的原理其實(shí)是一樣的炕倘,先啟動(dòng)藍(lán)牙钧大,然后掃面附近的藍(lán)牙(只有android5.0+的手機(jī)的藍(lán)牙才會(huì)被掃描到,或支持ble的藍(lán)牙單片機(jī))罩旋,拿到所有掃描的藍(lán)牙進(jìn)行篩選匹配到你想要連接的藍(lán)牙啊央,然后連接。(代碼如下)
'''
代碼塊
// 初始化藍(lán)牙
private void initBLE() {
? ? ? ? // TODO Auto-generated method stub
? ? ? ? ? ? // 檢查當(dāng)前手機(jī)是否支持ble 藍(lán)牙,如果不支持退出程序
? ? ? ? ? ? if (!mContext.getPackageManager().hasSystemFeature(
? ? ? ? ? ? ? ? ? ? PackageManager.FEATURE_BLUETOOTH_LE)) {
? ? ? ? ? ? ? ? Toast.makeText(mContext, "手機(jī)不支持ble藍(lán)牙", Toast.LENGTH_SHORT)
? ? ? ? ? ? ? ? ? ? ? ? .show();
? ? ? ? ? ? }
? ? ? ? ? ? // 初始化 Bluetooth adapter,
? ? ? ? ? ? // 通過藍(lán)牙管理器得到一個(gè)參考藍(lán)牙適配器(API必須在以上android4.3或以上和版本)
? ? ? ? ? ? final BluetoothManager bluetoothManager = (BluetoothManager) mContext
? ? ? ? ? ? ? ? ? ? .getSystemService(Context.BLUETOOTH_SERVICE);
? ? ? ? ? ? mBluetoothAdapter = bluetoothManager.getAdapter();
? ? ? ? ? ? // 檢查設(shè)備上是否支持藍(lán)牙
? ? ? ? ? ? if (mBluetoothAdapter == null) {
? ? ? ? ? ? ? ? Toast.makeText(mContext, "手機(jī)不支持藍(lán)牙", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? // 打開藍(lán)牙
? ? ? ? ? ? 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);
? ? ? ? ? ? ? ? ? ? // 開始連接涨醋,篩選后的藍(lán)牙的mac地址
? ? ? ? ? ? ? ? ? ? mBLE.connect(mac)
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }, SCAN_PERIOD);
? ? ? ? ? ? mScanning = true;
? ? ? ? ? ? boolean scan = mBluetoothAdapter.startLeScan(mLeScanCallback);
? ? ? ? } else {
? ? ? ? ? ? mScanning = false;
? ? ? ? ? ? mBluetoothAdapter.stopLeScan(mLeScanCallback);
? ? ? ? }
? ? }
// 設(shè)備掃描
? ? 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())) {
? ? ? ? ? ? //對(duì)比獲取到的設(shè)備的mac地址和rssi值進(jìn)行處理
? ? ? ? ? ? ? ? dAddress.add(device.getAddress());
? ? ? ? ? ? ? ? rssiDistance.put(device.getAddress(),
? ? ? ? ? ? ? ? ? ? ? ? (int) BLEUtil.RssiToDistance(rssi));
? ? ? ? ? ? }
? ? ? ? }
? ? };
'''
附上封裝好的BluetoothLeClass瓜饥,主要封裝好:連接,收發(fā)數(shù)據(jù)的回調(diào)浴骂,斷開藍(lán)牙乓土。調(diào)用mBLE.connect(mac)連接藍(lán)牙后進(jìn)入public
boolean connect(final String address)
{}方法,根據(jù)你是否是二次連接進(jìn)行處理靠闭,如果是首次連接將創(chuàng)建mGattCallback 帐我,方法private final
BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
{}里面封裝了藍(lán)牙狀態(tài)改變(連接/斷開)回調(diào)onConnectionStateChange坎炼;連接成功后調(diào)mBluetoothGatt.discoverServices()獲取收發(fā)數(shù)據(jù)的服務(wù),在onServicesDiscovered中獲取到服務(wù)拦键,我這邊在前臺(tái)mOnServiceDiscoverListener.onServiceDiscover(gatt);回調(diào)中進(jìn)行數(shù)據(jù)收發(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回調(diào)得到的藍(lán)牙RSSI值 private static int BLERSSI = 0; public final static String UUID_KEY_Write = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";// 手機(jī)發(fā)送數(shù)據(jù)給設(shè)備 public final static String UUID_KEY_Read = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";// 設(shè)備發(fā)送數(shù)據(jù)給手機(jī) 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) { // 當(dāng)藍(lán)牙設(shè)備已經(jīng)連接 if (newState == BluetoothProfile.STATE_CONNECTED) { if (mOnConnectListener != null) mOnConnectListener.onConnect(gatt); LogUtil.i(TAG, "已連接 to GATT server." + System.currentTimeMillis()); // 成功連接后發(fā)現(xiàn)服務(wù)的嘗試谣光。 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } LogUtil.i(TAG, System.currentTimeMillis() / 1000 + "發(fā)現(xiàn)服務(wù)的嘗試" + mBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { try { // 當(dāng)設(shè)備無(wú)法連接 if (mOnDisconnectListener != null) mOnDisconnectListener.onDisconnect(gatt); LogUtil.i(TAG, "斷開 GATT server."); close2(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); close(); } } } // 發(fā)現(xiàn)新服務(wù) @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ā)現(xiàn)新服務(wù)端失敗" + status); close(); Intent intent = new Intent(); intent.setAction(MainActivity.ACTION_UPDATEUI); Bundle bundle = new Bundle(); bundle.putString("msg", "發(fā)現(xiàn)服務(wù)129失敗,請(qǐng)重新開門!"); 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); // 將回調(diào)的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; } /** * 開始連接藍(lán)牙 * * @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)建一個(gè)BluetoothGattCallback(藍(lán)牙連接狀態(tài)萄金,收發(fā)數(shù)據(jù)的回調(diào)) mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback); LogUtil.i(TAG, "試圖創(chuàng)建一個(gè)新的連接。"); 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, "釋放資源氧敢,關(guān)閉加載動(dòng)畫!"); } /** * 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, "開始回調(diào)询张!" + 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(); } // 獲取已經(jīng)得到的RSSI值 public static int getBLERSSI() { return BLERSSI; } // 是都能讀取到已連接設(shè)備的RSSI值 // 執(zhí)行該方法一次孙乖,獲得藍(lán)牙回調(diào)onReadRemoteRssi()一次 /** * Read the RSSI for a connected remote device. * */ public boolean getRssiVal() { if (mBluetoothGatt == null) return false; return mBluetoothGatt.readRemoteRssi(); } }
'''
前臺(tái)private BluetoothLeClass.OnServiceDiscoverListener
mOnServiceDiscover = new OnServiceDiscoverListener()
{}中通過獲取的服務(wù)綁定數(shù)據(jù)改變后發(fā)送通知 mBLE.setCharacteristicNotification(RxChar,
true);然后在調(diào)mBluetoothGatt.writeDescriptor(descriptor);在onDescriptorWrite(連接首次發(fā)送數(shù)據(jù))回調(diào)中開始向BLE終端寫入數(shù)據(jù),藍(lán)牙單片機(jī)收到數(shù)據(jù)后向手機(jī)返回?cái)?shù)據(jù)份氧,數(shù)據(jù)從onCharacteristicChanged方法回調(diào)到前端唯袄,前端收到數(shù)據(jù)后進(jìn)行二次發(fā)送數(shù)據(jù),具體數(shù)據(jù)發(fā)送流程見代碼
(代碼如下)
'''
/**
? ? * 搜索到BLE終端服務(wù)的事件
? ? */
? ? private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener() {
? ? ? ? @Override
? ? ? ? public void onServiceDiscover(BluetoothGatt gatt) {
? ? ? ? ? ? if (gatt != null) {
? ? ? ? ? ? ? ? // 設(shè)置UUID_KEY_Read字服務(wù)數(shù)據(jù)改變監(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) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //綁定數(shù)據(jù)改變后發(fā)送通知
? ? ? ? ? ? ? ? ? ? ? ? ? ? mBLE.setCharacteristicNotification(RxChar, true);
? ? ? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //獲取服務(wù)失敗蜗帜,關(guān)閉藍(lán)牙
? ? ? ? ? ? ? ? ? ? ? ? ? ? mBLE.close();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? //獲取服務(wù)失敗恋拷,關(guān)閉藍(lán)牙
? ? ? ? ? ? ? ? ? ? ? ? mBLE.close();? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? };
? ? /**
? ? * 收到BLE終端數(shù)據(jù)交互的事件
? ? */
? ? private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener() {
? ? ? ? /**
? ? ? ? * 讀取BLE終端數(shù)據(jù)回調(diào)
? ? ? ? */
? ? ? ? @Override
? ? ? ? public void onCharacteristicRead(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattCharacteristic characteristic, int status) {
? ? ? ? ? ? if (status == BluetoothGatt.GATT_SUCCESS)
? ? ? ? ? ? ? ? LogUtil.i(TAG, "讀取BLE終端數(shù)據(jù)回調(diào)");
? ? ? ? }
? ? ? ? /**
? ? ? ? * BLE終端數(shù)據(jù)改變通知回調(diào)
? ? ? ? */
? ? ? ? @Override
? ? ? ? public void onCharacteristicChanged(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattCharacteristic characteristic) {
? ? ? ? ? ? byte[] value = characteristic.getValue();
? ? ? ? ? ? LogUtil.i(TAG, "BLE終端數(shù)據(jù)改變通知回調(diào)" + new String(value));
? ? ? ? ? ? //在這里接受藍(lán)牙單片機(jī)的數(shù)據(jù)
? ? ? ? ? ? //進(jìn)行處理后
? ? ? ? ? ? //發(fā)送二次數(shù)據(jù)
? ? ? ? ? ? byte[] content = BLEParams.sendReturnCode(commandcode);//封裝好的數(shù)據(jù)
? ? ? ? ? ? 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終端數(shù)據(jù)成功回調(diào)
? ? ? ? */
? ? ? ? @Override
? ? ? ? public void onCharacteristicWrite(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattCharacteristic characteristic) {
? ? ? ? ? ? //二次發(fā)送數(shù)據(jù)成功后在此返回發(fā)送的數(shù)據(jù)
? ? ? ? ? ? ? ? LogUtil.i(TAG, new String(characteristic.getValue()));
? ? ? ? ? ? //可以在此進(jìn)行分包發(fā)送數(shù)據(jù)
? ? ? ? }
? ? ? ? /**
? ? ? ? * 寫入BLE終端通知數(shù)據(jù)成功回調(diào)
? ? ? ? */
? ? ? ? @Override
? ? ? ? public void onDescriptorWrite(BluetoothGatt gatt,
? ? ? ? ? ? ? ? BluetoothGattDescriptor descriptor) {
? ? ? ? ? ? // TODO Auto-generated method stub
? ? ? ? ? ? // 開始向BLE終端寫入數(shù)據(jù)(連接首次發(fā)送數(shù)據(jù))? ? ? ? ?
? ? ? ? ? ? ? ? // 查詢版本號(hào)
? ? ? ? ? ? ? ? sendGetConnectId(0x01, gatt);
? ? ? ? }
? ? };
? ? // 倒數(shù)
? ? /* 定義一個(gè)倒計(jì)時(shí)的內(nèi)部類 */
? ? class TimeCount extends CountDownTimer { public TimeCount(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval);// 參數(shù)依次為總時(shí)長(zhǎng),和計(jì)時(shí)的時(shí)間間隔 } @Override public void onFinish() {// 計(jì)時(shí)完畢時(shí)觸發(fā) if (isTimeout) { // 超時(shí) mBLE.close(); } } @Override public void onTick(long millisUntilFinished) {// 計(jì)時(shí)過程顯示 } }
'''