前言

  本人之前所做项目需要使用风速仪进行风速检测,市面上所购买的风速仪产品可以多通过串口与上位机进行通信,为满足后续需求且提高更高的灵活性,本人决定采用QT软件自主开发风速仪上位机,同时读取多组风速仪数据,提高检测效率。

风速仪

产品概览

  本项目所使用风速仪为标智仪表GM8904(其余通过串口通信的风速仪同样适用该上位机),产品图片如下

通信协议

  首先介绍该风速仪的接口定义。

红色 DC电源输入(3.6V-5V)
绿色 RXD
白色 TXD
黑色 GND

概述

  本协议是用于约定本风速仪(下位机)与上位机(电脑或智能设备)进行命令控制和数据交换的通信协议,采用UART规范,便于开发人员进行二次开发。

涉及范围

  《协议》中规定的功能主要有:
  1)上位机通过命令控制风速测量模块完成指定任务,如获取风速、风温数据等;
  2)接收超时、接收的响应不是确认响应、接收校验和不正确、接收长度字节非法,则为本次通讯失败故障,可立即重发一次命令。

专用术语

上位机:管理风速测量模块的专用只能设备;
下位机:本风速仪;
RS232:一种2线串行通信标准,可支持双工串行近程通讯;

物理接口

串行通信口电气标准

模块用TTL电平(3.3V)以RS232协议方式与上位机通讯。

信息传输方式

采用异步方式,1个起始位,8个数据位,1个停止位,无校验位。

数据传输速率

支持波特率19200bps

通信方式

基本过程

  上位机与模块是主从关系,上位机为主节点,模块为从节点。模块上电或复位后,处于接收状态,等待上位机发出命令,模块以命令指定命息格式上报上机位。

数据帧格式

长度 命令字 信息域 校验和

其中:
长度:1个字节的整形数,等于长度+命令字+信息域+校验字字节数之和
命令:1个字节,命令的信息;
信息域:命令的参数,0个字节或多字节。
校验字:1个字节,一帧字节的累加和。
注意:上位机无信息域

其中命令字各位定义如下

模块上传数据帧的格式定义如下

通信举例

例子1:
上位机发送:

长度 命令字 信息域 校验和
03 80 83

模块发送:

长度 命令字 1,2字节 3字节 4,5字节 校验和
03 80 00 0C 01 02 F3 8A

  该例子结果为:当前风速1.2m/s,蒲氏风级1级,风温为华氏75.5度,并重置最大大风速值,平均风速值,最小风速值为当前风速值。

例子2:
上位机发送:

长度 命令字 信息域 校验和
03 51 54

模块发送:

长度 命令字 1,2字节 3字节 4,5字节 校验和
03 51 01 0C 08 00 FC 6A

  该例子结果为:最大风速值26.8Km/h, 蒲氏8级风,摄氏25.2度。

上位机开发

经过分析可知,上位机需求为

  • 可同时读写多个风速仪发送的数据
  • 计算并显示当前测量半径下的风量与风速
  • 支持检测风速类型的选择(最大风速,最小风速,当前风速,平均风速)
  • 支持风速与风量单位的选择
  • 支持检测圆环半径的选择
  • 支持检测数据下载

界面设计

  根据上述需求,将上位机设计包含风速显示区、端口选择区、风速选择区、单位选择区、风量显示区、圆环半径选择区、功能选择区、风速存储区,具体界面设计如下:

各功能区说明如下:

  • 风速显示区:显示各端口采集的风速
  • 端口选择区:选择各测量点对应端口
  • 风速选择区:选择所需测量的风速类型,包括最大风速、最小风速、当前风速以及平均风速
  • 单位选择区:选择风速单位,包括m/s与km/h
  • 风量显示区:显示当前检测圆环半径下的平均风量
  • 圆环半径选择区:选择检测圆环的半径
  • 功能选择区:提供风速检测开始/停止、数据清空以及数据下载功能
  • 风速存储区:存储所测量风速以待后续下载

QT代码开发

串口参数设置函数

  根据风速仪的通讯协议,设计如下串口初始函数

/*-----------------串口选择----------------*/
bool MyWidget::port_config()
{portA.setPortName(ui->comboBox_A_com->currentText());portAA.setPortName(ui->comboBox_AA_com->currentText());portB.setPortName(ui->comboBox_B_com->currentText());portBB.setPortName(ui->comboBox_BB_com->currentText());portA.setBaudRate(QSerialPort::Baud19200);portA.setParity(QSerialPort::NoParity);portA.setDataBits(QSerialPort::Data8);portA.setStopBits(QSerialPort::OneStop);portA.setFlowControl(QSerialPort::NoFlowControl);portAA.setBaudRate(QSerialPort::Baud19200);portAA.setParity(QSerialPort::NoParity);portAA.setDataBits(QSerialPort::Data8);portAA.setStopBits(QSerialPort::OneStop);portAA.setFlowControl(QSerialPort::NoFlowControl);portB.setBaudRate(QSerialPort::Baud19200);portB.setParity(QSerialPort::NoParity);portB.setDataBits(QSerialPort::Data8);portB.setStopBits(QSerialPort::OneStop);portB.setFlowControl(QSerialPort::NoFlowControl);portBB.setBaudRate(QSerialPort::Baud19200);portBB.setParity(QSerialPort::NoParity);portBB.setDataBits(QSerialPort::Data8);portBB.setStopBits(QSerialPort::OneStop);portBB.setFlowControl(QSerialPort::NoFlowControl);portA.open(QSerialPort::ReadWrite);portAA.open(QSerialPort::ReadWrite);portB.open(QSerialPort::ReadWrite);portBB.open(QSerialPort::ReadWrite);return portA.isOpen()&&portAA.isOpen()&&portB.isOpen()&&portBB.isOpen();}

各参数选择函数

  通过反馈量确定当前选择的选项

/*-----------------测量模式选择-----------------*/
int MyWidget::wind_speed_choose()
{if(ui->radioButton_speedave->isChecked() == true){ui->radioButton_speedcur->setChecked(false);ui->radioButton_speedmax->setChecked(false);ui->radioButton_speedmin->setChecked(false);return 2;}else if(ui->radioButton_speedmax->isChecked() == true){ui->radioButton_speedcur->setChecked(false);ui->radioButton_speedave->setChecked(false);ui->radioButton_speedmin->setChecked(false);return 3;}else if(ui->radioButton_speedmin->isChecked() == true){ui->radioButton_speedave->setChecked(false);ui->radioButton_speedmax->setChecked(false);ui->radioButton_speedcur->setChecked(false);return 4;}else{ui->radioButton_speedcur->setChecked(true);ui->radioButton_speedave->setChecked(false);ui->radioButton_speedmax->setChecked(false);ui->radioButton_speedmin->setChecked(false);return 1;}}/*-----------------风速单位选择-----------------*/
int MyWidget::wind_speedunit_choose()
{if(ui->radioButton_km_h->isChecked() == true){ui->radioButton_m_s->setChecked(false);return 2;}else{ui->radioButton_m_s->setChecked(true);ui->radioButton_km_h->setChecked(false);return 1;}
}/*-----------------圆环半径选择-----------------*/
int MyWidget::circle_round_choose()
{if(ui->radioButton_120->isChecked() == true){return 1;}else if(ui->radioButton_200->isChecked() == true){return 2;}else if(ui->radioButton_280->isChecked() == true){return 3;}else if(ui->radioButton_360->isChecked() == true){return 4;}else if(ui->radioButton_440->isChecked() == true){return 5;}else if(ui->radioButton_520->isChecked() == true){return 6;}else if(ui->radioButton_600->isChecked() == true){return 7;}else if(ui->radioButton_680->isChecked() == true){return 8;}else if(ui->radioButton_760->isChecked() == true){return 9;}else if(ui->radioButton_840->isChecked() == true){return 10;}else if(ui->radioButton_920->isChecked() == true){return 11;}else if(ui->radioButton_1000->isChecked() == true){return 12;}else{return 0;}
}

风速数据读取与显示

/*---------------提取数据转化--------------------*/
float MyWidget::rece_tran(QByteArray &args)
{quint8 a = args[2];quint8 b = args[3];float temp=int(a)*25.6+int(b)*0.1;return temp;
}/*---------------读取端口数据并显示--------------------*/
void MyWidget::on_port_A_readyRead()
{if(true == mIsOpen){QByteArray receive_data_A = portA.readAll();ui->lcdNumber_speedA->setSmallDecimalPoint(true);ui->lcdNumber_speedA->setDigitCount(5);float speed_A = rece_tran(receive_data_A);ui->lcdNumber_speedA->display(speed_A);Wind_liang_show();}
}void MyWidget::on_port_AA_readyRead()
{if(true == mIsOpen){QByteArray receive_data_AA = portAA.readAll();ui->lcdNumber_speedAA->setSmallDecimalPoint(true);ui->lcdNumber_speedAA->setDigitCount(5);float speed_AA = rece_tran(receive_data_AA);ui->lcdNumber_speedAA->display(speed_AA);Wind_liang_show();}
}void MyWidget::on_port_B_readyRead()
{if(true == mIsOpen){QByteArray receive_data_B = portB.readAll();ui->lcdNumber_speedB->setSmallDecimalPoint(true);ui->lcdNumber_speedB->setDigitCount(5);float speed_B = rece_tran(receive_data_B);ui->lcdNumber_speedB->display(speed_B);Wind_liang_show();}
}void MyWidget::on_port_BB_readyRead()
{if(true == mIsOpen){QByteArray receive_data_BB = portBB.readAll();ui->lcdNumber_speedBB->setSmallDecimalPoint(true);ui->lcdNumber_speedBB->setDigitCount(5);float speed_BB = rece_tran(receive_data_BB);ui->lcdNumber_speedBB->display(speed_BB);Wind_liang_show();}}
}

开始、清空、下载等功能函数

  出于安全考虑,为该上位机增加安全机制,当处于接收下位机上传数据时设置各功能选项锁定,待风速存储区数据测量完毕后方可下载。具体函数定义如下:

/*------------------------------------*               slots* --------------------------------------*/void MyWidget::on_pushButton_start_clicked()
{QByteArray command_code;command_code[0] = 0x03;/*--------------执行关闭操作------------------*/if(mIsOpen == true){portA.close();portAA.close();portB.close();portBB.close();ui->pushButton_start->setText("开始");ui->pushButton_clear->setEnabled(true);ui->pushButton_clear->setStyleSheet("background-color: rgb(195, 0, 97);font: 75 18pt \"Times New Roman\";color: rgb(255, 255, 255);");ui->pushButton_download->setEnabled(true);ui->pushButton_download->setStyleSheet("background-color: rgb(222, 222, 0);font: 75 18pt \"Times New Roman\";color: rgb(255, 255, 255);");mIsOpen = false;choose_change(true);/*--------------将风速加载到表格中------------------*/QTableWidgetItem *speedA = new QTableWidgetItem(QString::number(ui->lcdNumber_speedA->value(),10,1));//ui->tableWidget_windspeed->item(circle_round_choose(),1)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);ui->tableWidget_windspeed->setItem(circle_round_choose(),1,speedA);QTableWidgetItem *speedAA = new QTableWidgetItem(QString::number(ui->lcdNumber_speedAA->value(),10,1));//ui->tableWidget_windspeed->item(circle_round_choose(),2)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);ui->tableWidget_windspeed->setItem(circle_round_choose(),2,speedAA);QTableWidgetItem *speedB = new QTableWidgetItem(QString::number(ui->lcdNumber_speedB->value(),10,1));//ui->tableWidget_windspeed->item(circle_round_choose(),3)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);ui->tableWidget_windspeed->setItem(circle_round_choose(),3,speedB);QTableWidgetItem *speedBB = new QTableWidgetItem(QString::number(ui->lcdNumber_speedBB->value(),10,1));//ui->tableWidget_windspeed->item(circle_round_choose(),1)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);ui->tableWidget_windspeed->setItem(circle_round_choose(),4,speedBB);QTableWidgetItem *Wind_liang = new QTableWidgetItem(QString::number(ui->lcdNumber_Wind_liang->value(),10,1));ui->tableWidget_windspeed->setItem(circle_round_choose(),5,Wind_liang);}/*--------------执行打开操作------------------*/else {if(port_config() == true){mIsOpen = true;ui->pushButton_start->setText("停止");ui->pushButton_clear->setEnabled(false);ui->pushButton_clear->setStyleSheet("background-color: rgb(180, 180, 180);font: 75 18pt \"Times New Roman\";color: rgb(255, 255, 255);");ui->pushButton_download->setEnabled(false);ui->pushButton_download->setStyleSheet("background-color: rgb(180, 180, 180);font: 75 18pt \"Times New Roman\";color: rgb(255, 255, 255);");choose_change(false);/*----------命令字------------*/switch (wind_speed_choose()) {/*----------当前风速------------*/case 1:switch (wind_speedunit_choose()) {/*----------m/s------------*/case 1:command_code[1] = 0xC0;break;/*----------Km/h------------*/case 2:command_code[1] = 0xC1;break;/*----------ft/min------------*/default:command_code[1] = 0xC2;break;}break;/*----------平均风速------------*/case 2:switch (wind_speedunit_choose()) {case 1:command_code[1] = 0xF0;break;case 2:command_code[1] = 0xF1;break;default:command_code[1] = 0xF2;break;}break;/*----------最大风速------------*/case 3:switch (wind_speedunit_choose()) {case 1:command_code[1] = 0xD0;break;case 2:command_code[1] = 0xD1;break;default:command_code[1] = 0xD2;break;}break;/*----------最小风速------------*/default:switch (wind_speedunit_choose()) {case 1:command_code[1] = 0xE0;break;case 2:command_code[1] = 0xE1;break;default:command_code[1] = 0xE2;break;}break;}command_code[2] = command_code[0] + command_code[1];}else{/*----------显示错误消息框----------*/QMessageBox *msgBox;msgBox = new QMessageBox("Warning","检测开始失败,请检查串口选择!",QMessageBox::Critical,QMessageBox::Ok | QMessageBox::Default,QMessageBox::Cancel | QMessageBox::Escape,0);msgBox->show();}}/*--------------串口发送指令-----------------*/portA.write(command_code, 3);portAA.write(command_code, 3);portB.write(command_code, 3);portBB.write(command_code, 3);//qDebug()<<command_code.toHex();}/*-----------------清空操作-----------------*/
void MyWidget::on_pushButton_clear_clicked()
{QByteArray clear_code;clear_code[0] = 0x03;clear_code[1] = 0x80;clear_code[2] = 0x83;portA.open(QSerialPort::ReadWrite);portAA.open(QSerialPort::ReadWrite);portB.open(QSerialPort::ReadWrite);portBB.open(QSerialPort::ReadWrite);portA.write(clear_code, 3);portAA.write(clear_code, 3);portB.write(clear_code, 3);portBB.write(clear_code, 3);portA.close();portAA.close();portB.close();portBB.close();ui->lcdNumber_speedA->display(0);ui->lcdNumber_speedAA->display(0);ui->lcdNumber_speedB->display(0);ui->lcdNumber_speedBB->display(0);ui->lcdNumber_Wind_liang->display(0);/*----------显示清空消息框----------*/QMessageBox *msgBox;msgBox = new QMessageBox("Tips","清空完成!",QMessageBox::Warning,QMessageBox::Ok | QMessageBox::Default,QMessageBox::Cancel| QMessageBox::Escape,0);msgBox->show();
}/*-----------------下载操作-----------------*/
void MyWidget::on_pushButton_download_clicked()
{/*----------显示下载确认消息框----------*/QMessageBox msgBox;msgBox.setMinimumSize(2000,2000);msgBox.setMaximumSize(2000,2000);msgBox.setText("提示!");msgBox.setInformativeText("表格数据是否已经测试完成?");msgBox.setStandardButtons(QMessageBox::Yes| QMessageBox::No);msgBox.setDefaultButton(QMessageBox::Yes);int ret = msgBox.exec();if(ret == QMessageBox::Yes){/*----------下载数据----------*/QString fileName = QFileDialog::getSaveFileName(this,tr("Excel file"),QString("./test.xlsx"),tr("Excel Files(*.xlsx)"));    //设置保存的文件名if(fileName != NULL){QAxObject *excel = new QAxObject;if(excel->setControl("Excel.Application")){excel->dynamicCall("SetVisible (bool Visible)",false);excel->setProperty("DisplayAlerts",false);QAxObject *workbooks = excel->querySubObject("WorkBooks");            //获取工作簿集合workbooks->dynamicCall("Add");                                        //新建一个工作簿QAxObject *workbook = excel->querySubObject("ActiveWorkBook");        //获取当前工作簿QAxObject *worksheet = workbook->querySubObject("Worksheets(int)", 1);QAxObject *cell;/*添加Excel表头数据*/for(int i = 1; i <= ui->tableWidget_windspeed->columnCount(); i++){cell = worksheet->querySubObject("Cells(int,int)", 1, i);cell->setProperty("RowHeight", 40);cell->dynamicCall("SetValue(const QString&)", ui->tableWidget_windspeed->horizontalHeaderItem(i-1)->data(0).toString());}/*将form列表中的数据依此保存到Excel文件中*/for(int j = 2; j <= ui->tableWidget_windspeed->rowCount()+1;j++){for(int k = 1;k <= ui->tableWidget_windspeed->columnCount();k++){cell = worksheet->querySubObject("Cells(int,int)", j, k);cell->dynamicCall("SetValue(const QString&)",ui->tableWidget_windspeed->item(j-2,k-1)->text()+ "\t");}}/*将生成的Excel文件保存到指定目录下*/workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(fileName)); //保存至fileNameworkbook->dynamicCall("Close()");                                                   //关闭工作簿excel->dynamicCall("Quit()");                                                       //关闭exceldelete excel;excel = NULL;if (QMessageBox::question(NULL,QString::fromUtf8("完成"),QString::fromUtf8("文件已经导出,是否现在打开?"),QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes){QDesktopServices::openUrl(QUrl("file:///" + QDir::toNativeSeparators(fileName)));}}}}else{/*----------显示下载取消消息框----------*/QMessageBox *msgBox;msgBox = new QMessageBox("Tips","下载已取消!",QMessageBox::Warning,QMessageBox::Ok | QMessageBox::Default,QMessageBox::Cancel| QMessageBox::Escape,0);msgBox->show();}
}/*-----------------选项锁定-------------------*/
void MyWidget::choose_change(bool args)
{ui->comboBox_A_com->setEnabled(args);ui->comboBox_AA_com->setEnabled(args);ui->comboBox_B_com->setEnabled(args);ui->comboBox_BB_com->setEnabled(args);ui->radioButton_speedave->setEnabled(args);ui->radioButton_speedcur->setEnabled(args);ui->radioButton_speedmax->setEnabled(args);ui->radioButton_speedmin->setEnabled(args);ui->radioButton_m_s->setEnabled(args);ui->radioButton_km_h->setEnabled(args);ui->radioButton_40->setEnabled(args);ui->radioButton_120->setEnabled(args);ui->radioButton_200->setEnabled(args);ui->radioButton_280->setEnabled(args);ui->radioButton_360->setEnabled(args);ui->radioButton_440->setEnabled(args);ui->radioButton_520->setEnabled(args);ui->radioButton_600->setEnabled(args);ui->radioButton_680->setEnabled(args);ui->radioButton_760->setEnabled(args);ui->radioButton_840->setEnabled(args);ui->radioButton_920->setEnabled(args);ui->radioButton_1000->setEnabled(args);ui->radioButton_CFM->setEnabled(args);ui->radioButton_CMM->setEnabled(args);
}

系统初始化与槽函数

void MyWidget::system_init()
{/*---------------智能识别系统当前可用串口----------------*/QList<QSerialPortInfo> serialPortInfo = QSerialPortInfo::availablePorts();int count = serialPortInfo.count();for (int i=0;i<count;i++) {ui->comboBox_A_com->addItem(serialPortInfo.at(i).portName());ui->comboBox_AA_com->addItem(serialPortInfo.at(i).portName());ui->comboBox_B_com->addItem(serialPortInfo.at(i).portName());ui->comboBox_BB_com->addItem(serialPortInfo.at(i).portName());}/*---------------智能识别添加串口----------------*///必须各串口均打开才能正常运行,否则将发生程序崩溃ui->comboBox_A_com->setCurrentText(serialPortInfo.at(0).portName());ui->comboBox_AA_com->setCurrentText(serialPortInfo.at(1).portName());ui->comboBox_B_com->setCurrentText(serialPortInfo.at(2).portName());ui->comboBox_BB_com->setCurrentText(serialPortInfo.at(3).portName());/*---------------表格设置----------------*/ui->tableWidget_windspeed->setRowCount(13);ui->tableWidget_windspeed->setColumnCount(6);ui->tableWidget_windspeed->verticalHeader()->setVisible(false);ui->tableWidget_windspeed->horizontalHeader()->setVisible(true);ui->tableWidget_windspeed->horizontalHeader()->setStyleSheet("QHeaderView::section{background:silver;}");for(int i =0;i<13;i++){ui->tableWidget_windspeed->item(i,0)->setBackground(QBrush(QColor(192,192,192)));}ui->tableWidget_windspeed->setAlternatingRowColors(true);ui->tableWidget_windspeed->setStyleSheet("selection-background-color:lightblue;");/*------------connect----------------*/connect(&portA, SIGNAL(readyRead()), this, SLOT(on_port_A_readyRead()));connect(&portAA, SIGNAL(readyRead()), this, SLOT(on_port_AA_readyRead()));connect(&portB, SIGNAL(readyRead()), this, SLOT(on_port_B_readyRead()));connect(&portBB, SIGNAL(readyRead()), this, SLOT(on_port_BB_readyRead()));
}

上位机演示视频

  关于此上位机的演示视频,可点击该链接:上位机演示视频

总结

  基于QT软件开发风速仪上位机,实现多合一,即同时检测多组风速数据,同时提供下载功能,通过实践检验,该上位机稳定可靠。欢迎大家交流。

QT设计风速仪上位机实例(串口读写数据处理数据下载)相关推荐

  1. 上位机通过串口获取单片机数据

    上位机通过串口获取单片机数据(C#) 需求 准备 功能设计 界面设计 关键程序 打开串口 发送数据 接收数据 实现页面 完整源码 后续 需求 我们在平时使用单片机制作项目时,总是希望能够实时显示一些数 ...

  2. 从零开始编写一个上位机(串口助手)QT Creator + Python

    提示:本博客作为学习笔记,有错误的地方希望指正,此文可能会比较长,作为学习笔记的积累,希望对来着有帮助.   绪论:笔者这里使用的是QTCreator和Python来实现一个简单的串口上位机的开发的简 ...

  3. 从零开始编写一个上位机(串口助手)QT Creator + C++

    提示:本博客作为学习笔记,有错误的地方希望指正,此文可能会比较长,作为学习笔记的积累,希望对来着有帮助.   绪论:笔者这里使用的是QTCreator和C++来实现一个简单的串口上位机的开发的简单过程 ...

  4. Qt上位机软件串口通讯

    录了一个串口讲解的视频,分别放在了抖音和B站,大家可以看看,主要讲了一下原理, B站我的主页:laorenshen的个人空间_哔哩哔哩_Bilibili 视频源码下载免费:Qt上位机软件串口通讯,视频 ...

  5. 服务器网页版上位机设计 - 03 - 上位机 (完结)

    服务器网页版上位机设计 03 上位机 (完结) 本设计主要涉及三个方面: 服务器,网页版,上位机. 书接上回,介绍完网页页面的设计,现在来说说上位机的功能设计. 也就是js文件的内容编写. 1.获取h ...

  6. 最简单DIY基于ESP32CAM的物联网相机系统⑤(用C#上位机实现串口图传)

    第一篇:最简单DIY基于ESP32CAM的物联网相机系统①(用网页实现拍照图传) 第二篇:最简单DIY基于ESP32CAM的物联网相机系统②(在JAVAWEB服务器实现图片查看器) 第三篇:最简单DI ...

  7. C#上位机开发串口通信编程——倒计时器开发

    C#上位机开发串口通信编程--倒计时器开发 一.介绍 这是我按照B站上的一个上位机开发视频教程开发的倒计时器开发,本来只有开始计时功能,没有停止计时功能,停止计时功能后面我自己添加了. 视频网址:C# ...

  8. 基于STM32C8T6、ESP8266-01S、JavaWeb、JSP、Html、JavaScript、Android、服务器和客户端设计、上位机和下位机设计等技术融合的物联网智能监控系统设计与实现

    系列文章目录 第一章ESP8266的java软件仿真测试 第二章ESP8266硬件与软件测试 第三章ESP8266客户端与Java后台服务器联调 第四章ESP8266客户端与JavaWeb服务器联调 ...

  9. Qt开发的上位机 硬件:固高八轴运动控制卡,海康威视相机,金橙子板卡,喷码机

    Qt开发的上位机 硬件:固高八轴运动控制卡,海康威视相机,金橙子板卡,喷码机 功能:激光镭射,喷码机,光学点定位,二维码读码与等级评测,MES系统对接,多语言切换,多样式切换. 喷码机:多米诺,lin ...

  10. C#上位机:串口通讯

    C#上位机:串口通讯 基本介绍 参数配置 串口开关与检测 数据发送 数据接收 相关功能函数 基本介绍 语言与开发技术: C#.Winform. 串口通讯是上位机的基础功能,可以通过USB等COM串口进 ...

最新文章

  1. ICPC2008哈尔滨-A-Array Without Local Maximums
  2. 六十.完全分布式 、 节点管理 、 NFS网关
  3. Java SPI机制分析
  4. Java工程师知识图谱
  5. CTF-RSA解密脚本
  6. 3DMax的OFusion插件的使用问题
  7. laravel部署到服务器显示Permission denied
  8. 软件工程 - 设计模式学习之工厂方法模式Factory Method
  9. 2009年存储市场10大预测 厂商并购在所难免
  10. Install and Run Docker on NON ROOT
  11. oracle获取sysdba权限,Oracle 学习笔记: SYSDBA登陆权限问题
  12. 【建站教程】如何快速搭建自己独立的个人博客?
  13. 思科 ASA5505 防火墙放行流量简单配置案例
  14. (保姆式教程:从下数据到画图)python如何利用EOF分析SSTA海温异常现象并画图
  15. 微信图片盗链php,微信公众号图片如何实现反防盗链接
  16. 游戏策划试题(1)——摘自牛客网
  17. dell进入u盘启动模式_如何调整戴尔电脑硬盘模式设置U盘第一启动
  18. Unity3d Ugui 20 Grid Layout Group Aspect Ratio Fitter
  19. 【Python课程作业】食物数据的爬取及分析(详细介绍及分析)
  20. CentOS7.5 重装 SSH 与 禁止网卡休眠

热门文章

  1. 多任务学习之MRI重建(Reconstruction)与超分辨率(Super-Resolution)论文阅读
  2. IT蚁族:蜗居和逃离
  3. python数据分析实验报告_数据聚合
  4. 题解 P1774 【最接近神的人_NOI导刊2010提高(02)】
  5. nfs总结之工作原理
  6. springboot自定义start解析(start中配置从数据源)
  7. Adobe Dreamweaver CS6(或者CC 2018.2 SP)安装失败解决方案
  8. 计算机切换用户快捷键,电脑操作中如何快速切换用户?
  9. PDF文件怎么转Word文档?教你两种转换方法
  10. 在我眼中的生活的苦难