Android 藍牙和BLE應用開發經驗參考
傳統藍牙和BLE的區別
技術規範 |
經典藍牙(2.1 &3.0) |
低功耗藍牙(4.0) |
無線電頻率 |
2.4GHz |
2.4GHz |
距離 |
10米/100米 |
30米 |
數據速率 |
1-3Mbps |
1Mbps |
應用吞吐量 |
0.7-2.1Mbps |
0.2Mbps |
發送數據的總時間 |
100ms |
<6ms |
耗電量 |
1 |
0.01至0.5 |
最大操作電流 |
<30mA |
<15mA(最高運行時爲15 mA) |
主要用途 |
手機,遊戲機,耳機,立體聲音頻流,汽車和PC等 |
手機,遊戲機,PC,表,體育和健身,醫療保健,汽車,家用電子,自動化和工業等 |
藍牙的應用開發參考
藍牙基礎
l 藍牙是一種無線技術標準,可實現固定設備、移動設備和樓宇個人域網之間的短距離數據交換(使用2.4—2.485GHz的ISM波段的UHF無線電波)。藍牙技術最初由電信巨頭愛立信公司於1994年創制,當時是作爲RS232數據線的替代方案。藍牙可連接多個設備,克服了數據同步的難題。
l 如今藍牙由藍牙技術聯盟(Bluetooth SpecialInterest Group,簡稱SIG)管理
l 所有的藍牙標準版本都支持向下兼容
l 藍牙最新的版本號是藍牙4.2,還有未發佈的藍牙5.0
l 一個藍牙主設備最能多和7個藍牙從設備進行通信
l 藍牙的數據傳輸速率在1Mbps以內
l 藍牙的理論最大通信距離是100米,傳輸距離越大,功耗越高,現在市面上的流行藍牙設備的傳輸距離大約在30米以內,傳輸距離在10米左右的藍牙設備最多。
l 藍牙使用的頻率是2.4GHZ
藍牙應用場景
l 藍牙應用在手機上
l 藍牙應用在PC上,現在很多PC都帶有藍牙模塊
l 藍牙應用於其它數字設備,如數字照相機、數字攝像機等
l 籃牙技術構成的電子錢包和電子鎖,這方面現在慢慢被主流的NFC所替代
l 籃牙技術在嵌入式設備上的應用如藍牙音箱,藍牙耳機,微波爐、洗衣機、電冰箱、空調機等
藍牙開發基礎
常用的藍牙術語
l 掃描
l 綁定
l 配對
l 連接
藍牙的通訊模型
藍牙和普通的網絡通信一樣,是基於socket進行通訊的,採用的是典型的C/S模型
常用的藍牙協議(Profile)
Profile名稱 |
主要用途 |
SPP即Serial Port Profile串口通訊協議 |
主要用於藍牙基礎數據流的傳輸 |
A2DP即Advanced Audio Distribution Profile 藍牙音頻傳輸模型協議 |
主要用來播放高品質的音樂,主要應用場景是藍牙音箱,藍牙耳機 |
AVRCP即Audio Video Remote Control Profile音頻/視頻遠程控制協議 |
主要用於藍牙設備的遠程控件,比如控制藍牙耳機的播放,暫停,繼續等 |
HFP即Hands Free Profile 免提協議 |
主要用於車載藍牙中,可以實現免提功能,可以控制電話的接聽,掛斷,拒接,音頻撥號等 |
HDP即Health Device Profile 健康設備協議 |
主要用於藍牙血壓計,藍牙體重稱等 |
OPP即Object Push Profile 對象推送協議 |
主要用於手機與手機或者手機與電腦之間通過藍牙進行文件操作,比如通過藍牙發送文件 |
藍牙開發核心類
類名 |
解釋 |
BluetoothAdapter |
藍牙適配器類 |
BluetoothDevice |
藍牙設備信息類 |
BluetoothSocket |
藍牙客戶端Socket類 |
BluetoothServerSocket |
藍牙服務端Socket類 |
BluetoothHeadset |
藍牙HFP協議支持類 |
BluetoothA2dp |
藍牙A2DP協議支持類 |
BluetoothHealth |
藍牙HDP協議支持類 |
藍牙開發示例
public static String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";//SPP Profile UUID,這個UUID是藍牙SIG組織定義的官方ID
BluetoothSocketclientSocket
= null;
BluetoothServerSocket serverSocket =
null;
InputStreamis;
OutputStream os;
Android藍牙API歷史
Google從Android 3.0開始提供傳統藍牙相關的開發API,藍牙API在android.bluetooth包下面
藍牙權限聲明
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
打開藍牙
通過代碼打開
BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter(); if(adapter.isEnabled()) { adapter.disable();//關閉設備藍牙 } else{ adapter.enable();//打開設備藍牙 }
通過系統藍牙對話框打開
public static void openBluetoothWithDialog(Activity activity,
int seconds) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
300);
activity.startActivityForResult(intent, REQUEST_ENABLE);
}
註冊藍牙狀態監聽器
public void registerBluetoothReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); // filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); // filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); mActivity.registerReceiver(receiver, filter); } private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); deviceList.add(device); createBond(device); } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(device.getBondState()==BluetoothDevice.BOND_BONDED){ try { clientSocket = BluetoothHelper.connect(device); remoteDevice = clientSocket.getRemoteDevice(); showToast("綁定成功"); is = clientSocket.getInputStream(); os = clientSocket.getOutputStream(); sendMessage("hello boy"); startMessageLoop(); } catch (Exception e) { e.printStackTrace(); } } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED .equals(action)) { showToast("掃描完成"); } } };
建立藍牙客戶端Socket
public static BluetoothSocket createBluetoothSocket(BluetoothDevice device) {
BluetoothSocket socket = null;
UUID uuid = UUID.fromString(SPP_UUID);
try {
// Method m =device.getClass().getMethod("createRfcommSocket", newClass[]{int.class});
// socket = (BluetoothSocket)m.invoke(device, Integer.valueOf(1));
socket =device.createRfcommSocketToServiceRecord(uuid);
} catch (Exception e) {
try {
socket =device.createInsecureRfcommSocketToServiceRecord(uuid);
} catch (Exception ex) {
socket = null;
}
}
return socket;
}
建立藍牙服務端Socket
public static BluetoothServerSocket createServerSocket() { BluetoothServerSocket socket = null; UUID uuid = UUID.fromString(SPP_UUID); BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter(); String name=adapter.getName(); try { // Method listenMethod = adapter.getClass().getMethod("listenUsingRfcommOn", new Class[]{int.class}); // socket = ( BluetoothServerSocket) listenMethod.invoke(adapter, new Object[]{ 29}); socket = adapter.listenUsingRfcommWithServiceRecord(name, uuid); } catch (Exception e) { e.printStackTrace(); } return socket; }
藍牙掃描
BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
if(adapter.isDiscovering())
{
adapter.cancelDiscovery();//取消掃描
}
else{
adapter.startDiscovery();//開始掃描
}
綁定藍牙設備
public static boolean createBond(BluetoothDevice device) {
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
return device.createBond();
// Method createBondMethod = null;
// try {
// createBondMethod =BluetoothDevice.class.getMethod("createBond");
// Object objResult = createBondMethod.invoke(device);
// if (objResult != null)
// returnBoolean.parseBoolean(objResult.toString());
// return false;
// } catch (Exception e) {
// e.printStackTrace();
// return false;
// }
}
return true;
}
獲取系統藍牙綁定的設備列表
public static ArrayList<BluetoothDevice> getBondedDevices()
{
ArrayList<BluetoothDevice>deviceList=new
ArrayList<BluetoothDevice>();
BluetoothAdapter bluetoothAdapter =BluetoothAdapter.getDefaultAdapter();
Object[] devices =bluetoothAdapter.getBondedDevices().toArray();
for (int
i = 0; i < devices.length; i++) {
BluetoothDevice device =(BluetoothDevice) devices[i];
if(!deviceList.contains(device))
{
deviceList.add(device);
}
}
return deviceList;
}
判斷藍牙A2DP的連接狀態
public static boolean isA2DPDeviceConnected(Context context)
{
BluetoothAdapteradapter=BluetoothAdapter.getDefaultAdapter();
int state=adapter.getProfileConnectionState(BluetoothProfile.A2DP);
return state==BluetoothProfile.STATE_CONNECTED;
}
藍牙連接
public static BluetoothSocket connect(BluetoothDevice device) {
BluetoothSocket socket = null;
try {
socket = createBluetoothSocket(device);
if (socket == null)
return null;
socket.connect();
} catch (Exception e) {
socket = null;
e.printStackTrace();
}
return socket;
}
建立服務端消息循環
serverSocket =createServerSocket();
private void startServerListening() {
new Thread(new
Runnable() {
@Override
public void run() {
while (true) {
try {
clientSocket =
serverSocket.accept();
is = clientSocket.getInputStream();
os = clientSocket.getOutputStream();
startMessageLoop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
建立客戶端消息循環
private void startMessageLoop() {
new Thread(new
Runnable() {
@Override
public void run() {
while (true) {
byte[] buffer =
new byte[1024];
try {
int readed =
is.read(buffer);
String msg = new String(buffer,
0, readed);
Stringresult=HexHelper.encodeHexStr(buffer);
if(TextUtils.isEmpty(result))
return;
if(result.equalsIgnoreCase("aa12040042")){
//do something
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
發送藍牙消息
//aa12040042
byte[]cmd_get_pid=new byte[]
{
(byte)0xaa,
(byte)0x12,
(byte)0x04,
(byte)0x00,
(byte)0x42
};
os.write(cmd_get_pid);
os.flush();
接收藍牙消息
int readed =
is.read(buffer);
String msg = new String(buffer,
0,readed);
String result=HexHelper.encodeHexStr(buffer);
if(TextUtils.isEmpty(result))
return;
if(result.equalsIgnoreCase("aa12040042")){
//do something
}
BLE的應用開發參考
BLE基礎
l BLE技術是低成本、短距離、可互操作的魯棒性無線技術,工作在免許可的2.4GHz ISM射頻頻段。它從一開始就設計爲超低功耗(ULP)無線技術。它利用許多智能手段最大限度地降低功耗。藍牙低功耗技術採用可變連接時間間隔,這個間隔根據具體應用可以設置爲幾毫秒到幾秒不等。另外,因爲BLE技術採用非常快速的連接方式,因此平時可以處於“非連接”狀態(節省能源),此時鏈路兩端相互間只是知曉對方,只有在必要時纔開啓鏈路,然後在儘可能短的時間內關閉鏈路。
l BLE是藍牙4.0的核心Profile,主打功能是快速搜索,快速連接,超低功耗保持連接和傳輸數據,弱點是數據傳輸速率低,由於BLE的低功耗特點,因此普遍用於穿戴設備
l 低功耗
l 傳輸距離短
l 傳輸速度慢
BLE應用場景
l 智能穿戴設備,比如智能手環,智能手錶等
l 智能家居,智能燈,智能空調,藍牙電子稱等
l 健康設備,心率監測儀等
BLE開發基礎
BLE調試工具
BLE Scanner
BLE Explorer
拼接完整的藍牙設備UUID
public static final String BASE_ADDRESS="00000000-0000-1000-8000-00805F9B34FB"; //藍牙UUID的基地址
如上圖,假如我們要拼接DeviceInformation Service服務的完整的UUID,我們可以拿藍牙UUID的基地址的首段+0x180A,
所以我們得到的完整的DeviceInformation Service的UUID是0000180A-0000-1000-8000-00805F9B34FB,我們可以把這個拼接後的UUID用在我們的代碼中
BLE服務,特性,特性掃描器的UUID拼接也是這個道理
常用的BLE術語
l GATT
l 服務
l 特性
l 特性描述符
l 中央設備
l 外圍設備
關鍵術語和概念
l Generic Attribute Profile(GATT)—GATT配置文件是一個通用規範,用於在BLE鏈路上發送和接收被稱爲“屬性”的數據塊。目前所有的BLE應用都基於GATT。 藍牙SIG規定了許多低功耗設備的配置文件。配置文件是設備如何在特定的應用程序中工作的規格說明。注意一個設備可以實現多個配置文件。例如,一個設備可能包括心率監測儀和電量檢測。
l Attribute Protocol(ATT)—GATT在ATT協議基礎上建立,也被稱爲GATT/ATT。ATT對在BLE設備上運行進行了優化,爲此,它使用了儘可能少的字節。每個屬性通過一個唯一的的統一標識符(UUID)來標識,每個String類型UUID使用128 bit標準格式。屬性通過ATT被格式化爲characteristics和services。
l Characteristic 一個characteristic包括一個單一變量和0-n個用來描述characteristic變量的descriptor,characteristic可以被認爲是一個類型,類似於類。
l Descriptor Descriptor用來描述characteristic變量的屬性。例如,一個descriptor可以規定一個可讀的描述,或者一個characteristic變量可接受的範圍,或者一個characteristic變量特定的測量單位。
l Service service是characteristic的集合。例如,你可能有一個叫“Heart Rate Monitor(心率監測儀)”的service,它包括了很多characteristics,如“heart rate measurement(心率測量)”等。你可以在bluetooth.org 找到一個目前支持的基於GATT的配置文件和服務列表。
角色和責任
l 中央 VS 外圍設備。 適用於BLE連接本身。中央設備掃描,尋找廣播;外圍設備發出廣播。
l GATT 服務端 VS GATT 客戶端。決定了兩個設備在建立連接後如何互相交流。
BLE核心類
類名 |
解釋 |
BluetoothAdapter |
藍牙適配器類 |
BluetoothDevice |
藍牙設備類 |
BluetoothGatt |
作爲中央設備來使用和處理數據,是BLE的核心工具類,可以通過這個類來獲取BLE設備提供的服務 |
BluetoothGattService |
BLE服務類 |
BluetoothGattCharacteristic |
BLE特性類 |
BluetoothGattDescriptor |
BLE特性描述器類 |
BluetoothGattCallback |
BLE核心通訊回調類 |
LeScanCallback |
BLE掃描回調類 |
BLE核心類關係圖
BLE開發示例
List<BluetoothGattService> mGattServiceList=null; BluetoothDevice mRemoteDevice; BluetoothGatt mBluetoothGatt;
public static final String DEVICE_INFO_SERVICE="00001800-0000-1000-8000-00805F9B34FB"; public static final String SYSTEM_ID_CHAR="00002a04-0000-1000-8000-00805F9B34FB";
BLE開發API歷史
Google從Android 4.3開始提供BLE的開發API,所以BLE開發只支持Android 4.3以上的手機
藍牙權限聲明
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
掃描BLE設備
BLE設備掃描回調
LeScanCallback leScanCallback=new LeScanCallback() { /** * BLE設備掃描結果回調函數 * @param device 掃描到的BLE設備 * @param rssi BLE設備的信號強度 * @param scanRecord BLE設備發出的特定信息,可能通過這個信息來判斷掃描到的BLE設備是不是指定類型的BLE設備 */ @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { mActivity.runOnUiThread(new Runnable() { @Override public void run() { if(!mDeviceList.contains(device)) { mDeviceList.add(device); mAdapter.notifyDataSetChanged(); } } }); } };
開始BLE設備掃描
public static void startLeScan(UUID[] serviceUuids,LeScanCallback callback) { BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter(); if(serviceUuids==null) { adapter.startLeScan(callback); } else { adapter.startLeScan(serviceUuids, callback); } }
停止BLE設備掃描
public static void stopLeScan(LeScanCallback callback)
{
BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
adapter.stopLeScan(callback);
}
判斷特性的屬性
判斷特性是否可讀
public static boolean isBluetoothGattCharacteristicRead(BluetoothGattCharacteristic characteristic) { return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ)== BluetoothGattCharacteristic.PROPERTY_READ; }
判斷特性是否可寫
public static boolean isBluetoothGattCharacteristicRead(BluetoothGattCharacteristic characteristic) { return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ)== BluetoothGattCharacteristic.PROPERTY_READ; }
判斷特性是否是Notify特性
public static boolean isBluetoothGattCharacteristicNotify(BluetoothGattCharacteristic characteristic) { return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY; }
連接BLE設備
和BLE設備通訊的回調
BluetoothGattCallback mGattCallback=new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if(newState==BluetoothGatt.STATE_CONNECTED) { boolean success=gatt.discoverServices();//查找BLE設備提供的所有服務 showToast(gatt.getDevice().getName()+" connected"); } else if(newState==BluetoothGatt.STATE_DISCONNECTED) { if(mGattServiceList!=null) mGattServiceList.clear(); showToast(gatt.getDevice().getName()+" disconnected"); } super.onConnectionStateChange(gatt, status, newState); } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if(status==BluetoothGatt.GATT_SUCCESS) { mGattServiceList=gatt.getServices();//獲取BLE設備提供的所有服務列表 BluetoothGattService devInfoService=gatt.getService(UUID.fromString(DEVICE_INFO_SERVICE)); BluetoothGattCharacteristic systemID=devInfoService.getCharacteristic(UUID.fromString(SYSTEM_ID_CHAR)); gatt.readCharacteristic(systemID); } super.onServicesDiscovered(gatt, status); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { try { byte[]value=characteristic.getValue(); if(value!=null) { String str=HexHelper.encodeHexStr(value); Log.i("hello", str); } } catch(Exception e) { Log.i("hello", e.getMessage()); } super.onCharacteristicChanged(gatt, characteristic); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if(status==BluetoothGatt.GATT_SUCCESS) { try { byte[]value=characteristic.getValue(); if(value!=null) { String str=HexHelper.encodeHexStr(value); Log.i("hello", str); } } catch(Exception e) { Log.i("hello", e.getMessage()); } } super.onCharacteristicRead(gatt, characteristic, status); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); } };
異步連接BLE設備
public void connectGatt(BluetoothDevice device) { mBluetoothGatt=device.connectGatt(App.Instance, false, mGattCallback); }
斷開和BLE設備的連接
public void disconnect() { if (mBluetoothGatt != null) { mBluetoothGatt.disconnect(); mBluetoothGatt.close(); mBluetoothGatt=null; } }
設置BLE通知特性
public void setCharacteristicNotification(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, UUID uuid, boolean enable) { if (gatt == null || characteristic==null || uuid==null) return; gatt.setCharacteristicNotification(characteristic, enable); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(uuid); descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor); }