我正在开发一个 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() 方法,正如文档中所说。