树莓派3B Qt 多线程测温测距(12)

内容

利用超声测距(hc-sr04)和温湿度检测(dht11)来判断人的靠近对环境温度造成的影响,结果状态以文字形式显示于文本框,温湿度曲线用qchart绘制。
项目全部代码在此:monitor_lx_code.zip 提取码:0ooc
或者愿意用c币支持的朋友在这里:monitor_lx_code.zip
(两个资源都是一样的,百度云那个免费下载)

界面

温湿度曲线、温湿度坐标轴、当前温湿度距离显示、当前状态:温湿度读取情况、有没有人靠近、正常记录的次数、温湿度状态(上升)下降、无变化)、数据开始获取按钮、暂停按钮等。
在这里插入图片描述

大致结构

主线程分析数据、绘制图像、写数据库;
一个线程测温;
一个线程测距。

使用多线程的原因

温湿度检测和距离检测要同时进行,而且根据dht11的特性要间隔两秒以上读取一次数据,所以如果放在主线程的话只能两秒读取一次数据。而当人移动较快时,超声传感器两秒检测一次是不能检测到人的经过的。所以在一个线程中2s一次获取温湿度数据,在另一个线程中每100ms检测一次距离一次判断是否有人。

一个简单多线程样例

样例来自:如何使用QT5实现多线程(一)
点击button后,在主线程开启定时器和启动线程,定时器每100ms更新一次LCD number。新线程启动后5s发出信号给主线程,主线程收到信号后停止定时器。
下面是实现步骤:

1、新建一个qt工程;

2、添加 一个类thread_one(名字任意);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
添加代码:
thread_one.h

#ifndef THREAD_ONE_H
#define THREAD_ONE_H

#include <QThread>

class thread_one : public QThread
{
    Q_OBJECT
public:
    explicit thread_one(QObject *parent = nullptr);
protected:
    void run();//多线程执行的内容将通过重新该虚函数实现
signals:
    void over1();//发出的信号
public slots:
};

#endif // THREAD_ONE_H

thread_one.cpp

#include "thread_one.h"

thread_one::thread_one(QObject *parent) : QThread(parent)
{
}
void thread_one::run()
{
    sleep(5);//模拟一个时长5s的复杂函数
    emit over1();//复杂函数结束后发出信号
}

3、在ui里添加一个LCD Number和一个button

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、mainwindow

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include<QTimer>
#include<QThread>
#include <QWidget>
#include"thread_one.h"
#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void dealclicked();
    void dealtimeout();
private slots:
    void dealover();//处理新线程返回的结束信号

private:
    Ui::MainWindow *ui;
    QTimer *timer;
    thread_one *thread;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    timer = new QTimer(this);
    thread = new thread_one(this);

    connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::dealclicked);//按下按钮后执行dealclicked()槽函数


    connect(timer,&QTimer::timeout,this,&MainWindow::dealtimeout);//根据定时器发出的信号更新LCD显示器的数字
    connect(thread,&thread_one::over1,this,&MainWindow::dealover);//当开辟的线程内的复杂函数执行完后,发出over信号,接收到该信号后便停下计时器
}

MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::dealclicked()
{
    timer->start(100);//启动计时器,每0.1秒发出一次信号
    thread->start();//QThread 的对象通过start()函数调用线程文件中的run()函数
}
void MainWindow::dealtimeout()//更新LCD显示器的数字
{
    static int time = 0;
    ui->lcdNumber->display(time);
    time++;
}
void MainWindow::dealover()//接收到信号后停下计时器
{
    timer->stop();
}

结果:
在这里插入图片描述

1、配置Qt chart

见此:树莓派3B Qt Chart安装与使用(11)

2、多线程

先新建一个qt工程。
先列出最后项目下所有文件
在这里插入图片描述
pro文件

#-------------------------------------------------
#
# Project created by QtCreator 2020-05-30T16:32:58
#
#-------------------------------------------------

QT       += core gui sql
QT       += charts

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = monitor_lx
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

CONFIG += c++11

SOURCES += \
        main.cpp \
        mainwindow.cpp \
    dht11_lx.cpp \
    thread_1.cpp \
    thread_2.cpp \
    hcsr04_lx.cpp

HEADERS += \
        mainwindow.h \
    dht11_lx.h \
    thread_1.h \
    thread_2.h \
    hcsr04_lx.h

FORMS += \
        mainwindow.ui

LIBS     += -lwiringPi

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

1、线程1,获取温湿度数据

新建一个类thread_1,从QThread继承,主要通过虚函数run()和信号函数over()实现线程的运行和信号的发出

thread_1.h

#ifndef THREAD_1_H
#define THREAD_1_H
#include "dht11_lx.h"
#include <QThread>

class thread_1 : public QThread
{
    Q_OBJECT
public:
    explicit thread_1(QObject *parent = nullptr);
    volatile bool finishflag=0;
    volatile bool thread1_start=0;
    volatile bool checkerrorflag=0;
    volatile double out_hum;
    volatile double out_tem;

    dht11_lx dht11;
protected:
    void run();//多线程执行的内容将通过重新该虚函数实现
signals:
    void over1();
public slots:
};
#endif // THREAD_ONE_H

在cpp里面写run()函数需要做的事情,这里是通过写好的dht11的类来读取dht11数据;读取完成后发出完成一次读取的信号over1,主线程与此信号建立函数关系连接,收到信号后执行相应的数据处理函数;每次读取前要通过完成主线程对应数据处理完成标志finishflag来等待上一次数据处理完成,再进行数据读取;为保证dht11正常运行,读取一次就delay(2000),目的是每2s读取一次;再通过while判断thread1_start标志位来让读取一直进行,标志位由主线程的开始和暂停按钮控制改变;

thread_1.cpp

#include "thread_1.h"

thread_1::thread_1(QObject *parent) : QThread(parent)
{
    dht11.pininit();//引脚初始化
}
void thread_1::run()
{
//out_tem=20;
    finishflag=1;
    while(thread1_start==1)
    {
        while(finishflag==0);//wait for work in mian thread done.
        uchar buffer[5];
        buffer[0]=buffer[1]=buffer[2]=buffer[3]=buffer[4]=0;
        if (dht11.dht11_read_data(buffer) == 0)
        {
                out_hum = buffer[0] + buffer[1] / 10.0;//整数位+小数位
                out_tem = buffer[2] + buffer[3] / 10.0;
                checkerrorflag=0;
        }
            else
            {
                checkerrorflag=1;
            }
        emit over1();
        delay(2000);
    }
}

dht11_lx.h

#ifndef DHT11_LX_H
#define DHT11_LX_H

#include <iostream>
#include <wiringPi.h>

//#define uchar   unsigned char
#define dht11_pin   7	//wiringPi方式的引脚,dht11信号线接引脚7
class dht11_lx
{
public:
    dht11_lx();
//    ~dht11_lx();
    void pininit();
    void dht11_reset(void);
    uint dht11_read_bit(void);
    uint dht11_read_byte(void);
    uint dht11_read_data(unsigned char buffer[5]);
};

#endif // DHT11_LX_H

dht11_lx.cpp

#include "dht11_lx.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
dht11_lx::dht11_lx()
{
    wiringPiSetup();
}

void dht11_lx::pininit()
{

    pinMode(dht11_pin,OUTPUT);//设置Pin 0为输出模式,若为’INPUT‘则为输入模式,编码格式为wiringpi
    digitalWrite( dht11_pin, HIGH );
}

void dht11_lx::dht11_reset(void)
{
//    pinMode( dht11_pin, OUTPUT );
//    digitalWrite( dht11_pin, HIGH );
//    delay(5);
    digitalWrite( dht11_pin, LOW );
    delay(20);
    digitalWrite( dht11_pin, HIGH );
    delayMicroseconds(30);
    pinMode(dht11_pin,INPUT);
    pullUpDnControl(dht11_pin,PUD_UP);

}


uint dht11_lx::dht11_read_bit(void)
{
    uchar count1=0;
    while (digitalRead( dht11_pin ) == 0);
    delayMicroseconds(35);
    if (digitalRead( dht11_pin ) == 1)
    {
//        while (digitalRead( dht11_pin ) == 1);
        while (digitalRead( dht11_pin ) == 1)
        {
            delayMicroseconds(1);
            count1++;
            if(count1>50)
                break;
        }
        return 1;
    }
    else
    {
        return 0;
    }
}

uint dht11_lx::dht11_read_byte(void)
{
    uint i;
    uint data = 0;
    for (i = 0; i < 8; i++)
    {
        data <<= 1;
        data |= dht11_read_bit();
    }
    return data;
}

uint dht11_lx::dht11_read_data(uchar buffer[5])
{
    uchar count2=0;
    uint i = 0;
    uchar checksum;
    dht11_reset();
    if (digitalRead( dht11_pin ) == 0)
    {

        while (digitalRead( dht11_pin ) == 0);
        while (digitalRead( dht11_pin ) == 1);
        for (i = 0; i < 5; i++)
        {
            buffer[i] = dht11_read_byte();
        }

        while (digitalRead( dht11_pin ) == 0);
        pinMode( dht11_pin, OUTPUT );
        digitalWrite( dht11_pin, HIGH );

        checksum = buffer[0] + buffer[1] + buffer[2] + buffer[3];
        if (checksum != buffer[4])
        {
            // checksum error
            return 1;
        }
    }

    return 0;
}

2、线程2,获取距离数据

同理,在线程2的run函数里面使用写好的类来获取距离信息:
thread_2.h

#ifndef THREAD_2_H
#define THREAD_2_H
#include "hcsr04_lx.h"
#include <QThread>

class thread_2 : public QThread
{
    Q_OBJECT
public:
    explicit thread_2(QObject *parent = nullptr);
    volatile bool finishflag2=0;
    volatile bool errorflag2=0;
    volatile bool thread2_start=0;
    float out_distance;
    hcsr04_lx hcsr04;

protected:
    void run();//多线程执行的内容将通过重新该虚函数实现
signals:
    void over2();
public slots:
};

#endif // THREAD_2_H

thread_2.cpp

#include "thread_2.h"

thread_2::thread_2(QObject *parent) : QThread(parent)
{
    hcsr04.pininit();//引脚初始化
}

void thread_2::run()
{

    finishflag2=1;
    while(thread2_start==1)
    {
        while(finishflag2==0);//wait for work in mian thread done.
        errorflag2=hcsr04.hcsr04_read_data(&out_distance);
        while(errorflag2==1)
        {
            errorflag2=hcsr04.hcsr04_read_data(&out_distance);
        }

        msleep(100);
        emit over2();
    }

}

hcsr04_lx.h

#ifndef HCSR04_LX_H
#define HCSR04_LX_H
#include <iostream>
#include <wiringPi.h>
#include <QTime>
#include <QThread>
#define trig_pin   28	//wiringPi方式的引脚,dht11信号线接引脚7
#define echo_pin   29	//wiringPi方式的引脚,dht11信号线接引脚7

class hcsr04_lx
{
public:
    hcsr04_lx();

    void pininit();
    bool hcsr04_read_data(float *distance);

};

#endif // HCSR04_LX_H

hcsr04_lx.cpp

#include "hcsr04_lx.h"
#include <sys/time.h>


hcsr04_lx::hcsr04_lx()
{

}

void hcsr04_lx::pininit()
{
//    wiringPiSetup();
    pinMode(trig_pin,OUTPUT);//设置Pin 为输出模式,若为’INPUT‘则为输入模式,编码格式为wiringpi
    digitalWrite( trig_pin, LOW );
    pinMode(echo_pin,INPUT);
    pullUpDnControl(echo_pin,PUD_UP);
}

bool hcsr04_lx::hcsr04_read_data(float *distance)
{
    struct timeval tv1;
    struct timeval tv2;
    long start, stop;
    int tempcount=0;

    pinMode(trig_pin,OUTPUT);//设置Pin 为输出模式,若为’INPUT‘则为输入模式,编码格式为wiringpi
    digitalWrite( trig_pin, HIGH );
    delayMicroseconds(15);
    digitalWrite( trig_pin, LOW );
//    while (digitalRead( echo_pin ) == 0);
    while (digitalRead( echo_pin ) == 0)
    {
        delayMicroseconds(10);
        if(tempcount++ > 250)
            return  1;
    }

    gettimeofday(&tv1, NULL);
    while (digitalRead( echo_pin ) == 1);

    gettimeofday(&tv2, NULL);

//    double elapsed = startTime.msecsTo(stopTime);//ms
    start = tv1.tv_sec * 1000000 + tv1.tv_usec;   //微秒级的时间
    stop  = tv2.tv_sec * 1000000 + tv2.tv_usec;

    float dis = (float)(stop - start) / 1000000 * 34000 / 2;  //求出距离
    *distance=dis;
    return 0;

}

3、主线程获取数据并处理

1、开启另外两个线程获取测得数据

利用按钮,开启线程
在这里插入图片描述
Start()被调用后,直接转到对应线程的run()函数执行

2、信号关联

主线程定义两个slots槽函数,与两个over函数建立联系,另外两个线程在数据读取完成后分别发出信号给主线程,主线程分别执行两个数据处理函数
在这里插入图片描述
在这里插入图片描述

3、数据处理

定义线程对象,通过访问对象的变量来获取或改变温湿度、距离、标志位等信息
在这里插入图片描述
改变温湿度线程标志位、读取温湿度数据:
在这里插入图片描述
并在获取数据后,对数据进行处理:
–判断检验值是否出错
–判断数据是否在正常范围
–数据正常则调用函数来显示、写入数据库、绘制曲线(state_dis,writedata,drawline)
4、连接数据库、写入数据库

4、数据分析(人对温度影响)

1、温度数据分析

调用readdata函数对数据库的最新5个数据进行读取分析:
让最新的数据与前一个数据求差,差值保存;
保存最新的5个差值,差值求和,如果大于一个正的阈值,那么温度上升,如果小于一个负的阈值,那么温度下降;
设置阈值不直接用大于小于零,是因为相对于昼夜温差变化,人靠近和离开会造成温度变化较快,会在短期内累积差值超过一定阈值,而比如半夜里的降温则很缓慢,短期内的累积差值不会超过阈值,这就判断出了是人的出现造成的温度上升和下降,而不是自然温度变化。
写入数据库的数据包含:时间、温湿度、距离、有没有人、温度上升下降情况
在这里插入图片描述

2、距离数据分析

在距离线程里数据更新是100ms一次,直接取最新的5个数据存入数组并取平均,因为超声传感器就放在电脑旁,所以就判断阈值小于70cm则为人靠近
在这里插入图片描述

5、代码

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <QtCharts/QChartView>//显示图表
#include <QtCharts/QLineSeries>//线系列
#include <QDoubleSpinBox>
#include <QGridLayout>
#include <QTimer>
#include <QtCharts/QSplineSeries>
#include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis>
#include <QtCharts/QDateTimeAxis>

#include<QThread>

#include"thread_1.h"
#include"thread_2.h
QT_CHARTS_USE_NAMESPACE

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    thread_1 thread_dht11;//温湿度读取线程对象
    thread_2 thread_hcsr04;//距离读取线程对象
    ~MainWindow();


private:
    Ui::MainWindow *ui;

    double  hum;//湿度
    double tem;//温度
    double  lasthum;
    double lasttem;
    int records;//成功读取温湿度的次数
    bool drawstart;//绘制曲线标志
    bool humanflag;//有无人的标志
    int tem_changeflag;//温度变化标志


    qreal m_maxY1;//Y轴最大最小值(Y轴范围)
    qreal m_minY1;
    qreal m_maxY2;
    qreal m_minY2;


    QString recordstmp;//成功读取温湿度的次数QString
    int interval;//写入数据库的时间间隔
    double distance[5];//存最新5个距离
    int dis_count;//当前循环到第几个距离数据
    double temperature[6];//前5个是最新的温度数据,temperature[5]是当前循环到第几个温度数据
    double difference[5];//5个最新的温差
    int tem_count;//
    QString S_checksum;//dht11校验:normal、error
    QString S_human;//有没有人:man close、no one
    QString S_temp;//温度状态:rising、normal、falling

    QTimer m_timer;
    QChart chart;//Qchart
//    QLineSeries m_series;
//    QSplineSeries m_series1;//temperature
    QLineSeries m_series1;//temperature
    QLineSeries m_series2;//humidity
    QValueAxis my_axisY1;//Y1轴
    QValueAxis my_axisY2;//Y2轴
    QDateTimeAxis my_axisX;//横轴,时间轴

    qreal m_x;
    qreal m_y1;//y值,用于绘制曲线
    qreal m_y2;
    int axis_max;


    void writedata(double tem,double hum,double distance);
    void readdata();

    void timerEvent( QTimerEvent *event );

    void drawline(QLineSeries *series1, QLineSeries *series2, double temperature, double humidity);
    void state_dis(QString checksum,QString human,QString temp);


private slots:
    void on_pushButton_clicked();//开始按钮
    void on_pushButton_2_clicked();//暂停按钮
    void dht11data();//温湿度数据处理
    void hcsr04data();//距离数据处理
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"

#include <QSplineSeries>
#include <QChartView>
#include <QChart>
QT_CHARTS_USE_NAMESPACE

#include "ui_mainwindow.h"

#include <math.h>
#include <QTimer>
#include <math.h>
#include <QLayout>
#include <math.h>
#include <wiringPi.h>
#include <QSqlDatabase>
#include <QDebug>
#include <QMessageBox>
#include <QSqlError>
#include <QSqlQuery>
#include <QVariantList>

#include <QtSql>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_x=0;
    m_y1=0;
    m_y2=0;
    m_maxY1=0.1;
    m_minY1=0;
    m_maxY2=40;
    m_minY2=80;
    hum=0;//湿度
    tem=0;//温度
    records=0;
    lasthum=0;
    lasttem=0;
    drawstart=0;
    interval=0;
    humanflag=0;
    tem_changeflag=0;
    dis_count=0;
    tem_count=0;
    S_human="no one";
    S_temp="nomal";
    for(int i = 0; i < 5; i++)
    {
        temperature[i]=0;
    }
    for(int i = 0; i < 5; i++)
    {
        difference[i]=0;
    }

    qDebug()<<QSqlDatabase::drivers();
    qDebug()<<QCoreApplication::libraryPaths();//数据库驱动的文件位置
    QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL");//QMYSQL是采用QMYSQL数据库驱动
    db.setHostName("127.0.0.1");

    db.setUserName("noel"); //数据库账号
    db.setPassword("123456");//数据库密码
    db.setDatabaseName("noeldb"); //你的数据库名
    if(!db.open()){
    qDebug()<<"连接数据库失败";
    return;
    }else{
    qDebug()<<"连接数据库成功";
    }

    chart.addSeries(&m_series1);//添加曲线m_series1
    chart.addSeries(&m_series2);

    m_series1.setName("temperature");//设置曲线名称
    m_series2.setName("humidity");
//    m_series1.setColor("red");
    //m_series1.setColor("green");//设置曲线颜色

//    my_axisX.setFormat("h:mm:ss");//设置横轴时间格式(时分秒)
    my_axisX.setFormat("h:mm");//时分

    my_axisY1.setMin(0);//设置Y轴最大最小值
    my_axisY1.setMax(30);

    my_axisY2.setMin(50);
    my_axisY2.setMax(100);


    my_axisY1.setTickCount(15);//将轴划分n格
    my_axisY2.setTickCount(10);
    my_axisX.setTickCount(10);

    my_axisX.setTitleText("time-axis");//轴标题
    my_axisY1.setTitleText("temperature");
    my_axisY2.setTitleText("humidity");

    my_axisY1.setLinePenColor(QColor(Qt::darkBlue));//画笔
    my_axisY1.setGridLineColor(QColor(Qt::darkBlue));

    my_axisY2.setLinePenColor(QColor(Qt::darkGreen));
    my_axisY2.setGridLineColor(QColor(Qt::darkGreen));

    my_axisY1.setGridLineVisible(false);//网格线不可见
    my_axisY2.setGridLineVisible(false);


    QPen penY1(Qt::darkBlue, 3,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin);
    QPen penY2(Qt::darkGreen,3,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin);

    my_axisY1.setLinePen(penY1);
    my_axisY2.setLinePen(penY2);

    chart.addAxis(&my_axisX,Qt::AlignBottom);//添加轴
    chart.addAxis(&my_axisY1,Qt::AlignLeft);
    chart.addAxis(&my_axisY2,Qt::AlignRight);

    m_series1.attachAxis(&my_axisX);
    m_series1.attachAxis(&my_axisY1);

    m_series2.attachAxis(&my_axisX);
    m_series2.attachAxis(&my_axisY2);

    QChartView *chartView = new QChartView(&chart);
    QGridLayout *baseLayout = new QGridLayout(); //便于显示,创建网格布局

    chartView->setRenderHint(QPainter::Antialiasing);

    baseLayout->addWidget(chartView, 0, 0);
    ui->widget->setLayout(baseLayout); //显示到QWidget控件

    connect(&thread_dht11,&thread_1::over1,this,&MainWindow::dht11data);//当开辟的线程内的复杂函数执行完后,发出over信号
    connect(&thread_hcsr04,&thread_2::over2,this,&MainWindow::hcsr04data);//当开辟的线程内的复杂函数执行完后,发出over信号
}

void MainWindow::on_pushButton_clicked()
{
    thread_dht11.dht11.pininit();
    thread_hcsr04.hcsr04.pininit();//引脚初始化

    delay(2000);

    thread_dht11.thread1_start=1;//线程里的 while(thread1_start==1),进入循环
    thread_dht11.start();//线程开始运行
    thread_hcsr04.thread2_start=1;
    thread_hcsr04.start();

}

void MainWindow::on_pushButton_2_clicked()
{

    thread_dht11.thread1_start=0;//停止线程里的while循环
    thread_dht11.finishflag=1;//任务完成标志
    thread_hcsr04.thread2_start=0;
    thread_hcsr04.finishflag2=1;
    QThread::msleep(2500);
    thread_hcsr04.quit();//退出线程
    thread_dht11.quit();

}

void MainWindow::writedata(double tem,double hum,double distance)
{
    //写数据进数据库
        QSqlQuery query;
//        query.exec("drop table data0608_1_lx;");//数据太多了就打开,删除表单dht11data_lx,下一条指令会新建
        //时间、温湿度数据成功读取次数、温度、湿度、距离、人状态(1有人,0无人)、温度状态(1上升、0无变化、-1下降)
        query.exec("create table data0608_3_lx(Time datetime, num int, Temperature double, Humidity double, Distance double, Human bool, Tempflag int);");//新建表单
        QString sqlquery = QObject::tr("insert into data0608_3_lx(Time,num,Temperature,Humidity,Distance,Human,Tempflag) values(now(),'%1','%2','%3','%4','%5','%6')").arg(records).arg(tem).arg(hum).arg(distance).arg(humanflag).arg(tem_changeflag);//插入一个数据
        query.exec(sqlquery);
}

void MainWindow::readdata()
{
    //从数据库读取最新5个数据来分析
    double current_temp=0;
    double temp_difference=0;
    QSqlQuery query;
    bool success = query.exec("select * from data0608_3_lx order by Time desc limit 0,6");//访问最新的(6-1)=5个数据
    if(!success){
        qDebug() << "查询user失败";
    }
    QSqlRecord rec = query.record();
    qDebug() << "user表的字段总数为:" << rec.count();
    query.last();//最后的数据
    while(query.previous()){//往前读取
        //显示当前最新的数据库数据
        qDebug() <<  query.value(0).toString()<<  query.value(1).toString() << query.value(2).toString()<< query.value(3).toString()<< query.value(4).toString()<< query.value(5).toString()<< query.value(6).toString();
        current_temp=query.value(2).toDouble();//数据取出到变量
    }
    temperature[(int)temperature[5]]=current_temp;//写进数组
    //当前温度
    qDebug() <<  "current_temp: "<< QString::number(temperature[(int)temperature[5]])<<QString::number((int)temperature[5]);
    //最新5个温度
    qDebug() <<  "temperature: "<< QString::number(temperature[0])<<QString::number(temperature[1])<<QString::number(temperature[2])<< QString::number(temperature[3])<< QString::number(temperature[4]);

    //计算温差,最新数据减去上一个数据
    if((int)temperature[5]==0)
    {
        temp_difference=temperature[0]-temperature[4];
    }
    else {
        temp_difference=temperature[(int)temperature[5]]-temperature[(int)temperature[5]-1];
    }
    //保存最新温差进数组
    difference[(int)temperature[5]]=temp_difference;
    //当前温差
    qDebug() <<  "current_dif: "<< QString::number(difference[(int)temperature[5]])<<QString::number((int)temperature[5]);
    //
    qDebug() << "difference: "<<QString::number(difference[0])<<QString::number(difference[1])<<QString::number(difference[2])<< QString::number(difference[3])<< QString::number(difference[4]);

    temperature[5]++;

    temp_difference=0;//清零
    for(int i = 0; i < 5; i++)
    {
        temp_difference+=difference[i];//温差累积
    }
    qDebug() << "t_difference: "<<QString::number(temp_difference);//现实累积温差值
    //阈值判定
    if(temp_difference >= 0.3)//大于等于0.3判定为升温
    {
        tem_changeflag=1;
        S_temp=" rising";
        state_dis(S_checksum,S_human,S_temp);
    }
    else if(temp_difference <= -0.3)//小于等于-0.3判定为升降温
    {
        tem_changeflag=-1;
        S_temp=" falling";
        state_dis(S_checksum,S_human,S_temp);
    }
    else{//否则为温度未变化
        tem_changeflag=0;
        S_temp=" normal";
        state_dis(S_checksum,S_human,S_temp);
    }

    if(((int)temperature[5])==5)
    {
        temperature[5]=0;//已更新完5个最新数据,清零再次更新
    }

}

void MainWindow::timerEvent( QTimerEvent *event)
{
    ;
}
void MainWindow::drawline(QLineSeries *series1, QLineSeries *series2, double temperature, double humidity)
{

    m_y1=temperature;//y值
    m_y2=humidity;

    QDateTime bjtime = QDateTime::currentDateTime();//时间

    chart.axisX()->setMin(QDateTime::currentDateTime().addSecs(-28800*1));//8小时,(8×60×60=28800)
    chart.axisX()->setMax(QDateTime::currentDateTime().addSecs(0));

    series1->append(bjtime.toMSecsSinceEpoch(),m_y1);//绘制当前值
    series2->append(bjtime.toMSecsSinceEpoch(),m_y2);

    //曲线移动
    if(series1->count()>10000)
    {
        series1->removePoints(0,series1->count()-10000);
    }
    if(series2->count()>10000)
    {
        series2->removePoints(0,series2->count()-10000);
    }
    //改变温度轴范围在当前值的+5 -5区间
    m_maxY1 = m_y1 + 5;
    m_minY1 = m_y1 - 5;
//    chart.axisY()->setRange(m_minY1, m_maxY1);
    my_axisY1.setMin(m_minY1);
    my_axisY1.setMax(m_maxY1);

    thread_dht11.finishflag=1;//处理完成标志

}
void MainWindow::hcsr04data()
{
    double dis_ava=0;
    thread_hcsr04.finishflag2=0;//开始出处理标志
    while(thread_dht11.finishflag==0);//等待dht11数据处理完毕
    QString distancetmp = QString::number((double)thread_hcsr04.out_distance);//double转QSTRING
    ui->lineEdit_distance->setText(distancetmp);//显示
    distance[dis_count]=(double)thread_hcsr04.out_distance;
    dis_count++;
    if(dis_count==5)
        dis_count=0;

    dis_ava=(distance[0]+distance[1]+distance[2]+distance[3]+distance[4])/5;//取5个最新数据的均值

    if(dis_ava<=70)//阈值判定,小于70则判定有人
    {
        humanflag=1;
        S_human="man close";
    }
    else {
        humanflag=0;
        S_human="no one";
    }
    state_dis(S_checksum,S_human,S_temp);//状态显示
    thread_hcsr04.finishflag2=1;//处理完成标志
}

void MainWindow::state_dis(QString checksum,QString human,QString temp)
{
    //状态显示函数
    ui->lineEdit_state->setText("checksum:"+checksum+"   records:"+recordstmp+"   "+human+"  temperature:"+temp);//dht11校验和错误
}

void MainWindow::dht11data()
{
    thread_dht11.finishflag=0;//开始处理标志
    interval++;//写数据间隔
//    while(thread_hcsr04.finishflag2==0);//wait for hcsr04 done.

    hum=thread_dht11.out_hum;//数据获取到主线程
    tem=thread_dht11.out_tem;

    if(thread_dht11.checkerrorflag==1)//dht11校验值判定
    {
        S_checksum="error";
        state_dis(S_checksum,S_human,S_temp);

        drawstart=0;//不绘图标志
        thread_dht11.dht11.pininit();//重新初始化引脚
        thread_dht11.finishflag=1;//处理完成标志
        return;
    }


    QString humtmp = QString::number(hum);
    ui->lineEdit_hum->setText(humtmp);//显示湿度
    QString temtmp = QString::number(tem);
    ui->lineEdit_tem->setText(temtmp);//显示温度
    //阈值判定,不在阈值内的就是错误数据
    if((tem<=40)&&(tem>=10)&&(hum<=100)&&(hum>=40))
    {
        lasttem=tem;
        lasthum=hum;
        drawstart=1;//可以绘图标志
    }

    else//error data
    {
        drawstart=0;
        ui->lineEdit_state->setText("data error");//
        thread_dht11.dht11.pininit();//重新初始化引脚``````````
        thread_dht11.finishflag=1;
        return;
    }

    records++;//正确读取数据的次数
    recordstmp = QString::number(records);
    S_checksum="normal";
//    ui->lineEdit_state->setText("records:"+recordstmp);
    state_dis(S_checksum,S_human,S_temp);

    thread_dht11.dht11.pininit();
    double distance=(double)thread_hcsr04.out_distance;//读取当前距离值

    /*interval=1 min ,(30×2s) */
    if(interval==1)
    {
        writedata(tem,hum,distance);
        readdata();
    }
    if(interval>=30)
    {
        interval=1;
        writedata(tem,hum,distance);
        readdata();
    }

    if(drawstart==1)
    {
        drawline(&m_series1,&m_series2,tem,hum);//绘制曲线
    }

}

MainWindow::~MainWindow()
{
    delete ui;
    thread_hcsr04.thread2_start=0;//停止线程里面的while循环
    thread_dht11.thread1_start=0;
    QThread::msleep(2500);//等待2.5s,保证线程的while循环已经停止
    thread_hcsr04.quit();//退出线程
    thread_dht11.quit();

}

main.cpp

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

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.ui
在这里插入图片描述

4、界面显示

1、曲线

在ui初始化时,设置两条曲线、两个y轴分别温湿度、x轴为时间轴;
设置曲线的属性,颜色、粗细、线型等等;
曲线随时间移动。

曲线初始化部分代码
在这里插入图片描述
绘制温湿度曲线
在这里插入图片描述

2、当前值、状态

温湿度:
在这里插入图片描述
距离:
在这里插入图片描述
状态:
在这里插入图片描述

5、运行结果

1、传感器放置:

在这里插入图片描述

2、软件界面:

点击getdata按钮开始获取数据、分析、绘图
如图,当前时刻温度29.2,湿度62,dht11校验值正常,正常读取了162次,距离49.742cm,有人状态;
最新的5个温度数据为28.9,28.9,28.9,28.9,29.2,温度上升状态
在这里插入图片描述
下图是人离开,温度下降状态
在这里插入图片描述
温度稳定状态:
在这里插入图片描述

3、数据库查看

倒数第二列有人是1,无人是0;最后一列,温度上升是1,下降是-1,无变化为0(一分钟记录一次):
在这里插入图片描述
长时段记录结果(正常运行并记录超过9小时):
1、23:44有人状态,温度升高:
在这里插入图片描述
2、0点人离开后,温度下降:
在这里插入图片描述
3、夜间温度自然下降,但是不是人引起的,所以温度下降标志无变化,符合预期
在这里插入图片描述
4、早上人有人状态,温度标志为1,温度又开始上升:
在这里插入图片描述

课程作业,边学边用,如有错漏,敬请指正
--------------------------------------------------------------------------------------------诺有缸的高飞鸟202007

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诺有缸的高飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值