在工作中,从事软硬件工作的小伙伴经常会用到串口通信,尤其是windows系统下,传输消息。笔者前段时间做的麦克风阵列上的本地AI引擎(ASR、TTS、NLP)用于语音交互,其中的结果通过串口协议发送给PC机上,在Github找了个两个串口模块,分别是WzSerialPort和CSerialPort,二者均封装了Windows的API,从工程化程度和使用对比看,CSerialPort更优。主要是因为WzSerialPort在调试中经常出现偶现的报错,没有具体去分析原因。
在使用CSerialPort的过程中,希望能实现串口自动重连,实现热插拔功能,官方也是将该功能放在TODO List中,但是尚未实现,笔者就从上层实现了一个热插拔功能。
主要的原理是通过接口异常来检测串口是否异常,开启线程读取串口数据,如果读取失败,则认为串口异常,重启串口。
1.放弃使用样例中的信号-槽模式,他是通过回调的方式来通知接收串口数据,在异常场景下,该回调无法被触发
2.写接口调用时间不确定,无法及时获取串口异常信息,故采用读接口
主要实现方法如下:
开启线程开实时读取串口消息
// 启动时开启线程
...
boost::function0<void> f_run = boost::bind(&serial::run, this);
run_thread_ = new boost::thread(f_run);
...
// 线程函数
void serial::run() {
int ret = HY_SUCCESS;
char str[10240] = { 0 }; // 大小可适当放大点,防止读取数据超过buffer
while (!exit_flag_) {
//read
int recLen = com_->readAllData(str);
if (recLen > 0)
{
std::string msg(str, recLen);
LOG->information("serial::run | receive com port msg : %s", msg); // 日志信息
// WS::get_mutable_instance().send_msg(msg); // 消息处理,笔者通过websocket发送给客户端
}
else if (recLen < 0){
reset_serial();
LOG->information("serial::run | reset com port");
}
}
}
// reset
int serial::reset_serial() {
if (com_) {
com_->close();
int ret = open_port(); // 具体方法自行实现
if (ret != HY_SUCCESS) {
LOG->error("serial::run | restart com port failed, ret = %d", ret);
boost::thread::sleep(boost::get_system_time() + boost::posix_time::seconds(5)); // 防止频繁调用,占用CPU
}
else {
LOG->information("serial::run | restart com port success");
}
}
return HY_SUCCESS;
}