Android 蓝牙4.0(BLE)开发实现对蓝牙的写入数据和读取数据
由于最近學校組織了一個移動APP(安卓)設計大賽,自己也學習安卓有一段時間了,就跟同學商量一起去參加試試,一拍即合,然后我們就開始想idea,因為最近可穿戴設備比較火,我們也就想試試。經過商量,我負責Android上位機的開發,同學負責下位機的開發。
上位機的開發主要是低功耗藍牙BLE的開發,然后就開始找資料,各種找,最后谷歌官方提供的demo還有其他網友基于官方demo修改的demo,結合網上的博客對demo進行理解。剛開始是覺得看起來有點費勁,各種廣播,跳來跳去的,剛開始就暈菜了。但是經過三四天的理解,算是把官方的demo理解了。
代碼基本上都是官方的demo,只是通過修改獲得自己想要的結果,下面就簡單介紹一下自己的理解。
一、掃描BLE設備activity
檢查該設備是否支持BLE設備,谷歌在Android4.3才開始支持BLE設備(暈死,很長一段時間都沒有一臺4.3的設備,看著程序修改了也不能測試!)。
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();finish();}
初始化獲得一個bluetoothManager,并檢測設備是否支持藍牙
final BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);mBluetoothAdapter = bluetoothManager.getAdapter(); // Checks if Bluetooth is supported on the device.if (mBluetoothAdapter == null) {Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();finish();return;} 掃描BLE設備,然后添加到listView里面。
private void scanLeDevice(final boolean enable) {if (enable) {// Stops scanning after a pre-defined scan period.mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback);invalidateOptionsMenu();}}, SCAN_PERIOD);mScanning = true;mBluetoothAdapter.startLeScan(mLeScanCallback);} else {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback);}invalidateOptionsMenu();}
二、藍牙控制的服務BluetoothLeService
在這個服務里面有一個很重要的回調函數BluetoothGattCallback(),藍牙的數據讀取和狀態改變都會回調這個函數。
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {String intentAction;//收到設備notify值 (設備上報值)if (newState == BluetoothProfile.STATE_CONNECTED) {intentAction = ACTION_GATT_CONNECTED;mConnectionState = STATE_CONNECTED;broadcastUpdate(intentAction);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) {intentAction = ACTION_GATT_DISCONNECTED;mConnectionState = STATE_DISCONNECTED;Log.i(TAG, "Disconnected from GATT server.");broadcastUpdate(intentAction);}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);} else {Log.w(TAG, "onServicesDiscovered received: " + status);System.out.println("onServicesDiscovered received: " + status);}}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {//讀取到值,在這里讀數據if (status == BluetoothGatt.GATT_SUCCESS) {broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);}}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);}}; 在官方的demo中還使用到廣播,可能是因為人大神寫的,要嚴謹些。我一開始看的時候就得這有點麻煩,跳轉的多麻煩。
private void broadcastUpdate(final String action) {final Intent intent = new Intent(action);sendBroadcast(intent);}private void broadcastUpdate(final String action,final BluetoothGattCharacteristic characteristic) {final Intent intent = new Intent(action);// This is special handling for the Heart Rate Measurement profile. Data parsing is// carried out as per profile specifications:// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xmlif (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {int flag = characteristic.getProperties();int format = -1;if ((flag & 0x01) != 0) {format = BluetoothGattCharacteristic.FORMAT_UINT16;Log.d(TAG, "Heart rate format UINT16.");} else {format = BluetoothGattCharacteristic.FORMAT_UINT8;Log.d(TAG, "Heart rate format UINT8.");}final int heartRate = characteristic.getIntValue(format, 1);Log.d(TAG, String.format("Received heart rate: %d", heartRate));intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));} else {// For all other profiles, writes the data formatted in HEX.對于所有的文件,寫入十六進制格式的文件//這里讀取到數據final byte[] data = characteristic.getValue();for (int i = 0; i < data.length; i++) {System.out.println("data......" + data[i]);}if (data != null && data.length > 0) {final StringBuilder stringBuilder = new StringBuilder(data.length);for(byte byteChar : data)//以十六進制的形式輸出stringBuilder.append(String.format("%02X ", byteChar));// intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());intent.putExtra(EXTRA_DATA, new String(data));}}sendBroadcast(intent);}發送了廣播之后就肯定有廣播接收器,這個是在控制藍牙的activity中,后面再說。
還有幾個重要的函數比如readCharacteristic(BluetoothGattCharacteristic characteristic)函數,讀取藍牙中數據。
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {if (mBluetoothAdapter == null || mBluetoothGatt == null) {Log.w(TAG, "BluetoothAdapter not initialized");return;}mBluetoothGatt.readCharacteristic(characteristic);} 有readCharacteristic(BluetoothGattCharacteristic characteristic)函數,當然就有writeCharacteristic(BluetoothGattCharacteristic characteristic),向藍牙中寫入數據。
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {if (mBluetoothAdapter == null || mBluetoothGatt == null) {Log.w(TAG, "BluetoothAdapter not initialized");return;}mBluetoothGatt.writeCharacteristic(characteristic);} 另外在這個service中還有其他的一些函數例如初始化initialize()函數、連接藍牙函數connect(final String address)、斷開藍牙連接函數disconnect()等。
三、藍牙控制DeviceControlActivity
掃描到藍牙設備之后就是對藍牙進行自己需要的控制,比如寫數據,讀數據,獲取設備信息,設備電量等。
在Service中講到有一個廣播,廣播接收器就在這個activity中,通過不同的action做出相應的操作。
注冊的幾種事件
private static IntentFilter makeGattUpdateIntentFilter() {final IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);return intentFilter;}// Handles various events fired by the Service.處理服務所激發的各種事件// ACTION_GATT_CONNECTED: connected to a GATT server.連接一個GATT服務// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.從GATT服務中斷開連接// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.查找GATT服務// ACTION_DATA_AVAILABLE: received data from the device. This can be a result of read// or notification operations.從服務中接受數據private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {final String action = intent.getAction();if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {mConnected = true;updateConnectionState(R.string.connected);invalidateOptionsMenu();} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {mConnected = false;updateConnectionState(R.string.disconnected);invalidateOptionsMenu();clearUI();} //發現有可支持的服務else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {//寫數據的服務和characteristicmnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe5-0000-1000-8000-00805f9b34fb"));characteristic = mnotyGattService.getCharacteristic(UUID.fromString("0000ffe9-0000-1000-8000-00805f9b34fb"));//讀數據的服務和characteristicreadMnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));readCharacteristic = readMnotyGattService.getCharacteristic(UUID.fromString("0000ffe4-0000-1000-8000-00805f9b34fb"));} //顯示數據else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {//將數據顯示在mDataField上String data = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);System.out.println("data----" + data);displayData(data);}}}; 在發現了有可支持的服務之后會回調Service中的onServicesDiscovered()函數,并發送廣播,在官方的demo中發現了可用的Service之后,就查找該BLE設備支持的所有服務和characteristic,在這里不需要查找所有的服務,只需要向藍牙寫數據和讀取數據的 Service和characteristic的UUID即可。通過查詢低功耗藍牙(BLE)的數據手冊可以得到所需要的UUID。
有了這兩個Service和characteristic的UUID,就可以對藍牙發送數據,并發出通知(當寫數據發生改變時發出)。
private void read() {//mBluetoothLeService.readCharacteristic(readCharacteristic);//readCharacteristic的數據發生變化,發出通知mBluetoothLeService.setCharacteristicNotification(readCharacteristic, true);//Toast.makeText(this, "讀成功", Toast.LENGTH_SHORT).show();}檢測readCharacteristic的數據發生變化,發出通知。
向藍牙發送數據。
read();final int charaProp = characteristic.getProperties();//如果該char可寫if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {// If there is an active notification on a characteristic, clear// it first so it doesn't update the data field on the user interface.if (mNotifyCharacteristic != null) {mBluetoothLeService.setCharacteristicNotification( mNotifyCharacteristic, false);mNotifyCharacteristic = null;}//讀取數據,數據將在回調函數中//mBluetoothLeService.readCharacteristic(characteristic);byte[] value = new byte[20];value[0] = (byte) 0x00;if(edittext_input_value.getText().toString().equals("")){Toast.makeText(getApplicationContext(), "請輸入!", Toast.LENGTH_SHORT).show();return;}else{WriteBytes = edittext_input_value.getText().toString().getBytes();characteristic.setValue(value[0],BluetoothGattCharacteristic.FORMAT_UINT8, 0);characteristic.setValue(WriteBytes);mBluetoothLeService.writeCharacteristic(characteristic);Toast.makeText(getApplicationContext(), "寫入成功!", Toast.LENGTH_SHORT).show();}}if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {mNotifyCharacteristic = characteristic;mBluetoothLeService.setCharacteristicNotification(characteristic, true);}edittext_input_value.setText("");}一旦數據發生改變,就會發出通知,通知發出后就會調用下面的函數并發出廣播。
@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);}
在廣播接收器中接收到廣播后,把數據顯示在EditText上。下面是測試的兩張圖片。
demo下載地址:BLE 讀寫
總結
以上是生活随笔為你收集整理的Android 蓝牙4.0(BLE)开发实现对蓝牙的写入数据和读取数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring cloud(Finchle
- 下一篇: 今日资本市值多少 投资最高收益率达800