Android 蓝牙InputStream实时读取

2024-01-05

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

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

数据格式如下:

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

byte_2:轴1的位置

byte_3:轴2的位置

byte_4:轴3的位置

我按照特定的顺序连续发送这 4 个字节。我每 5 毫秒发送一次 4 字节的数据包,发送该数据包大约需要 4.7 毫秒(波特率 9600)。 微控制器输出的数据在精度和时序方面都是完美的(用逻辑分析仪检查)。

问题是,当从手机接收数据时,一些字节似乎丢失了。这是代码的一部分,我在其中读取输入流:

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”保存位置标识字节的位置。它用于检查是否有任何变量已更新。

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

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

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

我尝试使用更长的接收缓冲区(使用修改后的接收算法),但这没有帮助,因为一次只接收几个字节。

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

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

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

有类似问题的线程:如何使用Java Android SDK做好实时数据流处理 https://stackoverflow.com/questions/24934464/how-to-do-good-real-time-data-streaming-using-java-android-sdk

谢谢。

UPDATE:

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

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

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

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

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


如果您想从微控制器板获取数据,则数据处理不正确。你必须使用 bytesAvaliable 因为 android 蓝牙套接字比带有蓝牙的微控制器板相当慢。但是“bytesAvaliable way”有细微差别 - 由于套接字是慢速接收器,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;
                }
            }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 蓝牙InputStream实时读取 的相关文章

随机推荐

  • PHP 客户端无法连接到本地主机上的 RabbitMQ 服务器

    OS CentOS 6 4我正在尝试使用 php 客户端连接到 RabbitMQ 服务器 如下所示 connection new AMQPConnection 10 1 150 109 5672 guest guest channel co
  • Homebrew 在 m1 Mac 上安装了错误的 minikube (amd64) 而不是“arm64”

    自制 brew install minikube 正在使用 M1 在 Macbook Air 上安装 amd64 minikube 运行任何 minikube 命令时 它会打印以下消息 You are trying to run the a
  • 编写一个通用的遍历函数,可以灵活地处理具有不同参数的多个函数

    我想使用 std function 来帮助我运行一个通用遍历函数 该函数遍历 BST 并调用参数化函数 我的困难是参数化函数的参数各不相同 因此 例如 我要概括以下三个函数 参数均不同 populates an array with the
  • iOS8接口旋转方法未调用

    Since willAnimateRotationToInterfaceOrientation duration is 已弃用 https developer apple com library ios documentation UIKi
  • vscode从子目录激活主目录中的虚拟环境

    我正在 vscode 中使用 ssh 远程连接到服务器 在我的主目录中 我有两个 文件夹 py3其中一个虚拟环境和project1 当我连接到主目录时 我可以激活py3没有什么问题 但是当我直接连接到project1文件夹 我看不到py3在
  • 我应该排除 .gitignore 中的 Aurelia 脚本文件夹吗?

    我是否应该将 Aurelia 项目的 scripts 文件夹放入 gitignore 中 因为据我所知 它们无论如何都会在每次运行时重建 我的 gitignore 当前包含 node modules jspm packages idea D
  • Soundcloud 重定向 Https -> Http

    我有一个使用 SoundCloud js sdk 来传输音频的页面 初始化看起来像这样 SC initialize client id myId redirect uri https window location host soundcl
  • MVC3 网站内的作业调度程序

    我正在寻找一些指导 以获取在我的 MVC3 网站内部使用的作业计划程序设置 我一直在寻找 Quartz NET 来解决这个问题 但在网上找不到有关设置的指导 以确保它在网站运行时运行 考虑到上述信息 我知道在 IIS 中设置作业调度程序有几
  • 如何在 Google 计算引擎实例上安装自定义内核?

    我想在 Google Compute Engine 实例上安装自定义内核映像 我有一个运行的实例 foo instance 1 boot efi uname a Linux instance 1 4 10 0 22 generic 24 U
  • Activity.finish() 方法到底在做什么?

    我正在开发 Android 应用程序一段时间 并关注了很多有关 Activity 生命周期和应用程序生命周期的帖子 I know Activity finish 方法在某处调用Activity onDestroy 并且还从堆栈中删除活动 我
  • 为什么 inet_ntoa 和 inet_ntop “反转”字节?

    这是一个相当基本的问题 令我惊讶的是 我今天遇到了问题 在我看来 inet pton 和 inet ntoa 正在反转给定的 IP 地址的字节 DWORD IP inet pton AF INET 192 168 0 1 IP printf
  • 我可以在没有扩展名的 zip 文件上使用 PowerShell `Expand-Archive`

    我正在编写一个 PowerShell 脚本 我必须从其中提取内容 zip存档的扩展名已被删除 因此 存档的名称可以说不是test zip只是test 并且它被压缩为 zip档案 我正在尝试为此目的使用 PowerShell cmdletEx
  • 改变 redux 状态深处的值

    context 我正在渲染一个带有一组动态文本元素的表单 我已经使用标准化了我的状态规范化原则 https github com paularmstrong normalizr因此有一个 elementIds 数组和一个包含 element
  • Python,Try、Except语句在X秒后超时?

    我一直在搜索这个问题 但似乎找不到确切的答案 大多数人都会陷入更复杂的事情 如多线程等 我只想做一些类似 Try except 语句的事情 如果过程没有完成在 X 秒内它将抛出异常 编辑 原因是我正在使用网站测试软件 selenium 其配
  • ASP.NET 日志记录 - 最佳实践 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Symfony 4,主义,getResult和getArrayResult和getScalarResult返回相同的结构结果

    从 symfony 4 开始 我创建了一个示例存储库类 在这个课程中 我创建了一个方法来获取所有电子邮件用户的列表 我想要一个像这样的数组结构 array email1 email2 email3 但通过 getResult 我得到了一个多
  • 如何在 TypeScript 中将类型重新导出为全局类型

    我希望有一个可全局访问的接口 在单独的文件中定义 我该怎么做 这是我的globals d ts file import Theme Style from style themes theme types declare global The
  • 我如何判断我的上下文是否仍然有效?

    我现在正在处理一个相当常见的情况 通过网络下载一些数据 然后更新视图以显示它 显然 我想在后台进行 Web 下载 然后更新主 UI 线程上的视图 现在看看我的代码 我有点担心我的 Activity 及其 UI 元素在更新之前被杀死 这是我的
  • 相对日期格式,输出过去的日期?

    我正在考虑使用 NSDateFormatter setDoesRelativeDateFormatting 将日期表示为 今天 或 昨天 我只查看过去的日期 但很好奇我会看到哪些针对英国本地化的选项 Just Today 昨天 或者任何更复
  • Android 蓝牙InputStream实时读取

    我正在开发一个 Android 应用程序 它通过蓝牙接收实时数据并将其绘制在屏幕上 该数据是陀螺仪传感器位置信息 我从定制的 Freescale Kinetis K10 微控制器板 由我自己设计和测试 发送它 对于蓝牙通信 我使用 HC 0