基于蓝牙适配器的PC与Android端通讯

news/2025/3/12 12:40:53/

Demo

首先,直接给Demo,对于只想使用的朋友,直接下载使用即可。Demo其实也是从网上爬来的,之后做了各种调试和修改。

原有Demo代码下载,可见地址。

修改后Demo效果如下。效果不太清晰,见谅。

(1)PC端


(2)Android端


细节实现

Android端

android端做为客户端要与PC通讯,需要完成以下几步。

添加蓝牙权限

    <uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

注册广播接收器

个人认为该步骤可选。但最好存在。
原因在于,虽然蓝牙适配器可以获取周围蓝牙设备的列表,但对于周围蓝牙设备的扫描比较耗时。
返回蓝牙设备列表时,可能仍处于搜索过程中。
当扫描完成时,广播接收器将受到action为ACTION_DISCOVERY_FINISHED的广播。此时,获取设备列表,将比较全面。

建议接收器监听,以下三个action。

    IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//发现设备intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描完毕intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//扫描结束registerReceiver(broadcastReceiver, intentFilter);

开启蓝牙

    if (!bluetoothAdapter.isEnabled()) {bluetoothAdapter.enable();}
开启适配器后,android将开始遍历周围可以被访问的蓝牙设备。该动作将触发广播。

当然,也可以通过触发bluetoothAdapter.startDiscovery()重新扫描。

选定目标蓝牙设备

由适配器获取设备列表,并根据PC的bluetooth MAC地址,选定目标蓝牙设备。SERVICE_ADDRESS为PC端蓝牙mac地址,格式为XX:XX:XX:XX:XX:XX

    Set<BluetoothDevice> mySet = bluetoothAdapter.getBondedDevices();for (BluetoothDevice device : mySet) {if (device.getAddress().equalsIgnoreCase(SERVICE_ADDRESS) ) {service = device;break;}}

创建RfcommSocket并建立连接

    private static final String serverUUID = "00001101-0000-1000-8000-00805F9B34FB"private BluetoothSocket bluetoothSocket;bluetoothSocket = service.createRfcommSocketToServiceRecord(UUID.fromString(serverUUID));bluetoothSocket.connect();
这里需要说明的有两点。
1.连接操作比较耗时,视具体设备不同。所以,需要将连接操作放到子线程中完成。

2.在Android端,需要使用UUID,完成与其他蓝牙设备的连接。关于UUID,之后小结会提到。

收发数据

收发数据也较为耗时,需要放在子线程实现。

    OutputStream outputStream;try {outputStream = bluetoothSocket.getOutputStream();outputStream.write("A message from android device".getBytes());showMessage("Successfully send message");} catch (IOException e) {showMessage(e.getMessage()+", during output");}InputStream inputStream;try {inputStream = bluetoothSocket.getInputStream();byte[] buffer = new byte[200];inputStream.read(buffer);showMessage("Concurrently receive message : " + new String(buffer));} catch (IOException e) {showMessage(e.getMessage()+", during get input");}

关闭RfcommSocket

bluetoothSocket.close();

PC端

设置PC蓝牙设备可见

LocalDevice.getLocalDevice().setDiscoverable(DiscoveryAgent.GIAC);

创建连接流监听器

private StreamConnectionNotifier streamConnectionNotifier;
streamConnectionNotifier = (StreamConnectionNotifier) Connector.open("btspp://localhost:" + SERVER_UUID.toString());
注意,此处使用的UUID,必须与android端的UUID一致。

开启监听

StreamConnection streamConnection = null;
streamConnection = streamConnectionNotifier.acceptAndOpen();
acceptAndOpen()方法调用后,将进入等待。

获取输入流和输出流

while (isListening) {if ((inputStream.available()) <= 0) {Thread.sleep(1000);}System.out.println("message is comming");outputStream.write("hello android BT".getBytes());inputStream.read(buffer);String message = new String(buffer);System.out.println("Receive message : " + message);if (message.contains("EXIT_APP")) {System.out.println("Listener closed");isListening = false;}
}

关闭连接

inputStream.close();
outputStream.close();
streamConnection.close();

问题

BUG

java.io.IOException: read failed, socket might closed or timeout, read ret: -1
在连接时,常会遇到该BUG。网上很多方法说,可以通过修改UUID的方式,来FIX该BUG。

但翻看createRfcommSocketToServiceRecord方法的注释,发现该UUID不能随便修改。

     * <p>Hint: If you are connecting to a Bluetooth serial board then try* using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.* However if you are connecting to an Android peer then please generate* your own unique UUID.

因此,若是采用串口通信,必须使用“00001101-0000-1000-8000-00805F9B34FB”。

造成连接失败的另一原因,可能是channel ID的问题。
在创建Socket时,createRfcommSocketToServiceRecord使用UUID作为唯一传参,而默认channel为-1。

网上建议,通过反射使用BluetoothDevice的隐藏public createRfcommSocket方法,利用传参指定的channel值,创建Socket。


channel的取值范围为1至30.

Demo中给出了工具方法。

    public BluetoothSocket cretateBluetoothSocketbyChannel(BluetoothDevice Device,int channel,boolean autoForward){BluetoothSocket socket=null;try {showMessage("Trying fallback on channel "+channel);socket =(BluetoothSocket) Device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(Device,channel);socket.connect();Log.d(TAG,"createRfcommSocket on channel "+channel);showMessage("Successfully connect");} catch (Exception e) {showMessage(e.getMessage());if(channel<30){if(autoForward){socket=cretateBluetoothSocketbyChannel(Device,channel+1,autoForward);}else {showMessage("Connect Failed");}}}return socket;}

另外,就该问题,吐槽一下适配器。适配器的性能也是参差不齐。此前摁着绿联的蓝牙适配器,试了3天。同样的代码,问题百出。后来转用奥视通(ost108),几分钟便过了。不管是添加设备时的认证过程的人性化设计,还是设备服务驱动的安装速度,天壤之别~

驱动

Demo在实现时,遇到找不到设备的情况。个人感觉是由于适配器所提供的驱动问题造成的。在卸载后,使用通用驱动可以解决该问题。


Bluecove版本

在64位OS下开发,需要使用Bluecove 64bit版本,本Demo使用为64bit。下载地址

参考文献

在开发过程中,以下文章给予了很多帮助,一并列下。

https://blog.csdn.net/tingfengzheshuo/article/details/45292201

http://royal2xiaose.iteye.com/blog/1420138

https://blog.csdn.net/old_me_mory/article/details/18962701

https://blog.csdn.net/peceoqicka/article/details/51979469(着重感谢)

结语

第一次涉及蓝牙项目,耗时较长,且深入不够,若有疏漏,还望提出。


http://www.ppmy.cn/news/507383.html

相关文章

three.js常用几何体介绍以及自定义几何体

一、自定义三角形几何体 核心代码&#xff1a; // 添加物体 // 创建几何体 for (let i 0; i < 50; i) {// 每一个三角形&#xff0c;需要3个顶点&#xff0c;每个顶点需要3个值const geometry new THREE.BufferGeometry();const positionArray new Float32Array(9);for …

android 魅族手机bug多,魅族Flyme出现大面积Bug

大家知道这个世上不可能存在十全十美的事&#xff0c;智能手机也一样。特别是智能手机的应用系统&#xff0c;总会出现这样或那样的问题&#xff0c;即使是苹果的iOS也是如此。无论是正式版还是开发版都会出现问题&#xff0c;但是只要不是经常影响使用一般情况下大家都会理解的…

魅族设置语音录音服务器,魅族手机留言录音功能使用方法介绍

魅族手机这录音功能对于用户来说日常使用也是很方便的&#xff0c;这点上相信很多用户也是有体会的&#xff0c;下面就一起来看看这录音功能的介绍。 一.首先进入魅族手机“电话”&#xff0c;直接在Flyme桌面点击【电话图标】即可&#xff0c;如图所示。 二.然后点击右下角的“…

魅族插了卡显示无服务器,魅族手机SD卡无法读取怎么办解决方案

现在玩家对手机的要求越来越高,不仅在手机上下很大应用软件,还要下很多资料在手机上。为了满足广大用户的需要,很多手机不但增大了手机自身的内存,而且还使用SD卡扩大手机内存。但是如果遇到手机不能读取SD卡怎么办?下面我们以魅族手机为例,提供几个解决办法以供参考。 当…

魅族Android10内测招募答案,魅族flyme9内测招募答案

魅族flyme9内测招募答案&#xff0c;需要进行答题才能获取测试资格的&#xff0c;很多小伙伴们都是知道的&#xff0c;答题的内容都是和系统相关的&#xff0c;有之前的也有最新的。因为今天是刚刚公布的&#xff0c;小编也还在整理中&#xff0c;为您带来一些之前的常规问题的…

关闭数字健康 android 魅族,魅族手机隐私模式开启关闭使用方法详解

隐私模式与正常模式有什么不一样&#xff1f;当您开启隐私模式后&#xff0c;隐私模式就会区别于正常模式独立存在&#xff0c;保护您的隐私安全。将正常模式下手机上的隐私信息设置在隐私模式下&#xff0c;为了让他人无法察觉手机上有两个模式&#xff0c;正常模式和隐私模式…

魅族pro5 刷机 android,魅族 PRO5中文Recovery刷机教程

魅族 PRO5如何用Recovery刷机&#xff0c;今天安致小编为大家带来这篇魅族 PRO5中文Recovery刷机教程&#xff0c;该教程以我们常见的Recovery为例&#xff0c;对魅族 PRO5刷机进行详细讲解&#xff0c;希望对刷机新手有帮助。 注&#xff1a;本文中的Recovery只作为参考&#…

A feasibility study on SSVEP-based interaction with motivating and immersive virtual and augmented r

A feasibility study on SSVEP-based interaction with motivating and immersive virtual and augmented reality 基于SSVEP的交互与刺激和沉浸式虚拟和增强现实的可行性研究 文章目录 A feasibility study on SSVEP-based interaction with motivating and immersive virtua…