串口通信通过Tcp传到服务器实现串口数据转发

主要目的:通过串口获取外设的数据,转发到服务器进行处理并在Web端显示设备的信息。

主要用到的知识点:

第一:串口通信相关的知识。

1.串口通信用到的两个头文件:

#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>

QSerialPort:用于访问串口,并对串口进行操作。

QSerialPortInfo:提供了系统中存在的串口的信息。

2.工程文件(.pro)中加下面一行代码:

QT       += serialport

注意:工程文件中添加上述代码后,要保存后才能生效,方法:在QT Creator中文件—保存所有文件。

第二:Tcp通信相关知识。

1.Tcp通信用到的头文件:

#include <QTcpSocket>

2.需要.pro文件中加入下面代码:

QT       += network

 代码实现:

第一:设计模块截图

          下面的红色是各个控件的QbjectNname,在代码要用到,为了方便读者方便阅读所以都标记出来了。下面“发送”按钮是预留按钮,在代码中没有用到,准备在后面的更新中再优化用到此按钮哦。

第二:代码展示

#-------------------------------------------------
#
# Project created by QtCreator 2019-09-30T15:51:56
#
#-------------------------------------------------

QT       += core gui network serialport

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = qianxunSerialPort
TEMPLATE = app


SOURCES += main.cpp\
        widget.cpp

HEADERS  += widget.h

FORMS    += widget.ui

注意:QT += core gui network serialport,一定要添加后在文件中保存,不然会出问题的哦。

widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include<QHostAddress>
#include<QDebug>
#include<QSerialPort>
#include<QSerialPortInfo>
#include<QTextCodec>
#include<QByteArray>
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    void InitTcp();
    void InitPort();
    void doProcessWrite();
    void doProcessTcpWrite();
    void writeByteArray();
private slots:
    void doProssConnected();
    void doProcessReadyRead();
    void doProssDisconnected();
    void doProcessSerialRead();

private slots:
    void on_pushButton_clicked();

    void on_ConnectBtn_clicked();

    //void on_SendBtn_clicked();

    void on_ClearBtn_clicked();

    void on_CheckBtn_clicked();

private:
    Ui::Widget *ui;
    QTcpSocket *myTcpSocket;
    bool pushBtnFlag=false;
    QSerialPort *mySerial;
    QByteArray tempStr;//保存的从服务器获取的字节流
    QByteArray readComDataMsg;//保存从串口获取的字节流
};

#endif // WIDGET_H
main.cpp:

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    w.setWindowTitle("串口转Tcp工具");
    return a.exec();
}
widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include<QFile>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //初始化端口
    InitPort();
    //初始化Tcp
    InitTcp();
   // ui->SendBtn->setEnabled(false);

}
Widget::~Widget()
{
    delete ui;
}
void Widget::InitTcp()
{
    myTcpSocket=new QTcpSocket(this);
    //由于connectToHost没有返回值,所以通过三个connect来判断和服务器的状态
    connect(myTcpSocket,SIGNAL(disconnected()),this,SLOT(doProssDisconnected()));
    connect(myTcpSocket,SIGNAL(connected()),this,SLOT(doProssConnected()));
    connect(myTcpSocket,SIGNAL(readyRead()),this,SLOT(doProcessReadyRead()));
}
//点击打开Tcp连接服务器
void Widget::on_pushButton_clicked()
{
    if(ui->pushButton->text()=="打开TCP")
    {
        QString ServceIp=ui->lineEdit_IP->text();
        QString ServcePort=ui->lineEdit_Port->text();
        myTcpSocket->connectToHost(QHostAddress(ServceIp),ServcePort.toUInt());
        ui->pushButton->setText("关闭TCP");
    }
    else if(ui->pushButton->text()=="关闭TCP")
    {
      myTcpSocket->close();
      ui->pushButton->setText("打开TCP");
      ui->textEdit_Client->append("TCP关闭成功!");
    }
}
void Widget::doProssConnected()
{
    QString str="打开服务器成功";
    ui->textEdit_Client->append(str);
}
void Widget::doProcessReadyRead()
{
    QTcpSocket *myTcpSocket=(QTcpSocket *)this->sender();
    //读取服务器向缓冲区的存储数据
    while (!myTcpSocket->atEnd())
    {
     tempStr= myTcpSocket->readAll();
   // tempStr=QString::fromLocal8Bit(tempMsg);
     qDebug()<<"服务器向缓冲区存储字节数据";
    }
    //处理向串口写入数据
    doProcessWrite();
    writeByteArray();

}
void Widget::doProssDisconnected()
{
//    QString msg="服务器断开";
//    ui->textEdit_Client->append(msg);
    ui->pushButton->setText("关闭TCP");

}
//向服务器写入数据。
void Widget::doProcessTcpWrite()
{
    if(!readComDataMsg.isEmpty())
    {
     int ret= myTcpSocket->write(readComDataMsg);
     readComDataMsg.clear();
       if(ret<0)
       {
           return;
       }
    }
    else{
         qDebug()<<"向服务器写入数据失败";
    }

}


//........................................................................
//.............................串口.......................................
void Widget::InitPort()
{
    mySerial = new QSerialPort(this);
    foreach (const QSerialPortInfo&info,QSerialPortInfo::availablePorts())
    {

        QSerialPort serial;
        serial.setPort(info);
        //串口每打开一次就要close一次,不然下次打不开。
        qDebug()<<"#######";
        if(serial.open(QIODevice::ReadWrite))
        {
        ui->ComBC->addItem(info.portName());
        serial.close();
        }
    }

    QStringList baudList;//波特率
    baudList<<"115200"<<"57600"<<"38400"<<"19200"<<"9600"<< "4800"<<"2400"<<"1200";
    ui->BaudCB->addItems(baudList);
    QStringList dataBitsList;
    dataBitsList<<"8"<<"7"<<"6"<<"5";
    ui->DataBitsCB->addItems(dataBitsList);
    QStringList parityList;
    parityList<<"无校验"<<"奇校验"<<"偶校验";
    ui->ParityCB->addItems(parityList);
    QStringList stopBitsList;
    stopBitsList<<"1"<<"1.5"<<"2";
    ui->StopCB->addItems(stopBitsList);
    QStringList setFlowCtrl;
    setFlowCtrl<<"off"<<"RTS/CTS"<<"XON/XOFF";
    ui->FlowsCB->addItems(setFlowCtrl);
    //ui->SendBtn->setEnabled(false);
}
void Widget::on_ConnectBtn_clicked()
{
    if(ui->ConnectBtn->text()=="串口连接")
      {
       mySerial->setPortName(ui->ComBC->currentText());
       qDebug()<<"***********";
       bool openSerial= mySerial->open(QIODevice::ReadWrite);

       if(openSerial)
        {
            //设置波特率
            if ( ui->BaudCB->currentText()=="115200")
            {
                mySerial->setBaudRate(QSerialPort::Baud115200);
            }
                //qDebug()<<"115200";
            else if ( ui->BaudCB->currentText()==" 9600")
            {
                 mySerial->setBaudRate(QSerialPort::Baud9600);
            }
            else if ( ui->BaudCB->currentText()==" 1200")
            {
                mySerial->setBaudRate(QSerialPort::Baud1200);
               }
             else if ( ui->BaudCB->currentText()==" 2400")
              {
                 mySerial->setBaudRate(QSerialPort::Baud2400);
                }
            else if ( ui->BaudCB->currentText()==" 4800")
            {

                mySerial->setBaudRate(QSerialPort::Baud4800);
             }
             else if ( ui->BaudCB->currentText()==" 19200")
            {
                 mySerial->setBaudRate(QSerialPort::Baud19200);
             }
            else if ( ui->BaudCB->currentText()==" 38400")
            {
                mySerial->setBaudRate(QSerialPort::Baud38400);
             }
            else if ( ui->BaudCB->currentText()==" 57600")
            {
                mySerial->setBaudRate(QSerialPort::Baud57600);
             }

            //设置数据位
            if (ui->DataBitsCB->currentText()=="8")

                  mySerial->setDataBits(QSerialPort::Data8);
            else if (ui->DataBitsCB->currentText()=="7")

                 mySerial->setDataBits(QSerialPort::Data7);
            else if (ui->DataBitsCB->currentText()=="6")

                 mySerial->setDataBits(QSerialPort::Data6);
            else if (ui->DataBitsCB->currentText()=="5")

                mySerial->setDataBits(QSerialPort::Data5);

            //设置校验位
            if (ui->ParityCB->currentText()=="0")
                 mySerial->setParity(QSerialPort::NoParity);
            else  if (ui->ParityCB->currentText()=="2")
                mySerial->setParity(QSerialPort::EvenParity);
            else if (ui->ParityCB->currentText()=="3")
                 mySerial->setParity(QSerialPort::OddParity);

            //停止位
            if (ui->StopCB->currentText()=="1")

                mySerial->setStopBits(QSerialPort::OneStop);
             else if (ui->StopCB->currentText()=="3")
                  mySerial->setStopBits(QSerialPort::OneAndHalfStop);
             else if (ui->StopCB->currentText()=="2")
                mySerial->setStopBits(QSerialPort::TwoStop);
            //流控制
            if (ui->FlowsCB->currentText()=="0")

                 mySerial->setFlowControl(QSerialPort::NoFlowControl);
            else if (ui->FlowsCB->currentText()=="1")
                mySerial->setFlowControl(QSerialPort::HardwareControl);
            else if (ui->FlowsCB->currentText()=="2")
                mySerial->setFlowControl(QSerialPort::SoftwareControl);
        }
       connect(mySerial,SIGNAL(readyRead()),this,SLOT(doProcessSerialRead()));
       QString msg1="log gpgga ontime 5\r\n";
       QString msg2="interfacemode auto auto on\r\n";
       //mySerial->write(msg1+msg2);
       mySerial->write(msg1.toLatin1());
       mySerial->write(msg2.toLatin1());
        ui->ConnectBtn->setText("串口关闭");
        ui->BaudCB->setEnabled(false);
        ui->DataBitsCB->setEnabled(false);
        ui->FlowsCB->setEnabled(false);
        ui->ParityCB->setEnabled(false);
        ui->StopCB->setEnabled(false);
        ui->ComBC->setEnabled(false);
      }
    else if(ui->ConnectBtn->text()=="串口关闭")
        {
          mySerial->close();
          ui->ConnectBtn->setText("串口连接");
          ui->BaudCB->setEnabled(true);
          ui->DataBitsCB->setEnabled(true);
          ui->FlowsCB->setEnabled(true);
          ui->ParityCB->setEnabled(true);
          ui->StopCB->setEnabled(true);
          ui->ComBC->setEnabled(true);
        }
}
//读取串口的数据
void Widget::doProcessSerialRead()
{
     readComDataMsg = mySerial->readAll();

     QString str=QString::fromLocal8Bit(readComDataMsg);
     QString strTemp=str.replace(QString("\r\n"),QString(" "));
     ui->textEdit_Servce->append(strTemp);
     //ui->textEdit_Servce->setText(readComDataMsg);
//     }
       doProcessTcpWrite();
      // readComData.clear();

}
//void Widget::on_SendBtn_clicked()
//{
//    //QString serialTemp=ui->textEdit_Client->toPlainText();
//   QString serialTemp="log versionb ontime 1\r\n";
//    //QString serialTemp="log gpgga ontime 1 \r\n";
//    //qDebug()<<serialTemp;
//   int ret= mySerial->write(serialTemp.toLatin1());
//   qDebug()<<ret;
//   qDebug()<<serialTemp;
//   if(ret<0)
//   {
//       qDebug()<<ret;
//   }
//}
void Widget::on_ClearBtn_clicked()
{
    ui->textEdit_Servce->clear();
    ui->textEdit_Client->clear();
}
//向串口写入数据
void Widget::doProcessWrite()
{

    if(!tempStr.isEmpty())
    {
        qDebug()<<"向串口写入数据正确";
//        char *ch=tempStr.data();
//    int ret= mySerial->write(ch);
      mySerial->write(tempStr);
      int byteLen= mySerial->bytesToWrite();
      qDebug()<<"写数据的大小:"<<byteLen;
      //qDebug()<<"这个是向串口写入数据:"<<mySerial;
//       if(tempStr.size()<0)
//       {
//           return;
//       }
    }
    else{
         qDebug()<<"向串口写入数据错误";
        return;
    }
    QString str=QString::fromLocal8Bit(tempStr);
    QString strReplace= str.replace(QString("\r\n"),QString(""));
    if(!strReplace.isEmpty())
    {
    ui->textEdit_Client->append(strReplace);
    //qDebug()<<strReplace;
    }
    else {
         return;
    }
    //tempStr.clear();
}

void Widget::writeByteArray()
{
    //写二进制文件
       QFile file("D://testBase.txt");
       if(!file.open(QIODevice::WriteOnly | QIODevice::Append))
       {
           qDebug() << "Can't open file for writing";
       }
//       QDataStream out(&file);
//       out.setVersion(QDataStream::Qt_5_6);
//       out << tempStr;
       qDebug()<<"向文件写入数据正确";
       file.write(tempStr);
       file.close();
       tempStr.clear();
}
//刷新串口
void Widget::on_CheckBtn_clicked()
{
    ui->ComBC->clear();
    foreach (const QSerialPortInfo&info,QSerialPortInfo::availablePorts())
    {

        QSerialPort serial;
        serial.setPort(info);
        if(serial.open(QIODevice::ReadWrite))
        {
        ui->ComBC->addItem(info.portName());
        serial.close();
        }
    }
}

代码思路梳理:从服务器获取字节流保存在tempStr全局变量中,然后写入到串口,从串口中获取的数据保存在readComDataMsg全局变量中,写入到服务器和在界面上显示。

继续优化的问题:

1.进行大量串口数据的转发,运行一天软件正常运行,数据转发正常,可以优化成多线程间通信,拓展知识。

2.doProcessWrite()/doProcessTcpWrite()这两个函数时直接调用的,应该使用Qt的风格,用emit发送信号,使用信号和槽函数模式。

       欢迎有问题的小伙伴或者对代码有见解的,多多交流哦。

  • 18
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 32
    评论
### 回答1: LWIP(Lightweight IP)是一个用于嵌入式系统的轻量级TCP/IP协议栈。使用LWIP库可以方便地实现通过串口发送数据TCP服务器的功能。 首先,需要初始化LWIP协议栈,并创建一个TCP客户端连接。通过配置串口通信参数,确保串口与设备之间的数据传输正常。然后,使用串口接收数据的中断函数,获取待发送的数据,将其存储在一个缓冲区中。 在TCP客户端连接建立后,可以通过LWIP库提供的API函数,将缓冲区中的数据发送到TCP服务器。通过调用lwip_send()函数,将数据写入到TCP发送缓冲区中,并通过TCP协议栈将数据发送到远程服务器。同时,可以使用lwip_recv()函数接收服务器返回的数据,并进行相关处理。 在发送数据时,应注意TCP发送缓冲区的空闲空间。如果发送速度过快,发送缓冲区可能会满,导致数据丢失或发送失败。因此,可以使用lwip_sndbuf()函数查询缓冲区剩余的可用空间,以便根据情况适时调整发送速度。 另外,为了提高数据传输的可靠性,可以使用LWIP的重传机制。如果发送的数据在网络中丢失或未收到响应,TCP协议栈会自动重传丢失的数据包,以确保数据的可靠传输。 需要注意的是,在使用LWIP发送数据时,应遵循TCP协议的相关规定,例如TCP的流控制和拥塞控制等机制,以避免网络拥塞和数据丢失的问题。 总之,通过使用LWIP库,可以方便地实现通过串口发送数据TCP服务器的功能,并提供可靠的数据传输机制,以满足嵌入式系统中对网络通信的需求。 ### 回答2: LWIP是一个轻量级的开源的TCP/IP协议栈。它提供了一种在嵌入式系统中实现TCP/IP网络通信的解决方案。 在LWIP中,实现串口数据通过TCP发送的过程需要以下步骤: 首先,需要配置串口的相关参数,例如波特率、数据位数、停止位等。可以通过调用LWIP提供的接口函数来进行配置。 然后,需要创建一个TCP连接。可以通过调用lwip_tcp_new函数来创建一个新的TCP连接,并将其绑定到一个特定的端口号上。 接下来,需要绑定一个回调函数到TCP连接上,用于处理接收到的数据。当有数据串口读取并发送到TCP连接上时,回调函数将被自动触发。 在回调函数中,可以通过调用lwip_tcp_write函数将接收到的串口数据写入到TCP连接中。也可以通过调用lwip_tcp_output函数将数据发送出去。需要注意的是,在发送数据之前,应确保TCP连接已经建立成功。 最后,需要调用lwip_tcp_close函数来关闭TCP连接,释放相关资源。 需要特别注意的是,由于LWIP是一个单线程的协议栈,所以在实际使用中可能需要使用操作系统提供的多线程功能,例如使用操作系统提供的线程库将串口读取和TCP发送的过程分别放在不同的线程中。 总的来说,使用LWIP实现串口数据通过TCP发送,需要配置串口参数、创建TCP连接、绑定回调函数、读取串口数据并发送到TCP连接中,并最后关闭TCP连接。以上是一个简单的描述,具体实现还需要根据具体的应用场景和需求进行相应的定制。 ### 回答3: 使用lwIP(轻型IP协议栈)库,可以很方便地实现通过串口发送数据TCP连接。 首先,需要在代码中引入lwIP库,并初始化lwIP协议栈。具体的初始化过程可以参考lwIP官方文档或者示例代码。 在初始化完成后,需要创建一个TCP服务器或者客户端连接。创建TCP服务器可以使用`tcp_new()`函数,指定一个回调函数来处理接收到的数据。创建TCP客户端可以使用`tcp_connect()`函数,指定要连接的服务器地址和回调函数。回调函数可以在接收到数据时进行相应的处理操作。 若要通过串口发送数据,可以使用标准串口库,例如STM32的HAL库中的函数`HAL_UART_Transmit()`。在接收到需要发送的数据后,可以通过调用该函数将数据通过串口发送出去。 具体的步骤如下: 1. 初始化lwIP协议栈。 2. 创建TCP服务器或者客户端连接,并指定回调函数。 3. 接收到需要发送的数据后,调用串口发送函数将数据发送出去。 4. 回调函数中可以针对接收到的数据进行处理,例如将数据缓存起来或者做其他操作。 需要注意的是,lwIP是一个基于事件驱动的协议栈,需要在主循环中定期调用`tcpip_input()`函数来处理网络数据。此外,还需要配置lwIP的IP地址、子网掩码、默认网关等网络参数,以便能够正常连接到网络。 总之,使用lwIP库可以方便地实现通过串口发送数据TCP连接。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值