实时蓝牙SPP数据流在Android上只能持续5秒 [英] Real-time Bluetooth SPP data streaming on Android only works for 5 seconds

查看:1123
本文介绍了实时蓝牙SPP数据流在Android上只能持续5秒的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个自制的蓝牙设备测量心电图在500Hz的:每2毫秒设备发送9个字节的数据(头,心电measurment,页脚)。因此,这是一个大致9 * 500 = 4.5kbytes / s的数据流。

我有一个C ++ Windows程序能够连接设备和检索数据流(使用Qt / QWT显示它)。在这种情况下,我使用的是Windows控制面板粘合装置,我使用boost serial_port接口,通过虚拟COM端口连接。这完美的作品,我收到我的数据流进行实时:我得到一个measurment点每2ms左右

我通过QtCreator(QT 5.3.2)移植了整个C ++程序在Android上。我有实时的问题。数据流是实时的前5秒钟,然后再表现会显着放缓(见<一href="http://stackoverflow.com/questions/24934464/how-to-do-good-real-time-data-streaming-using-java-android-sdk">How使用Java Android SDK中)做好实时数据流。

由于我的吼声问题可能是由于C ++ / Qt的,我写了一个完全空白的纯Java / Android的使用Eclipse项目。和它有同样的问题!!!

问题是:是不是有什么不对的code?为什么我收到的数据进行实时的只有5第一秒? Android平台5秒密集BT使用后会发生什么,以及为什么它减慢BT数据接收?

下面是我的Java程序:

BluetoothHelper.java(带功能可以连接/断开/读写数据:

 包com.example.helloworld;

进口android.util.Log;
进口android.content.Context;
进口android.os.Bundle;
进口java.util.Locale中;
进口java.util.concurrent.Semaphore中;
进口java.lang.String中;
进口java.lang.Thread中;
进口java.io.IOException异常;
进口java.io.OutputStream中;
进口的java.io.InputStream;
进口java.text.SimpleDateFormat的;
进口java.lang.InterruptedException;
进口android.app.Activity;
进口android.app.AlertDialog;
进口android.bluetooth.BluetoothAdapter;
进口android.bluetooth.BluetoothDevice;
进口android.bluetooth.BluetoothSocket;
进口android.bluetooth.BluetoothManager;
进口android.util.SparseArray;
进口android.content.Intent;
进口android.content.IntentFilter;
进口android.content.BroadcastReceiver;
进口java.util.UUID中;
进口java.util.Date;
进口的java.util.Calendar;
进口java.util.Vector中;
进口java.util.Set中;
进口java.util.Arrays中;

公共类BluetoothHelper
{
    私人BluetoothManager mBluetoothManager;
    私人BluetoothAdapter mBluetoothAdapter;
    私人BluetoothDevice类mDevice;
    私有的BluetoothSocket mSocket;
    私人的OutputStream mOutputStream;
    私人的InputStream mInputStream;
    私人BroadcastReceiver的mReceiver;
    私人活动myActivity;
    私人矢量&lt; BluetoothDevice类&GT; mDevices;
    私人字节[] mHeader;
    私人字节[] mFrame;

    公共BluetoothHelper(活性)
    {
        myActivity =一个;
        mHeader =新字节[3];
        mFrame =新的字节[256];
        mDevices =新的向量();
    }

    / *检查蓝牙启用,回归如果正常,否则,返回的错误字符串* /
    公共字符串initializeBluetooth(){

        字符串错误=;
        的System.out.println(初始化蓝牙......);

        mBluetoothManager =(BluetoothManager)myActivity.getSystemService(Context.BLUETOOTH_SERVICE);
        如果(mBluetoothManager == NULL)
        {
            错误=蓝牙管理器没有找到;
        }
        其他
        {
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            如果(mBluetoothAdapter == NULL)
            {
                错误=蓝牙适配器没有找到;
            }
            否则如果(!mBluetoothAdapter.isEnabled())
            {
                错误=蓝牙适配器关闭;
            }
            其他
            {
                的System.out.println(蓝牙初始化成功);
                返回 ;
            }
        }

        返回错误;
    }

    私人无效的AddDevice(最终BluetoothDevice类设备)
    {
        mDevices.add(设备);
    }

    公共矢量&lt; BluetoothDevice类&GT; getDevices(){返回mDevices; }

    / *清除previously检测设备列表* /
    公共布尔clearDeviceList(){
        //清除旧列表
        mDevices.clear();
        返回true;
    }

    / *填充本地设备列表与配对设备的* /
    公共布尔addPairedDevices(){
        //System.out.println("Entering addPairedDevices);

        如果(mBluetoothAdapter == NULL)
        {
            的System.out.println(没有蓝牙适配器);
            返回false;
        }

        设置&LT; BluetoothDevice类&GT; pairedDevices = mBluetoothAdapter.getBondedDevices();
        //如果有配对设备
        如果(pairedDevices.size()大于0)
        {
            //System.out.println("Found配对设备);
            //循环配对设备
            对于(BluetoothDevice类设备:pairedDevices)
            {
                的AddDevice(设备);
            }
        }

        返回true;
    }

    公共字符串connectToDevice(最终BluetoothDevice类设备)
    {
        如果(mDevice!= NULL)
            disconnectDevice();

        如果(mBluetoothAdapter == NULL || myActivity == NULL)
            回归系统未初始化或蓝牙未激活;

        如果(device.getBondState()!= BluetoothDevice.BOND_BONDED)
        {
            // TODO:找到一个方法来做到同步包围操作
            回归设备未结合;
        }

        最终布尔[] the_result =新的布尔[1];
        the_result [0] = FALSE;

        最后的信号量互斥=新的信号量(0);

        可运行connectRunnable =新的Runnable(){
                @覆盖
                公共无效的run(){

                    UUID MY_UUID = UUID.fromString(00001101-0000-1000-8000-00805F9B34FB);

                   尝试
                   {
                        mSocket = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
                        的System.out.println(创建RFCOMM插座);
                        mSocket.connect();
                        如果(mSocket.isConnected())
                        {
                            的System.out.println(连接RFCOMM插座);
                            mOutputStream = mSocket.getOutputStream();
                            mInputStream = mSocket.getInputStream();
                            的System.out.println(检索输出流);
                            the_result [0] =真;
                        }
                        其他
                        {
                            的System.out.println(无法连接RFCOMM插座);
                        }
                   }
                   赶上(IOException异常E)
                   {
                        的System.out.println(无法打开RFCOMM插座(createRfcommSocketToServiceRecord));
                        的System.out.println(e.toString());
                   }

                   mutex.release();
                }
            };

        myActivity.runOnUiThread(connectRunnable);

        //等待线程完成...
        尝试 {
            mutex.acquire();
        }赶上(InterruptedException异常E){
            e.printStackTrace();
        }

        如果(the_result [0])
        {
            的System.out.println(连接成功);
            返回 ;
        }
        其他
        {
            的System.out.println(连接失败);
            返回无法连接设备;
        }
    }

    / *请求断开设备* /
    公共布尔disconnectDevice(){

        的System.out.println(断开装置......);

        如果(mSocket!= NULL)
        {
            //块读/写
            mOutputStream = NULL;
            mInputStream = NULL;

            尝试
            {
                mSocket.close();
            }
            赶上(IOException异常E)
            {
                e.printStackTrace();
                返回false;
            }
            mSocket = NULL;
        }

        mDevice = NULL;

        返回true;
    }

    / *发送字节连接的设备* /
    公共布尔writeData(byte []的缓冲区)
    {
        如果(mOutputStream == NULL)
        {
            的System.out.println(没有连接,不能发送数据);
        }
        其他
        {
            尝试
            {
                mOutputStream.write(缓冲液);
                返回true;
            }
            赶上(IOException异常E)
            {
                的System.out.println(无法发送数据);
                e.printStackTrace();
            }
        }
        返回false;
    }

    公共静态字符串byteArrayToHex(byte []的一个,诠释大小){
        StringBuilder的SB =新的StringBuilder(尺寸* 5);
        的for(int i = 0;!我=大小; ++ I)
           sb.append(的String.Format(为0x%02X,A [1]放大器;为0xFF));
        返回sb.toString();
    }

    公众诠释getBytesPending()
    {
        尝试
        {
            返回mInputStream.available();
        }
        赶上(IOException异常E)
        {
            返回0;
        }
    }

    / *非阻塞读取功能。阅读所连接设备的字节。
     *字节数回归阅读
     *返回0,如果没有足够的字节可用
     *返回-1错误的情况下
     * /
    公众诠释READDATA(byte []的缓冲区,诠释大小,布尔阻塞)
    {
        如果(mInputStream == NULL)
        {
            的System.out.println(没有连接,无法接收数据);
        }
        其他
        {
            尝试
            {
                最终布尔型的verbose = FALSE;

                如果(阻塞)
                {
                    如果(详细)
                        的System.out.println(锁定的请求+ buffer.length +字节(S));
                    INT RES = 0;
                    INT TEMP = 0;
                    而(真)
                    {
                        临时= mInputStream.read(缓冲,资源,规模 - 资源);

                        RES + =温度;

                        如果(RES&GT; =大小)
                        {
                            打破;
                        }
                        其他
                        {
                            如果(详细)
                                的System.out.println(接收+资源+字节时远:+ byteArrayToHex(缓冲区大小));
                        }

                        尝试 {
                            视频下载(10);
                        }赶上(InterruptedException的前){

                        }
                    }
                    如果(详细)
                        的System.out.println(接收+资源+字节(S):+ byteArrayToHex(缓冲区大小));
                    返回水库;
                }
                其他
                {
                    INT可用= mInputStream.available();

                    如果(详细&安培;&安培;!可= 0)
                    {
                        日历C = Calendar.getInstance();
                        日期日期=新的日期();
                        c.setTime(日期);
                        c.get(Calendar.MILLISECOND);

                        SimpleDateFormat的SDF =新的SimpleDateFormat(HH:MM:SS);
                        字符串currentTime的= sdf.format(日期);

                        的System.out.println(currentTime的+:+ c.get(Calendar.MILLISECOND)+ - +提供+字节可用,请+ buffer.length);
                    }

                    如果(可用&GT; =大小)
                    {
                        INT RES = mInputStream.read(缓冲,0,大小); //只有调用read,如果我们知道这不会阻止
                        如果(详细)
                            的System.out.println(接收+资源+字节(S):+ byteArrayToHex(缓冲区大小));
                        返回水库;
                    }
                    其他
                    {
                        返回0;
                    }
                }
            }
            赶上(IOException异常E)
            {
                的System.out.println(无法读取数据......断开?);
                //e.printStackTrace();
            }
        }
        返回-1;
    }

    公共字节[] readNextFrame(布尔阻塞)
    {
        如果(READDATA(mHeader,mHeader.length,阻塞)== mHeader.length)
        {
            INT大小= mHeader [2];
            如果(大小℃,)
                大小= -size;

            如果(READDATA(mFrame,大小,阻塞)==大小)
            {
                byte []的解析度=新的字节[mHeader.length +大小]
                System.arraycopy(mHeader,0,水库,0,mHeader.length);
                System.arraycopy(mFrame,0,资源,mHeader.length,大小);
                返回水库;
            }
        }

        返回null;
    }

    * /阅读框架,但没有分配任何内存,不retur condumed字节* /
    公共布尔eatNextFrame(布尔阻塞)
    {
        如果(READDATA(mHeader,mHeader.length,阻塞)== mHeader.length)
        {
            INT大小= mHeader [2];
            如果(大小℃,)
                大小= -size;

            如果(READDATA(mFrame,大小,阻塞)==大小)
            {
                返回true;
            }
        }

        返回false;
    }

    公共布尔startECG()
    {
        //一些code发送的指令来配置我的设备
    }
}
 

主要的Java文件,连接并做了10秒采集:

  //这里是$ C $下梅多克:
    BluetoothHelper帮手=新BluetoothHelper(本);
    字符串错误= helper.initializeBluetooth();
    如果(error.isEmpty())
    {
        如果(helper.addPairedDevices())
        {
            如果(!helper.getDevices()。的isEmpty())
            {
                如果(helper.getDevices()。大小()== 1)
                {
                    BluetoothDevice类设备= helper.getDevices()firstElement()。

                    错误= helper.connectToDevice(设备);

                    如果(error.isEmpty())
                    {
                        如果(helper.startECG())
                        {
                            // acquiere数据进行10秒
                            日期开始=新的日期();
                            日期结束=新的日期();
                            日空= NULL;
                            INT最后一分钟= 0;
                            INT maxBufferSize = 0;
                            布尔receivedData = FALSE;
                            而(end.getTime() -  start.getTime()小于10 * 1000)
                            {
                                INT currentMinute =(int)的((end.getTime() -  start.getTime())/ 1000);
                                如果(currentMinute!=最后一分钟)
                                {
                                    如果(receivedData)
                                        的System.out.println(在第二#+最后一分钟+最大缓存大小为:+ maxBufferSize);
                                    其他
                                        的System.out.println(在第二#+最后一分钟+没有数据接收!);
                                    maxBufferSize = 0;
                                    receivedData = FALSE;
                                    最后一分钟= currentMinute;
                                }

                                如果(helper.eatNextFrame(假))
                                {
                                    receivedData =真;
                                }
                                如果(helper.getBytesPending()== 0)
                                {
                                    如果(空== NULL)
                                    {
                                        空=新的日期();
                                    }
                                }
                                其他
                                {
                                    如果(空!= NULL)
                                    {
                                        日期现在=新的日期();
                                        INT经过=(int)的(now.getTime() -  empty.getTime());
                                        如果(经过的大于100)
                                            的System.out.println(无待处理的数据,在+经过+MS);
                                        空= NULL;
                                    }
                                }

                                maxBufferSize = Math.max(helper.getBytesPending(),maxBufferSize);

                                结束=新的日期();
                            }

                            AlertDialog.Builder dlgAlert =新AlertDialog.Builder(本);
                            dlgAlert.setMessage(完成);
                            dlgAlert.setPositiveButton(OK,NULL);
                            dlgAlert.create()显示()。
                        }
                        其他
                        {
                            错误=无法启动心电监护;
                        }

                        helper.disconnectDevice();
                    }
                }
                其他
                {
                    错误=太多的设备发现;
                }
            }
            其他
            {
                错误=未找到设备;
            }
        }
        其他
        {
            错误=无法扫描设备;
        }
    }

    如果(!error.isEmpty())
    {
        AlertDialog.Builder dlgAlert2 =新AlertDialog.Builder(本);
        dlgAlert2.setMessage(错误);
        dlgAlert2.setPositiveButton(OK,NULL);
        dlgAlert2.create()显示()。
    }
 

在这里,该程序的输出:

  12-01 14:12:51.755:我/的System.out(15940):在第二#0最大缓冲区大小为:63
12-01 14:12:52.755:我/的System.out(15940):在第二#1的最大缓冲区大小为:133
12-01 14:12:53.755:我/的System.out(15940):在第二#2的最大缓存大小为:66
12-01 14:12:54.755:我/的System.out(15940):在第二#3的最大缓存大小为:61
12-01 14:12:55.755:我/的System.out(15940):在第二#4最大的缓冲区大小为:129
12-01 14:12:56.705:我/的System.out(15940):没有挂起的数据,在501ms
12-01 14:12:56.755:我/的System.out(15940):在第二#5最大的缓冲区大小为:939
12-01 14:12:57.755:我/的System.out(15940):在第二次#6最大的缓冲区大小为:980
12-01 14:12:58.755:我/的System.out(15940):在第二#7最大的缓冲区大小是:1008
12-01 14:12:59.195:我/的System.out(15940):没有挂起的数据,在488ms
12-01 14:12:59.695:我/的System.out(15940):没有挂起的数据,在489ms
12-01 14:12:59.755:我/的System.out(15940):在第二次#8的最大缓冲区大小为:990
12-01 14:13:00.185:我/的System.out(15940):没有挂起的数据,在490ms
12-01 14:13:01.205:我/的System.out(15940):断开设备...
 

正如你所看到的,第一个5秒内,读缓存仍然是prettry小,没有一刻缓冲区是空的时间超过100毫秒(见code允许输出无待数据)。然后,从第五第二我们:

  • 在开始有长期(〜500毫秒),而读缓冲区保持为空(的InputStream :: available()返回0),即使我的设备被永久地将数据发送到Android。
  • 可以看到缓冲区最大尺寸显著的增长。

数据采集的第一5秒钟后,就好像数据得到bufferized某处和用于通过块读入的InputStream〜500ms的.....

是可用

有时,它可能是更坏,也没有接收到数据后5秒在所有的:

  12-01 14:35:54.595:我/的System.out(16386):在第二#0最大缓冲区大小为:22
12-01 14:35:55.595:我/的System.out(16386):在第二#1的最大缓冲区大小为:93
12-01 14:35:56.595:我/的System.out(16386):在第二#2的最大缓存大小为:108
12-01 14:35:57.595:我/的System.out(16386):在第二#3的最大缓存大小为:61
12-01 14:35:58.595:我/的System.out(16386):在第二#4最大的缓冲区大小为:64
12-01 14:35:59.595:我/的System.out(16386):在第二#5最大的缓冲区大小为:63
12-01 14:36:00.595:我/的System.out(16386):在第二次#6未接收到的数据!
12-01 14:36:01.595:我/的System.out(16386):在第二#7没有收到的数据!
12-01 14:36:02.595:我/的System.out(16386):在第二次#8没有接收到的数据!
 

注:我尝试创建之前 BluetoothHelper 并呼吁睡觉前几秒钟 startECG()。相同的行为(收购减慢或5秒后停止)。

编辑:我遇到的:

  • 的Nexus 5手机,安卓4.4.2
  • 的Nexus 7平板电脑,安卓4.4.2
  • 在银河S4采用Android 4.4.2

但不是在银河S3自定义的CyanogenMod 11的Andr​​oid 4.4.2:数据流似乎完美,没有冻结5秒后数据到达的实时...

修改12月15日:

作为建议,移动阅读到一个单独的线程: 国产 BluetoothHelper 实施的Runnable 并添加这些方法/属性的类:

 私人诠释mFramesReceived;
专用长mLongestPause;
公共无效clearReceived()
{
    mFramesReceived = 0;
    mLongestPause = 0;
}

接受公众诠释()
{
    返回mFramesReceived;
}

众长longestPause()
{
    返回mLongestPause;
}

@覆盖
公共无效的run(){

    的System.out.println(已启动线程);

    INT lastSeconde = 0;
    长的currentTimeMillis = System.currentTimeMillis的();
    长开始= System.currentTimeMillis的();

    //继续听InputStream的,直到发生异常
    而(真){
        如果(eatNextFrame(真))
        {
            //System.out.println(得到了一些数据);
            mLongestPause = Math.max(mLongestPause,System.currentTimeMillis的() - 的currentTimeMillis);
            的currentTimeMillis = System.currentTimeMillis的();
            mFramesReceived ++;

            INT currentSeconde =(INT)((System.currentTimeMillis的() - 开始)/ 1000);
            如果(currentSeconde!= lastSeconde)
            {
                如果(mFramesReceived!= 0)
                    的System.out.println(在第二#+ lastSeconde +最大暂停是:+ mLongestPause);
                其他
                    的System.out.println(在第二#+ lastSeconde +没有数据接收!);

                clearReceived();

                lastSeconde = currentSeconde;
            }
        }
        其他
        {
            的System.out.println(无法获得一些数据,连不上?);
            打破;
        }
    }
}
 

再改为主叫方:

 如果(helper.startECG())
{
    新的Thread(辅助)。开始();

    尝试 {
        视频下载(10000); //等待10秒
    }赶上(InterruptedException的前){
        Thread.currentThread()中断()。
    }

    AlertDialog.Builder dlgAlert =新AlertDialog.Builder(本);
    dlgAlert.setMessage(完成);
    dlgAlert.setPositiveButton(OK,NULL);
    dlgAlert.create()显示()。
}
其他
{
    错误=无法启动心电监护;
}

helper.disconnectDevice();
 

和它没有解决这个问题,这里是输出:

 在第二#0最大暂停是:48
在第二#1最大的停顿是:45
在第二#2最大的停顿是:33
在第二#3最大的停顿是:35
在第二#4最大的停顿是:58
在第二#5最大的停顿是:498
在第二次#6最大的停顿是:477
在第二#7最大的停顿是:480
在第二#8最大的停顿是:986
在第二#9最大暂停是:497
 

解决方案

这个问题显然是一个类似报道这里

5秒后,我无论是连接丢失,无论是实时流是显着放缓。

正如所说这里的Andr​​oid> 4.3显然不喜欢单向通信超过5 secondes。所以,我现在发送假命令,设备每隔1 Seconde系列(一种保持活跃命令),现在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 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 );
                    }

                    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
    }
}

main Java file, connecting and doing a 10sec acquisition:

    // 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();
    }

And here, the output of the program:

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...

As you can see, during the 5 first seconds, read buffer remains prettry small and there is no moment when buffer is empty for more than 100ms (see code outputing "No pending data"). Then, from the fifth second we :

  • start having long periods (~500ms) where the read buffer remains empty (InputStream::available() returns 0) even if my device is permanently sending data to Android.
  • can see the buffer max size grows significantly.

After the 5 first seconds of data acquisition, it's as if data are getting bufferized somewhere and are made available for reading in the InputStream by blocks of ~500ms.....

Sometimes, it could be even worst, there is no data being received at all after 5sec:

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!

Note: I tried to sleep some seconds before creating BluetoothHelper and before calling startECG(). Same behaviour (acquisition slows down or stops after 5 seconds).

Edit: I'm experiencing that on:

  • Nexus 5 phone, Android 4.4.2
  • Nexus 7 tablet, Android 4.4.2
  • Galaxy S4 with Android 4.4.2

But not on 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...

Edit December 15th:

As proposed, moved read to a separate thread: Made BluetoothHelper implement Runnable and added those methods/attributes to the class:

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

Then changed caller to:

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

And it did not fix the problem, here is the output:

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

解决方案

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!

这篇关于实时蓝牙SPP数据流在Android上只能持续5秒的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆