我有一个自制的蓝牙装置,测量心电图500赫兹:每2毫秒,设备发送9个字节的数据(标头,心电图测量,页脚)。因此,这大约是一个9*500=4.5千字节/秒的数据流。
我有一个C++ Windows程序,可以连接设备并检索数据流(用Qt/qwt显示它)。在本例中,我使用连接设备,并使用boost serial_port接口通过虚拟COM端口连接设备。这是完美的工作,我正在接收我的数据流实时:我得到一个测量点每2ms左右。
我通过C++ (QT5.3.2)在Android上移植了整个QtCreator程序。我有实时问题。数据流在前5秒处于“实时”状态,然后性能会急剧下降(参见How to do good real-time data streaming using Java Android SDK)。
因为我认为问题可能是C++/Qt造成的,所以使用Eclipse编写了一个完全空白的纯项目。也有同样的问题!
问题是:这个代码有什么问题吗?为什么我只接收了5秒的实时数据?在Android平台上密集使用BT 5秒后会发生什么?为什么它会减慢BT的数据接收速度?
以下是我的Java程序:
BluetoothHelper.java (具有连接/断开/读取和写入数据的功能:
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 );
}
if ( available >= size )
{
int res = mInputStream.read( buffer, 0, size ); // only call read if we know it's not blocking
if ( verbose )
System.out.println( "Received " + res + " byte(s) : " + byteArrayToHex(buffer,size) );
return res;
}
else
{
return 0;
}
}
}
catch (IOException e)
{
System.out.println( "Failed to read data...disconnected?" );
//e.printStackTrace();
}
}
return -1;
}
public byte[] readNextFrame( boolean blocking )
{
if ( readData( mHeader, mHeader.length, blocking ) == mHeader.length )
{
int size = mHeader[2];
if ( size < 0 )
size = -size;
if ( readData( mFrame, size, blocking ) == size )
{
byte[] res = new byte[mHeader.length + size];
System.arraycopy(mHeader, 0, res, 0, mHeader.length);
System.arraycopy(mFrame, 0, res, mHeader.length, size);
return res;
}
}
return null;
}
*/ read frame but without allocating any memory, does not retur condumed bytes */
public boolean eatNextFrame( boolean blocking )
{
if ( readData( mHeader, mHeader.length, blocking ) == mHeader.length )
{
int size = mHeader[2];
if ( size < 0 )
size = -size;
if ( readData( mFrame, size, blocking ) == size )
{
return true;
}
}
return false;
}
public boolean startECG()
{
// some code sending instructions to configure my device
}
}主Java文件,连接并执行10秒的获取:
// Here is the code for Medoc:
BluetoothHelper helper = new BluetoothHelper(this);
String error = helper.initializeBluetooth();
if ( error.isEmpty() )
{
if ( helper.addPairedDevices( ) )
{
if ( !helper.getDevices().isEmpty() )
{
if ( helper.getDevices().size() == 1 )
{
BluetoothDevice device = helper.getDevices().firstElement();
error = helper.connectToDevice( device );
if ( error.isEmpty() )
{
if ( helper.startECG() )
{
// acquiere data for 10 seconds
Date start = new Date();
Date end = new Date();
Date empty = null;
int lastMinute = 0;
int maxBufferSize = 0;
boolean receivedData = false;
while ( end.getTime() - start.getTime() < 10 * 1000 )
{
int currentMinute = (int) (( end.getTime() - start.getTime() ) / 1000);
if ( currentMinute != lastMinute )
{
if ( receivedData )
System.out.println( "During second #" + lastMinute + " max buffer size was : " + maxBufferSize );
else
System.out.println( "During second #" + lastMinute + " no data was received!" );
maxBufferSize = 0;
receivedData = false;
lastMinute = currentMinute;
}
if ( helper.eatNextFrame(false) )
{
receivedData = true;
}
if ( helper.getBytesPending() == 0 )
{
if ( empty == null )
{
empty = new Date();
}
}
else
{
if ( empty != null )
{
Date now = new Date();
int elapsed = (int) ( now.getTime() - empty.getTime() );
if ( elapsed > 100 )
System.out.println( "No pending data, during " + elapsed + "ms" );
empty = null;
}
}
maxBufferSize = Math.max( helper.getBytesPending(), maxBufferSize );
end = new Date();
}
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
dlgAlert.setMessage( "Done" );
dlgAlert.setPositiveButton("Ok",null);
dlgAlert.create().show();
}
else
{
error = "Failed to start ECG";
}
helper.disconnectDevice();
}
}
else
{
error = "Too many devices found";
}
}
else
{
error = "No device found";
}
}
else
{
error = "Failed to scan for devices";
}
}
if ( !error.isEmpty() )
{
AlertDialog.Builder dlgAlert2 = new AlertDialog.Builder(this);
dlgAlert2.setMessage( error );
dlgAlert2.setPositiveButton("Ok",null);
dlgAlert2.create().show();
}在这里,程序的输出:
12-01 14:12:51.755: I/System.out(15940): During second #0 max buffer size was : 63
12-01 14:12:52.755: I/System.out(15940): During second #1 max buffer size was : 133
12-01 14:12:53.755: I/System.out(15940): During second #2 max buffer size was : 66
12-01 14:12:54.755: I/System.out(15940): During second #3 max buffer size was : 61
12-01 14:12:55.755: I/System.out(15940): During second #4 max buffer size was : 129
12-01 14:12:56.705: I/System.out(15940): No pending data, during 501ms
12-01 14:12:56.755: I/System.out(15940): During second #5 max buffer size was : 939
12-01 14:12:57.755: I/System.out(15940): During second #6 max buffer size was : 980
12-01 14:12:58.755: I/System.out(15940): During second #7 max buffer size was : 1008
12-01 14:12:59.195: I/System.out(15940): No pending data, during 488ms
12-01 14:12:59.695: I/System.out(15940): No pending data, during 489ms
12-01 14:12:59.755: I/System.out(15940): During second #8 max buffer size was : 990
12-01 14:13:00.185: I/System.out(15940): No pending data, during 490ms
12-01 14:13:01.205: I/System.out(15940): Disconnecting device...正如您所看到的,在最初的5秒期间,读取缓冲区仍然很小,并且没有超过100 is缓冲区为空的时刻(请参见输出“无挂起数据”的代码)。然后,从第五秒开始,我们:
在数据采集的第5秒之后,就好像数据在某个地方被缓冲了,并且以大约500 as的块可以在InputStream中读取。
有时,情况甚至可能更糟,5秒后根本没有收到任何数据:
12-01 14:35:54.595: I/System.out(16386): During second #0 max buffer size was : 22
12-01 14:35:55.595: I/System.out(16386): During second #1 max buffer size was : 93
12-01 14:35:56.595: I/System.out(16386): During second #2 max buffer size was : 108
12-01 14:35:57.595: I/System.out(16386): During second #3 max buffer size was : 61
12-01 14:35:58.595: I/System.out(16386): During second #4 max buffer size was : 64
12-01 14:35:59.595: I/System.out(16386): During second #5 max buffer size was : 63
12-01 14:36:00.595: I/System.out(16386): During second #6 no data was received!
12-01 14:36:01.595: I/System.out(16386): During second #7 no data was received!
12-01 14:36:02.595: I/System.out(16386): During second #8 no data was received!注意:在创建BluetoothHelper之前和调用startECG()之前,我试着睡上几秒钟。同样的行为(获得速度减慢或在5秒后停止)。
编辑:我正在体验:
但在具有定制的S3 11 Android4.4.2的Galaxy S3上却不是这样:数据流看起来很完美,5秒后不会冻结,数据也会实时到达.
编辑12月15日:
按照建议,将read移到单独的线程: Made BluetoothHelper实现Runnable,并将这些方法/属性添加到类中:
private int mFramesReceived;
private long mLongestPause;
public void clearReceived()
{
mFramesReceived = 0;
mLongestPause = 0;
}
public int received()
{
return mFramesReceived;
}
public long longestPause()
{
return mLongestPause;
}
@Override
public void run() {
System.out.println( "Started thread" );
int lastSeconde = 0;
long currentTimeMillis = System.currentTimeMillis();
long started = System.currentTimeMillis();
// Keep listening to the InputStream until an exception occurs
while (true) {
if ( eatNextFrame( true ) )
{
//System.out.println( "Got some data" );
mLongestPause = Math.max( mLongestPause, System.currentTimeMillis() - currentTimeMillis );
currentTimeMillis = System.currentTimeMillis();
mFramesReceived++;
int currentSeconde = (int) (( System.currentTimeMillis() - started ) / 1000);
if ( currentSeconde != lastSeconde )
{
if ( mFramesReceived != 0 )
System.out.println( "During second #" + lastSeconde + " max pause was : " + mLongestPause );
else
System.out.println( "During second #" + lastSeconde + " no data was received!" );
clearReceived();
lastSeconde = currentSeconde;
}
}
else
{
System.out.println( "Failed to get some data, connection closed?" );
break;
}
}
}然后将呼叫者更改为:
if ( helper.startECG() )
{
new Thread(helper).start();
try {
Thread.sleep(10000); // wait 10 seconds
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
dlgAlert.setMessage( "Done" );
dlgAlert.setPositiveButton("Ok",null);
dlgAlert.create().show();
}
else
{
error = "Failed to start ECG";
}
helper.disconnectDevice();它没有解决问题,这是输出:
During second #0 max pause was : 48
During second #1 max pause was : 45
During second #2 max pause was : 33
During second #3 max pause was : 35
During second #4 max pause was : 58
During second #5 max pause was : 498
During second #6 max pause was : 477
During second #7 max pause was : 480
During second #8 max pause was : 986
During second #9 max pause was : 497发布于 2015-01-16 12:55:41
发布于 2014-12-15 13:00:32
使用线程概念在外围设备上同时读取和写入Bytes。使用android蓝牙数据传输实例解决问题。您正在使用普通的java类向另一个无效的设备发送和接收数据。您应该使用线程概念通过蓝牙发送和接收数据。
请参考下面的链接,通过蓝牙读写数据。
http://developer.android.com/guide/topics/connectivity/bluetooth.html
发布于 2014-12-15 12:30:36
您不应该依赖InputStream.available()来判断流中可用的字节数(参见https://developer.android.com/reference/java/io/InputStream.html#available() )。由于您知道数据包的确切大小(9个字节),所以每次将9个字节读入缓冲区:mInputStream.read(buffer, 0, 9)。
有了蓝牙,就很难保证实时的二进制传送,因为可能有很多原因造成延迟(例如设备之间的距离增加,障碍等等)。因此,通常更好的做法是不断调用read并将检索到的数据部分转发到处理组件。例如,在我的一个项目中,我实现了Android Service,等待来自蓝牙的数据包并用接收到的数据通知UI。您可以为此实现一个Service或AsyncTask。
另一个建议是:避免在经常调用的方法(如readData)中进行不必要的内存分配。您可以使用System.currentTimeMillis()来度量经过的时间。垃圾收集可能是导致性能下降的原因之一。
https://stackoverflow.com/questions/27229813
复制相似问题