如何通过jSerialComm API从Arduino的COM口获取传感器数据,并在TextDatabase类中调用Sensor类方法将数据保存至文本文件
解决方案:通过jSerialComm获取Arduino数据并保存到文本文件
针对你的问题,核心是解决异步串口数据接收与同步数据获取/保存的衔接问题,同时修复现有代码中的参数错误和资源管理问题。以下是完整的修改方案:
1. 重构Sensor类:实现线程安全的数据缓存
原Sensor类仅在终端打印数据,没有将数据暴露给外部类的机制。我们用BlockingQueue实现异步数据的缓存,确保线程安全的同时,让外部类可以阻塞等待新数据。
import com.fazecast.jSerialComm.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class Sensor { // 阻塞队列:缓存串口数据,支持异步接收+同步获取 private BlockingQueue<String> dataQueue = new LinkedBlockingQueue<>(); private SerialPort comPort; public void connect() { // 获取第一个可用串口(实际使用建议让用户选择,避免选错端口) comPort = SerialPort.getCommPorts()[0]; // 配置串口参数:与Arduino默认配置一致(9600波特率、8数据位、1停止位、无校验) comPort.setComPortParameters(9600, 8, 1, 0); comPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0); if (comPort.openPort()) { System.out.println("串口连接成功!"); } else { System.err.println("无法打开串口,请检查端口是否被占用"); return; } // 添加串口数据监听器 comPort.addDataListener(new SerialPortDataListener() { @Override public int getListeningEvents() { return SerialPort.LISTENING_EVENT_DATA_RECEIVED; } @Override public void serialEvent(SerialPortEvent event) { byte[] newData = event.getReceivedData(); // 将字节数组转为字符串(编码与Arduino发送一致,这里用UTF-8) String receivedData = new String(newData, java.nio.charset.StandardCharsets.UTF_8); try { // 将数据存入队列,供外部类获取 dataQueue.put(receivedData); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("数据存入队列失败:" + e.getMessage()); } // 保留终端输出,方便调试 System.out.print(receivedData); } }); } // 从队列获取传感器数据:无数据时阻塞等待,有数据时立即返回 public String extractDataFromCOM() throws InterruptedException { return dataQueue.take(); } // 关闭串口连接,避免资源泄漏 public void disconnect() { if (comPort != null && comPort.isOpen()) { comPort.closePort(); System.out.println("串口已关闭"); } } }
2. 修改TextDatabase类:正确调用Sensor并优化文件写入
原TextDatabase存在参数错误、重复创建文件流的问题,我们调整调用逻辑并优化资源管理:
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; public class TextDatabase { public static void main(String[] args) { Sensor s1 = new Sensor(); BufferedWriter bw = null; try { // 先建立串口连接 s1.connect(); // 初始化文件写入流:放在循环外,避免重复打开/关闭文件(提升效率) // 第二个参数true表示追加写入,不会覆盖原有文件 bw = new BufferedWriter(new FileWriter("mydata.txt", true)); System.out.println("开始获取并保存数据,按Ctrl+C退出程序"); // 循环获取数据并写入文件 while (true) { // 从Sensor阻塞获取数据 String mydata = s1.extractDataFromCOM(); // 写入文件(保留Arduino发送的换行符,方便后续解析) bw.write(mydata); // 立即刷新缓冲区,确保数据实时写入文件 bw.flush(); } } catch (InterruptedException e) { // 捕获用户中断(比如Ctrl+C),优雅退出 System.out.println("\n程序即将退出"); } catch (IOException e) { System.err.println("文件操作错误:" + e.getMessage()); } finally { // 关闭资源:无论程序正常退出还是异常,都要释放资源 if (bw != null) { try { bw.close(); System.out.println("文件写入流已关闭"); } catch (IOException e) { System.err.println("关闭文件流失败:" + e.getMessage()); } } // 关闭串口连接 s1.disconnect(); } } }
关键改动说明
- 线程安全的数据传递:用
BlockingQueue解决串口异步接收与外部类同步获取的矛盾,避免空循环浪费CPU资源。 - 修正extractDataFromCOM方法:移除多余参数,改为从队列取数据,让调用者可以阻塞等待新数据。
- 串口参数配置:添加波特率等参数,确保与Arduino的串口配置一致,避免乱码或无法接收数据。
- 优化文件写入:将文件流放在循环外,减少IO操作开销;每次写入后flush,确保数据实时写入文件。
- 资源管理:添加disconnect方法和finally块,确保程序退出时关闭串口和文件流,避免资源泄漏。
额外注意事项
- 串口选择:如果电脑有多个串口,
SerialPort.getCommPorts()[0]可能不是Arduino的端口,建议添加串口选择逻辑(比如遍历打印所有端口,让用户输入选择)。 - Arduino代码配合:确保Arduino端用
Serial.println()发送数据(带换行符),方便后续按行解析数据。 - 数据解析:如果需要提取传感器数值(比如温度、湿度),可以在
extractDataFromCOM中添加解析逻辑,或者在TextDatabase中处理(比如用字符串分割提取数值)。
内容的提问来源于stack exchange,提问作者gola
相关产品推荐
相关产品推荐

