赞
踩
一,简介
1.1 现在的手机设备基本上都支持蓝牙模块,蓝牙与蓝牙之前可以相互通信,所以只要物联网机器上配有蓝牙模块,就可以用手机蓝牙连接机器蓝牙,从而和机器通信
1.2 蓝牙按协议常见可以分为经典蓝牙和低功耗蓝牙,下面是主要区别:
对比 | 经典蓝牙 | 低功耗蓝牙 |
---|---|---|
协议 | 4.0以下 | 4.0以上 |
传输速度 | 慢,100ms | 快,3ms |
传输大小 | 大,可以传大文件 | 小,大文件需要分包 |
网络拓扑 | 点对点 | 点对点,广播,Mesh组网 |
应用领域 | 无线耳机,无线音箱 | 鼠标,单车,智能家居,监控系统,灯光组网 |
二,经典蓝牙
2.1 开启蓝牙
获取BluetoothAdapter对象
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
2.2 判断设备是否支持蓝牙
- /**
- * 设备是否支持蓝牙 true为支持
- * @return
- */
- public boolean isSupportBlue(){
- return mBluetoothAdapter != null;
- }
2.3 判断蓝牙是否开启
- /**
- * 蓝牙是否打开
- * @return
- */
- public boolean isBlueEnable(){
- return mBluetoothAdapter.isEnabled();
- }
2.4 添加权限
- <!-- 使用蓝牙的权限 -->
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <!-- 扫描蓝牙设备或者操作蓝牙设置 -->
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
- <!--模糊定位权限,仅作用于6.0+-->
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <!--精准定位权限,仅作用于6.0+-->
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
2.5 动态请求权限
- /**
- * 检查权限
- */
- private void checkPermissions() {
- String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
- List<String> permissionDeniedList = new ArrayList<>();
- for (String permission : permissions) {
- int permissionCheck = ContextCompat.checkSelfPermission(this, permission);
- if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
- onPermissionGranted(permission);
- } else {
- permissionDeniedList.add(permission);
- }
- }
- if (!permissionDeniedList.isEmpty()) {
- String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);
- ActivityCompat.requestPermissions(this, deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION);
- }
- }
-
- /**
- * 权限回调
- * @param requestCode
- * @param permissions
- * @param grantResults
- */
- @Override
- public final void onRequestPermissionsResult(int requestCode,
- @NonNull String[] permissions,
- @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- switch (requestCode) {
- case REQUEST_CODE_PERMISSION_LOCATION:
- if (grantResults.length > 0) {
- //权限成功做其它任务
- }
- break;
- }
- }
2.6 检查GPS是否打开
- /**
- * 检查GPS是否打开
- * @return
- */
- private boolean checkGPSIsOpen() {
- LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
- if (locationManager == null)
- return false;
- return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);
- }
2.7 前往设置里面开启GPS
- Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
- startActivityForResult(intent, REQUEST_CODE_OPEN_GPS);
2.8 扫描蓝牙
- //发现蓝牙
- public void startBluetoothDiscovery() {
- //当前是否在扫描,如果是就取消当前的扫描,重新扫描
- if (mBluetoothAdapter.isDiscovering()){
- mBluetoothAdapter.cancelDiscovery();
- }
- mBluetoothAdapter.startDiscovery();
- }
2.9 监听蓝牙扫描状态
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//蓝牙开关状态
- filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//蓝牙开始搜索
- filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//蓝牙搜索结束
- filter.addAction(BluetoothDevice.ACTION_FOUND);//蓝牙发现新设备(未配对的设备)
- registerReceiver(scanBlueReceiver, filter);
- /**
- * 扫描广播接收类
- */
-
- public class ScanBlueReceiver extends BroadcastReceiver {
- private static final String TAG = ScanBlueReceiver.class.getName();
- private ScanBlueCallBack callBack;
-
- public ScanBlueReceiver(ScanBlueCallBack callBack){
- this.callBack = callBack;
- }
-
- //广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- Log.d(TAG, "action:" + action);
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- switch (action){
- case BluetoothAdapter.ACTION_STATE_CHANGED:
- int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
- switch (blueState) {
- case BluetoothAdapter.STATE_TURNING_ON:
- Log.e(TAG,"蓝牙正在打开中");
- break;
- case BluetoothAdapter.STATE_ON:
- Log.e(TAG,"蓝牙已经打开");
- mListener.stateOn();
- break;
- case BluetoothAdapter.STATE_TURNING_OFF:
- Log.e(TAG,"蓝牙正在关闭中");
-
- break;
- case BluetoothAdapter.STATE_OFF:
- Log.e(TAG,"蓝牙已经关闭");
- mListener.stateOff();
- break;
- }
- break;
- case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
- Log.d(TAG, "开始扫描...");
- callBack.onScanStarted();
- break;
- case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
- Log.d(TAG, "结束扫描...");
- callBack.onScanFinished();
- break;
- case BluetoothDevice.ACTION_FOUND:
- Log.d(TAG, "发现设备...");
- callBack.onScanning(device);
- break;
- }
- }
- }
2.10 连接蓝牙
- /*
- * 连接蓝牙线程
- */
-
- public class ConnectBlueTask extends AsyncTask<BluetoothDevice, Integer, BluetoothSocket> {
- private static final String TAG = ConnectBlueTask.class.getName();
- private BluetoothDevice bluetoothDevice;
- private ConnectBlueCallBack callBack;
-
- public ConnectBlueTask(ConnectBlueCallBack callBack){
- this.callBack = callBack;
- }
-
- @Override
- protected BluetoothSocket doInBackground(BluetoothDevice... bluetoothDevices) {
- bluetoothDevice = bluetoothDevices[0];
- BluetoothSocket socket = null;
- try{
- Log.d(TAG,"开始连接socket,uuid:" + ClassicsBluetooth.UUID);
- socket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(ClassicsBluetooth.UUID));
- if (socket != null && !socket.isConnected()){
- socket.connect();
- }
- mOutputStream = socket.getOutputStream();
- mInputStream = socket.getInputStream();
- }catch (IOException e){
- Log.e(TAG,"socket连接失败");
- try {
- socket.close();
- } catch (IOException e1) {
- e1.printStackTrace();
- Log.e(TAG,"socket关闭失败");
- }
- }
- return socket;
- }
-
- @Override
- protected void onPreExecute() {
- Log.d(TAG,"开始连接");
- if (callBack != null) callBack.onStartConnect();
- }
-
- @Override
- protected void onPostExecute(BluetoothSocket bluetoothSocket) {
- if (bluetoothSocket != null && bluetoothSocket.isConnected()){
- Log.d(TAG,"连接成功");
- if (callBack != null) callBack.onConnectSuccess(bluetoothDevice, bluetoothSocket);
- }else {
- Log.d(TAG,"连接失败");
- if (callBack != null) callBack.onConnectFail(bluetoothDevice, "连接失败");
- }
- }
- }
2.11 判断蓝牙是否连接
- /**
- * 当前设备与指定设备是否连接
- */
- public boolean isBlueToothConnected() {
- boolean connected = (bluetoothSocket != null && bluetoothSocket.isConnected());
- if (bluetoothDevice == null)
- return connected;
- return connected && bluetoothSocket.getRemoteDevice().equals(bluetoothDevice);
- }
2.12 读取蓝牙消息
- private class ReadThread extends Thread {
-
- @Override
- public void run() {
- super.run();
- while (!isInterrupted()) {
- int size;
- try {
- byte[] buffer = new byte[512];
- if (mInputStream == null) return;
- size = mInputStream.read(buffer);
- if (size > 0) {
- String mReception=new String(buffer, 0, size);
- String msg = mReception.toString().trim();
- Log.e(TAG, "接收短消息:" + msg);
- }
- } catch (IOException e) {
- e.printStackTrace();
- return;
- }
- }
- }
- }
2.13 发送蓝牙指令
- private class WriteRunnable implements Runnable {
- @Override
- public void run() {
- try {
- String cmd="KZMT;";
- Log.e(TAG, "发送短消息:" + cmd);
- mOutputStream.write(cmd.getBytes());
- mOutputStream.flush();
- } catch (IOException e) {
-
- }
- }
- }
2.14 断开连接
- /**
- * 关闭蓝牙Socket连接
- */
- public void closeBluetoothStream() {
- try {
- if (mOutputStream != null) {
- mOutputStream.close();
- mOutputStream = null;
- }
- if (mInputStream != null) {
- mInputStream.close();
- mInputStream = null;
- }
- if (bluetoothSocket != null) {
- if (bluetoothSocket.isConnected()) {
- bluetoothSocket.close();
- }
- bluetoothSocket = null;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
三,低功耗蓝牙
3.1 低功耗蓝牙扫描之前的步骤都一样,开启,请求权限等,从扫描开始有变化,所以扫描之前步骤可以参考经典蓝牙,下面是扫描的操作:
- private BluetoothAdapter mBluetoothAdapter;
- private boolean isScanning;//是否正在搜索
- private Handler mHandler;
- //15秒搜索时间
- private static final long SCAN_PERIOD = 15000;
-
- private void scanLeDevice(final boolean enable) {
- if (enable) {//true
- //15秒后停止搜索
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- isScanning = false;
- mBluetoothAdapter.stopLeScan(mLeScanCallback);
- }
- }, SCAN_PERIOD);
- isScanning = true;
- mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索
- } else {//false
- isScanning = false;
- mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索
- }
- }
-
- 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() {
- //在这里可以把搜索到的设备保存起来
- //device.getName();获取蓝牙设备名字
- //device.getAddress();获取蓝牙设备mac地址
- //这里的rssi即信号强度,即手机与设备之间的信号强度。
- }
- });
- }
-
- };
3.2 停止扫描
mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索
3.3 连接蓝牙
- //这个方法需要三个参数:一个Context对象,自动连接(boolean值,表示只要BLE设备可用是否自动连接它),和BluetoothGattCallback调用。
- BluetoothGatt mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback);
-
- BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
- @Override
- public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
- super.onPhyUpdate(gatt, txPhy, rxPhy, status);
- }
-
- @Override
- public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
- super.onPhyRead(gatt, txPhy, rxPhy, status);
- }
-
- //当连接状态发生改变
- @Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
- super.onConnectionStateChange(gatt, status, newState);
- }
-
- //发现新服务,即调用了mBluetoothGatt.discoverServices()后,返回的数据
- @Override
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- super.onServicesDiscovered(gatt, status);
- }
-
- //调用mBluetoothGatt.readCharacteristic(characteristic)读取数据回调,在这里面接收数据
- @Override
- public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
- super.onCharacteristicRead(gatt, characteristic, status);
- }
-
- //发送数据后的回调
- @Override
- public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
- super.onCharacteristicWrite(gatt, characteristic, status);
- }
-
- @Override
- public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
- super.onCharacteristicChanged(gatt, characteristic);
- }
-
- @Override
- public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//descriptor读
- super.onDescriptorRead(gatt, descriptor, status);
- }
-
- @Override
- public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//descriptor写
- super.onDescriptorWrite(gatt, descriptor, status);
- }
-
- @Override
- public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
- super.onReliableWriteCompleted(gatt, status);
- }
-
- //调用mBluetoothGatt.readRemoteRssi()时的回调,rssi即信号强度
- @Override
- public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {//读Rssi
- super.onReadRemoteRssi(gatt, rssi, status);
- }
-
- @Override
- public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
- super.onMtuChanged(gatt, mtu, status);
- }
- };
3.4 断开连接
mBluetoothGatt.disconnect(); //主动断开连接
3.5 写入数据,通过mBluetoothGatt.writeCharacteristic(characteristic)
写入的数据长度是有限制的,如果要改变这个限制,可以通过MTU来改变
- /**
- * 向蓝牙发送数据
- */
- public void dataSend(){
- //byte[] send={(byte) 0xaa,0x01,0x01,(byte)0x81,(byte) 0xff};
- byte[] send = new byte[20];
- send = hexStringToBytes(et_send.getText().toString());
- byte[] sendData=new byte[send.length+2];
- sendData[0]=(byte) 0xaa;
- sendData[sendData.length-1]=(byte) 0xff;
- for(int i=1;i<sendData.length-1;i++){
- sendData[i]=send[i-1];
- }
- mCharacteristic.setValue(sendData);
- boolean status = mBluetoothGatt.writeCharacteristic(mCharacteristic);
- }
3.6 读取数据,通过mBluetoothGatt.readCharacteristic(characteristic),在BluetoothGattCallback 回调方法onCharacteristicRead中获取到数据。
- //读取数据时调用这个方法,数据会在回调接口中(BluetoothGattCallback )获取到
- mBluetoothGatt.readCharacteristic(characteristic)
-
-
- BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
-
- //调用mBluetoothGatt.readCharacteristic(characteristic)读取数据回调,在这里面接收数据
- @Override
- public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
- super.onCharacteristicRead(gatt, characteristic, status);
- //这里面就是数据
- characteristic.getValue();
- }
- }
3.7 总结
第一步:我们要判断手机是否支持BLE,并且获得各种权限,才能让我们之后的程序能正常运行。
然后,我们去搜索BLE设备,得到它的MAC地址。
第二步:我们通过这个MAC地址去连接,连接成功后,去遍历得到Characteristic的uuid。
在我们需要发送数据的时候,通过这个uuid找到Characteristic,去设置其值,最后通过writeCharacteristic(characteristic)方法发送数据。
如果我们想知道手机与BLE设备的距离,则可以通过readRemoteRssi()去得到rssi值,通过这个信号强度,就可以换算得到距离。
第三步:连接上后,我们就可以用BluetoothGatt的各种方法进行数据的读取等操作
3.8 相关api
1、BluetoothManager
通过BluetoothManager来获取BluetoothAdapter。
2、BluetoothAdapter
代表了移动设备的本地的蓝牙适配器, 通过该蓝牙适配器可以对蓝牙进行基本操作,一个Android系统只有一个BluetoothAdapter,通过BluetoothManager获取。
3、BluetoothDevice
扫描后发现可连接的设备,获取已经连接的设备,通过它可以获取到BluetoothGatt。
4、BluetoothGatt
继承BluetoothProfile,通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback,可以看成蓝牙设备从连接到断开的生命周期。
5、BluetoothGattService
服务,Characteristic的集合。
6、BluetoothGattCharacteristic
相当于一个数据类型,可以看成一个特征或能力,它包括一个value和0~n个value的描述(BluetoothGattDescriptor)。
7、BluetoothGattDescriptor
描述符,对Characteristic的描述,包括范围、计量单位等。
8、BluetoothProfile
一个通用的规范,按照这个规范来收发数据。
9、BluetoothGattCallback
已经连接上设备,对设备的某些操作后返回的结果。
10、总结:当我们扫描后发现多个设备BluetoothDevice,每个设备下会有很多服务BluetoothGattService,这些服务通过service_uuid(唯一标识符)来区分,每个服务下又会有很多特征BluetoothGattCharacteristic,这些特征通过uuid来区分的,它是手机与BLE终端设备交换数据的关键。而BluetoothGatt可以看成手机与BLE终端设备建立通信的一个管道,只有有了这个管道,才有了通信的前提
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。