如何让蓝牙RFCOMM持续工作?

发布于 2024-10-30 14:56:59 字数 10109 浏览 1 评论 0原文

我正在尝试构建一个 Android 应用程序,该应用程序将通过蓝牙串行端口配置文件 (SPP) 与外部 GPS 接收器连接。我使用的是运行 2.3.3 的 Nexus One。我已经设法让我的应用程序从 GPS 接收数据,但有两个问题:1) 当我连接到设备时,它只能在某些时候工作。有时连接只是超时,有时则显示设备正忙或正在使用。 2)我无法弄清楚如何将数据发送回设备,这可能是我如何使用流的问题,因为传入流是阻塞调用。

我只是将相关代码移动到一个新的 Android 应用程序中进行测试,如下所示:

/res/layout/main.xml (两个按钮和一个文本视图)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">
<Button android:id="@+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Connect"></Button>
<Button android:id="@+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send Message"></Button>
<TextView android:id="@+id/textStatus" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Status Goes Here" />
</LinearLayout>

/src/com.example.bluetoothspp/MainActivity.java

package com.example.bluetoothspp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    private static final String BTAG = "BTThread";
    static final int MSG_BT_GOT_DATA = 1;
    static final int MSG_BT_STATUS_MSG = 2;
    static final int MSG_BT_FINISHED = 99;

    Button btnStart, btnSend;
    TextView textStatus;
    private BluetoothAdapter mBluetoothAdapter = null;
    private BluetoothDevice btdevice = null;
    Thread bThread;
    BluetoothSocket bsocket;
    InputStream bis = null; //Bluetooth input stream
    OutputStream bos = null; //Bluetooth output stream
    private String MACAddress = "00:01:95:06:1F:32";


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btnStart = (Button)findViewById(R.id.btnStart);
        btnSend = (Button)findViewById(R.id.btnSend);
        textStatus = (TextView)findViewById(R.id.textStatus);
        btnStart.setOnClickListener(btnStartListener);
        btnSend.setOnClickListener(btnSendListener);

        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    private OnClickListener btnStartListener = new OnClickListener() {
        public void onClick(View v){
            if(btnStart.getText().equals("Connect")){
                Log.i(TAG, "Connect button pressed");
                if (mBluetoothAdapter == null) { //No adapter. Fail
                    Log.e(TAG, "getDefaultAdapter returned null");
                    textStatus.setText("getDefaultAdapter returned null");

                } else {
                    if (!mBluetoothAdapter.isEnabled()) { //Bluetooth disabled
                        Log.e(TAG, "Bluetooth is Disabled");
                        textStatus.setText("Bluetooth is Disabled");

                    } else {
                        Log.i(TAG, "Connecting to Device: " + MACAddress);
                        btdevice = mBluetoothAdapter.getRemoteDevice(MACAddress);
                        Log.i(TAG, "Device: " + btdevice.getName());
                        Log.i(TAG, "Trying to Connect...");
                        textStatus.setText("Trying to Connect...");
                        Log.i(TAG, "Starting Thread");
                        try {
                            bThread = new Thread(new BluetoothClient(btdevice, true));
                            bThread.start();
                        } catch (IOException e) {
                            Log.e(TAG, "Could not create thread for bluetooth: " + e);
                            textStatus.setText("Could not create thread for bluetooth...");
                        }
                        btnStart.setText("Disconnect");
                    }
                }

            } else {
                Log.i(TAG, "Disconnect button pressed");

                btnStart.setText("Connect");
            }
        }
    };
    private OnClickListener btnSendListener = new OnClickListener() {
        public void onClick(View v){
            textStatus.setText("Sending Message to Thread.");
            SendDataToBluetooth("something\r\n");
        }
    };


    public class BluetoothClient implements Runnable {
        public BluetoothClient(BluetoothDevice device, boolean IsAnHTCDevice) throws IOException {
            if (IsAnHTCDevice) {
                //This is a workaround for HTC devices, but it likes to throw an IOException "Connection timed out"
                try {
                    Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
                    bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));
                } catch (Exception e) {
                    Log.e(BTAG, "Error at HTC/createRfcommSocket: " + e);
                    e.printStackTrace();
                    handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
                }
            } else {
                //This is the normal method, but on a Nexus One it almost always throws an IOException "Service discovery failed" message
                try {
                    UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
                    bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);
                } catch (Exception e) {
                    Log.e(BTAG, "Error at createRfcommSocketToServiceRecord: " + e);
                    e.printStackTrace();
                    handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
                }
            }
        }

        public void run() {
            try {
                Log.i(BTAG, "Cancelling Discovery");
                mBluetoothAdapter.cancelDiscovery();
                Log.i(BTAG, "Connecting to Socket");
                bsocket.connect();
                bis = bsocket.getInputStream();
                bos = bsocket.getOutputStream();
                Log.i(BTAG, "Socket created, streams assigned");
                handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "Device Connected"));
                Log.i(BTAG, "Waiting for data...");
                byte[] buffer = new byte[4096];
                int read = bis.read(buffer, 0, 4096); // This is blocking
                Log.i(BTAG, "Getting data...");
                while (read != -1) {
                    byte[] tempdata = new byte[read];
                    System.arraycopy(buffer, 0, tempdata, 0, read);
                    handler.sendMessage(handler.obtainMessage(MSG_BT_GOT_DATA, tempdata));
                    read = bis.read(buffer, 0, 4096); // This is blocking
                }
            } catch (SocketTimeoutException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Log.i(BTAG, "Finished");
                handler.sendMessage(handler.obtainMessage(MSG_BT_FINISHED));
            }
        }
    }
    public void SendDataToBluetooth(String cmd) { // You run this from the main thread.
        try {
            if (bsocket != null) {
                bos.write(cmd.getBytes());
            }
        } catch (Exception e) {
            Log.e("SendDataToBluetooth", "Message send failed. Caught an exception: " + e);
        }
    }

    public Handler handler = new Handler() { // Handler for data coming from the network and bluetooth sockets
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_BT_GOT_DATA:
                Log.i("handleMessage", "MSG_BT_GOT_DATA: " + (String) msg.obj);
                textStatus.setText((String) msg.obj);
                break;
            case MSG_BT_STATUS_MSG:
                Log.i("handleMessage", "MSG_BT_STATUS_MSG: " + (String) msg.obj);
                textStatus.setText((String) msg.obj);
                break;
            case MSG_BT_FINISHED:
                Log.i("handleMessage", "MSG_BT_FINISHED");
                btnStart.setText("Connect");
                break;
            default:
                super.handleMessage(msg);
            }
        }
    };


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (bThread != null) { // If the thread is currently running, close the socket and interrupt it.
            Log.i(BTAG, "Killing BT Thread");
            try {
                bis.close();
                bos.close();
                bsocket.close();
                bsocket = null;
            } catch (IOException e) {
                Log.e(BTAG, "IOException");
                e.printStackTrace();
            } catch (Exception e) {
                Log.e(BTAG, "Exception");
                e.printStackTrace();
            }
            try {
                Thread moribund = bThread;
                bThread = null;
                moribund.interrupt();
            } catch (Exception e) {}
            Log.i(BTAG, "BT Thread Killed");
        }
    }
}

我发现使用正常的“bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);”方法通常会导致我收到“服务发现失败”消息,因此我还尝试了“bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));”方法。这更有效,但当我尝试连接时喜欢超时。

我在这里做错了什么?

I am trying to build an Android application that will interface with an external GPS receiver via the Bluetooth Serial Port Profile (SPP). I am using a Nexus One running 2.3.3. I have managed to get my application to receive data from GPS, but I have two issues: 1) When I connect to the device, it only works some of the time. Sometimes the connection just times out, other times it says the device is busy or in use. 2) I haven't been able to figure out how to send data back to the device, which is probably an issue of how I'm using the streams since the incoming stream is a blocking call.

I moved just the relevant code to a new Android application for testing, which is the following:

/res/layout/main.xml (two buttons and a textview)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">
<Button android:id="@+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Connect"></Button>
<Button android:id="@+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send Message"></Button>
<TextView android:id="@+id/textStatus" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Status Goes Here" />
</LinearLayout>

/src/com.example.bluetoothspp/MainActivity.java

package com.example.bluetoothspp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    private static final String BTAG = "BTThread";
    static final int MSG_BT_GOT_DATA = 1;
    static final int MSG_BT_STATUS_MSG = 2;
    static final int MSG_BT_FINISHED = 99;

    Button btnStart, btnSend;
    TextView textStatus;
    private BluetoothAdapter mBluetoothAdapter = null;
    private BluetoothDevice btdevice = null;
    Thread bThread;
    BluetoothSocket bsocket;
    InputStream bis = null; //Bluetooth input stream
    OutputStream bos = null; //Bluetooth output stream
    private String MACAddress = "00:01:95:06:1F:32";


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btnStart = (Button)findViewById(R.id.btnStart);
        btnSend = (Button)findViewById(R.id.btnSend);
        textStatus = (TextView)findViewById(R.id.textStatus);
        btnStart.setOnClickListener(btnStartListener);
        btnSend.setOnClickListener(btnSendListener);

        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    private OnClickListener btnStartListener = new OnClickListener() {
        public void onClick(View v){
            if(btnStart.getText().equals("Connect")){
                Log.i(TAG, "Connect button pressed");
                if (mBluetoothAdapter == null) { //No adapter. Fail
                    Log.e(TAG, "getDefaultAdapter returned null");
                    textStatus.setText("getDefaultAdapter returned null");

                } else {
                    if (!mBluetoothAdapter.isEnabled()) { //Bluetooth disabled
                        Log.e(TAG, "Bluetooth is Disabled");
                        textStatus.setText("Bluetooth is Disabled");

                    } else {
                        Log.i(TAG, "Connecting to Device: " + MACAddress);
                        btdevice = mBluetoothAdapter.getRemoteDevice(MACAddress);
                        Log.i(TAG, "Device: " + btdevice.getName());
                        Log.i(TAG, "Trying to Connect...");
                        textStatus.setText("Trying to Connect...");
                        Log.i(TAG, "Starting Thread");
                        try {
                            bThread = new Thread(new BluetoothClient(btdevice, true));
                            bThread.start();
                        } catch (IOException e) {
                            Log.e(TAG, "Could not create thread for bluetooth: " + e);
                            textStatus.setText("Could not create thread for bluetooth...");
                        }
                        btnStart.setText("Disconnect");
                    }
                }

            } else {
                Log.i(TAG, "Disconnect button pressed");

                btnStart.setText("Connect");
            }
        }
    };
    private OnClickListener btnSendListener = new OnClickListener() {
        public void onClick(View v){
            textStatus.setText("Sending Message to Thread.");
            SendDataToBluetooth("something\r\n");
        }
    };


    public class BluetoothClient implements Runnable {
        public BluetoothClient(BluetoothDevice device, boolean IsAnHTCDevice) throws IOException {
            if (IsAnHTCDevice) {
                //This is a workaround for HTC devices, but it likes to throw an IOException "Connection timed out"
                try {
                    Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
                    bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));
                } catch (Exception e) {
                    Log.e(BTAG, "Error at HTC/createRfcommSocket: " + e);
                    e.printStackTrace();
                    handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
                }
            } else {
                //This is the normal method, but on a Nexus One it almost always throws an IOException "Service discovery failed" message
                try {
                    UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
                    bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);
                } catch (Exception e) {
                    Log.e(BTAG, "Error at createRfcommSocketToServiceRecord: " + e);
                    e.printStackTrace();
                    handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
                }
            }
        }

        public void run() {
            try {
                Log.i(BTAG, "Cancelling Discovery");
                mBluetoothAdapter.cancelDiscovery();
                Log.i(BTAG, "Connecting to Socket");
                bsocket.connect();
                bis = bsocket.getInputStream();
                bos = bsocket.getOutputStream();
                Log.i(BTAG, "Socket created, streams assigned");
                handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "Device Connected"));
                Log.i(BTAG, "Waiting for data...");
                byte[] buffer = new byte[4096];
                int read = bis.read(buffer, 0, 4096); // This is blocking
                Log.i(BTAG, "Getting data...");
                while (read != -1) {
                    byte[] tempdata = new byte[read];
                    System.arraycopy(buffer, 0, tempdata, 0, read);
                    handler.sendMessage(handler.obtainMessage(MSG_BT_GOT_DATA, tempdata));
                    read = bis.read(buffer, 0, 4096); // This is blocking
                }
            } catch (SocketTimeoutException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Log.i(BTAG, "Finished");
                handler.sendMessage(handler.obtainMessage(MSG_BT_FINISHED));
            }
        }
    }
    public void SendDataToBluetooth(String cmd) { // You run this from the main thread.
        try {
            if (bsocket != null) {
                bos.write(cmd.getBytes());
            }
        } catch (Exception e) {
            Log.e("SendDataToBluetooth", "Message send failed. Caught an exception: " + e);
        }
    }

    public Handler handler = new Handler() { // Handler for data coming from the network and bluetooth sockets
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_BT_GOT_DATA:
                Log.i("handleMessage", "MSG_BT_GOT_DATA: " + (String) msg.obj);
                textStatus.setText((String) msg.obj);
                break;
            case MSG_BT_STATUS_MSG:
                Log.i("handleMessage", "MSG_BT_STATUS_MSG: " + (String) msg.obj);
                textStatus.setText((String) msg.obj);
                break;
            case MSG_BT_FINISHED:
                Log.i("handleMessage", "MSG_BT_FINISHED");
                btnStart.setText("Connect");
                break;
            default:
                super.handleMessage(msg);
            }
        }
    };


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (bThread != null) { // If the thread is currently running, close the socket and interrupt it.
            Log.i(BTAG, "Killing BT Thread");
            try {
                bis.close();
                bos.close();
                bsocket.close();
                bsocket = null;
            } catch (IOException e) {
                Log.e(BTAG, "IOException");
                e.printStackTrace();
            } catch (Exception e) {
                Log.e(BTAG, "Exception");
                e.printStackTrace();
            }
            try {
                Thread moribund = bThread;
                bThread = null;
                moribund.interrupt();
            } catch (Exception e) {}
            Log.i(BTAG, "BT Thread Killed");
        }
    }
}

I found that using the normal "bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);" method would usually result in a "Service discovery failed" message for me, so I also tried the "bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));" method. That works more often, but likes to time out when I try to connect.

What am I doing wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

那一片橙海, 2024-11-06 14:56:59

尝试在单独的线程中侦听传入数据并写入设备。这样您就可以分离阻塞调用。
您看过蓝牙聊天示例吗?该示例使用类似的线程技术。

Try listening to the incoming data and writing to the device in separate threads. This way you are separating blocking calls.
Did you have a look at Bluetooth chat sample? The sample uses the similar threading technique.

靖瑶 2024-11-06 14:56:59

如果您的目标是 2.3 及更高版本(当前安装超过 50% Android 设备)您可以使用 createInsecureRfcommSocketToServiceRecord 方法与设备通信,这肯定会使其更好、更可连接。

If you are targeting 2.3 and up (which is currently installed on over 50% of android devices out there) you can use the createInsecureRfcommSocketToServiceRecord method to communicate with the device which will surely make it better and more connectable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文