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 C++ program on Android via QtCreator (Qt 5.3.2). I had real-time issues. Data stream was in "real-time" for the first 5 seconds, and then performance would dramatically slow down (see How to do good real-time data streaming using Java Android SDK).
Because I thougth the problem could be due to C++/Qt, I wrote a completely blank pure Java/Android project using Eclipse. And it has the same problem!!!
Questions are: Is there something wrong with this code? Why am I receiving data in real-time for only the 5 first seconds? What happens after 5 seconds of intensive BT usage on Android platform and why does it slow down the BT data reception?
Here is my Java program:
BluetoothHelper.java (with functions to connect/disconnect/read and write data:
package com.example.helloworld;
import android.util.Log;
import android.content.Context;
import android.os.Bundle;
import java.util.Locale;
import java.util.concurrent.Semaphore;
import java.lang.String;
import java.lang.Thread;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.lang.InterruptedException;
import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothManager;
import android.util.SparseArray;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import java.util.UUID;
import java.util.Date;
import java.util.Calendar;
import java.util.Vector;
import java.util.Set;
import java.util.Arrays;
public class BluetoothHelper
{
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothDevice mDevice;
private BluetoothSocket mSocket;
private OutputStream mOutputStream;
private InputStream mInputStream;
private BroadcastReceiver mReceiver;
private Activity myActivity;
private Vector<BluetoothDevice> mDevices;
private byte[] mHeader;
private byte[] mFrame;
public BluetoothHelper(Activity a)
{
myActivity = a;
mHeader = new byte[3];
mFrame = new byte[256];
mDevices = new Vector();
}
/* Check bluetooth is enabled, return "" if OK, else, return error string */
public String initializeBluetooth(){
String error = "";
System.out.println("Initializing bluetooth...");
mBluetoothManager = (BluetoothManager) myActivity.getSystemService(Context.BLUETOOTH_SERVICE);
if ( mBluetoothManager == null )
{
error = "Bluetooth manager is not found";
}
else
{
mBluetoothAdapter = mBluetoothManager.getAdapter();
if( mBluetoothAdapter == null )
{
error = "Bluetooth adapter is not found";
}
else if( ! mBluetoothAdapter.isEnabled() )
{
error = "Bluetooth adapter is off";
}
else
{
System.out.println("Bluetooth successfully initialized");
return "";
}
}
return error;
}
private void addDevice( final BluetoothDevice device )
{
mDevices.add(device);
}
public Vector<BluetoothDevice> getDevices() { return mDevices; }
/* Clear previously detected device list */
public boolean clearDeviceList(){
// Clear old list
mDevices.clear();
return true;
}
/* Fill local device list with paired devices */
public boolean addPairedDevices(){
//System.out.println("Entering addPairedDevices");
if( mBluetoothAdapter == null )
{
System.out.println("No bluetooth adapter");
return false;
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0)
{
//System.out.println("Found paired devices");
// Loop through paired devices
for (BluetoothDevice device : pairedDevices)
{
addDevice( device );
}
}
return true;
}
public String connectToDevice(final BluetoothDevice device)
{
if ( mDevice != null )
disconnectDevice();
if( mBluetoothAdapter == null || myActivity == null )
return "System not initialized or bluetooth not active";
if ( device.getBondState() != BluetoothDevice.BOND_BONDED )
{
// TODO: find a way to do a synchronized bounding operation
return "Device is not bonded";
}
final boolean[] the_result = new boolean[1];
the_result[0] = false;
final Semaphore mutex = new Semaphore(0);
Runnable connectRunnable = new Runnable() {
@Override
public void run() {
UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
try
{
mSocket = device.createInsecureRfcommSocketToServiceRecord( MY_UUID );
System.out.println("Created RFcomm socket");
mSocket.connect();
if ( mSocket.isConnected() )
{
System.out.println("Connected RFcomm socket");
mOutputStream = mSocket.getOutputStream();
mInputStream = mSocket.getInputStream();
System.out.println("Retrieved output stream");
the_result[0] = true;
}
else
{
System.out.println("Failed to connect RFcomm socket");
}
}
catch (IOException e)
{
System.out.println("Failed to open RFcomm socket (createRfcommSocketToServiceRecord)");
System.out.println(e.toString());
}
mutex.release();
}
};
myActivity.runOnUiThread( connectRunnable );
// waiting for thread to be completed...
try {
mutex.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
if ( the_result[0] )
{
System.out.println("Connection succeeded");
return "";
}
else
{
System.out.println("Connection failed");
return "Failed to connect device";
}
}
/* Request to disconnect the device */
public boolean disconnectDevice(){
System.out.println("Disconnecting device...");
if ( mSocket != null )
{
// block read/write
mOutputStream = null;
mInputStream = null;
try
{
mSocket.close();
}
catch( IOException e )
{
e.printStackTrace();
return false;
}
mSocket = null;
}
mDevice = null;
return true;
}
/* Send bytes to the connected device */
public boolean writeData( byte[] buffer )
{
if( mOutputStream == null )
{
System.out.println("No connection, can't send data");
}
else
{
try
{
mOutputStream.write( buffer );
return true;
}
catch (IOException e)
{
System.out.println( "Failed to send data" );
e.printStackTrace();
}
}
return false;
}
public static String byteArrayToHex(byte[] a, int size) {
StringBuilder sb = new StringBuilder(size * 5);
for( int i = 0; i != size; ++i )
sb.append(String.format("0x%02x ", a[i] & 0xff));
return sb.toString();
}
public int getBytesPending()
{
try
{
return mInputStream.available();
}
catch (IOException e)
{
return 0;
}
}
/* Non blocking read function. Read bytes from the connected device.
* Return number of bytes read
* return 0 if not enough bytes available
* return -1 in case of error
*/
public int readData( byte[] buffer, int size, boolean blocking )
{
if ( mInputStream == null )
{
System.out.println("No connection, can't receive data");
}
else
{
try
{
final boolean verbose = false;
if ( blocking )
{
if ( verbose )
System.out.println( "Blocking request of " + buffer.length + " byte(s)" );
int res = 0;
int temp = 0;
while ( true )
{
temp = mInputStream.read( buffer, res, size - res );
res += temp;
if ( res >= size )
{
break;
}
else
{
if ( verbose )
System.out.println( "Received " + res + " byte(s) to far : " + byteArrayToHex(buffer,size) );
}
try {
Thread.sleep(10);
} catch(InterruptedException ex) {
}
}
if ( verbose )
System.out.println( "Received " + res + " byte(s) : " + byteArrayToHex(buffer,size) );
return res;
}
else
{
int available = mInputStream.available();
if ( verbose && available != 0 )
{
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);
System.out.println( currentTime + ":" + c.get(Calendar.MILLISECOND) + " - " + available + " bytes available, requested " + buffer.length );
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…