在一家偏硬件的一家公司從事穿戴式設(shè)備的開(kāi)發(fā),通過(guò)近幾個(gè)月學(xué)習(xí)與研究對(duì)于藍(lán)牙4.0的通訊還有有點(diǎn)自己的見(jiàn)解,有不足的地方大家可以一起討論,互相學(xué)習(xí),廢話不多說(shuō),那么如何進(jìn)行藍(lán)牙4.0的通訊與數(shù)據(jù)傳輸呢?本demo比較簡(jiǎn)單,大家應(yīng)該都可以很好理解與學(xué)習(xí)的!
有基本的幾個(gè)步驟,下面是一些代碼段,希望對(duì)大家有所幫助吧。
添加藍(lán)牙權(quán)限,
<uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/><uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>判斷手機(jī)手機(jī)是否支持藍(lán)牙ble// 檢查當(dāng)前手機(jī)是否支持ble 藍(lán)牙,如果不支持退出程序if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "設(shè)備不支持BLE 藍(lán)牙", Toast.LENGTH_SHORT).show(); finish();}//獲得藍(lán)牙適配器對(duì)象BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();對(duì)于藍(lán)牙的搜索過(guò)程采用廣播:/** 因?yàn)樗{(lán)牙在搜索到設(shè)備和搜索完畢都是通過(guò)廣播發(fā)送的,這里我們需要注冊(cè)廣播接收器 */IntentFilter intentFilter = new IntentFilter( BluetoothDevice.ACTION_FOUND);registerReceiver(receiver, intentFilter);intentFilter = new IntentFilter( BluetoothAdapter.ACTION_DISCOVERY_FINISHED);registerReceiver(receiver, intentFilter);//注冊(cè)藍(lán)牙信號(hào)強(qiáng)度intentFilter = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);registerReceiver(receiver, intentFilter);廣播的注冊(cè)://注冊(cè)廣播 PRivate final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context arg0, Intent intent) { String action = intent.getAction(); // 判斷這個(gè)廣播是否是藍(lán)牙搜索到設(shè)備的廣播 if (action.equals(BluetoothDevice.ACTION_FOUND)) { // 獲取到傳遞過(guò)來(lái)的設(shè)備信息 BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.getBondState() != BluetoothDevice.BOND_BONDED) { //信號(hào)強(qiáng)度 short rssi = intent.getExtras().getShort( BluetoothDevice.EXTRA_RSSI); String s = String.valueOf(rssi); mDeviceList.add(device.getName() + "/n" + device.getAddress() + "/n" + "信號(hào)強(qiáng)度:" + s +"dbm"); arrayAdapter.notifyDataSetChanged(); } // 判斷是否為搜索完畢的廣播 } else if (action .equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) { /***/// setTitle("連接藍(lán)牙設(shè)備"); bt_search.setText("搜索完畢"); } } };項(xiàng)目創(chuàng)建時(shí)需要打開(kāi)藍(lán)牙,詳細(xì)代碼片段// 為了確保設(shè)備上藍(lán)牙能使用, 如果當(dāng)前藍(lán)牙設(shè)備沒(méi)啟用,彈出對(duì)話框向用戶要求授予權(quán)限來(lái)啟用if (!mBluetoothAdapter.isEnabled()) { Intent enabletIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enabletIntent, REQUEST_CODE);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_CANCELED) { finish(); return; }}基本工作有了這些后,我們建立一個(gè)listview來(lái)顯示搜索到的藍(lán)牙設(shè)備,//適配器 + Listview顯示arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mDeviceList);listView.setAdapter(arrayAdapter);2.如何進(jìn)行掃描呢?這里采用的是按鈕觸發(fā):bt_search.setText("正在搜索藍(lán)牙設(shè)備...");if(mBluetoothAdapter.isDiscovering()){ mBluetoothAdapter.cancelDiscovery();}mBluetoothAdapter.startDiscovery();其實(shí)這個(gè)時(shí)候點(diǎn)擊按鈕,并沒(méi)有任何顯示,因?yàn)槲覀兊糜靡粋€(gè)集合加載搜索到的設(shè)備,前面采用MVC的模式進(jìn)行適配,將搜索到的設(shè)備加載到鏈表里面//數(shù)據(jù)源存放藍(lán)牙設(shè)備----------------獲取已綁定的設(shè)備,將其存放于set集合Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();if(devices.size() > 0){ for (BluetoothDevice device:devices) { mDeviceList.add(device.getName() + "/n" + device.getAddress()); }}將搜索到的設(shè)備顯示到listview上后對(duì)其item項(xiàng)進(jìn)行進(jìn)一步的連接,并進(jìn)行通信、因?yàn)橐B接設(shè)備的地址,這里進(jìn)行字符串的截取String s = mDeviceList.get(position); String a[] = s.split("/n"); String name = a[0]; String address = a[1]; Log.i(TAG, "address =" + address); if (mBluetoothAdapter == null || address == null) { Log.w(TAG, "藍(lán)牙適配器為空或者地址為空."); return; } //根據(jù)地址取得設(shè)備 device = mBluetoothAdapter.getRemoteDevice(address); //獲取鏈接 這個(gè)時(shí)候需要實(shí)現(xiàn)BluetoothGattCallback bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);接下來(lái)執(zhí)行回調(diào)方法,回調(diào)方法主要有onConnectionStateChange, // 返回鏈接狀態(tài)onServicesDiscovered, //發(fā)現(xiàn)服務(wù)時(shí)調(diào)用此方法onCharacteristicRead //字段讀onCharacteristicWrite //字段寫(xiě)onCharacteristicChanged //藍(lán)牙通信內(nèi)容寫(xiě)入寫(xiě)出時(shí)調(diào)用此方法onDescriptorRead // 字段讀onDescriptorWrite //字段寫(xiě)//回調(diào)方法 private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { // 返回鏈接狀態(tài) @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); Log.i(TAG,"onConnectionStateChange"); if (newState == BluetoothProfile.STATE_CONNECTED) { //連接成功 因?yàn)槭钱惒秸{(diào)用的 所以刷新UI的操作要放在主線程中,當(dāng)然也可以使用hanlder Eventbus等 隨便 runOnUiThread(new Runnable() { @Override public void run() { link = 1; tv_address.setText("連接成功" + device.getAddress()); } }); //發(fā)現(xiàn)服務(wù),連接成功 gatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //斷開(kāi)連接 link = 2; runOnUiThread(new Runnable() { @Override public void run() { tv_address.setText("連接斷開(kāi)"); } }); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); Log.i(TAG,"onServicesDiscovered"); if (status == BluetoothGatt.GATT_SUCCESS) { bluetoothGattServiceList = bluetoothGatt.getServices(); for (int i = 0; i < bluetoothGattServiceList.size(); i++) { BluetoothGattService bluetoothGattService = bluetoothGattServiceList .get(i); if (bluetoothGattService.getUuid().equals(BleConstants.TEMP_SERVICE_UUID)) { BleConstants.type = 1; break; } else if (bluetoothGattService.getUuid().equals(BleConstants.TEMP_SERVICE_UUID_G)) { BleConstants.type = 0; break; } } //------------------------- for (int i = 0; i < bluetoothGattServiceList.size(); i++) { BluetoothGattService bluetoothGattService = bluetoothGattServiceList .get(i); Log.i(TAG,"Service UUID-" + i + ":" + bluetoothGattService.getUuid()); List<BluetoothGattCharacteristic> bluetoothGattCharacteristicList = bluetoothGattService .getCharacteristics(); for (int j = 0; j < bluetoothGattCharacteristicList.size(); j++) { BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattCharacteristicList .get(j); Log.i(TAG,"Characteristic UUID-" + i + "-" + j + ":" + bluetoothGattCharacteristic.getUuid()); if (bluetoothGattCharacteristic.getUuid().equals(BleConstants.TEMP_CHAR_UUID)) { Log.i(TAG, "TEMP_CHAR_UUID"); } else if (bluetoothGattCharacteristic.getUuid().equals(BleConstants.TEMP_BATTERY_CHAR_UUID)) { Log.i(TAG, "BATTERY_CHAR_UUID" + bluetoothGattCharacteristic.getUuid().toString()); } else if (bluetoothGattCharacteristic.getUuid() .equals(BleConstants.TEMP_UART_WRITE_CHAR_UUID)) { } else if (bluetoothGattCharacteristic.getUuid() .equals(BleConstants.TEMP_UART_READ_CHAR_UUID)) { } } } read(); Log.i(TAG, "BluetoothGatt.GATT_SUCCESS"); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); Log.i(TAG,"onCharacteristicRead"); } /** * write成功(發(fā)送值成功) 寫(xiě)入Characteristic成功與否的回調(diào) 可以根據(jù) * characteristic.getValue()來(lái)判斷是哪個(gè)值發(fā)送成功了,比如 連接上設(shè)備之后你有一大串命令需要下發(fā),你調(diào)用多次寫(xiě)命令, * 這樣你需要判斷是不是所有命令都成功了,因?yàn)閍ndroid不太穩(wěn)定,有必要來(lái)check命令是否成功,否則你會(huì)發(fā)現(xiàn)你明明調(diào)用 * 寫(xiě)命令,但是設(shè)備那邊不響應(yīng) */ @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); Log.i(TAG,"onCharacteristicWrite"); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); Log.i(TAG,"onCharacteristicChanged"); byte[] read_data = characteristic.getValue(); String as = byteToHexString(read_data); Log.i(TAG, "as = " + as); float abcd = 0; if(read_data[1] == 0x01){ //溫度 abcd = (float) ((read_data[2]) * 256 + (0x000000FF & read_data[3])) / 10; } string1 = String.valueOf(abcd); Log.i(TAG, "string1 =" + string1); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); Log.i(TAG,"onDescriptorRead"); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); Log.i(TAG,"onDescriptorWrite"); } };read()方法如下:public void read() { if (BleConstants.type == 1) { BluetoothGattService bluetoothGattServic = bluetoothGatt .getService(BleConstants.TEMP_UART_SERVICE_UUID); BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattServic .getCharacteristic(BleConstants.TEMP_UART_READ_CHAR_UUID); for (int k = 0; k < bluetoothGattCharacteristic.getDescriptors().size(); k++) { BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic .getDescriptors().get(k); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); bluetoothGatt.writeDescriptor(descriptor); } //發(fā)送通知 bluetoothGatt.setCharacteristicNotification( bluetoothGattCharacteristic, true); } else if (BleConstants.type == 0) { BluetoothGattService bluetoothGattServic = bluetoothGatt .getService(BleConstants.TEMP_SERVICE_UUID_G); BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattServic .getCharacteristic(BleConstants.TEMP_READ_CHAR_UUID_G); for (int k = 0; k < bluetoothGattCharacteristic.getDescriptors().size(); k++) { BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptors().get(k); //ENABLE_NOTIFICATION_VALUE descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); bluetoothGatt.writeDescriptor(descriptor); } bluetoothGatt.setCharacteristicNotification( bluetoothGattCharacteristic, true); }}進(jìn)制轉(zhuǎn)換的方法:public String byteToHexString(byte[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); Log.i(TAG, "bArray.length = " + bArray.length); //長(zhǎng)度為5 String sTemp; for (int i = 0; i < bArray.length; i++) { Log.i(TAG, "bArray = " + bArray[i]); // 5, 1, 0, -2, -6 //將整數(shù)變?yōu)樽址?/em> // 0xff是十六進(jìn)制FF的表示方法,因?yàn)橐粋€(gè)十六進(jìn)制數(shù)字轉(zhuǎn)換成二進(jìn)制是四位,即F=1111, // 所以0xff占用一個(gè)字節(jié) 。也就是說(shuō)是1B,1KB是1024B。 //另外你表達(dá)不太清楚,如果你問(wèn)FF KB是多少,十六進(jìn)制FF=15*16+15*1=255,即255KB sTemp = Integer.toHexString(0xFF & bArray[i]);// Log.i(TAG, "sTemp" + sTemp); //Log: sTemp 5, sTemp 1, sTemp 0, sTemp fc, sTemp f8 ---as: 05 01 00F6 F2 if (sTemp.length() < 2) sb.append(0); sb.append(sTemp.toUpperCase()); } return sb.toString(); }藍(lán)牙通信需要用到的UUID,關(guān)于uuid不懂的就自己百度了。
// 溫度值藍(lán)牙特征服務(wù)UUIDpublic static final UUID TEMP_SERVICE_UUID = UUID.fromString("00001809-0000-1000-8000-00805f9b34fb");// 溫度值藍(lán)牙特征值UUIDpublic static final UUID TEMP_CHAR_UUID = UUID.fromString("00002A1C-0000-1000-8000-00805f9b34fb");那為了保證數(shù)據(jù)傳輸并顯示,這里主要測(cè)試了溫度的顯示。
// handler類接收數(shù)據(jù) Handler handler = new Handler() { public void handleMessage(Message msg) { if (msg.what == 1) { tv_display.setText(string1 + "℃"); } }; }; // 線程類 class ThreadShow implements Runnable { @Override public void run() { while (true) { try { Thread.sleep(300); Message msg = new Message(); msg.what = 1; handler.sendMessage(msg); } catch (Exception e) { e.printStackTrace(); } } } }以上就是關(guān)于藍(lán)牙如何連接以及通信數(shù)據(jù)的自己的一點(diǎn)學(xué)習(xí)經(jīng)驗(yàn),目前還在深圳找工作,期待找到一家適合自己的工作吧!
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注