zoukankan      html  css  js  c++  java
  • Android BLE开发之Android手机与BLE终端通信

    本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处!

    近期穿戴设备发展得非常火。把相关技术也带旺了,当中一项是BLE(Bluetooth Low Energy)。BLE是蓝牙4.0的核心Profile,主打功能是高速搜索,高速连接。超低功耗保持连接和数据传输,弱点是数据传输速率低,因为BLE的低功耗特点,因此普遍用于穿戴设备。Android 4.3才開始支持BLE API,所以请各位客官把本文代码执行在蓝牙4.0和Android 4.3及其以上的系统,另外本文所用的BLE终端是一个蓝牙4.0的串口蓝牙模块。

    PS:我的i9100刷了4.4系统后,居然也能跟BLE蓝牙模块通信。


    BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。

    一个蓝牙4.0的终端能够包括多个Service,一个Service能够包括多个Characteristic。一个Characteristic包括一个Value和多个Descriptor,一个Descriptor包括一个Value。一般来说,Characteristic是手机与BLE终端交换数据的关键,Characteristic有较多的跟权限相关的字段,比如PERMISSION和PROPERTY,而当中最经常使用的是PROPERTY。本文所用的BLE蓝牙模块居然没有标准的Characteristic的PERMISSION。Characteristic的PROPERTY能够通过位运算符组合来设置读写属性,比如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此读取PROPERTY后要分解成所用的组合(本文代码已含此分解方法)。


    本文代码改自Android 4.3 Sample的BluetoothLeGatt。把冗余代码去掉,获取的BLE设备信息都通过Log。另一些必要的读写蓝牙方法,应该算是简化到大家一看就能够懂了。

    本文代码能够到http://download.csdn.net/detail/hellogv/7228819下载。接下来贴出本文执行的结果,首先是连接BLE设备后。枚举出设备全部Service、Characteristic、Descriptor,而且手机会往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb写入“send data->”字符串,BLE终端收到数据通过串口传到PC串口助手(见PC串口助手的截图):

    04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service type:PRIMARY
    04-21 18:28:25.465: E/DeviceScanActivity(12254): -->includedServices size:0
    04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service uuid:00001800-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a00-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
    04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char property:READ
    04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a01-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
    04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ
    04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a02-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
    04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
    04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a03-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
    04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
    04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char uuid:00002a04-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
    04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ
    04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service type:PRIMARY
    04-21 18:28:25.475: E/DeviceScanActivity(12254): -->includedServices size:0
    04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service uuid:00001801-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:00002a05-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
    04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:INDICATE
    04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
    04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service type:PRIMARY
    04-21 18:28:25.480: E/DeviceScanActivity(12254): -->includedServices size:0
    04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service uuid:0000ffe0-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:0000ffe1-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
    04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:READ|WRITE_NO_RESPONSE|NOTIFY|
    04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
    04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002901-0000-1000-8000-00805f9b34fb
    04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
    04-21 18:28:26.025: E/DeviceScanActivity(12254): onCharRead BLE DEVICE read 0000ffe1-0000-1000-8000-00805f9b34fb -> 00

    这里红字是由BluetoothGattCallback的onCharacteristicRead()回调而打出Log




    下面Log是PC上的串口工具通过BLE模块发送过来。由BluetoothGattCallback的 onCharacteristicChanged()打出Log
    04-21 18:30:18.260: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:18.745: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:19.085: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:19.350: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:19.605: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:19.835: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:20.055: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:20.320: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:20.510: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:20.735: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
    04-21 18:30:21.000: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone

    接下来贴出本文核心代码:

    public class DeviceScanActivity extends ListActivity {
    	private final static String TAG = DeviceScanActivity.class.getSimpleName();
    	private final static String UUID_KEY_DATA = "0000ffe1-0000-1000-8000-00805f9b34fb";
    
        private LeDeviceListAdapter mLeDeviceListAdapter;
        /**搜索BLE终端*/
        private BluetoothAdapter mBluetoothAdapter;
        /**读写BLE终端*/
        private BluetoothLeClass mBLE;
        private boolean mScanning;
        private Handler mHandler;
    
        // Stops scanning after 10 seconds.
        private static final long SCAN_PERIOD = 10000;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getActionBar().setTitle(R.string.title_devices);
            mHandler = new Handler();
    
            // Use this check to determine whether BLE is supported on the device.  Then you can
            // selectively disable BLE-related features.
            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
                finish();
            }
    
            // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
            // BluetoothAdapter through 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;
            }
            //开启蓝牙
            mBluetoothAdapter.enable();
            
            mBLE = new BluetoothLeClass(this);
            if (!mBLE.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth");
                finish();
            }
            //发现BLE终端的Service时回调
            mBLE.setOnServiceDiscoverListener(mOnServiceDiscover);
            //收到BLE终端数据交互的事件
            mBLE.setOnDataAvailableListener(mOnDataAvailable);
        }
    
    
        @Override
        protected void onResume() {
            super.onResume();
    
            // Initializes list view adapter.
            mLeDeviceListAdapter = new LeDeviceListAdapter(this);
            setListAdapter(mLeDeviceListAdapter);
            scanLeDevice(true);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            scanLeDevice(false);
            mLeDeviceListAdapter.clear();
            mBLE.disconnect();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            mBLE.close();
        }
        
        @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
            final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
            if (device == null) return;
            if (mScanning) {
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
                mScanning = false;
            }
            
            mBLE.connect(device.getAddress());
        }
    
        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);
                        invalidateOptionsMenu();
                    }
                }, SCAN_PERIOD);
    
                mScanning = true;
                mBluetoothAdapter.startLeScan(mLeScanCallback);
            } else {
                mScanning = false;
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }
            invalidateOptionsMenu();
        }
    
        /**
         * 搜索到BLE终端服务的事件
         */
        private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener(){
    
    		@Override
    		public void onServiceDiscover(BluetoothGatt gatt) {
    			displayGattServices(mBLE.getSupportedGattServices());
    		}
        	
        };
        
        /**
         * 收到BLE终端数据交互的事件
         */
        private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener(){
    
        	/**
        	 * BLE终端数据被读的事件
        	 */
    		@Override
    		public void onCharacteristicRead(BluetoothGatt gatt,
    				BluetoothGattCharacteristic characteristic, int status) {
    			if (status == BluetoothGatt.GATT_SUCCESS) 
    				Log.e(TAG,"onCharRead "+gatt.getDevice().getName()
    						+" read "
    						+characteristic.getUuid().toString()
    						+" -> "
    						+Utils.bytesToHexString(characteristic.getValue()));
    		}
    		
    	    /**
    	     * 收到BLE终端写入数据回调
    	     */
    		@Override
    		public void onCharacteristicWrite(BluetoothGatt gatt,
    				BluetoothGattCharacteristic characteristic) {
    			Log.e(TAG,"onCharWrite "+gatt.getDevice().getName()
    					+" write "
    					+characteristic.getUuid().toString()
    					+" -> "
    					+new String(characteristic.getValue()));
    		}
        };
    
        // Device scan callback.
        private BluetoothAdapter.LeScanCallback mLeScanCallback =
                new BluetoothAdapter.LeScanCallback() {
    
            @Override
            public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mLeDeviceListAdapter.addDevice(device);
                        mLeDeviceListAdapter.notifyDataSetChanged();
                    }
                });
            }
        };
    
        private void displayGattServices(List<BluetoothGattService> gattServices) {
            if (gattServices == null) return;
    
            for (BluetoothGattService gattService : gattServices) {
            	//-----Service的字段信息-----//
            	int type = gattService.getType();
                Log.e(TAG,"-->service type:"+Utils.getServiceType(type));
                Log.e(TAG,"-->includedServices size:"+gattService.getIncludedServices().size());
                Log.e(TAG,"-->service uuid:"+gattService.getUuid());
                
                //-----Characteristics的字段信息-----//
                List<BluetoothGattCharacteristic> gattCharacteristics =gattService.getCharacteristics();
                for (final BluetoothGattCharacteristic  gattCharacteristic: gattCharacteristics) {
                    Log.e(TAG,"---->char uuid:"+gattCharacteristic.getUuid());
                    
                    int permission = gattCharacteristic.getPermissions();
                    Log.e(TAG,"---->char permission:"+Utils.getCharPermission(permission));
                    
                    int property = gattCharacteristic.getProperties();
                    Log.e(TAG,"---->char property:"+Utils.getCharPropertie(property));
    
                    byte[] data = gattCharacteristic.getValue();
            		if (data != null && data.length > 0) {
            			Log.e(TAG,"---->char value:"+new String(data));
            		}
    
            		//UUID_KEY_DATA是能够跟蓝牙模块串口通信的Characteristic
            		if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){        			
            			//測试读取当前Characteristic数据。会触发mOnDataAvailable.onCharacteristicRead()
            			mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                            	mBLE.readCharacteristic(gattCharacteristic);
                            }
                        }, 500);
            			
            			//接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
            			mBLE.setCharacteristicNotification(gattCharacteristic, true);
            			//设置数据内容
            			gattCharacteristic.setValue("send data->");
            			//往蓝牙模块写入数据
            			mBLE.writeCharacteristic(gattCharacteristic);
            		}
            		
            		//-----Descriptors的字段信息-----//
    				List<BluetoothGattDescriptor> gattDescriptors = gattCharacteristic.getDescriptors();
    				for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) {
    					Log.e(TAG, "-------->desc uuid:" + gattDescriptor.getUuid());
    					int descPermission = gattDescriptor.getPermissions();
    					Log.e(TAG,"-------->desc permission:"+ Utils.getDescPermission(descPermission));
    					
    					byte[] desData = gattDescriptor.getValue();
    					if (desData != null && desData.length > 0) {
    						Log.e(TAG, "-------->desc value:"+ new String(desData));
    					}
            		 }
                }
            }//
    
        }
    }

  • 相关阅读:
    日期时间格式
    input表单加disable的后无法获取其value值
    登录判断,清除中间变量
    弹出层嵌套
    转换qq音乐格式为mp3
    layui双击表格,跳转修改页面
    设置输入电话号码为11位
    layui表格设置,分数合计
    解决数据库传回时间类型为数字
    自定义标签
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5247780.html
Copyright © 2011-2022 走看看