Real-time temperature transmission system based on Qt

introduction

This program is based on the upper computer made of Qt, which monitors the temperature change of the lower computer in real time. The lower computer transmits the temperature to the upper computer through the serial port. After receiving the temperature, the upper computer draws the change graph of temperature and time, and displays it on the right The temperature corresponding to each time. The specific value of the temperature can be displayed below.
The program uses QCustomPlot for drawing.

Host computer design

head File

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPen>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QFileDialog>
#include "qcustomplot.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void init();                            //initialization

    void insert(QString date,QString t);    //table insert data

private slots:
    void setAxisRange(QCPRange);//x,y axis scaling or movement will not appear negative values

    void receData();            //Receive data slot function

    void on_searchButton();     //Search Serial Slot Functions

    void on_openButton();       //Open the serial slot function

    void on_closeButton();      //Close the serial port slot function

    void on_startButton();      //start plotting slot function

    void on_stopButton();       //stop drawing slot function

    void on_exportButton();     //export slot function

    void on_saveButton();       //save data slot function

    void on_clearButton();      //empty text slot function

    void drawShow();            //plot and display data

private:
    Ui::MainWindow *ui;

    QPen pen;
    QSerialPort *serialPort;
    qreal temperature = 0;      //get temperature
    QTimer *qtimer;             //timer
};
#endif // MAINWINDOW_H

ui interface design

initialization work

The drawing interface QCustomPlot has been written before, so I won't add it here. Here mainly write the initialization of LCD control and form control.

//  ***********LCD initialization**********
    ui->tempNumber->setDigitCount(5);                   //Set the number of digits
    ui->tempNumber->setMode(QLCDNumber::Dec);           //decimal mode
    ui->tempNumber->setSmallDecimalPoint(true);         //show decimal point
    ui->tempNumber->setSegmentStyle(QLCDNumber::Flat);  //display appearance
//  ***********Form initialization**********
    ui->dataTable->setColumnCount(2);                   //set to 2 columns
    ui->dataTable->setAlternatingRowColors(true);       //set interlaced color
    ui->dataTable->setSelectionBehavior(QAbstractItemView::SelectRows);//Select the entire row
    //set header
    QStringList header;
    header << tr("time") << tr("temperature");
    ui->dataTable->setHorizontalHeaderLabels(header);
    ui->dataTable->setColumnWidth(0,80);               //Set the column width of the first column
    ui->dataTable->setColumnWidth(1,80);                //set the width of the second column

button state initialization

    ui->openBtn->setEnabled(true);
    ui->closeBtn->setEnabled(false);
    ui->startBtn->setEnabled(false);
    ui->stopBtn->setEnabled(false);

Serial port function design

The functions of the serial port are all in the button control, here is the slot function code of the button directly.

//Search Serial Slot Functions
void MainWindow::on_searchButton()
{
    int count = ui->portCom->count();
    if(count == 0){
        QStringList serialPortName;
        foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()){
            serialPortName.append(info.portName()); //Add available serial port name
        }
        ui->portCom->addItems(serialPortName);
    }
}
//Open the serial port button slot function
void MainWindow::on_openButton()
{
    //baud rate
    QSerialPort::BaudRate baudRate = QSerialPort::Baud115200;
    //data bit
    QSerialPort::DataBits dataBits = QSerialPort::Data8;
    //stop bit
    QSerialPort::StopBits stopBits = QSerialPort::OneStop;
    //Record selected parameters
    QString baudText = ui->baudCom->currentText();
    QString dataText = ui->dataCom->currentText();
    QString stopText = ui->stopCom->currentText();

    if(baudText == "115200")    baudRate = QSerialPort::Baud115200;
    else if(baudText == "4800")    baudRate = QSerialPort::Baud4800;
    else if(baudText == "9600")    baudRate = QSerialPort::Baud9600;

    if(dataText == "8") dataBits = QSerialPort::Data8;
    else if(dataText == "5") dataBits = QSerialPort::Data5;
    else if(dataText == "6") dataBits = QSerialPort::Data6;
    else if(dataText == "7") dataBits = QSerialPort::Data7;

    if(stopText == "1") stopBits = QSerialPort::OneStop;
    else if(stopText == "1.5") stopBits = QSerialPort::OneAndHalfStop;
    else if(stopText == "2") stopBits = QSerialPort::TwoStop;

    //Set serial port parameters
    serialPort->setPortName(ui->portCom->currentText());
    serialPort->setBaudRate(baudRate);
    serialPort->setDataBits(dataBits);
    serialPort->setStopBits(stopBits);
    serialPort->setParity(QSerialPort::NoParity);

    if(serialPort->open(QIODevice::ReadWrite) == true){
        QMessageBox::information(this,"hint","The serial port is opened successfully!");
        //change button state
        ui->openBtn->setEnabled(false);
        ui->closeBtn->setEnabled(true);
        ui->startBtn->setEnabled(true);
        ui->stopBtn->setEnabled(true);
    }else{
        QMessageBox::warning(this,"hint","Serial port opening failed!");
    }  
}
//Close the serial port button slot function
void MainWindow::on_closeButton()
{
    serialPort->close();        //close the serial port
    qtimer->stop();             //off timer
    QMessageBox::information(this,"hint","The serial port is closed!");
    ui->openBtn->setEnabled(true);
    ui->closeBtn->setEnabled(false);
    ui->startBtn->setEnabled(false);
    ui->stopBtn->setEnabled(false);
}

When the serial port receives the temperature data transmitted from the lower computer, it needs to use the signal and slot mechanism to receive it, as follows:

//Receive serial data
    connect(serialPort,SIGNAL(readyRead()),this,SLOT(receData()));
//Receive data slot function
void MainWindow::receData()
{   
    QByteArray buffer = serialPort->readAll();
    if(!buffer.isEmpty()){
        QString s = tr(buffer);
        temperature = s.toDouble()/100;
        ui->tempNumber->display(temperature);
    }
    buffer.clear();     //clear cache
}

table insert data

//table insert data
void MainWindow::insert(QString date, QString t)
{
    update();
    int row_count = ui->dataTable->rowCount();  //Get the total number of rows, insert a row based on this
    ui->dataTable->insertRow(row_count);        //insert row
    QTableWidgetItem *item0 = new QTableWidgetItem();
    QTableWidgetItem *item1 = new QTableWidgetItem();
    item0->setText(date);
    item1->setText(t);
    ui->dataTable->setItem(row_count,0,item0);
    ui->dataTable->setItem(row_count,1,item1);
}

save table data

//save data slot function
void MainWindow::on_saveButton()
{
    QString filename = QFileDialog::getSaveFileName();
    //file name
    QDateTime time = QDateTime::currentDateTime();  //Get the current time of the system
    QString date = time.toString("MM.dd.hh.mm.ss");          //Set display format
    filename += date;
    filename += ".txt";
    //Create file object
    QFile file(filename);
    if(!file.open(QFile::WriteOnly | QFile::Text)){ //write-only
        QMessageBox::warning(this,tr("double file edit"),tr("no write").arg(filename).arg(file.errorString()));
        return;
    }
    //Create a file stream object
    QTextStream out(&file);
    out << "time" << "\t\t" << "temperature" << "\n";
    //traverse data
    int romcount = ui->dataTable->rowCount();   //Get the total number of rows
    for(int i = 0;i < romcount;i++){
        QString rowstring;
        for(int j = 0;j < 2;j++){
            rowstring += ui->dataTable->item(i,j)->text();
            rowstring += "      ";
        }
        rowstring += "\n";
        out << rowstring;
    }
    file.close();   //close file
}

Clear form data

//empty text slot function
void MainWindow::on_clearButton()
{
    while(ui->dataTable->rowCount()){
        ui->dataTable->removeRow(0);
    }
}

start drawing

//start plotting slot function
void MainWindow::on_startButton()
{
    qtimer->start();
    ui->startBtn->setEnabled(false);
    ui->stopBtn->setEnabled(true);
}

pause drawing

//stop drawing slot function
void MainWindow::on_stopButton()
{
    qtimer->stop();
    ui->startBtn->setEnabled(true);
    ui->stopBtn->setEnabled(false);
}

graphic design

//plot and display data
void MainWindow::drawShow()
{
    QDateTime time = QDateTime::currentDateTime();  //Get the current system time
    qreal key = time.toMSecsSinceEpoch()/1000;
    static qreal lastPointKey = 0;
    if(key - lastPointKey > 0.2){   //Add a data at most every 2ms
        ui->customplot->graph(0)->addData(key,temperature);//adding data
        lastPointKey = key;
    }

    ui->customplot->xAxis->setRange(key,10,Qt::AlignRight);//Set the x-axis range, the number of display is 10
    ui->customplot->replot(QCustomPlot::rpQueuedReplot);

    QString date = time.toString("hh:mm:ss");          //Set display format
    QString tempe = QString("%1").arg(temperature);
    insert(date,tempe);

}

Export temperature change graph

//export slot function
void MainWindow::on_exportButton()
{
    QString filename = QFileDialog::getSaveFileName();
    if(filename == ""){
        QMessageBox::warning(this,"fail","Save failed!");
        return;
    }
    QMessageBox::information(this,"success","Saved successfully!");
    ui->customplot->savePng(filename.append(".png"),ui->customplot->width(),ui->customplot->height());
}

timer

The drawing and data display are operated by timers, so the timer object must be created first, and the drawing and data display can be performed through the overflow event of the timer. as follows:

qtimer = new QTimer;                //Create a timer object
qtimer->setInterval(1000);          //Set the timing interval to 1s
connect(qtimer,SIGNAL(timeout()),this,SLOT(drawShow()));

Lower computer design

The board of the lower computer is stm32f103zet6. The main operation of the lower computer is to initialize and use the serial port, and the temperature is obtained by using the internal temperature sensor of stm32, which is ADC16.

Delay function design

The delay.h code is as follows:

#ifndef __DELAY_H
#define __DELAY_H

#include "stm32f10x.h"

static u8  fac_us=0;							//us delay multiplier			   
static u16 fac_ms=0;							//ms delay multiplier
							
void delay_init(void);						//delayed initialization
void delay_us(u32 nus);						//microsecond delay function
void delay_ms(u16 nms);						//Millisecond delay function


#endif

The delay.c code is as follows:

#include "delay.h"

void delay_init(void)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//Select external clock HCLK/8
	fac_us=SystemCoreClock/8000000;				//1/8 of the system clock
	fac_ms=(u16)fac_us*1000;					//Under non-OS, it represents the number of systick clocks required per ms
	
}

//Delay nus
//nus is the number of us to be delayed.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 					//time to load	  		 
	SysTick->VAL=0x00;        					//clear counter
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//start counting down	  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//waiting time to arrive   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//off counter
	SysTick->VAL =0X00;      					 //clear counter	 
}
//Delay nms
//Pay attention to the range of nms
//SysTick->LOAD is a 24-bit register, so the maximum delay is:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK unit is Hz,nms unit is ms
//Under the condition of 72M, nms<=1864 
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;				//Time loading (SysTick->LOAD is 24bit)
	SysTick->VAL =0x00;							//clear counter
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//start counting down  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//waiting time to arrive   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//off counter
	SysTick->VAL =0X00;       					//clear counter	  	    
} 

Serial port design

usart.h code is as follows:

#ifndef __USART_H
#define __USART_H

#include "stm32f10x.h"
#include "stdio.h"

//Serial port 1-USART1
#define	DEBUG_USARTx												USART1
#define DEBUG_USART_CLK											RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd							RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE								115200

//USART GPIO pin macro definition
#define DEBUG_USART_GPIO_CLK								(RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd					RCC_APB2PeriphClockCmd

#define DEBUG_USART_TX_GPIO_PORT						GPIOA
#define DEBUG_USART_TX_GPIO_PIN							GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT						GPIOA
#define DEBUG_USART_RX_GPIO_PIN							GPIO_Pin_10

#define DEBUG_USART_IRQ											USART1_IRQn
#define DEBUG_USART_IRQHandler							USART1_IRQHandler

//static void NVIC_Configuration(void); //Interrupt initialization
void USART_Config(void);										//Serial port initialization
void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data);//send a byte of data
void Usart_SendHalfWord(USART_TypeDef* pUSARTx,uint16_t data);//Send two bytes of data
void Usart_SendStr(USART_TypeDef* pUSARTx,uint8_t *str);//send string

#endif

usart.c code is as follows:

//Serial port initialization
void USART_Config(void)
{
	GPIO_InitTypeDef	GPIO_InitStructure;
	USART_InitTypeDef	USART_InitStructure;
	
	//Open serial port GPIO clock
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK,ENABLE);
	//Open the serial port peripheral clock
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK,ENABLE);
	//Configure the GPIO of USART TX as push-pull multiplexing mode
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT,&GPIO_InitStructure);
	//Configure the GPIO of USART RX as floating input mode
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStructure);
	
	//Configure the working parameters of the serial port
	//Configure baud rate
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	//Configuration pin data word length
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	//configure stop bits
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	//Configuration check digit
	USART_InitStructure.USART_Parity = USART_Parity_No;
	//Configure hardware flow control
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	//Configure the working mode, send and receive together
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	//Complete the serial port initialization configuration
	USART_Init(DEBUG_USARTx,&USART_InitStructure);
	
//	//Serial port interrupt priority configuration
//	NVIC_Configuration();
//	//Enable serial port receive interrupt
//	USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,ENABLE);
	
	//enable serial port
	USART_Cmd(DEBUG_USARTx,ENABLE);
}
//send a byte of data
void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data)
{
	USART_SendData(pUSARTx,data);
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE) == RESET);
}
//Redirects the fputc function of the C library function to use the putchar function
int fputc(int ch,FILE* f)
{
	//send a byte to the serial port
	USART_SendData(DEBUG_USARTx,(uint8_t)ch);
	//wait for send
	while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TXE) == RESET);
	return (ch);
}

Internal Temperature Sensor Design

The temperature.h code is as follows:

#ifndef __TEMPERATURE_H
#define __TEMPERATURE_H

#include "stm32f10x.h"
#include "delay.h"

void T_Adc_Init(void);								//ADC channel initialization
u16 T_Get_Adc(u8 ch);									//Get ADC value
u16 T_Get_Temp(void);									//Get the value of the internal temperature sensor sampled by the ADC
u16 T_Get_Adc_Average(u8 ch,u8 times);//Get the conversion value of the channel ch
short Get_Temperate(void);						//get temperature value

#endif

The temperature.c code is as follows:

#include "temperature.h"

//ADC initialization
void T_Adc_Init(void)
{
	ADC_InitTypeDef ADC_InitStructure;
	//Enable GPIOA, ADC1 channel clock
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);		//The frequency division factor 6 clock is 72M/6 = 12MHz
	ADC_DeInit(ADC1);										//Reset all registers of peripheral ADC1 to default values
	
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;			//Set ADC to work in independent mode
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;						//Analog-to-digital conversion works in single-channel mode
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;			//Analog-to-digital conversion works in single conversion mode
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//Conversion initiated by software rather than external trigger
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC data right justified
	ADC_InitStructure.ADC_NbrOfChannel = 1;									//Number of ADC channels sequentially performing regular conversions
	ADC_Init(ADC1,&ADC_InitStructure);											//Initialize the registers of the peripheral ADCx
	
	ADC_TempSensorVrefintCmd(ENABLE);						//Turn on the internal temperature sensor
	ADC_Cmd(ADC1,ENABLE);												//Enable ADC1
	ADC_ResetCalibration(ADC1);									//Reset the reset register of the specified ADC1
	while(ADC_GetResetCalibrationStatus(ADC1));	//Get the status of the ADC1 reset calibration register, set the status and wait
	ADC_StartCalibration(ADC1);			
	while(ADC_GetCalibrationStatus(ADC1));			//Get the calibration program of the specified ADC1, set the status and wait
}
//Get ADC value
u16 T_Get_Adc(u8 ch)
{
	//Channel 3 of ADC1, first conversion, sampling time is 239.5 cycles
	ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_239Cycles5);	
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);				//Enable the software conversion start function of the specified ADC1
	while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));	//Wait for the conversion to complete
	return ADC_GetConversionValue(ADC1);					//Returns the conversion result of the latest ADC1 rule group
}

//Get the value of the internal temperature sensor sampled by the ADC
//Take 10 times and average
u16 T_Get_Temp(void)
{
	u16 temp_val = 0;
	u8 t;
	for(t = 0;t < 10;t++){
		temp_val += T_Get_Adc(ADC_Channel_16);
		delay_ms(5);
		return temp_val/10;
	}
}	

//Get the conversion value of the channel ch
//Take times times, then average
u16 T_Get_Adc_Average(u8 ch,u8 times)
{
	u32 temp_val = 0;
	u8 t;
	for(t = 0;t < times;t++){
		temp_val += T_Get_Adc(ch);
		delay_ms(5);
	}
	return temp_val/times;
}

//get temperature value
short Get_Temperate(void)
{
	u32 adcx;
	short result;
	double temperate;
	adcx = T_Get_Adc_Average(ADC_Channel_16,20);	//Read channel 16, take the average of 20 times
	temperate = (float)adcx*(3.3/4096);						//Voltage value
	temperate = (1.43 - temperate)/0.0043 + 25;		//Convert to temperature value
	result += temperate*=100;											//Expand 100 times
	return result;
}	

main function

while(1){
		temp = Get_Temperate();	//get temperature value
		printf("%d",temp);
		delay_ms(3000);
	}

end

Friends who need source code can contact me!

Tags: stm32 Qt

Posted by Jezz on Sat, 04 Feb 2023 03:07:43 +0530