
在物联网应用中,常会遇到从微控制器(如arduino)通过蓝牙模块(如hc-05)向移动应用发送多路传感器数据的情况。例如,使用三个超声波传感器测量不同方向的距离,并将这些距离值实时显示在android应用中的三个独立textview中。
原始的发送方式,如简单地连续使用Serial1.print()发送不同传感器的值,而不加明确的分隔符,会导致一个常见的问题:蓝牙传输线程在接收数据时可能以任意的“数据块”(burst)形式进行,这可能导致一个完整的消息被拆分成多个接收块,或者多个消息混杂在一个接收块中。这使得Android应用难以准确地识别和分离出每个传感器的独立数据。例如,如果Arduino发送"Left Sensor 10cm"、"Right Sensor 20cm"、"Center Sensor 30cm",Android端可能一次性收到"Left Sensor 10cmRight Sensor 20cmCenter Sensor 30cm",或者收到"Left Sensor 10cmR"然后是"ight Sensor 20cmCenter Sensor 30cm",这都使得直接解析变得异常困难。
解决此问题的关键在于在数据发送端(Arduino)对每条消息进行“帧化”(framing),即为每条消息定义一个明确的结束标志。最常用的方法是使用换行符(\n)作为消息的结束符。这样,接收端(Android)就可以通过识别这个结束符来判断一条完整的消息是否已经到达。
原始的Arduino代码使用了Serial1.print()和delay()来发送数据,但没有明确的行结束符,且每次发送后都有delay(500),这可能导致数据发送不连续或接收端难以同步。
原始发送代码片段:
Serial1.print("Left Sensor ");
Serial1.print((String) distanceL + " cm" );
delay(500);
Serial1.println(" "); // 这里只发送了一个空行,而不是数据行的结束符
Serial1.print("Right Sensor ");
Serial1.print((String) distanceR + " cm" );
delay(500);
Serial1.println(" ");
Serial1.print("Center Sensor ");
Serial1.print((String) distanceC + " cm" );
delay(500);
Serial1.println(" ");修改后的Arduino发送代码:
为了确保每条数据都是一个完整的“行”,我们应该使用Serial1.println()来发送数据,它会自动在末尾添加换行符(\r\n)。同时,为了方便Android端解析,每条消息应包含一个明确的标识符(如“Left Sensor”、“Right Sensor”、“Center Sensor”)以及具体的数值。
void sensor() {
int durationL, distanceL;
int durationR, distanceR;
int durationC, distanceC;
// 左传感器测量
digitalWrite(LtriggerPin, HIGH);
delayMicroseconds(10); // 使用微秒延迟
digitalWrite(LtriggerPin, LOW);
durationL = pulseIn(LechoPin, HIGH);
distanceL = (durationL / 2) / 29.1;
// 右传感器测量
digitalWrite(RtriggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(RtriggerPin, LOW);
durationR = pulseIn(RechoPin, HIGH);
distanceR = (durationR / 2) / 29.1;
// 中间传感器测量
digitalWrite(CtriggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(CtriggerPin, LOW);
durationC = pulseIn(CechoPin, HIGH);
distanceC = (durationC / 2) / 29.1;
// 发送数据,每条数据后带换行符
Serial1.print("Left Sensor ");
Serial1.println(distanceL); // println 会自动添加换行符
// 适当的延迟,确保蓝牙模块有时间发送,但不必太长
delay(100); // 调整此延迟以适应你的应用需求
Serial1.print("Right Sensor ");
Serial1.println(distanceR);
delay(100);
Serial1.print("Center Sensor ");
Serial1.println(distanceC);
delay(100);
}注意: 这里的delay(100)是为了确保每条消息发送之间有短暂间隔,防止在某些低速蓝牙连接下数据过快导致丢失,但实际应用中,如果Android接收端处理得当,可以适当缩短甚至移除这些延迟。
Android端蓝牙通信通常在一个独立的线程(如ConnectedThread)中进行数据的读取。原始的run()方法试图一次性读取所有可用字节,并使用SystemClock.sleep(100)等待更多数据,但这并不能保证读取到的是一个完整的消息帧。
原始run()方法片段:
本文档主要讲述的是使用JSON进行网络数据交换传输;JSON(JavaScript ObjectNotation)是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成,非常适合于服务器与客户端的交互。JSON采用与编程语言无关的文本格式,但是也使用了类C语言的习惯,这些特性使JSON成为理想的数据交换格式。 和 XML 一样,JSON 也是基于纯文本的数据格式。由于 JSON 天生是为 JavaScript 准备的,因此,JSON的数据格式非常简单,您可以用 JSON 传输一个简单的 St
0
@Override
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
while (true) {
try {
bytes = mmInStream.available(); // 获取可用字节数
if(bytes != 0) {
buffer = new byte[1024]; // 每次都新建buffer,可能导致数据丢失
SystemClock.sleep(100); // 暂停等待更多数据
bytes = mmInStream.available();
bytes = mmInStream.read(buffer, 0, bytes); // 读取数据
hesler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget(); // 发送给UI线程
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}修改后的Android run()方法:
为了正确处理带结束符的数据流,run()方法需要进行逐字节读取和缓冲。当检测到换行符时,才将缓冲区中的完整数据作为一个消息发送到UI线程。
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; // 需要导入 Arrays 类
// ... 其他代码 ...
public class ConnectedThread extends Thread {
private final InputStream mmInStream;
private final Handler hesler; // 假设 hesler 是指向主线程 Handler 的引用
// 构造函数等...
@Override
public void run() {
byte[] buffer = new byte[1024]; // 缓冲区,用于存储接收到的字节
int c; // 从输入流读取的一个字节,或-1表示流结束
int position = 0; // 当前在缓冲区中的写入位置
// 持续监听输入流,直到发生异常
while (true) {
try {
// 从输入流中读取一个字节
c = mmInStream.read();
if (c == -1) { // 如果流结束
break; // 停止接收
}
if (c == '\n') { // 如果检测到换行符(消息结束)
// 复制已接收的完整行数据到新数组
// 注意:这里需要处理 '\r',因为 Arduino 的 println 会发送 '\r\n'
// 通常,我们只关心 '\n' 作为行结束标志,'\r' 可以忽略或一同复制
// 为了简化,这里我们假设只处理 '\n',并复制到当前 position
// 如果需要精确移除 '\r',可以检查 buffer[position-1] == '\r'
int actualLength = position;
if (position > 0 && buffer[position - 1] == '\r') {
actualLength--; // 移除回车符
}
byte[] copyBuffer = Arrays.copyOf(buffer, actualLength);
// 将完整消息发送到UI活动
hesler.obtainMessage(MainActivity.MESSAGE_READ, actualLength, -1, copyBuffer)
.sendToTarget();
position = 0; // 重置缓冲区位置,准备接收下一条消息
} else if (c != '\r') { // 忽略回车符,只存储有效数据字节
// 将字节存储到缓冲区
if (position < buffer.length) { // 防止缓冲区溢出
buffer[position] = (byte) c;
position++; // 推进位置,准备存储下一个字节
} else {
// 缓冲区溢出处理,例如清空缓冲区或发送错误消息
position = 0; // 简单处理:清空缓冲区并重新开始
}
}
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
}代码解释:
当Handler接收到MESSAGE_READ消息时,msg.obj中包含的就是一个完整的、以换行符结尾(但已在run()方法中移除换行符)的字节数组,代表一条来自传感器的完整数据行。此时,我们需要将这个字节数组转换为字符串,然后根据字符串中的关键字来识别是哪个传感器的数据,并提取数值更新到对应的TextView。
修改后的handleMessage()方法片段:
// 在 MainActivity 或其他包含 Handler 的类中
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg){
if(msg.what == MESSAGE_READ){
// msg.obj 现在是一个完整的字节数组,msg.arg1 是其长度
String readMessage = new String((byte[]) msg.obj, 0, msg.arg1, StandardCharsets.UTF_8);
// 根据关键字解析数据并更新对应的TextView
if (readMessage.contains("Left Sensor")) {
// 提取数字部分,例如 "Left Sensor 123" -> "123"
String distanceStr = readMessage.replaceAll("[^0-9]", ""); // 移除所有非数字字符
TvL.setText(distanceStr + " cm");
} else if (readMessage.contains("Right Sensor")) {
String distanceStr = readMessage.replaceAll("[^0-9]", "");
TvR.setText(distanceStr + " cm");
} else if (readMessage.contains("Center Sensor")) {
String distanceStr = readMessage.replaceAll("[^0-9]", "");
TvC.setText(distanceStr + " cm");
}
}
if(msg.what == CONNECTING_STATUS){
// 保持原有的连接状态显示逻辑
if(msg.arg1 == 1)
Tv3.setText(getString(R.string.BTConnected) + msg.obj);
else
Tv3.setText(getString(R.string.BTconnFail));
}
}
};代码解释:
通过在发送端(Arduino)利用Serial.println()添加明确的行结束符,并在接收端(Android)的蓝牙读取线程中实现逐字节读取、缓冲和基于结束符的消息组装,我们能够有效地解决多路传感器数据传输中的数据分包和解析问题。这种方法确保了每条消息的完整性,使得Android应用可以准确地识别并显示来自不同传感器的数据,从而构建稳定可靠的物联网应用。
以上就是基于HC-05的多传感器数据传输与Android应用解析教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号