如何正确将安卓应用程序连接到启用蓝牙的Arduino微控制器上的RFCOMM套接字?

25
我正在与一些大学生合作开发一个简单的蓝牙Android应用程序,用于与连接了蓝牙模块的Arduino微控制器进行串行通信(RFCOMM)。据我所知,我正在使用正确的蓝牙地址和UUID RFCOMM/SPP 00001101-0000-1000-8000-00805F9B34FB。我的应用程序启动了一个线程,尝试使用BluetoothDevice.createRfcommSocketToServiceRecord(UUID)连接到设备。但出于某种原因,我们没有看到成功的连接。在上面的调用中得到的BluetoothSocket调用connect()时,操作总是失败。在我的HTC Evo上测试时,运行HTC的Gingerbread变体,connect()调用通常会出现异常消息“无法启动服务发现”。我阅读了一些资料,并发现有人说HTC的蓝牙堆栈中RFCOMM的实现存在缺陷,因此我们决定在另一个学生的Samsung Galaxy S上尝试。代码第一次运行时,一切正常。Arduino微控制器连接到一个小电机上,该电机按预期工作。我还没有排除问题可能在微控制器方面的可能性。
在三星设备上后续使用该应用程序失败,显示“服务发现失败”消息。对我来说,似乎设备端的蓝牙模块认为RFCOMM服务仍在使用中。但是我们已经重新启动了微控制器,仍然看到了相同的结果。
我只列出了线程代码,因为这是真正相关的内容。我读到有一种常见的解决方法(黑客)可以使用反射解决这些问题。我的尝试也失败了,但注释中有尝试的代码。希望有人能引导我正确的方向。还请注意,我已经在清单中启用了必要的权限,并且在两种情况下,设备都成功地使用Android的用户界面与Arduino配对。
private class ClientThread extends Thread {

    private String _btAddress;

    /**
     * A handle to the local device's Bluetooth adapter hardware.
     */
    private BluetoothAdapter _btAdapter = BluetoothAdapter.getDefaultAdapter();

    /**
     * A handle to the remote device Bluetooth context.
     */
    private BluetoothDevice _btRemoteDevice;

    /**
     * A handle to the Bluetooth serial socket.
     */
    private BluetoothSocket _btSocket;

    /**
     * Constructor.
     * @param btAddress The BluetoothHardware address.
     */
    public ClientThread(String btAddress)
    {
        _btAddress = btAddress;
    }

    public void run()
    {
        // Retrieves the device identified by the _btAddress property.
        _btRemoteDevice = retrieveDevice();
        if ( _btRemoteDevice == null )
            sendUIMessage( CONNECTION_BT_DEVICE_NOT_BONDED );
        else
            sendBeacon();
    }

    /**
     * Retrieves the device associated with this client thread.
     * @return
     */
    private BluetoothDevice retrieveDevice()
    {
        Set<BluetoothDevice> btDevices = _btAdapter.getBondedDevices();
        for (BluetoothDevice btd : btDevices)
        {
            String addr = btd.getAddress();
            String name = btd.getName();
            if ( addr.equalsIgnoreCase(_btAddress) )
                return btd;
        }
        return null;
    }

    /**
     * Sends the beacon to the Bluetooth device.
     */
    private void sendBeacon()
    {
        // Holds the output stream of the BluetoothDevice.
        OutputStream os = null;

        try
        {
            _btSocket = _btRemoteDevice.createRfcommSocketToServiceRecord( UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB" ) );

            //Method m = _btRemoteDevice.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
            //_btSocket = (BluetoothSocket) m.invoke(_btRemoteDevice, 1);
            _btSocket.connect();
            os = _btSocket.getOutputStream();
            os.write('L');
        }
        catch (IOException e)
        {
            String message = e.getMessage();
            e.printStackTrace();
            sendUIMessage(CONNECTION_FAILURE_IO);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            sendUIMessage(CONNECTION_FAILURE_UNKNOWN);
        }
        finally
        {
            try
            {
                if (_btSocket != null)
                    _btSocket.close();
            }
            catch (IOException e)
            {
                System.out.println("Failed closing Bluetooth output stream.");
                e.printStackTrace();
            }
        }
    }
}

编辑: 蓝牙模块是MDFLY RF-BT0417CB。我知道在Arduino上运行的代码不多,只是使用Serial.available()和Serial.read()与BT模块通信。然而,我有一个新的信息可能更有用。当我的应用程序安装在三星设备上时,它只工作了一次,在后续尝试中失败了。之前,我和另一个学生一起工作,他使用Android App Inventor(一个拖放GUI工具,也可以创建逻辑工件)创建了一个连接相同BT模块/arduino板的简单应用程序,它能够正常工作。他说,当我的应用程序被安装后,其他应用程序无法连接到BT模块,这让我相信系统仍然认为该资源被分配给了我的应用程序。在卸载我的应用程序后,其他应用程序能够连接。他没有其他应用程序的源代码,但我打算自己尝试App Inventor,看看它生成的源代码是否有任何不同。据我所知,我遵守了Android文档中定义的大多数标准实践,因此可能是BT模块或Arduino代码并非以编程方式控制BT模块的某些奇怪因素。

另一个编辑: 我不是蓝牙专家,但我们成功找到了解决方法。正如某些人所知,有许多公共的BluetoothDevice API,在编译时被隐藏起来,但在运行时使用反射是合法的。其中之一是createRfCommSocket(int)。这个API没有出现在官方文档中,因为它是隐藏的,但你可以在这里阅读到它。我还没有尝试过文档支持的API,但问题似乎是手机和串口板之间的并发问题。手机发送消息,当然是阻塞调用,当它从那里返回时,关闭连接。串口板上的Shield将关闭连接,因此数据不可用于arduino应用程序。当我们在Android端的调试模式下观察到成功的通信,但发布模式下失败时,我们意识到了这一点。在传输和关闭BluetoothSocket之间在android端添加半秒延迟可以解决这个问题。我不能确定这个问题是否归因于arduino代码,因为我对架构不太熟悉,但我们作为学生缺乏经验,这并不会让我感到惊讶。


Arduino设备上运行哪个蓝牙模块和软件?您能发布一下您正在使用的设备的参考资料吗?如果您能在Android端发布完整的日志,那将会很有帮助。 - Dennis Mathews
Dennis,感谢您的回复,请接受我的道歉,因为考试和其他事务一直耽搁了我。我正在研究Arduino的型号和软件版本,但我已经发布了有关BT设备和问题的一些新信息。 - Joey Carson
嗨,我有一个类似的问题...我正在尝试将LG手机连接到TI PAN1315 Dev.板,我得到了具有相同UUID的套接字,但是connect()总是抛出IOException。在调试嵌入式PAN1315时,我已经将一些日志打印到控制台上,试图弄清楚是否从原始应用程序设计中搞砸了什么,我看到一个hci / acl断开请求来自手机,而不是来自嵌入式设备,在从嵌入式设备发送链接密钥到手机后...你能认同吗? - TacB0sS
嗨TacB0sS,看到我上面的评论了吗?Android蓝牙API似乎对于串行板连接有些不稳定,因此有许多解决方法。我在这里提到的是一个相当常见的方法,但除此之外,我们还遇到了板和手机之间的并发问题。这可能是您想要调查的问题,也可能不是。关于连接关闭和链接密钥交换,我确定任何一台设备都可以请求连接或关闭,并且链接密钥存储在设备上,以便它们中的任何一个稍后都可以连接到另一个设备。祝好运! - Joey Carson
1
虽然我承认我完全不知道如何帮忙,但你可能想尝试一下http://electronics.stackexchange.com/。 :) - Philippe Signoret
2个回答

1

你有什么进展吗?请告诉我。 - mH16

0

Amarino Toolkit是专门为在Android设备和带有蓝牙的Arduino之间方便进行串行蓝牙通信而构建的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接