Linux 北斗串口 ttyVIZ0 实时读取
一、场景说明
现场硬件:北斗对时模块,挂载 Linux 串口/dev/ttyVIZ0;
Java 业务代码使用 RXTX 配置串口参数:9600 8N1(9600 波特、8 数据位、1 停止位、无校验) 。
问题:直接cat /dev/ttyVIZ0不会实时输出,数据缓存攒满 / 遇换行才打印,表现和读取普通文件一致,无法实时抓取北斗 NMEA 报文。
原因:Linux 串口默认规范模式(cooked / 行缓冲),驱动缓存数据,收到 \r\n 才上送应用;必须切换 raw 原始模式,字节实时透传。
二、Java RXTX 原始串口配置(项目源码)
java
@EnableScheduling // 必须加
@SpringBootApplication
public class YourApp {
public static void main(String[] args) {
SpringApplication.run(YourApp.class, args);
}
}
java
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
@Component
public class BeidouSerialManager {
private static final String PORT_NAME = "/dev/ttyVIZ0";
private SerialPort serialPort;
private final List<String> beidouDataCache = new ArrayList<>(18);
private volatile boolean connected = false;
// ===================== 核心:每3秒自动检查连接 =====================
@Scheduled(fixedRate = 3000)
public void checkAndConnect() {
if (connected) {
return;
}
connect();
}
// ===================== 真正的连接逻辑(只在这里) =====================
private void connect() {
try {
closePort(); // 先清理旧资源
serialPort = SerialPort.getCommPort(PORT_NAME);
if (serialPort == null || !serialPort.openPort()) {
System.out.println("未检测到北斗设备,继续重试...");
return;
}
// 你的配置 9600 8N1
serialPort.setBaudRate(9600);
serialPort.setNumDataBits(8);
serialPort.setNumStopBits(1);
serialPort.setParity(0);
// 监听器
serialPort.addDataListener(dataListener());
connected = true;
System.out.println("✅ 北斗串口已成功连接");
} catch (Exception e) {
connected = false;
}
}
// ===================== 数据监听器 =====================
private SerialPortDataListener dataListener() {
return new SerialPortDataListener() {
@Override
public int getListeningEvents() {
return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
}
@Override
public void serialEvent(SerialPortEvent event) {
try {
if (!serialPort.isOpen()) {
connected = false;
return;
}
byte[] buffer = new byte[1024];
int len = serialPort.readBytes(buffer, buffer.length);
if (len > 0) {
String data = new String(buffer, 0, len);
beidouDataCache.add(data);
System.out.println("收到北斗数据:" + data);
}
} catch (Exception e) {
connected = false; // 异常=断开
}
}
};
}
// ===================== 安全关闭 =====================
@PreDestroy
public void closePort() {
try {
if (serialPort != null && serialPort.isOpen()) {
serialPort.closePort();
}
} catch (Exception ignored) {}
connected = false;
}
public List<String> getBeidouDataCache() {
return beidouDataCache;
}
}
- 启动没插串口 → 完全没问题
- 程序正常启动
- 每 3 秒自动扫描
- 插上立刻自动连接
- 中途拔掉 → 自动标记断开
- 读取异常 → connected = false
- 下一轮 3 秒自动重试
- 再次插上 → 自动恢复
- 全程不需要人工干预
- 不需要重启程序
参数释义:9600 8N1,工业北斗 / GPS 设备通用串口参数。
三、stty 命令与 Java 参数精准映射(核心配置)
3.1 对应配置命令(和 Java 完全等价)
bash
# 配置串口为raw原始模式,关闭行缓冲、回显、流控,对齐Java 9600 8N1
stty -F /dev/ttyVIZ0 9600 cs8 -cstopb -parenb raw -echo -icrnl -onlcr -ixon
| Java 配置项 | stty 参数 | 说明 |
|---|---|---|
| 9600 波特率 | 9600 | 串口速率 |
| 8 数据位 | cs8 | character size=8bit |
| 1 停止位 | -cstopb | 关闭双停止位,使用 1 停止位 |
| 无校验 (0) | -parenb | 关闭奇偶校验 |
| 实时无缓冲 | raw | 原始模式,取消行缓冲,单字节立即输出 |
| 关闭回显 | -echo | 禁止串口本地回显 |
-ixon:关闭 XON/XOFF 软件流控,北斗对时设备无需流控配置。
3.2 临时实时读取(调试用)
注意:配置完参数后,实时打印北斗报文:
bash
# 终端前台实时打印,Ctrl+C终止
cat /dev/ttyVIZ0
raw 模式下收到 1 字节立刻输出,不再等待换行,和 Java 程序读取逻辑一致。
四、常见故障排查
- 找不到串口 /dev/ttyVIZ0 不存在
bash
#查看系统识别串口
dmesg | grep tty
ls /dev/ttyVIZ0
- 乱码:波特率不匹配
北斗备选波特率:115200,替换命令中 9600 重试。
五、补充:恢复串口默认规范模式
如需切回系统默认行缓冲模式:
bash
stty -F /dev/ttyVIZ0 sane