Android 蓝牙4.0(BLE)开发实现对蓝牙的写入数据和读取数据

来源:互联网 发布:推广淘宝优惠券挣钱 编辑:程序博客网 时间:2024/05/19 17:26

From:http://blog.csdn.net/chenfengdejuanlian/article

/details/45787123

由于最近学校组织了一个移动APP(安卓)设计大赛,自己也学习安卓有一段时间了,就跟同学商量一起去参加试试,一拍即合,然后我们就开始想idea,因为最近可穿戴设备比较火,我们也就想试试。经过商量,我负责Android上位机的开发,同学负责下位机的开发。

上位机的开发主要是低功耗蓝牙BLE的开发,然后就开始找资料,各种找,最后谷歌官方提供的demo还有其他网友基于官方demo修改的demo,结合网上的博客对demo进行理解。刚开始是觉得看起来有点费劲,各种广播,跳来跳去的,刚开始就晕菜了。但是经过三四天的理解,算是把官方的demo理解了。

代码基本上都是官方的demo,只是通过修改获得自己想要的结果,下面就简单介绍一下自己的理解。

一、扫描BLE设备activity

检查该设备是否支持BLE设备,谷歌在Android4.3才开始支持BLE设备(晕死,很长一段时间都没有一台4.3的设备,看着程序修改了也不能测试!)。

[java] view plain copy
  1. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {  
  2.            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();  
  3.            finish();  
  4.        }  

初始化获得一个bluetoothManager,并检测设备是否支持蓝牙

[java] view plain copy
  1. final BluetoothManager bluetoothManager =  
  2.                 (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);  
  3.         mBluetoothAdapter = bluetoothManager.getAdapter();  
[java] view plain copy
  1. // Checks if Bluetooth is supported on the device.  
  2.         if (mBluetoothAdapter == null) {  
  3.             Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();  
  4.             finish();  
  5.             return;  
  6.         }  
扫描BLE设备,然后添加到listView里面。

[java] view plain copy
  1. private void scanLeDevice(final boolean enable) {  
  2.         if (enable) {  
  3.             // Stops scanning after a pre-defined scan period.  
  4.             mHandler.postDelayed(new Runnable() {  
  5.                 @Override  
  6.                 public void run() {  
  7.                     mScanning = false;  
  8.                     mBluetoothAdapter.stopLeScan(mLeScanCallback);  
  9.                     invalidateOptionsMenu();  
  10.                 }  
  11.             }, SCAN_PERIOD);  
  12.   
  13.             mScanning = true;  
  14.             mBluetoothAdapter.startLeScan(mLeScanCallback);  
  15.         } else {  
  16.             mScanning = false;  
  17.             mBluetoothAdapter.stopLeScan(mLeScanCallback);  
  18.         }  
  19.         invalidateOptionsMenu();  
  20.     }  

二、蓝牙控制的服务BluetoothLeService

在这个服务里面有一个很重要的回调函数BluetoothGattCallback(),蓝牙的数据读取和状态改变都会回调这个函数。

[java] view plain copy
  1. private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {  
  2.         @Override  
  3.         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {  
  4.             String intentAction;  
  5.           //收到设备notify值 (设备上报值)  
  6.             if (newState == BluetoothProfile.STATE_CONNECTED) {  
  7.                 intentAction = ACTION_GATT_CONNECTED;  
  8.                 mConnectionState = STATE_CONNECTED;  
  9.                 broadcastUpdate(intentAction);  
  10.                 Log.i(TAG, "Connected to GATT server.");  
  11.                 // Attempts to discover services after successful connection.  
  12.                 Log.i(TAG, "Attempting to start service discovery:" +  
  13.                         mBluetoothGatt.discoverServices());  
  14.   
  15.             } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {  
  16.                 intentAction = ACTION_GATT_DISCONNECTED;  
  17.                 mConnectionState = STATE_DISCONNECTED;  
  18.                 Log.i(TAG, "Disconnected from GATT server.");  
  19.                 broadcastUpdate(intentAction);  
  20.             }  
  21.         }  
  22.   
  23.         @Override  
  24.         public void onServicesDiscovered(BluetoothGatt gatt, int status) {  
  25.             if (status == BluetoothGatt.GATT_SUCCESS) {  
  26.                 broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);  
  27.             } else {  
  28.                 Log.w(TAG, "onServicesDiscovered received: " + status);  
  29.                 System.out.println("onServicesDiscovered received: " + status);  
  30.             }  
  31.         }  
  32.   
  33.         @Override  
  34.         public void onCharacteristicRead(BluetoothGatt gatt,  
  35.                                          BluetoothGattCharacteristic characteristic,  
  36.                                          int status) {  
  37.             //读取到值,在这里读数据  
  38.             if (status == BluetoothGatt.GATT_SUCCESS) {  
  39.                 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);  
  40.             }  
  41.         }  
  42.   
  43.         @Override  
  44.         public void onCharacteristicChanged(BluetoothGatt gatt,  
  45.                                             BluetoothGattCharacteristic characteristic) {  
  46.             broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);  
  47.         }  
  48.     };  
在官方的demo中还使用到广播,可能是因为人大神写的,要严谨些。我一开始看的时候就得这有点麻烦,跳转的多麻烦。

[java] view plain copy
  1. private void broadcastUpdate(final String action) {  
  2.         final Intent intent = new Intent(action);  
  3.         sendBroadcast(intent);  
  4.     }  
  5.   
  6.     private void broadcastUpdate(final String action,  
  7.                                  final BluetoothGattCharacteristic characteristic) {  
  8.         final Intent intent = new Intent(action);  
  9.   
  10.         // This is special handling for the Heart Rate Measurement profile.  Data parsing is  
  11.         // carried out as per profile specifications:  
  12.         // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml  
  13.         if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {  
  14.             int flag = characteristic.getProperties();  
  15.             int format = -1;  
  16.             if ((flag & 0x01) != 0) {  
  17.                 format = BluetoothGattCharacteristic.FORMAT_UINT16;  
  18.                 Log.d(TAG, "Heart rate format UINT16.");  
  19.             } else {  
  20.                 format = BluetoothGattCharacteristic.FORMAT_UINT8;  
  21.                 Log.d(TAG, "Heart rate format UINT8.");  
  22.             }  
  23.             final int heartRate = characteristic.getIntValue(format, 1);  
  24.             Log.d(TAG, String.format("Received heart rate: %d", heartRate));  
  25.             intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));  
  26.         } else {  
  27.             // For all other profiles, writes the data formatted in HEX.对于所有的文件,写入十六进制格式的文件  
  28.             //这里读取到数据  
  29.             final byte[] data = characteristic.getValue();  
  30.             for (int i = 0; i < data.length; i++) {  
  31.                 System.out.println("data......" + data[i]);  
  32.             }  
  33.             if (data != null && data.length > 0) {  
  34.                 final StringBuilder stringBuilder = new StringBuilder(data.length);  
  35.                 for(byte byteChar : data)  
  36.                     //以十六进制的形式输出  
  37.                     stringBuilder.append(String.format("%02X ", byteChar));  
  38.                // intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());  
  39.                 intent.putExtra(EXTRA_DATA, new String(data));  
  40.             }  
  41.         }  
  42.         sendBroadcast(intent);  
  43.     }  
发送了广播之后就肯定有广播接收器,这个是在控制蓝牙的activity中,后面再说。

还有几个重要的函数比如readCharacteristic(BluetoothGattCharacteristic characteristic)函数,读取蓝牙中数据。

[java] view plain copy
  1. public void readCharacteristic(BluetoothGattCharacteristic characteristic) {  
  2.         if (mBluetoothAdapter == null || mBluetoothGatt == null) {  
  3.             Log.w(TAG, "BluetoothAdapter not initialized");  
  4.             return;  
  5.         }  
  6.         mBluetoothGatt.readCharacteristic(characteristic);  
  7.     }  
有readCharacteristic(BluetoothGattCharacteristic characteristic)函数,当然就有writeCharacteristic(BluetoothGattCharacteristic characteristic),向蓝牙中写入数据。

[java] view plain copy
  1. public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {  
  2.       if (mBluetoothAdapter == null || mBluetoothGatt == null) {  
  3.           Log.w(TAG, "BluetoothAdapter not initialized");  
  4.           return;  
  5.       }  
  6.       mBluetoothGatt.writeCharacteristic(characteristic);  
  7.   }  
另外在这个service中还有其他的一些函数例如初始化initialize()函数、连接蓝牙函数connect(final String address)、断开蓝牙连接函数disconnect()等。
三、蓝牙控制DeviceControlActivity

扫描到蓝牙设备之后就是对蓝牙进行自己需要的控制,比如写数据,读数据,获取设备信息,设备电量等。

在Service中讲到有一个广播,广播接收器就在这个activity中,通过不同的action做出相应的操作。

注册的几种事件

[java] view plain copy
  1. private static IntentFilter makeGattUpdateIntentFilter() {  
  2.        final IntentFilter intentFilter = new IntentFilter();  
  3.        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);  
  4.        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);  
  5.        intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);  
  6.        intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);  
  7.        return intentFilter;  
  8.    }  

[java] view plain copy
  1. // Handles various events fired by the Service.处理服务所激发的各种事件  
  2.     // ACTION_GATT_CONNECTED: connected to a GATT server.连接一个GATT服务  
  3.     // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.从GATT服务中断开连接  
  4.     // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.查找GATT服务  
  5.     // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read  
  6.     //                        or notification operations.从服务中接受数据  
  7.     private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {  
  8.         @Override  
  9.         public void onReceive(Context context, Intent intent) {  
  10.             final String action = intent.getAction();  
  11.             if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {  
  12.                 mConnected = true;  
  13.                 updateConnectionState(R.string.connected);  
  14.                 invalidateOptionsMenu();  
  15.             } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {  
  16.                 mConnected = false;  
  17.                 updateConnectionState(R.string.disconnected);  
  18.                 invalidateOptionsMenu();  
  19.                 clearUI();  
  20.             }   
  21.             //发现有可支持的服务  
  22.             else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {  
  23.                 //写数据的服务和characteristic  
  24.                 mnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe5-0000-1000-8000-00805f9b34fb"));  
  25.                 characteristic = mnotyGattService.getCharacteristic(UUID.fromString("0000ffe9-0000-1000-8000-00805f9b34fb"));  
  26.                 //读数据的服务和characteristic  
  27.                 readMnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));  
  28.                 readCharacteristic = readMnotyGattService.getCharacteristic(UUID.fromString("0000ffe4-0000-1000-8000-00805f9b34fb"));  
  29.             }   
  30.             //显示数据  
  31.             else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {  
  32.                 //将数据显示在mDataField上  
  33.                 String data = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);  
  34.                 System.out.println("data----" + data);  
  35.                 displayData(data);  
  36.             }  
  37.         }  
  38.     };      
在发现了有可支持的服务之后会回调Service中的onServicesDiscovered()函数,并发送广播,在官方的demo中发现了可用的Service之后,就查找该BLE设备支持的所有服务和characteristic,在这里不需要查找所有的服务,只需要向蓝牙写数据和读取数据的Service和characteristic的UUID即可。通过查询低功耗蓝牙(BLE)的数据手册可以得到所需要的UUID。


有了这两个Service和characteristic的UUID,就可以对蓝牙发送数据,并发出通知(当写数据发生改变时发出)。

[java] view plain copy
  1. <span style="font-size:18px;">//写数据的服务和characteristic  
  2.                 mnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe5-0000-1000-8000-00805f9b34fb"));  
  3.                 characteristic = mnotyGattService.getCharacteristic(UUID.fromString("0000ffe9-0000-1000-8000-00805f9b34fb"));  
  4.                 //读数据的服务和characteristic  
  5.                 readMnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));  
  6.                 readCharacteristic = readMnotyGattService.getCharacteristic(UUID.fromString("0000ffe4-0000-1000-8000-00805f9b34fb"));</span>  
得到这两个Service和characteristic就可以向蓝牙发送数据了。

[java] view plain copy
  1. private void read() {  
  2.         //mBluetoothLeService.readCharacteristic(readCharacteristic);  
  3.         //readCharacteristic的数据发生变化,发出通知  
  4.         mBluetoothLeService.setCharacteristicNotification(readCharacteristic, true);  
  5.         //Toast.makeText(this, "读成功", Toast.LENGTH_SHORT).show();  
  6.     }  
检测readCharacteristic的数据发生变化,发出通知。

向蓝牙发送数据。

[java] view plain copy
  1. read();  
  2.                   
  3.                 final int charaProp = characteristic.getProperties();  
  4.                   
  5.                 //如果该char可写  
  6.                 if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {  
  7.                     // If there is an active notification on a characteristic, clear  
  8.                     // it first so it doesn't update the data field on the user interface.  
  9.                     if (mNotifyCharacteristic != null) {  
  10.                         mBluetoothLeService.setCharacteristicNotification( mNotifyCharacteristic, false);  
  11.                         mNotifyCharacteristic = null;  
  12.                     }  
  13.                     //读取数据,数据将在回调函数中  
  14.                     //mBluetoothLeService.readCharacteristic(characteristic);  
  15.                     byte[] value = new byte[20];  
  16.                     value[0] = (byte0x00;  
  17.                     if(edittext_input_value.getText().toString().equals("")){  
  18.                         Toast.makeText(getApplicationContext(), "请输入!", Toast.LENGTH_SHORT).show();  
  19.                         return;  
  20.                     }else{  
  21.                         WriteBytes = edittext_input_value.getText().toString().getBytes();  
  22.                         characteristic.setValue(value[0],BluetoothGattCharacteristic.FORMAT_UINT8, 0);  
  23.                         characteristic.setValue(WriteBytes);  
  24.                         mBluetoothLeService.writeCharacteristic(characteristic);  
  25.                         Toast.makeText(getApplicationContext(), "写入成功!", Toast.LENGTH_SHORT).show();  
  26.                     }  
  27.                 }  
  28.                 if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {  
  29.                     mNotifyCharacteristic = characteristic;  
  30.                     mBluetoothLeService.setCharacteristicNotification(characteristic, true);  
  31.                 }  
  32.                 edittext_input_value.setText("");  
  33.             }  
一旦数据发生改变,就会发出通知,通知发出后就会调用下面的函数并发出广播。

[java] view plain copy
  1. @Override  
  2.        public void onCharacteristicChanged(BluetoothGatt gatt,  
  3.                                            BluetoothGattCharacteristic characteristic) {  
  4.            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);  
  5.        }  

在广播接收器中接收到广播后,把数据显示在EditText上。下面是测试的两张图片。



demo下载地址:BLE 读写


0 0