Java:PC端作为客户端连接蓝牙设备并接收蓝牙发送的数据

拓拔泉
2023-12-01

感谢:(1)PC端蓝牙开发  https://www.cnblogs.com/zeussbook/p/12827479.html

                                             https://blog.csdn.net/svizzera/article/details/77434917

           (2)解决数据读取乱序  https://blog.csdn.net/clliu_hust/article/details/80874272

一、蓝牙流程

蓝牙客户端Socket的与Sokcet流程是一样的,只不过参数不同而已。如下:
1、创建客户端蓝牙Sokcet
2、创建连接
3、读写数据
4、关闭

 

二、导入jar包

需要导入第三方的蓝牙bluecove.jar包。其中32位系统和64位系统所导的包不同,需要区分。否则会报错。BlueCove还需要Apache的commons-io包,这个顺便下就可以的。

PS:如果启动报错:bluecove native library version mismatch,可尝试把commons-io包去掉。

链接:https://pan.baidu.com/s/1tFixZRIRaN4HdlslDlKhuQ 
提取码:rr12 

三、代码部分

3.1设备查找类

package com.jcsim;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Jcsim
 * @Date: 2020/11/25 15:15
 * @Description:设备查找类
 */
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;

import javax.bluetooth.DataElement;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
public class RemoteDeviceDiscovery {

    public final static Set<RemoteDevice> devicesDiscovered = new HashSet<RemoteDevice>();

    public final static Vector<String> serviceFound = new Vector<String>();

    final static Object serviceSearchCompletedEvent = new Object();
    final static Object inquiryCompletedEvent = new Object();


    /**
     * 发现监听
     */
    private static DiscoveryListener listener = new DiscoveryListener() {
        public void inquiryCompleted(int discType) {
            System.out.println("#" + "搜索完成");
            synchronized (inquiryCompletedEvent) {
                inquiryCompletedEvent.notifyAll();
            }
        }

        /**
         * 发现设备
         * @param remoteDevice
         * @param deviceClass
         */
        @Override
        public void deviceDiscovered(RemoteDevice remoteDevice, DeviceClass deviceClass) {
            devicesDiscovered.add(remoteDevice);

            try {
                System.out.println("#发现设备" + remoteDevice.getFriendlyName(false)+"   设备地址:"+remoteDevice.getBluetoothAddress());
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        /**
         * 发现服务
         * @param transID id
         * @param servRecord 服务记录
         */
        @Override
        public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
            for (int i = 0; i < servRecord.length; i++) {
                String url = servRecord[i].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
                if (url == null) {
                    continue;
                }
                serviceFound.add(url);
                DataElement serviceName = servRecord[i].getAttributeValue(0x0100);
                if (serviceName != null) {
                    System.out.println("service " + serviceName.getValue() + " found " + url);
                } else {
                    System.out.println("service found " + url);
                }
            }
            System.out.println("#" + "servicesDiscovered");
        }

        /**
         * 服务搜索已完成
         * @param arg0
         * @param arg1
         */
        @Override
        public void serviceSearchCompleted(int arg0, int arg1) {
            System.out.println("#" + "serviceSearchCompleted");
            synchronized(serviceSearchCompletedEvent){
                serviceSearchCompletedEvent.notifyAll();
            }
        }
    };


    /**
     * 查找设备
     * @throws IOException
     * @throws InterruptedException
     */
    private static void findDevices() throws IOException, InterruptedException {

        devicesDiscovered.clear();

        synchronized (inquiryCompletedEvent) {

            LocalDevice ld = LocalDevice.getLocalDevice();

            System.out.println("#本机蓝牙名称:" + ld.getFriendlyName());

            boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC,listener);

            if (started) {
                System.out.println("#" + "等待搜索完成...");
                inquiryCompletedEvent.wait();
                LocalDevice.getLocalDevice().getDiscoveryAgent().cancelInquiry(listener);
                System.out.println("#发现设备数量:" + devicesDiscovered.size());
            }
        }
    }

    /**
     * 获取设备
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public static Set<RemoteDevice> getDevices() throws IOException, InterruptedException {
        findDevices();
        return devicesDiscovered;
    }

    /**
     * 查找服务
     * @param btDevice
     * @param serviceUUID
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public static String searchService(RemoteDevice btDevice, String serviceUUID) throws IOException, InterruptedException {
        UUID[] searchUuidSet = new UUID[] { new UUID(serviceUUID, false) };

        int[] attrIDs =  new int[] {
                0x0100 // Service name
        };

        synchronized(serviceSearchCompletedEvent) {
            System.out.println("search services on " + btDevice.getBluetoothAddress() + " " + btDevice.getFriendlyName(false));
            LocalDevice.getLocalDevice().getDiscoveryAgent().searchServices(attrIDs, searchUuidSet, btDevice, listener);
            serviceSearchCompletedEvent.wait();
        }

        if (serviceFound.size() > 0) {
            return serviceFound.elementAt(0);
        } else {
            return "";
        }
    }
}

3.2蓝牙客户端类

package com.jcsim;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Jcsim
 * @Date: 2020/11/25 15:14
 * @Description:蓝牙客户端类
 */
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.bluetooth.BluetoothConnectionException;
import javax.bluetooth.RemoteDevice;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
public class BluetoothClient {
    private StreamConnection streamConnection;//流连接
    private OnDiscoverListener onDiscoverListener = null;//发现监听
    private OnClientListener onClientListener = null;//客户端监听

    /**
     * 客户端监听
     */
    public interface OnClientListener {
        void onConnected(DataInputStream inputStream, OutputStream outputStream);
        void onConnectionFailed();
        void onDisconnected();
        void onClose();
    }

    /**
     * 发现监听
     */
    public interface OnDiscoverListener {
        void onDiscover(RemoteDevice remoteDevice);
    }


    /**
     * 无参构造函数
     */
    public BluetoothClient() {
    }

    /**
     * 查找所有
     * @throws IOException
     * @throws InterruptedException
     */
    public void find() throws IOException, InterruptedException {
        //附近所有的蓝牙设备,必须先执行 runDiscovery
        Set<RemoteDevice> devicesDiscovered = RemoteDeviceDiscovery.getDevices();
        Iterator<RemoteDevice> itr = devicesDiscovered.iterator();
        //连接
        while (itr.hasNext()) {
            RemoteDevice remoteDevice = itr.next();

            onDiscoverListener.onDiscover(remoteDevice);
        }
    }

    /**
     * 启动连接
     * @param remoteDevice
     * @throws IOException
     * @throws InterruptedException
     */
    public void startClient(RemoteDevice remoteDevice) throws IOException, InterruptedException {
//        String url = RemoteDeviceDiscovery.searchService(remoteDevice, serviceUUID);
//        System.out.println("url=="+url);
//        1 为通道;authenticate=true;encrypt=true表示需要验证pin码
//        btspp://<蓝牙设备地址>:<通道号>
        String url = "btspp://"+remoteDevice.getBluetoothAddress()+":1;authenticate=true;encrypt=true";
        try{
            streamConnection = (StreamConnection) Connector.open(url);
            if (this.onClientListener != null) {
                this.onClientListener.onConnected(streamConnection.openDataInputStream(), streamConnection.openOutputStream());
            }else{
                System.out.println("请打开蓝牙");
            }
        } catch (BluetoothConnectionException e){
            e.printStackTrace();
            System.out.println("蓝牙连接错误,请查看蓝牙是否打开。");
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    public OnDiscoverListener getOnDiscoverListener() {
        return onDiscoverListener;
    }


    public void setOnDiscoverListener(OnDiscoverListener onDiscoverListener) {
        this.onDiscoverListener = onDiscoverListener;
    }


    public OnClientListener getClientListener() {
        return onClientListener;
    }


    public void setClientListener(OnClientListener onClientListener) {
        this.onClientListener = onClientListener;
    }


}

3.3 蓝牙客户端业务类

package com.jcsim;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Jcsim
 * @Date: 2020/11/25 15:17
 * @Description:蓝牙客户端业务类
 */

import javax.bluetooth.RemoteDevice;
import javax.microedition.io.ConnectionNotFoundException;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;

public class BluetoothClientService {

    public static void main(String[] argv) {

        final String serverUUID = "1000110100001000800000805F9B34FB"; //需要与服务端相同

        BluetoothClient client = new BluetoothClient();

        Vector<RemoteDevice> remoteDevices = new Vector<>();

        Boolean isConnect = false;

        client.setOnDiscoverListener(new BluetoothClient.OnDiscoverListener() {

            @Override
            public void onDiscover(RemoteDevice remoteDevice) {
                remoteDevices.add(remoteDevice);
            }

        });

        client.setClientListener(new BluetoothClient.OnClientListener() {

            @Override
            public void onConnected(DataInputStream inputStream, OutputStream outputStream) {
                System.out.printf("Connected");
                // 开启线程读写蓝牙上接收和发送的数据。
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println("客户端开始监听...");

//                            System.out.println("接收连接");
//                            System.out.println("开始读数据...");
                            SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
                            while (true) {
                                byte[] buffer = new byte[1024];
                                int bytes=0;
                                int ch;
//                                inputStream.read(buffer)
                                while ((ch = inputStream.read()) != '\n') {
                                    // 读数据。
//                                    String s = new String(buffer);
//                                    System.out.println("===========start=============");
//                                    System.out.println(s.trim());
//                                    System.out.println("------------end------------");
//                                    Thread.sleep(1000);
                                    if(ch!=-1){
                                        buffer[bytes] = (byte) ch;
                                        bytes++;
                                    }
                                }
                                buffer[bytes] = (byte)'\n';
                                bytes++;
                                String s = new String(buffer);
                                System.out.println("===========start=============");
                                System.out.println(df.format(new Date())+"->"+s.trim());
                                System.out.println("------------end------------");

//                                inputStream.close();
//                                onClose();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                }).start();

            }

            @Override
            public void onConnectionFailed() {
                System.out.printf("Connection failed");
            }

            @Override
            public void onDisconnected() {

            }

            @Override
            public void onClose() {

            }

        });

        try {
            client.find();
            if (remoteDevices.size() > 0 ) {
                for(int i=0;i<remoteDevices.size();i++){
                    System.out.println("第"+i+"个地址为:"+remoteDevices.get(i).getBluetoothAddress());
                    if( "所要连接的蓝牙的地址".equals(remoteDevices.get(i).getBluetoothAddress())){
                        isConnect = true;
                        client.startClient(remoteDevices.get(i));
                        break;
                    }
                }
                if (!isConnect){
                    System.out.println("请打开传感器蓝牙设备。");
                }
//                System.out.println("remoteDevices.firstElement="+remoteDevices.firstElement());
            }else {
                System.out.println("附件没有蓝牙设备");
            }
        } catch (ConnectionNotFoundException e){
            System.out.println("当前蓝牙不在线");
            e.printStackTrace();
        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
        }
    }
}

 

 

四、读数据

之前读取数据的时候,一直乱码或乱序(一段字符被分成几段)。

乱码解决方法:蓝牙发送的波特率问题,一般为9600。

乱序:使用InputStream读取时不能和串口一样设置一个结束符(串口的结束符一般是换行符'\r\n'),于是线程读取就很随机性了,你不知道它在什么时候就读完一刀下去,你的字符串就不完整了。

旧方法(乱序):

while (true) {
    
    byte[] buffer = new byte[1024];
                              
//                                
    while (inputStream.read(buffer) != -1) {
        // 读数据。
        String s = new String(buffer);
        System.out.println("===========start=============");
        System.out.println(s.trim());
        System.out.println("------------end------------");
        Thread.sleep(1000);
                                   
   }
                              
// inputStream.close();
// onClose();
}

乱序解决方法:使用InputStream.read()逐个读取字符,判读是不是结束符,如果没到结束符就一直读取写入

SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
                            while (true) {
                                byte[] buffer = new byte[1024];
                                int bytes = 0; //字符串长度
                                int ch;  // 读取字符的变量
                                while ((ch = inputStream.read()) != '\n') {
                                    // 读数据。
                                    if(ch!=-1){
                                        buffer[bytes] = (byte) ch; // 将读取到的字符写入
                                        bytes++;
                                    }
                                }
                                buffer[bytes] = (byte)'\n'; //最后加上一个换行
                                bytes++;
                                String s = new String(buffer);
                                System.out.println("===========start=============");
                                System.out.println(df.format(new Date())+"->"+s.trim());
                                System.out.println("------------end------------");
                            }

五、GitHub地址

https://github.com/jiancong952/bluetooth_java.git

 类似资料: