问题描述
我有一个自制的蓝牙设备,测量ECG为500Hz:每2毫秒,设备就会发送9个字节数据(标题,ECG测量,页脚).因此,这大约是一个9*500 = 4.5kbytes/s数据流.
我有一个C ++ Windows程序能够连接设备并检索数据流(使用QT/QWT显示).在这种情况下,我使用Windows Control面板键入设备,然后使用BOBOST SERIAL_PORT接口通过虚拟COM端口将其连接.这很好,我正在实时接收我的数据流:我每2ms左右获得一个测量点.
我通过QTCreator 3.0.1(QT 5.2.1)将整个程序移植在Android上.似乎无法通过Boost访问虚拟COM端口(可能不允许SDK权限),因此我编写了一块Java代码来打开和管理蓝牙连接.因此,我的应用程序仍然是C ++/QT,但仅在Java中重新设计了连接和读取数据的层(与CreateInSecurerFcommsockettoServicErecord打开连接):
Java代码读取数据:
public int readData( byte[] buffer ) { if( mInputStream == null ) { traceErrorString("No connection, can't receive data"); } else { try { final boolean verbose = false; int available = mInputStream.available(); if ( verbose ) { Calendar c = Calendar.getInstance(); Date date = new Date(); c.setTime(date); c.get(Calendar.MILLISECOND); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); String currentTime = sdf.format(date); traceDebugString( currentTime + ":" + c.get(Calendar.MILLISECOND) + " - " + available + " bytes available, requested " + buffer.length ); } if ( available >= buffer.length ) return mInputStream.read( buffer ); // only call read if we know it's not blocking else return 0; } catch (IOException e) { traceDebugString( "Failed to read data...disconnected?" ); } } return -1; }
从c ++调用这样:
bool ReceiveData( JNIEnv* env, char* data, size_t length, bool& haserror ) { bool result = false; jbyteArray array = env->NewByteArray(length); jint res = env->CallIntMethod(j_object, s_patchIfReceiveDataID, array ); if ( static_cast<size_t>(res) == length ) { env->GetByteArrayRegion(array, 0, length, reinterpret_cast<jbyte*>(data)); result = true; } else if ( res == -1 ) { haserror = true; } else { // not enough data in the stream buffer haserror = false; } return result; } bool readThread( size_t blockSize ) { BTGETANDCHECKENV // retrieving environment char* buf = new char[blockSize]; bool haserror = false; while ( !haserror ) { if ( !ReceiveData( env, buf, blockSize, haserror ) ) { // could not read data if ( haserror ) { // will stop this thread soon } else { boost::this_thread::sleep( boost::posix_time::milliseconds( 10 ) ); } } } delete [] buf; return true; }
这很好地工作... 在五个第一秒内我是在某种实时获得的值,然后:
- 有时它会永远冻结,这意味着minputstream.available()值仍低于要求.
- 有时它仅冻结一秒钟左右,然后继续进行,但是块由〜1秒的块收到数据.含义minputStream.available()可以在两个呼叫之间从0移动到3000多个(经过10ms).实际上,我在第5秒内看到了相同的情况,但是缓冲区的可用性永远不会超过150字节,5秒后,它可以达到3000字节.
这是记录的详细信息设置为true时的外观:
14:59:30:756 - 0 bytes available, requested 3 14:59:30:767 - 0 bytes available, requested 3 14:59:30:778 - 0 bytes available, requested 3 14:59:30:789 - 1728 bytes available, requested 3 14:59:30:790 - 1725 bytes available, requested 6 14:59:30:792 - 1719 bytes available, requested 3
我的ECG设备肯定没有以11ms的方式发送1728字节!
我知道我的设备每2ms发送9个字节(否则,它在我的PC应用程序上不起作用).看起来Java进行了一些意外的缓冲,并且每2ms每2ms都没有提供9个字节.
请注意,我尝试使用read()而不检查可用()(阻止版本),但经历了完全相同的行为.
所以我想知道我在做什么错...
- 有没有办法强制Java输入流进行自我更新?
- 有没有办法要求Java进行待处理的事件(就像我们有Qapplication :: ProcessEvents)?
- 是否有任何全局设置可以指定流的缓冲区大小(我在bluetoothdevice/bluetoothsocket级别没有找到任何缓冲区)
- 在PC上,打开虚拟COM端口时,我必须指定Baudrate,停止位,握手和类似的内容.在Android上,我只是打开RFCOMM插座,没有任何选择,这可能是问题(然后不会同步ECG设备和智能手机...)?
任何帮助或想法都会受到欢迎!
编辑:我在Nexus 5手机上体验到Android 4.4.2我刚刚在不同设备上测试了相同的APK包:
- 带有Android 4.4.2的星系S4:相同的问题.
- 带有自定义Cyanogenmod 11 Android 4.4.2的Galaxy S3:数据流似乎很完美,在5秒和数据实时到达后没有冻结....因此,看起来整个系统能够实现我想要的东西,但是看起来Android默认设置使事情变得太慢了.
编辑:由于我没有答案:-(我尝试使用纯Java程序(不C ++,没有QT)做同样的事情.遇到了相同的问题:推荐答案 这个问题显然与报道的在这里
5秒钟后,我要么丢失了连接,要么实时流媒体流急剧减慢. 如所述在这里 Android> 4.3显然不喜欢超过5秒的单向通信.因此,我现在每1秒钟将一个虚拟命令发送到设备(一种" keep-alive"命令),现在Android很高兴,因为它不再是单向通信...因此,数据流在此之后也一样好第五秒比以前!
问题描述
I have a home-made bluetooth device measuring ECG at 500Hz: every 2 ms the device sends 9 bytes of data (header, ECG measurment, footer). So this is roughly a 9*500=4.5kbytes/s data stream.
I have a C++ Windows program able to connect the device and retrieve the data stream (displaying it with Qt/qwt). In this case, I use Windows control panel to bond the device and I connect it via a virtual COM port using boost serial_port interface. This works perfectly and I'm receiving my data stream in real time: I get a measurment point every 2ms or so.
I ported the whole program on Android via QtCreator 3.0.1 (Qt 5.2.1). It appears that virtual COM ports cannot be accessed by boost (probably SDK permissions won't allow that) so I wrote a piece of Java code to open and manage the Bluetooth connection. So my app remains C++/Qt but only the layer connecting and reading data from the device was reworked in Java (opening the connexion with createInsecureRfcommSocketToServiceRecord):
Java code to read the data:
public int readData( byte[] buffer ) { if( mInputStream == null ) { traceErrorString("No connection, can't receive data"); } else { try { final boolean verbose = false; int available = mInputStream.available(); if ( verbose ) { Calendar c = Calendar.getInstance(); Date date = new Date(); c.setTime(date); c.get(Calendar.MILLISECOND); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); String currentTime = sdf.format(date); traceDebugString( currentTime + ":" + c.get(Calendar.MILLISECOND) + " - " + available + " bytes available, requested " + buffer.length ); } if ( available >= buffer.length ) return mInputStream.read( buffer ); // only call read if we know it's not blocking else return 0; } catch (IOException e) { traceDebugString( "Failed to read data...disconnected?" ); } } return -1; }
Called from C++ like that:
bool ReceiveData( JNIEnv* env, char* data, size_t length, bool& haserror ) { bool result = false; jbyteArray array = env->NewByteArray(length); jint res = env->CallIntMethod(j_object, s_patchIfReceiveDataID, array ); if ( static_cast<size_t>(res) == length ) { env->GetByteArrayRegion(array, 0, length, reinterpret_cast<jbyte*>(data)); result = true; } else if ( res == -1 ) { haserror = true; } else { // not enough data in the stream buffer haserror = false; } return result; } bool readThread( size_t blockSize ) { BTGETANDCHECKENV // retrieving environment char* buf = new char[blockSize]; bool haserror = false; while ( !haserror ) { if ( !ReceiveData( env, buf, blockSize, haserror ) ) { // could not read data if ( haserror ) { // will stop this thread soon } else { boost::this_thread::sleep( boost::posix_time::milliseconds( 10 ) ); } } } delete [] buf; return true; }
This works pretty well...for the five first seconds I'm gettings values in a sort of real time, then:
- Sometimes it freezes for ever, meaning the mInputStream.available() value remains lower than requested.
- Sometimes it freezes only for a second or so and then it continues but data are received by blocks of ~1 second. Meaning mInputStream.available() can move from 0 to more than 3000 between two calls (elapsed by 10ms). Actually, I see the same during the 5 firsts seconds, but the buffer availability never exceeds 150 bytes, after 5 seconds, it can go up to 3000 bytes.
Here is what the log can look like when verbose is set to true:
14:59:30:756 - 0 bytes available, requested 3 14:59:30:767 - 0 bytes available, requested 3 14:59:30:778 - 0 bytes available, requested 3 14:59:30:789 - 1728 bytes available, requested 3 14:59:30:790 - 1725 bytes available, requested 6 14:59:30:792 - 1719 bytes available, requested 3
My ECG device definitely did not send 1728 bytes in 11ms!!
I know my device sends 9 bytes every 2ms (otherwise, it would not work on my PC application). Looks like Java does some unexpected buffering and does not make available 9 bytes every 2ms.... It's also strange things appear to work fine for only 5 seconds at the beginning.
Note that I tried using read() without checking available() (blocking version) but experienced exactly the same behaviour.
So I'm wondering what I'm doing wrong...
- Is there a way to force a Java input stream to update itself?
- Is there a way to ask Java to proceed it's pending events (like we have QApplication::processEvents)?
- Is there any global settings to specify buffer sizes for streams (I did not find any at BluetoothDevice/BluetoothSocket level)
- On PC, when opening the virtual COM port, I have to specify baudrate, stop bit, handshaking and stuff like that. On Android I just open the Rfcomm socket with no option, could this be the problem (then ECG device and smartphone would not be synced...)?
Any help or idea would be welcomed!
Edit: I'm experiencing that on a Nexus 5 phone, Android 4.4.2 I just tested the same apk package on different devices:
- a Galaxy S4 with Android 4.4.2: Same problem.
- a Galaxy S3 with custom CyanogenMod 11 Android 4.4.2: data streaming seems perfect, no freezing after 5sec and data are arriving in real-time....so looks like the whole system is able to achieve what I want, but looks like Android default setup makes things too slow....dunno if there could be a setting to be changed at the OS level to fix this issue.
Edit: As I got no answer :-( I tried to do the same thing using a pure Java program (no C++, no Qt). Had the same problem: Real-time Bluetooth SPP data streaming on Android only works for 5 seconds
推荐答案
This problem is apparently similar to the one reported here.
After 5 seconds, I had either a connection lost, either real-time streaming being dramatically slow down.
As said here Android >4.3 apparently does not like one-way communication exceeding 5 secondes. So I'm now sending a dummy command to the device every 1 seconde (kind of "keep-alive" command) and now Android is happy because it's not a one-way communication anymore...and so data streaming is as good after the fifth second than before!