You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Android OutputStream.write合并多消息:蓝牙控制Arduino小车问题

解决Android蓝牙发送Arduino消息粘包问题

嘿,这个问题我之前帮好几个玩蓝牙小车的开发者搞定过——蓝牙串口本质是流传输,它本身没有“消息边界”的概念,连续调用write()的时候,系统会把相邻的字节流打包成一块发送,所以你看到两条消息合并成一条完全是正常行为,不用慌!

下面给你三个最实用的解决方案,按需选就行:

方案1:给每条消息加分隔符(最推荐,简单直观)

在每条消息末尾加一个特殊标记(比如换行符\n、分号;或者自定义的||),让Arduino端根据这个标记拆分消息。

Android端修改代码

// 发送时给每条消息末尾加换行符
connectedThread.write("200-100\n");
connectedThread.write("300-150\n");

Arduino端处理代码

String inputString = "";
boolean isMessageComplete = false;

void setup() {
  Serial.begin(9600); // 记得和蓝牙模块波特率一致
}

void loop() {
  // 读取串口缓存的字节
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    inputString += inChar;
    // 遇到换行符就认定一条消息接收完成
    if (inChar == '\n') {
      isMessageComplete = true;
    }
  }

  // 处理完整消息
  if (isMessageComplete) {
    Serial.println("收到完整指令:" + inputString);
    // 这里可以拆分指令,比如分割200和100来控制电机
    // ...你的业务代码...
    
    // 重置缓存,准备接收下一条消息
    inputString = "";
    isMessageComplete = false;
  }
}

方案2:固定消息长度(适合格式固定的场景)

如果你的每条指令长度完全一致(比如都是7个字符,像"200-100"),可以让Arduino端每次读取固定长度的字节。

Android端保持原调用

connectedThread.write("200-100");
connectedThread.write("300-150");

Arduino端处理代码

void setup() {
  Serial.begin(9600);
}

void loop() {
  // 等待串口缓存有足够长度的字节
  if (Serial.available() >= 7) {
    char buffer[8]; // 多留一位存字符串结束符
    Serial.readBytes(buffer, 7);
    buffer[7] = '\0'; // 转成合法字符串
    Serial.println("收到指令:" + String(buffer));
    // ...你的业务代码...
  }
}

方案3:先发送消息长度(最严谨,适合复杂通信)

如果你的指令长度不固定,可以先发送消息的字节数,再发送实际内容,让Arduino端先读长度再读对应字节数的内容。

Android端修改write方法

public void write(String message) {
  try {
    byte[] msgBytes = message.getBytes();
    // 先发送消息长度(这里用1字节存储,适合长度<256的场景)
    outputStream.write(msgBytes.length);
    // 再发送消息内容
    outputStream.write(msgBytes);
    outputStream.flush(); // 可选,确保字节立即发送
  } catch (IOException e) {
    e.printStackTrace();
  }
}

// 调用方式不变
connectedThread.write("200-100");
connectedThread.write("300-150");

Arduino端处理代码

void setup() {
  Serial.begin(9600);
}

void loop() {
  // 先读取消息长度
  if (Serial.available() >= 1) {
    int msgLength = Serial.read();
    // 等待足够的内容字节
    if (Serial.available() >= msgLength) {
      char buffer[256];
      Serial.readBytes(buffer, msgLength);
      buffer[msgLength] = '\0';
      Serial.println("收到指令:" + String(buffer));
      // ...你的业务代码...
    }
  }
}

总结

  • 新手优先选方案1,代码改动最小,调试也方便;
  • 如果指令格式完全固定,用方案2更高效;
  • 要是做复杂的蓝牙通信协议,方案3最可靠。

内容的提问来源于stack exchange,提问作者Cosmin

火山引擎 最新活动