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