本框架在QT4.8.6版本环境下编写。
看了下网上大多数人实现的在Linux下的4G模块AT指令收发控制,以及在QT上实现的,还有某些厂商实现的解析框架,其实就是在处理串口的收发,但未免做得有点糙,问题点也很多,比如很多人压根就没做指令回复的超时处理,万一要是发生了,那将给整个程序带来致命性的伤害。
最近的项目上需要在QT上处理AT指令的发送和回复,基于这样的环境,于是我决定实现两条线程,一条用于处理设备的初始化,一条用于处理在主进程指令的发送,由于AT指令收发存在延时,所以直接在主进程上延时,QT主进程就卡死了,这就是为什么我一定要用线程去处理的结果。
如果是在Linux操作串口设备,qt也要有相应的权限,使用如下指令即可。
sudo qtcreator &
由于QT4.8.6版本未有操作串口相关的类库,于是就只能移植开源的qextserialbase库了。
参考:https://blog.csdn.net/g1036583997/article/details/44805217
该文件主要是用于串口通信模块(支持AT指令)、以及一些全局参数的定义。
#ifndef GOBAL_ITEM_FLAG_H
#define GOBAL_ITEM_FLAG_H
#include "posix_qextserialport.h"
#define __NBIOT
#define DEV_NAME "/dev/ttyUSB0"
#define RECV_DATA_LEN 1024
extern char recv_buffer[RECV_DATA_LEN];
//AT_CMD_DEFINE Start
#ifdef __NBIOT
#define AT_TEST_OK "AT"
#define GET_IMSI "AT+CIMI"
#define GET_SIGNAL "AT+CSQ"
#define GET_SUPPORT_NBAND "AT+NBAND=?"
#define GET_CURRENT_NBAND "AT+NBAND?"
#define SET_NBAND "AT+NBAND=5"
#define GET_CGATT "AT+CGATT"
#define CREATE_TCP_SOCKET "AT+NSOCR=STREAM,6,56000,1"
#define CONNECT_TCP_SERVER "AT+NSOCO=1,120.78.136.134,9002"
#define HARDWARE_RESET "AT+NRB"
#elif __ESP8266
#define AT_TEST_OK "AT"
#define AT_SET_MODE "AT+CWMODE=%d"
#define AT_CONNECT_ROUTER "AT+CWJAP=\"%s\",\"%s\""
#define AT_CONNECT_SERVER "AT+CIPSTART=\"%s\",\"%s\",%d"
#define AT_SET_CIP_MODE "AT+CIPMODE=1"
#define AT_ENTER_CIP_MODE "AT+CIPSEND"
#elif __ME3630
#define AT_TEST_OK "AT"
#define CREATE_TCP_SOCKET "AT+ZIPCALL=1"
#define CONNECT_TCP_SERVER "AT+ZIPOPEN=1,0,120.78.136.134,9001"
#define AT_SEND_DATA "AT+ZIPSEND=1,%s";
#endif
//AT_CMD_DEFINE End
#endif // GOBAL_ITEM_FLAG_H
#ifndef NETWORK_INTERFACE_H
#define NETWORK_INTERFACE_H
#include
#include
#include
#include
#include "Gobal_Item_flag.h"
class NetWork_Interface : public QThread
{
Q_OBJECT
public:
explicit NetWork_Interface(QObject *parent = 0);
~NetWork_Interface();
//if Status is True,Thread is stop,else is Runing
void NetWork_Thread_change(bool Status);
int Send_AT_Cmd(QString cmd_buffer,const char *success_ack,const char *error_ack,int timeout);
private:
Posix_QextSerialPort *NetWork_Port ;
volatile bool Stop_flag ;
int Uart_Init(const char *dev_name,BaudRateType BaudRate,DataBitsType DataBits, \
ParityType ParityBits,StopBitsType StopBits,FlowType FlowControl,int TimeOut);
protected:
void run();
signals:
public slots:
};
#endif // NETWORK_INTERFACE_H
这里实现了串口的初始化,线程状态的控制,线程函数执行以及AT指令的发送函数。
#include "network_interface.h"
//全局串口数据接收缓存区
char recv_buffer[RECV_DATA_LEN] = {0};
//串口初始化
int NetWork_Interface::Uart_Init(const char *dev_name,BaudRateType BaudRate,DataBitsType DataBits, \
ParityType ParityBits,StopBitsType StopBits,FlowType FlowControl,int TimeOut)
{
this->NetWork_Port = new Posix_QextSerialPort(dev_name,QextSerialBase::Polling);
if(NetWork_Port->open(QIODevice::ReadWrite))
{
this->NetWork_Port->setBaudRate(BaudRate);
this->NetWork_Port->setDataBits(DataBits);
this->NetWork_Port->setParity(ParityBits);
this->NetWork_Port->setStopBits(StopBits);
this->NetWork_Port->setFlowControl(FlowControl);
this->NetWork_Port->setTimeout(TimeOut);
return 0 ;
}
return 1 ;
}
NetWork_Interface::NetWork_Interface(QObject *parent) :
QThread(parent)
{
int ret = Uart_Init(DEV_NAME,BAUD9600,DATA_8,PAR_NONE,STOP_1,FLOW_OFF,200);
if(0 != ret)
{
qDebug() << "Open Device Fail..." ;
this->NetWork_Thread_change(true);
return ;
}
//改变线程的状态为执行
this->NetWork_Thread_change(false);
}
NetWork_Interface::~NetWork_Interface()
{
this->NetWork_Port->close();
delete this->NetWork_Port;
}
//Change Thread Status
void NetWork_Interface::NetWork_Thread_change(bool Status)
{
Stop_flag = Status ;
}
//Runing Init Thread
void NetWork_Interface::run()
{
qDebug() << "Open Device Success....Start Uart_Recv Thread";
//Init Device
while(!Stop_flag)
{
QThread::msleep(200);
}
}
/AT指令收发
/*
cmd_buffer:要发送的AT指令,不需要带\r\n
success_ack:成功的预言字符串
error_ack:错误的语言字符串
timeout:超时,意思是如果发送超过timeout定义的,即返回超时失败。
return : 0 成功 1失败 -1超时
*/
int NetWork_Interface::Send_AT_Cmd(QString cmd_buffer,const char *success_ack,const char *error_ack,int timeout)
{
char *cmd = NULL;
char *send_cmd = NULL ;
QByteArray Byte_Data,temp;
temp.clear();
memset(recv_buffer,0,1024);
cmd_buffer.append("\r\n");
Byte_Data = cmd_buffer.toLatin1();
send_cmd = Byte_Data.data();
this->NetWork_Port->write(send_cmd);
while(timeout--)
{
temp.clear();
memset(recv_buffer,0,1024);
temp = this->NetWork_Port->readAll();
cmd = temp.data();
strcpy(recv_buffer,cmd);
if(strstr(recv_buffer,error_ack) != NULL)
{
qDebug() << "ackerror:" << recv_buffer ;
return 1 ;
}
if(strstr(recv_buffer,success_ack) != NULL)
{
qDebug() << "acksuccess:" << recv_buffer ;
return 0 ;
}
QThread::msleep(1);
}
qDebug() << "ackerror:AT cmd recv TimeOut";
return -1 ;
}
#ifndef NETWORK_CONTROL_THREAD_H
#define NETWORK_CONTROL_THREAD_H
#include
#include "network_interface.h"
class NetWork_Control_Thread : public QThread
{
Q_OBJECT
public:
explicit NetWork_Control_Thread(QObject *parent = 0);
~NetWork_Control_Thread();
void NetWork_Thread_change(bool Status);
private:
volatile bool Stop_flag ;
NetWork_Interface *netWork_Ptr ;
protected:
void run();
signals:
public slots:
};
#endif // NETWORK_CONTROL_THREAD_H
#include "network_control_thread.h"
NetWork_Control_Thread::NetWork_Control_Thread(QObject *parent) :
QThread(parent)
{
this->NetWork_Thread_change(false);
//在这里实例化网络接口线程
this->netWork_Ptr = new NetWork_Interface ;
//启动网络接口线程
this->netWork_Ptr->start();
}
NetWork_Control_Thread::~NetWork_Control_Thread()
{
this->NetWork_Thread_change(true);
delete this->netWork_Ptr ;
}
//改变线程的状态
void NetWork_Control_Thread::NetWork_Thread_change(bool Status)
{
this->Stop_flag = Status ;
}
//线程执行函数
void NetWork_Control_Thread::run()
{
//发送AT测试指令
this->netWork_Ptr->Send_AT_Cmd(AT_TEST_OK,"OK","ERROR",200);
while(!this->Stop_flag)
{
qDebug() << "NetWork_Control_Thread is Runing....";
QThread::msleep(1000);
}
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include "network_control_thread.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
NetWork_Control_Thread *network_control_ptr ;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//实例化网络控制线程
this->network_control_ptr = new NetWork_Control_Thread ;
//启动网络控制线程===>启动后将会自动启动网络接口线程
this->network_control_ptr->start();
}
MainWindow::~MainWindow()
{
delete this->network_control_ptr;
delete ui;
}
运行结果,这里我用的是NBIOT的BC28模块进行测试,本例程只提供测试框架,开发者需自行添加自己的业务逻辑处理。
以下是我利用这个框架写的一个类似上位机的软件,可以读取模块的基本信息: