一,简介

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()会被执行@Overridepublic 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;}@Overrideprotected 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;}@Overrideprotected void onPreExecute() {Log.d(TAG,"开始连接");if (callBack != null) callBack.onStartConnect();}@Overrideprotected 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 {@Overridepublic 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 {@Overridepublic 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() {@Overridepublic void run() {isScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback);}}, SCAN_PERIOD);isScanning = true;mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索} else {//falseisScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索}} private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {@Overridepublic void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {//这里是个子线程,下面把它转换成主线程处理runOnUiThread(new Runnable() {@Overridepublic 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() {@Overridepublic void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyUpdate(gatt, txPhy, rxPhy, status);}@Overridepublic void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyRead(gatt, txPhy, rxPhy, status);}//当连接状态发生改变@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);}//发现新服务,即调用了mBluetoothGatt.discoverServices()后,返回的数据@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);}//调用mBluetoothGatt.readCharacteristic(characteristic)读取数据回调,在这里面接收数据@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);}//发送数据后的回调@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);}@Overridepublic void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//descriptor读super.onDescriptorRead(gatt, descriptor, status);}@Overridepublic void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//descriptor写super.onDescriptorWrite(gatt, descriptor, status);}@Overridepublic void onReliableWriteCompleted(BluetoothGatt gatt, int status) {super.onReliableWriteCompleted(gatt, status);}//调用mBluetoothGatt.readRemoteRssi()时的回调,rssi即信号强度@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {//读Rssisuper.onReadRemoteRssi(gatt, rssi, status);}@Overridepublic 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)读取数据回调,在这里面接收数据@Overridepublic 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终端设备建立通信的一个管道,只有有了这个管道,才有了通信的前提

Android硬件通信之 蓝牙通信相关推荐

  1. Android 硬件通讯之 蓝牙,USB,WIFI(一.蓝牙)

    蓝牙 蓝牙通讯分为:经典蓝牙与低功耗蓝牙 现在所说的蓝牙设备,大部分都是在说4.0设备,ble也特指4.0设备. 在4.0之前重要的版本有2.1版本-基本速率/增强数据率(BR/EDR)和3.0 高速 ...

  2. Android Studio开发之蓝牙通信

    安卓开发-蓝牙通信 功能需求:在微信程序的第一子项中完成"蓝牙聊天功能" 开发步骤: 配置文件注册 设计界面布局 编写用于蓝牙会话的服务组件ChatService 分别建立供主Ac ...

  3. 实现Android和PC之间的蓝牙通信

    这两天想实现PC和安卓手机的通信,限于水平,知道的方法大概有两种:基于数据包的socket和蓝牙.虽然看起来简单,但调也调了两天多.自己测试了下socket,在室内WIFI环境下时延大概是0.1s.而 ...

  4. 手机APP开发之MIT Appinventor详细实战教程(一),利用通过蓝牙控制单片机,以及实现单片机与android设备之间的串口通信

    目录 (一)前期软件准备和硬件准备 ( 二 ) 实现的思路和操作原理 ( 三) 具体的操作方法 MIT Appinventor 是编程领域较为受欢迎且适用的编程软件 ,因其操作流程和使用方法简单,一直 ...

  5. Android BLE设备蓝牙通信框架BluetoothKit

    BluetoothKit是一款功能强大的Android蓝牙通信框架,支持低功耗蓝牙设备的连接通信.蓝牙广播扫描及Beacon解析. 关于该项目的详细文档请关注:https://github.com/d ...

  6. android蓝牙通信_Flutter通过BasicMessageChannel实现Flutter 与Android iOS 的双向通信

    题记: --不到最后时刻,千万别轻言放弃,无论结局成功与否,只要你拼博过,尽力过,一切问心无愧. 通过 Flutter 来进行移动应用开发,打包 Android .iOS 双平台应用程序,在调用如相机 ...

  7. Android蓝牙通信具体解释

    蓝牙通信的大概过程例如以下: 1.首先开启蓝牙 2,搜索可用设备 3,创建蓝牙socket.获取输入输出流 4,读取和写入数据 5.断开连接关闭蓝牙 还要发送配对码发送进行推断! 以下是全部的源码:不 ...

  8. Android -传统蓝牙通信聊天

    概述 Android 传统蓝牙的使用,包括开关蓝牙.搜索设备.蓝牙连接.通信等. 详细 代码下载:http://www.demodashi.com/demo/10676.html 原文地址: Andr ...

  9. Qt on Android 蓝牙通信开发

    版权声明:本文为MULTIBEANS ORG研发跟随文章,未经MLT ORG允许不得转载. 最近做项目,需要开发安卓应用,实现串口的收发,目测CH340G在安卓手机上非常麻烦,而且驱动都是Java版本 ...

最新文章

  1. 如何使用 Druid 和 Kafka 构造 Kappa 架构完成流量分析
  2. 好的MongoDB中文文档
  3. linux 下 mysql默认表_linux环境下mysql默认是区分表名大小写的
  4. mysql 一对多 关联一条最新的数据_不得不会的mysql锁
  5. sox处理mp3_音频处理常用Linux命令总结(一)
  6. axios post请求
  7. 重磅!李航《统计学习方法》最新资源,笔记、Python 代码一应俱全!
  8. ASP.NET Treeview控件中对Checkbox的联级选择
  9. Swift笔记(一):可选类型、语法基础
  10. 通过java下载B站视频
  11. 浅谈谷歌(Google)退出中国大陆市场事件
  12. QEMU 安装与使用
  13. 2021 秋招算法岗人间地狱?人工智能方向年薪 60w 起!
  14. channel小知识点
  15. 【好东西一定要转】关于Solaris安全配置的转贴和讨论
  16. audiocontext html5,HTML5 AudioContext使用实例 - 打字的声音
  17. 现在最火爆的引流脚本好用吗?真的可以每天轻松引流500+精准粉吗
  18. 卸载office失败的解决办法
  19. Doxygen常用命令
  20. 【上位机教程】CANopen通信一体化步进电机调试软件操作

热门文章

  1. flask----继承和bock
  2. 找不到com.sun.beans.introspect.PropertyInfo的类文件问题
  3. 合并两个递增的有序数组
  4. Daemon函数的用法
  5. 汉语是世界上最好的语言
  6. 【字节跳动面试题】字典序的第K小数字
  7. Linux结构体变量报错,C语言中的结构体
  8. java tpl文件_解决Layui tpl模板渲染文件上传不起作用
  9. 线性代数之——四个基本子空间
  10. 什么值传递和引用传递