
#百人创作先锋团# 基于STM32设计的遥控小车 原创
一、设计要求
智能小车是提前写入预置的控制程序,根据传感器信号反馈完成特定动作的小车。小车可以自主形式、也可以通过手机遥控行驶,在很多领域都在广泛使用。 比如:科学勘探小车、消防灭火小车、快递取件小车、野外动物拍摄场景等等。智能小车能够实时显示时间、速度、里程,具有自动寻迹、寻光、避障功能,可程控行驶速度、准确定位停车,远程传输图像等功能。
智能小车可以分为三部分——传感器部分、控制器部分、执行器部分。
控制器部分:接收传感器部分传递过来的信号,并根据事前写入的决策系统(软件程序),来决定机器人对外部信号的反应,将控制信号发给执行器部分。好比人的大脑。
执行器部分:驱动机器人做出各种行为,包括发出各种信号(点亮发光二极管、发出声音)的部分,并且可以根据控制器部分的信号调整自己的状态。对机器人小车来说,最基本的就是轮子。这部分就好比人的四肢一样。
传感器部分:机器人用来读取各种外部信号的传感器,以及控制机器人行动的各种开关。好比人的眼睛、耳朵等感觉器官。
下面介绍一款基于STM32设计的智能小车,支持手机远程遥控形式。
硬件设计:
有硬件小车实物,包含WI-FI模块,电机驱动模块,温度传感器模块等;
软件设计:
1)要求手机端APP控制小车行进方向;
2)小车到指定观测点采集温度和湿度数据后,记录所有
温湿度数据,包含采集时间,地点,温度和湿度;
3)手机端APP可实现实时查看小车当前所在位置温度和湿度,
可实现通过时间,地点查询温度和湿度的功能;实现统计某段时间,观测点温湿度平均值,最高值,最低值等功能;
二、设计总结
上位机设计:
设计一个Android手机APP和一个windows系统端的上位机(语言采用C++ + QT支持跨平台),上位机与小车之间采用WIFI通信;小车上的ESP8266设置为AP模式(路由器模式);上位机需要连接到ESP8266这个WIFI上,方便局域网通信;小车上的ESP8266创建一个TCP服务器,上位机当做TCP客户端,主动连接小车;连接上之后,上位机支持控制小车的方向,前进、后退、左、右、停止;支持显示小车实时上传的温度、湿度、GPS经纬度(室内GPS信号很差)、时间信息(这个时间直接从APP端取)。
可实现通过时间,地点查询温度和湿度的功能;实现统计某段时间,观测点温湿度平均值,最高值,最低值等功能;
小车设备:
小车电机驱动模块采用L298N、WIFI模块采用ESP8266、MCU采用STM32F103C8T6、温湿度模块采用DTH11、GPS模块采用北斗GPS+BDS
通信数据格式:
上位机向小车发送指令: a 前进、b后退、c左转、d右转、e停止
小车上上位机发送: #123,456,120.086199,30.139219
分别表示: 温度、湿度、经度、纬度
三、硬件模块
四、小车实物图
接线示例:
五、APP成品效果图
核心代码:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("遥控小车上位机");
SetStyle(":/CarControl.qss");
connect(&time_p, SIGNAL(timeout()), this, SLOT(time_update()));
time_p.start(1000);
/*1. 实例化 QNetworkAccessManager*/
manager = new QNetworkAccessManager(this);
/*2. 关联 finished 信号*/
connect(manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(replyFinished(QNetworkReply*)));
//设置并打开数据库
if (QSqlDatabase::contains(LOG_IN_DATABASE_CONNECT_NAME))
{
database = QSqlDatabase::database(LOG_IN_DATABASE_CONNECT_NAME);
}
else
{
//数据库类型
database = QSqlDatabase::addDatabase("QSQLITE",LOG_IN_DATABASE_CONNECT_NAME);
database.setDatabaseName(LOG_IN_DATABASE_NAME); //数据库名称
database.setUserName("xl"); //用户名
database.setPassword("123456"); //
}
//打开数据库,如果数据库存在就打开,不存在就自动创建
if(database.open()==false)
{
Log_Text_Display("数据库打开失败.请检查程序运行路径和权限.\n");
}
else
{
Log_Text_Display("连接数据库成功.\n");
}
//数据库: 建表,如果存在就不创建,不存在就创建
QSqlQuery sql_query(database);
//下面语句查询名称为CarData 的表是否存在.
sql_query.exec(QString("select count(*) from sqlite_master where type='table' and name='%1'").arg("CarData"));
if(sql_query.next())
{
if(sql_query.value(0).toInt()==0)
{
Log_Text_Display("数据库表是不存在的.准备创建.\n");
//创建表格 创建表格语句:create table <table_name> (f1 type1, f2 type2,…);
/* CREATE TABLE 是告诉数据库系统创建一个新表的关键字。
* CREATE TABLE 语句后跟着表的唯一的名称
* 或标识*/
/*下面的语句: 创建一个名称为CarData的表,字段分别是存放 时间、温度、湿度、经度、纬度*/
QString create_sql = "create table CarData(id int primary key, time varchar(100),temperature varchar(100), humidity varchar(100),longitude varchar(100),latitude varchar(100))";
sql_query.prepare(create_sql);
if(!sql_query.exec())
{
Log_Text_Display("数据库表创建失败.\n");
}
else
{
Log_Text_Display("数据库表创建成功.\n");
}
}
else
{
Log_Text_Display("数据库表是存在的.不需要创建.\n");
}
}
//显示主页面
ui->stackedWidget->setCurrentIndex(0);
}
Widget::~Widget()
{
delete ui;
}
//设置指定样式
void Widget::SetStyle(const QString &qssFile) {
QFile file(qssFile);
if (file.open(QFile::ReadOnly)) {
QString qss = QLatin1String(file.readAll());
qApp->setStyleSheet(qss);
QString PaletteColor = qss.mid(20, 7);
qApp->setPalette(QPalette(QColor(PaletteColor)));
file.close();
}
}
/*
工程: SmartHome
日期: 2021-04-26
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 日志显示
*/
void Widget::Log_Text_Display(QString text)
{
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit_log;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
//当文本数量超出一定范围就清除
if(plainTextEdit_log->toPlainText().size()>1024*4)
{
plainTextEdit_log->clear();
}
plainTextEdit_log->insertPlainText(text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 连接服务器
*/
void Widget::on_pushButton_connect_clicked()
{
if(ui->pushButton_connect->text()=="连接"){
//开始连接服务器
NewClinet();
}
else
{
if(LocalTcpClientSocket)
{
LocalTcpClientSocket->close();
}
}
}
//客户端模式:创建客户端
void Widget::NewClinet()
{
if(LocalTcpClientSocket)
{
LocalTcpClientSocket->close();
delete LocalTcpClientSocket;
LocalTcpClientSocket=nullptr;
}
/*1. 创建本地客户端TCP套接字*/
LocalTcpClientSocket = new QTcpSocket;
/*2. 设置服务器IP地址*/
QString Ipaddr=ui->lineEdit_ip->text();
QHostAddress FarServerAddr(Ipaddr);
/*3. 连接客户端的信号槽*/
connect(LocalTcpClientSocket,SIGNAL(connected()),this,SLOT(LocalTcpClientConnectedSlot()));
connect(LocalTcpClientSocket,SIGNAL(disconnected()),this,SLOT(LocalTcpClientDisconnectedSlot()));
connect(LocalTcpClientSocket,SIGNAL(readyRead()),this,SLOT(LocalTcpClientReadDtatSlot()));
/*4. 尝试连接服务器主机*/
int prot=ui->lineEdit_port->text().toInt();
LocalTcpClientSocket->connectToHost(FarServerAddr,prot);
}
//客户端模式:响应连接上服务器之后的操作
void Widget::LocalTcpClientConnectedSlot()
{
ui->pushButton_connect->setText("断开");
Log_Text_Display("成功连接服务器...\n");
}
//客户端模式:断开服务器
void Widget::LocalTcpClientDisconnectedSlot()
{
ui->pushButton_connect->setText("连接");
Log_Text_Display("与服务器断开连接...\n");
}
//客户端模式:读取服务器发过来的数据
//温湿度: #123,456,120.086199,30.139219
//分别表示: 温度、湿度、经度、纬度
void Widget::LocalTcpClientReadDtatSlot()
{
QString array=LocalTcpClientSocket->readAll();
QDateTime time = QDateTime::currentDateTime(); //获取系统现在的时间
QString time_str = time.toString("yyyy-MM-dd hh:mm:ss"); //设置显示格式
QString text;
text +=array;
text +="\n";
Log_Text_Display(text);
//解析数据
if(array.at(0)=='#')
{
array.remove('#');
QString T_str=array.section(',',0,0); //温度
QString H_str=array.section(',',1,1); //湿度
QString jin_str=array.section(',',2,2); //经度
QString wei_str=array.section(',',3,4); //纬度
ui->label_H->setText(H_str+"%");
ui->label_Temp->setText(T_str+"℃");
ui->label_latitude->setText(wei_str);
ui->label_longitude_jin->setText(jin_str);
//保存数据到数据库
QSqlQuery sql_query(database);
//查询最大ID
QString select_max_sql = "select max(id) from CarData";
int max_id = 0;
sql_query.prepare(select_max_sql);
if(!sql_query.exec())
{
Log_Text_Display("数据库最大ID查找失败.\n");
}
else
{
while(sql_query.next())
{
max_id = sql_query.value(0).toInt();
}
Log_Text_Display(QString("data base max id:%1\n").arg(max_id));
//添加数据
//插入数据 插入语句:insert into <table_name> values (value1, value2,…);
QString insert_sql = tr("insert into CarData values(?,?,?,?,?,?)");
sql_query.prepare(insert_sql);
sql_query.addBindValue(max_id+1); //id
sql_query.addBindValue(time_str); //时间
sql_query.addBindValue(T_str); //温度
sql_query.addBindValue(H_str); //湿度
sql_query.addBindValue(jin_str); //经度
sql_query.addBindValue(wei_str); //纬度
if(!sql_query.exec())
{
Log_Text_Display("数据插入失败.\n");
}
}
}
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 主页面
*/
void Widget::on_pushButton_home_clicked()
{
ui->stackedWidget->setCurrentIndex(0);
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 小车地图
*/
void Widget::on_pushButton_map_clicked()
{
ui->stackedWidget->setCurrentIndex(1);
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 历史数据
*/
void Widget::on_pushButton_data_history_clicked()
{
QDateTime time = QDateTime::currentDateTime(); //获取系统现在的时间
//设置截止时间
ui->dateTimeEdit_end->setDateTime(time);
ui->stackedWidget->setCurrentIndex(2);
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 前进
*/
void Widget::on_pushButton_qianjin_clicked()
{
if(LocalTcpClientSocket)LocalTcpClientSocket->write("a");
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 后退
*/
void Widget::on_pushButton_houtui_clicked()
{
if(LocalTcpClientSocket)LocalTcpClientSocket->write("b");
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 左
*/
void Widget::on_pushButton_zuo_clicked()
{
if(LocalTcpClientSocket)LocalTcpClientSocket->write("c");
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 右
*/
void Widget::on_pushButton_you_clicked()
{
if(LocalTcpClientSocket)LocalTcpClientSocket->write("d");
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 停止
*/
void Widget::on_pushButton_stop_clicked()
{
if(LocalTcpClientSocket)LocalTcpClientSocket->write("e");
}
/*
工程: CarControl
日期: 2021-04-28
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 时间更新
*/
void Widget::time_update()
{
QDateTime time = QDateTime::currentDateTime(); //获取系统现在的时间
QString text;
text = time.toString("yyyy/MM/dd hh:mm:ss ddd"); //设置显示格式
ui->label_time->setText(text);
}
/*
工程: CarControl
日期: 2021-04-28
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: QNetworkReply 返回值处理
*/
void Widget::replyFinished(QNetworkReply*network)
{
//无错误返回
if(network->error() == QNetworkReply::NoError)
{
//读取所有数据
QByteArray byte_array=network->readAll();
QPixmap pixmap;
//加载图片
if(pixmap.loadFromData(byte_array,"png",Qt::AutoColor))
{
//QPixmap scaledPixmap = pixmap.scaled(ui->label_MapDisplay->size(), Qt::KeepAspectRatio);
//ui->label_MapDisplay->setScaledContents(true);
ui->label_MapDisplay->setAlignment(Qt::AlignCenter|Qt::AlignVCenter);
ui->label_MapDisplay->setPixmap(pixmap);
}
}else
{
QMessageBox::warning(this, tr("错误信息提示"),tr("更新地图失败.\n""检查网络是否正常!"),
QMessageBox::Ok);
}
//删除 reply, 不能在 repyfinished 里直接 delete,要调用 deletelater;
network->deleteLater();
}
/*
工程: CarControl
日期: 2021-04-28
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 更新地图显示
*/
void Widget::on_pushButton_update_map_clicked()
{
QString type;
if(ui->radioButton_bd0911->isChecked())type="bd09ll";
if(ui->radioButton_bd09mc->isChecked())type="bd09mc";
if(ui->radioButton_gcj0211->isChecked())type="gcj02ll";
if(ui->radioButton_wgs8411->isChecked())type="wgs84ll";
QString url=QString("http://api.map.baidu.com/staticimage/v2?ak=CtgpfPgQrtkFfaK27XgXtSKG88Fva8zQ&width=400&height=800¢er=&markers=%1,%2&zoom=%3&markerS&coordtype=%4")
.arg(ui->label_longitude_jin->text()).arg(ui->label_latitude->text()).arg(ui->horizontalSlider->value()).arg(type);
/*3. 提交请求*/
manager->get(QNetworkRequest(QUrl(url)));
}
/*
工程: CarControl
日期: 2021-04-28
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 清空数据库
*/
void Widget::on_pushButton_clean_data_clicked()
{
QString cmd;
cmd = QString("delete from CarData");
QSqlQuery sql_query(database);
if(!sql_query.exec(cmd))
{
Log_Text_Display("delete table Error.\n");
}
}
/*
工程: CarControl
日期: 2021-04-28
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 历史最高温度
*/
void Widget::on_pushButton_tempH_clicked()
{
//指定操作的数据库
QSqlQuery sql_query(database);
//查询全部数据
sql_query.prepare("select * from CarData");
if(!sql_query.exec())
{
Log_Text_Display("数据库查询错误.\n");
}
else
{
QString T_max_time; //最高温度所在的时间
double T_max=ui->label_Temp->text().toInt(); //最高温度
double t_tmp=0; //当前温度
QString H_max_time; //最高湿度所在的时间
double H_max=ui->label_H->text().toInt(); //最高湿度
double h_tmp=0; //当前湿度
while(sql_query.next())
{
// int id = sql_query.value(0).toInt(); //ID
QString time = sql_query.value(1).toString(); //时间
QString T_str = sql_query.value(2).toString(); //温度
QString H_str = sql_query.value(3).toString(); //湿度
QString jin_str = sql_query.value(4).toString(); //经度
QString wei_str = sql_query.value(5).toString(); //纬度
//比较温度
t_tmp=T_str.toDouble();
if(t_tmp>T_max)
{
T_max=t_tmp;
T_max_time=time;
}
//比较湿度
h_tmp=H_str.toDouble();
if(h_tmp>H_max)
{
H_max=h_tmp;
H_max_time=time;
}
}
QString show_text=QString("最高温度:%1,时间:%2\n").arg(T_max).arg(T_max_time);
show_text+=QString("最高湿度:%1,时间:%2\n").arg(H_max).arg(H_max_time);
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit_temp_data;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
//当文本数量超出一定范围就清除
if(plainTextEdit_log->toPlainText().size()>1024*4)
{
plainTextEdit_log->clear();
}
plainTextEdit_log->insertPlainText(show_text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
}
/*
工程: CarControl
日期: 2021-04-28
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 历史最低温度
*/
void Widget::on_pushButton_tempL_clicked()
{
//指定操作的数据库
QSqlQuery sql_query(database);
//查询全部数据
sql_query.prepare("select * from CarData");
if(!sql_query.exec())
{
Log_Text_Display("数据库查询错误.\n");
}
else
{
QString T_min_time; //最低温度所在的时间
double T_min; //最低温度
double t_tmp; //当前温度
QString H_min_time; //最低湿度所在的时间
double H_min; //最低湿度
double h_tmp; //当前湿度
sql_query.next();
QString time = sql_query.value(1).toString(); //时间
QString T_str = sql_query.value(2).toString(); //温度
QString H_str = sql_query.value(3).toString(); //湿度
QString jin_str = sql_query.value(4).toString(); //经度
QString wei_str = sql_query.value(5).toString(); //纬度
T_min=T_str.toDouble();
H_min=H_str.toDouble();
T_min_time=time;
H_min_time=time;
while(sql_query.next())
{
// int id = sql_query.value(0).toInt(); //ID
time = sql_query.value(1).toString(); //时间
T_str = sql_query.value(2).toString(); //温度
H_str = sql_query.value(3).toString(); //湿度
jin_str = sql_query.value(4).toString(); //经度
wei_str = sql_query.value(5).toString(); //纬度
//比较温度
t_tmp=T_str.toDouble();
if(t_tmp<T_min)
{
T_min=t_tmp;
T_min_time=time;
}
//比较湿度
h_tmp=H_str.toDouble();
if(h_tmp<H_min && h_tmp>0)
{
H_min=h_tmp;
H_min_time=time;
}
}
QString show_text=QString("最低温度:%1,时间:%2\n").arg(T_min).arg(T_min_time);
show_text+=QString("最低湿度:%1,时间:%2\n").arg(H_min).arg(H_min_time);
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit_temp_data;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
//当文本数量超出一定范围就清除
if(plainTextEdit_log->toPlainText().size()>1024*4)
{
plainTextEdit_log->clear();
}
plainTextEdit_log->insertPlainText(show_text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
}
/*
工程: CarControl
日期: 2021-04-28
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 时间范围查询
*/
void Widget::on_pushButton_TimeQuery_clicked()
{
//指定操作的数据库
QSqlQuery sql_query(database);
//查询全部数据
sql_query.prepare("select * from CarData");
if(!sql_query.exec())
{
Log_Text_Display("数据库查询错误.\n");
}
else
{
QString T_max_time; //最高温度所在的时间
double T_max; //最高温度
double t_tmp; //当前温度
QString H_max_time; //最高湿度所在的时间
double H_max; //最高湿度
double h_tmp; //当前湿度
QString T_min_time; //最低温度所在的时间
double T_min; //最低温度
double t_tmp2; //当前温度
QString H_min_time; //最低湿度所在的时间
double H_min; //最低湿度
double h_tmp2; //当前湿度
sql_query.next();
QString time = sql_query.value(1).toString(); //时间
QString T_str = sql_query.value(2).toString(); //温度
QString H_str = sql_query.value(3).toString(); //湿度
QString jin_str = sql_query.value(4).toString(); //经度
QString wei_str = sql_query.value(5).toString(); //纬度
T_min=T_str.toDouble();
H_min=H_str.toDouble();
T_max=T_str.toDouble();
H_max=T_str.toDouble();
T_min_time=time;
H_min_time=time;
T_max_time=time;
H_max_time=time;
while(sql_query.next())
{
// int id = sql_query.value(0).toInt(); //ID
time = sql_query.value(1).toString(); //时间
T_str = sql_query.value(2).toString(); //温度
H_str = sql_query.value(3).toString(); //湿度
jin_str = sql_query.value(4).toString(); //经度
wei_str = sql_query.value(5).toString(); //纬度
int year=time.mid(0,4).toInt(); //年
int month=time.mid(5,2).toInt(); //月
int day=time.mid(8,2).toInt(); //日
QDate start_date=ui->dateTimeEdit_start->date();
QDate end_date=ui->dateTimeEdit_end->date();
//判断年范围
if(year<start_date.year() || year >end_date.year())continue;
//判断月范围
if(month<start_date.month() || month >end_date.month())continue;
//判断日范围
if(day<start_date.day() || day >end_date.day())continue;
//比较温度
t_tmp=T_str.toDouble();
if(t_tmp>T_max)
{
T_max=t_tmp;
T_max_time=time;
}
//比较湿度
h_tmp=H_str.toDouble();
if(h_tmp>H_max && h_tmp>0)
{
H_max=h_tmp;
H_max_time=time;
}
//比较温度
t_tmp2=T_str.toDouble();
if(t_tmp2<T_min && t_tmp2>0)
{
T_min=t_tmp2;
T_min_time=time;
}
//比较湿度
h_tmp2=H_str.toDouble();
if(h_tmp2<H_min && h_tmp2>0)
{
H_min=h_tmp2;
H_min_time=time;
}
}
QString show_text=QString("最高温度:%1,时间:%2\n").arg(T_max).arg(T_max_time);
show_text+=QString("最高湿度:%1,时间:%2\n").arg(H_max).arg(H_max_time);
show_text+=QString("最低温度:%1,时间:%2\n").arg(T_min).arg(T_min_time);
show_text+=QString("最低湿度:%1,时间:%2\n").arg(H_min).arg(H_min_time);
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit_temp_data;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
//当文本数量超出一定范围就清除
if(plainTextEdit_log->toPlainText().size()>1024*4)
{
plainTextEdit_log->clear();
}
plainTextEdit_log->insertPlainText(show_text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
}
/*
工程: CarControl
日期: 2021-04-29
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 查询所有数据
*/
void Widget::on_pushButton_all_data_clicked()
{
//指定操作的数据库
QSqlQuery sql_query(database);
//查询全部数据
sql_query.prepare("select * from CarData");
if(!sql_query.exec())
{
Log_Text_Display("数据库查询错误.\n");
}
else
{
while(sql_query.next())
{
// int id = sql_query.value(0).toInt(); //ID
QString time = sql_query.value(1).toString(); //时间
QString T_str = sql_query.value(2).toString(); //温度
QString H_str = sql_query.value(3).toString(); //湿度
QString jin_str = sql_query.value(4).toString(); //经度
QString wei_str = sql_query.value(5).toString(); //纬度
QString show_text=QString("%1,%2℃,%3%\n").arg(time).arg(T_str).arg(H_str);
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit_temp_data;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
plainTextEdit_log->insertPlainText(show_text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
}
}
/*
工程: CarControl
日期: 2021-04-29
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 清空显示
*/
void Widget::on_pushButton_clean_clicked()
{
ui->plainTextEdit_temp_data->clear();
}
六、程序下载
STM32核心代码(电机控制部分):
#include "motor.h"
//全局变量定义
unsigned int speed_count=0;//占空比计数器 50次一周期
int front_left_speed_duty=SPEED_DUTY;
int front_right_speed_duty=SPEED_DUTY;
int behind_left_speed_duty=SPEED_DUTY;
int behind_right_speed_duty=SPEED_DUTY;
unsigned char continue_time=0;
//根据占空比驱动电机转动
void CarMove(void)
{
BEHIND_RIGHT_EN;
//右前轮
if(front_right_speed_duty > 0)//向前
{
if(speed_count < front_right_speed_duty)
{
FRONT_RIGHT_GO;
}else //停止
{
FRONT_RIGHT_STOP;
}
}
else if(front_right_speed_duty < 0)//向后
{
if(speed_count < (-1)*front_right_speed_duty)
{
FRONT_RIGHT_BACK;
}else //停止
{
FRONT_RIGHT_STOP;
}
}
else //停止
{
FRONT_RIGHT_STOP;
}
//左后轮
if(behind_left_speed_duty > 0)//向前
{
if(speed_count < behind_left_speed_duty)
{
BEHIND_LEFT_GO;
} else //停止
{
BEHIND_LEFT_STOP;
}
}
else if(behind_left_speed_duty < 0)//向后
{
if(speed_count < (-1)*behind_left_speed_duty)
{
BEHIND_LEFT_BACK;
} else //停止
{
BEHIND_LEFT_STOP;
}
}
else //停止
{
BEHIND_LEFT_STOP;
}
}
//向前
void CarGo(void)
{
front_left_speed_duty=SPEED_DUTY;
front_right_speed_duty=SPEED_DUTY;
behind_left_speed_duty=SPEED_DUTY;
behind_right_speed_duty=SPEED_DUTY;
}
//后退
void CarBack(void)
{
front_left_speed_duty=-SPEED_DUTY;
front_right_speed_duty=-SPEED_DUTY;
behind_left_speed_duty=-SPEED_DUTY;
behind_right_speed_duty=-SPEED_DUTY;
}
//向左
void CarLeft(void)
{
front_left_speed_duty=-20;
front_right_speed_duty=SPEED_DUTY;
behind_left_speed_duty=-20;
behind_right_speed_duty=SPEED_DUTY+10;//增加后轮驱动力
}
//向右
void CarRight(void)
{
front_left_speed_duty=SPEED_DUTY;
front_right_speed_duty=-20;
behind_left_speed_duty=SPEED_DUTY+10;//增加后轮驱动力
behind_right_speed_duty=-20;
}
//停止
void CarStop(void)
{
front_left_speed_duty=0;
front_right_speed_duty=0;
behind_left_speed_duty=0;
behind_right_speed_duty=0;
}
/*
FRONT_LEFT_F_PIN PG13 左前前进IO
FRONT_LEFT_B_PIN PG11 左前后退IO
FRONT_RIGHT_F_PIN PC11 右前前进IO
FRONT_RIGHT_B_PIN PD0 右前后退IO
BEHIND_LEFT_F_PIN PD6 左后前进IO
BEHIND_LEFT_B_PIN PG9 左后后退IO
右后电机的两个控制IO这里改为两路使能EN1、EN2,高电平有效
BEHIND_RIGHT_F_PIN PD4 右电机使能IO
BEHIND_RIGHT_B_PIN PD2 左电机使能IO
*/
void MotorInit(void)
{
RCC->APB2ENR|=1<<8; //PG
RCC->APB2ENR|=1<<5; //PD
RCC->APB2ENR|=1<<4; //PC
GPIOG->CRH&=0xFF0F0F0F;
GPIOG->CRH|=0x00303030;
GPIOD->CRL&=0xF0F0F0F0;
GPIOD->CRL|=0x03030303;
GPIOC->CRH&=0xFFFF0FFF;
GPIOC->CRH|=0x00003000;
CarStop();
}
