Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
245 views
in Technique[技术] by (71.8m points)

java - Real-time Bluetooth SPP data streaming on Android only works for 5 seconds

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 );
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...