如何通过蓝牙一起读取所有字节?

Posted

技术标签:

【中文标题】如何通过蓝牙一起读取所有字节?【英文标题】:How to read all bytes together via Bluetooth? 【发布时间】:2012-03-03 03:32:38 【问题描述】:

我有一个使用蓝牙从其他设备接收一些数据(字节)的应用程序。一切进展顺利,但我在接收所有字节时遇到了一个小问题。收到字节后,我在 Toast 上显示它们只是为了测试它们。当其他设备一起发送 10 个字节时(例如:“ABCDEFGHIJ”),程序将只取第一个字节“A”并在 Toast 上显示,然后进行第二次迭代并读取其他 9 个字节并显示“ BCDEFGHIJ”在吐司上。这是我的代码:

byte[] buffer = new byte[1024]; // Read 1K character at a time.
int bytes = 0; // Number of bytes.

while(true)

    try
    
        // Read from the InputStream.
        bytes = bInStream.read(buffer);

        // Send the obtained bytes to the MainActivity.
        mainActivityHandler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
    
    catch(IOException e)
    
        connectionLost();
        break;
    

在 MainActivity 中,我有:

// The Handler that gets information back from the BluetoothManager.
private final Handler handler = new Handler()

    @Override
    public void handleMessage(Message msg)
    
        switch(msg.what)
        
            case MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;

                // construct a string from the valid bytes in the buffer.
                String readMessage = new String(readBuf, 0, msg.arg1);
                Toast.makeText(MainActivity.this, readMessage, Toast.LENGTH_SHORT).show();
                break;

            // ...
        
    
;

我怎样才能一起接收所有字节?!

【问题讨论】:

我可以看看您是如何发送消息的吗?你是在做 bOutStream.write("ABCDEFGHIJ".getBytes()) 之类的事情,还是一次写一个字符? arduino 附加了蓝牙模块,所以我使用 arduino 软件的串口监视器发送消息。 【参考方案1】:

以上答案都不适合我。 Thread.sleep(1000) 给了我解决方案

【讨论】:

【参考方案2】:

要一起读取所有字节,您需要在代码中用“\n”或“\r”或“\r\n”分隔数据..

例如:如果您想通过蓝牙将数据从 Arduino 发送到 android 应用程序:

(Arduino代码):

    int count =0;
    int sensorPin = 0;

    void setup()
    
      Serial.begin(9600);
    

    void loop()
    
    int val= analogRead(sensorPin);
      if(val<threshold) 
         a++; 
      
      else
        delay(2);
        count = count + 1;
        Serial.print(count);
        Serial.print("\n");
          

现在,要读取发送的数据(变量'count'的值),这里是Android Studio的代码:

 private class ConnectedThread extends Thread 
        private final InputStream mmInStream;

        public ConnectedThread(BluetoothSocket socket) 
            InputStream tmpIn = null;

            // Get the input streams, using temp objects because
            // member streams are final
            try 
                tmpIn = socket.getInputStream(); // opens the input stream in order to retrieve InputStream objects
             catch (IOException e) 
            

            mmInStream = tmpIn;
        

     public void run() 
        int bytes; // bytes returned from read()
        int availableBytes = 0;

        // Keep listening to the InputStream until an exception occurs
        while (true) 
            try 
                availableBytes = mmInStream.available();
                if(availableBytes>0)
                    byte[] buffer = new byte[availableBytes];  // buffer store for the stream
                    // Read from InputStream
                    bytes = mmInStream.read(buffer); // Get number of bytes and message in "buffer"
                    if (bytes>0)
                        h.obtainMessage(RECEIVE_MESSAGE, bytes, -1, buffer).sendToTarget();     // Send to message queue Handler
                    
                   
                 catch (IOException e) 
                break;
                        
        
    

还有这个:(在 onCreate() 方法中):

mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
// The Handler that gets information back
 h = new Handler()  // Handler-->used to communicate b/w UI & BG Thread
            public void handleMessage(android.os.Message msg) 
                switch (msg.what) 
                    case RECEIVE_MESSAGE:// if receive message
                        byte[] readBuf = (byte[]) msg.obj;
                       String strIncom = new String(readBuf, 0, msg.arg1); // create string from bytes array

                        sb.append(strIncom);                                                // append string
                        int endOfLineIndex = sb.indexOf("\n");                            // determine the end-of-line
                        if (endOfLineIndex > 0)                                             // if end-of-line,
                            String sbprint = sb.substring(0, endOfLineIndex);               // extract string
                            sb.delete(0, sb.length());// and clear
                            Toast.makeText(ledControl.this,sbprint,Toast.LENGTH_SHORT).show();
                            footSteps.setText(sbprint);            // update TextView
                        
                        break;
                ;

【讨论】:

【参考方案3】:

@broody 接受的答案是正确的。但如果数据本身包含'#',则可能难以获取数据。因此,根据我的说法,最好的方法是在将数据发送到您的 Android 应用程序的设备中附加 '\n' 后跟 '\r' (或任何其他不太可能作为数据Refer ASCII Table 的字符)。它只是充当换行符并标记数据的结尾。

例如:ABCDEFGH\n\r

那么你的代码可以是这样的:

byte[] buffer = new byte[1024];
 while (true) 

         // Read from the InputStream
          buffer[bytes] = (byte) mmInStream.read();                 
         // Send the obtained bytes to the UI Activity
 if ((buffer[bytes] == '\n')||(buffer[bytes]=='\r'))
 
   mHandler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
   bytes=0;
 
 else
 bytes++;

希望对你有帮助 问候

【讨论】:

太棒了!这解决了我的问题,无需在 Arduino 端添加额外的 # 字符。我只是跳过检查\r(我只检查\n),它就像一个魅力。【参考方案4】:

处理长于缓冲区的消息(可能很危险,因为如果从不清除可能会占用您的内存)的一种方法是分块处理它们:

String inString = "";
byte[] buffer = new byte[1024];  // buffer store for the stream
int bytes; // bytes returned from read()

while (true) 
    try 
        bytes = mmInStream.read(buffer);
        String chunk = new String(buffer, 0, bytes);
        if(chunk.contains(";"))
        
            inString += chunk.substring(0,chunk.indexOf(';'));
            Message msg = new Message();
            msg.obj  = inString;
            handler.sendMessage(msg);
            inString =  chunk.substring(chunk.indexOf(';'));
        
        else
        
            inString += chunk;
        
     catch (IOException e) 
        break;
    

【讨论】:

代码假定为 ';'是消息分隔符。该代码是一个示例实现,并不通用-您不能复制粘贴它并期望在不修改的情况下工作,但它背后显示了一个概念。它会读取所有消息。一旦消息结束,handler.sendMessage(msg);被调用来处理完整的消​​息,并继续处理缓冲区。您必须实现处理程序来处理/读取消息。就是这个概念【参考方案5】:

蓝牙连接是基于流的,而不是基于数据包的。不保证或尝试保持分包。因此,任意数量的写入都可能导致任意数量的读取,只是保证字节流是正确的。如果需要检测数据包,则需要提供自己的数据包结构来包装数据。例如,在每个数据包之前添加一个长度字段,以便您可以在接收端进行重构。

【讨论】:

@TJD 我怎么知道长度字段的长度? @TJD 您是否有任何证据/文件证明 Android 无法保留数据包,或重新形成数据包本身?只是问一下,在我目前正在开发的应用程序中,有时会,有时却不会,所以只是想弄清楚为什么!【参考方案6】:

嗯,罪魁祸首很可能是您发送消息的方式。您的接收没有问题,它将接收与写入一样多的字节(最多 1024)。

如果您无法控制消息的发送方式,您可能可以一次读取一个字节,然后在您遇到预定义的终止符时向您发送处理程序消息。例如:“ABCDEFGHIJ#”,其中 # 是终止符。

String msg = "";
byte ch;
while((ch=mInStream.read())!='#') 
    bytes++;
    msg+=ch;

【讨论】:

+1 很好的解决方案。在找到解决问题的另一种方法之前,我将使用此解决方案。谢谢! 如果我传输二进制数据会怎样?如何确定终结符不是数据流的一部分?

以上是关于如何通过蓝牙一起读取所有字节?的主要内容,如果未能解决你的问题,请参考以下文章

外接附件读取问题

iOS蓝牙BLE读取数据最大大小

如何为 Android 应用强制执行最低蓝牙 MTU?

用于读取测量数据的蓝牙 LE 配置文件

如何通过蓝牙获取附近的ios设备信息

ESP32 BLE蓝牙 微信小程序通信发送大于20字符数据