【问题标题】:Android Bluetooth InputStream real time readAndroid蓝牙InputStream实时读取
【发布时间】:2014-08-15 16:25:54
【问题描述】:

我正在开发一个通过蓝牙接收实时数据并将其绘制在屏幕上的 Android 应用程序。

数据是陀螺传感器位置信息。我从定制的飞思卡尔 Kinetis K10 微控制器板(由我自己设计和测试)发送它。对于蓝牙通信,我使用的是 HC-05 蓝牙模块。

数据格式如下:

byte_1:位置标识字节,始终等于-128

byte_2:轴1的位置

byte_3:轴2的位置

byte_4:轴3的位置

我以特定的顺序一个接一个地连续发送这 4 个字节。我每 5 毫秒发送一个 4 字节的数据包,发送数据包大约需要 4.7 毫秒(9600 波特率)。 从微控制器输出的数据在准确性和时序方面非常完美(用逻辑分析仪检查)。

问题是当它从手机接收时,一些字节似乎丢失了。这是代码的一部分,我正在阅读 InputStream:

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            Log.e("Printer Service", "temp sockets not created", e);
        }
        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    @Override
    public void run() {

        Log.i("BluetoothService", "BEGIN mConnectedThread");
        byte[] buffer = new byte[4];
        int bytes;

        while (true) {

                try {

                    bytes = mmInStream.read(buffer);

                    int position = 0;

                    if(buffer[0] == -128) {
                        if(bytes >= 2) {
                            sendArray.errorTilt = buffer[1];
                        }
                        if(bytes >= 3) {
                            sendArray.errorRoll = buffer[2];
                        }
                        if(bytes == 4) {
                            sendArray.errorPan = buffer[3];
                        }
                    }
                    else if(buffer[1] == -128) {
                        position = 1;
                        if(bytes >= 3) {
                            sendArray.errorTilt = buffer[2];
                        }
                        if(bytes == 4) {
                            sendArray.errorRoll = buffer[3];
                        }
                        if(bytes >= 2) {
                            sendArray.errorPan = buffer[0];
                        }
                    }
                    else if(buffer[2] == -128 && bytes >= 3) {
                        position = 2;
                        sendArray.errorRoll = buffer[0];
                        sendArray.errorPan = buffer[1];
                        if(bytes == 4) {
                            sendArray.errorTilt = buffer[3];
                        }
                    }
                    else if(buffer[3] == -128 && bytes == 4) {
                        position = 3;
                        sendArray.errorTilt = buffer[0];
                        sendArray.errorRoll = buffer[1];
                        sendArray.errorPan = buffer[2];
                    }

                    if(position <= bytes && bytes > 1) {
                        sendArray.errorUpdate = true;
                    }

                } catch (Exception e) {

                    e.printStackTrace();
                    connectionLost();
                    BluetoothService.this.stop();
                    break;
                }
        }
    }


    public void write(int oneByte) {
        try {
            mmOutStream.write(oneByte);

        } catch (IOException e) {
            Log.e("BluetoothService", "Exception during write", e);
        }
    }

    public void cancel() {
        try {
            mmSocket.close();

        } catch (IOException e) {
            Log.e("BluetoothService", "close() of connect socket failed", e);
        }
    }
}

sendArray 是一个保持许多不同变量的单例。

errorTilt、errorRoll 和 errorPan 是轴的当前值,正在从接收缓冲区更新。

"position" 保存位置标识字节的位置。它用于检查是否有任何变量已更新。

很多时候在输入缓冲区中只接收到一个字节,由于我不知道应该是哪个轴,因为我没有关于它与位置字节的相对位置的信息,所以这个特定字节是无用的然后迷路了。

我已经通过以下方法测试了接收的准确性。我让 MCU 在其中一个轴上输出三角波,而不是轴数据。在手机上,三角波的线条并不像想象的那样笔直,而是随机弯曲并包含伪影。

为了绘制我使用 GraphView 的数据,我正在从一个单独的线程中以相等的时间间隔更新图表。

我尝试过使用更长的接收缓冲区(使用修改后的接收算法),但这无济于事,因为一次只能接收几个字节。

我尝试实现 InputStream.available() 但它总是提供 127 个字节可用,这似乎不是真的。

我已经阅读了很多关于类似问题的帖子,并且我花了过去 5 天的时间来解决这个问题,但我找不到一个好的解决方案。

总而言之,我需要实现对所有字节的准确、实时(或接近实时)接收。

有类似问题的线程: How to do good real-time data streaming using Java Android SDK

谢谢。

更新:

我尝试只为其中一个轴发送信息,所以它简单明了,不需要位置字节。我每 5 毫秒再次发送一次,但这次连续字节之间的时间更长,因为它只是数据包中的一个字节。

这次我使用了 InputStream.read(),它不需要缓冲区。但是,传入的数据再次损坏,因为无法接收随机字节。

我已经看到不同的项目成功地使用了这种方法,我不知道为什么它不适合我。我以为可能是我正在使用的HC-05蓝牙模块有问题,但我尝试了一个不同的——HC-06,情况也是一样。我没有尝试过其他手机,但我的手机(Samsung Galaxy S3,Android 4.1.2)似乎工作正常。

UPDATE2:在从流中读取之前,我再次尝试使用 InputStream.available() 测试原始代码。

条件为available()>0时,没有大的变化,可能效果稍差。 当条件可用()> 1时,它永远不会读取。我想这是因为它在文档中所说的不可靠的 available() 方法。

【问题讨论】:

    标签: android bluetooth inputstream


    【解决方案1】:

    如果您想从微控制器板上获取数据,您的数据处理不正确。您必须使用 bytesAvaliable ,因为 android 蓝牙 Socket 在带有蓝牙的微控制器板上非常慢。但是“bytesAvaliable 方式”有细微差别 - 由于套接字是慢速接收器,bytesAvaliable 可以一次从板上捕获超过 1 个数据包,因此您必须自己分配读取的数据,请在下面查看我的代码!我的代码从惯性传感器获取 38 字节数据包,因此您只需更改字节数! 0xAA 是每个下一个数据包的第一个字节,因此如果您找到 0xAA 字节并且有 38 个字节,您将获得数据包并取消迭代器。但无论如何,我敢肯定你有时仍然会丢失数据,因为它不是高频数据传输方式

    public void run() {
                byte[] bytes = new byte[38];
                int iterator = 0;
                while (true) {
    
                    try {
                        int bytesAvailable = mmInStream.available();
                        if (bytesAvailable > 0) {
                            byte[] curBuf = new byte[bytesAvailable];
                            mmInStream.read(curBuf);
                            for (byte b : curBuf) {
                                if (b == (byte) 0xAA && iterator == 38) {
                                    mHandler.obtainMessage(MainActivity.DATA_READ, bytes.length, -1, bytes).sendToTarget();
                                    iterator = 0;
                                    bytes[iterator] = b;
                                } else {
                                        bytes[iterator] = b;
                                }
                                iterator++;
                            }
                        }
                    } catch (IOException ex) {
                        Log.e(TAG, "disconnected", ex);
    
                        connectionLost();
                        break;
                    }
                }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-16
      • 2012-08-30
      • 2016-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多