STM32MP157开发板Linux+Qt项目实战:智慧家庭
stm32mp157开发板FS-MP1A是华清远见自主研发的一款高品质、高性价比的Linux+单片机二合一的嵌入式教学级开发板。开发板搭载ST的STM32MP157高性能微处理器,集成2个Cortex-A7核和1个Cortex-M4 核,A7核上可以跑Linux操作系统,M4核上可以跑FreeRTOS、RT-Thread等实时操作系统。开发板搭配仿真器、显示屏、摄像头、资源扩展板等丰富的扩展模块,可拓展物联网、人工智能等相关技术学习,还可以拓展丰富的项目实战,非常贴合企业当下开发需求,是一款嵌入式Linux入门进阶必备开发板!
可学习技术:嵌入式Linux应用/系统/驱动开发、ARM裸机开发、Qt界面编程、STM32单片机、FreeRTOS、人工智能机器视觉等。其中ARM Cortex-A7裸机开发课程是华清远见独有特色课程,可关注:https://www.bilibili.com/video/BV1Xe4y1i7vm/,持续更新中。
![](https://img-blog.csdnimg.cn/img_convert/93427b9012440c6cb179237c44e83742.webp?x-oss-process=image/format,png)
可实战项目:14个Linux+Qt综合项目案例,8个MP1A物联网拓展项目
项目配套文档及源码,可在下方评论区留言索取~~
1、Linux+Qt综合项目案例:华清远见stm32mp157开发板优势特色部分,包括音乐播放器、智慧家庭、智能工业电表、智能出行助手、智能猫眼、环境监测、智能安防、智能语音识别等10余个项目案例,涉及家居、医疗、农业多种应用方向,在案例中使用了多种物联网和嵌入式技术,包括OT开发、linux应用开发、linux驱动开发、物联网云端接入、MQTT协议、json字符串等知识点。
基于Linux+Qt的智慧家庭项目
项目简介:
智慧家庭又可称为智慧家庭服务平台,是智慧城市的最小单元,是以家庭为载体,以家庭成员之间的亲情为纽带。智慧家庭综合利用物联网、云计算、移动互联网和大数据等新一代信息技术,结合自动控制技术,将家庭设备智能控制、家庭环境感知、家人健康感知、家居安全感知以及信息交流、消费服务等家居生活有效地结合起来,创造出健康、安全、舒适、低碳、便捷、个性化和充满关爱的家庭生活方式。
开发平台:
华清远见stm32mp157开发板豪华套餐(开发板+仿真器+五寸屏+摄像头+资源扩展板+tf卡+读卡器)
项目功能展示:
Wifi 模块
点击刷新按钮,可以实时更新附近的 wifi,选择要连接的 wifi,会弹出输入密码的页面,输入密码,点击连接即可连接成功。
![](https://img-blog.csdnimg.cn/img_convert/6376f061e5f94bf5f722b2384209eef6.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/2fe4430b7891295c121d86486df280a2.webp?x-oss-process=image/format,png)
选择要查看天气的城市,点击获取按钮
![](https://img-blog.csdnimg.cn/img_convert/39af8bfff29c17e5663cc2b2c733186b.webp?x-oss-process=image/format,png)
环境监测模块
![](https://img-blog.csdnimg.cn/img_convert/c31b03a1bf4d81794b514d8339d72fb5.webp?x-oss-process=image/format,png)
设备控制模块
选择按钮即可完成设备的控制
![](https://img-blog.csdnimg.cn/img_convert/f959a4f0b34dc4f3cf97c19d78ac7acf.webp?x-oss-process=image/format,png)
开启和关闭按钮控制智能监测的开启和关闭,提交按钮上的输入框输入的是智能监测触发的阈值。
![](https://img-blog.csdnimg.cn/img_convert/561929996a3e4128a8267e121613ed33.webp?x-oss-process=image/format,png)
连接百度云模块
输入百度云连接三元组,点击获取时间戳,点击计算、连接,即可实现向云端发送环境进而转发到微信小程序,并且通过微信小程序控制设备。
![](https://img-blog.csdnimg.cn/img_convert/7f6344b6b0293ded85f3d64819b6df3f.webp?x-oss-process=image/format,png)
在微信小程序显示温湿度。并且控制开发板 led 灯
![](https://img-blog.csdnimg.cn/img_convert/3fe77f5bb2b43f11c57689a7132a818c.webp?x-oss-process=image/format,png)
遮挡光电开关,会自动弹出门禁系统。
![](https://img-blog.csdnimg.cn/img_convert/6e2a9de7add9886221d5769b093a6609.webp?x-oss-process=image/format,png)
Qt开发环境搭建
主机开发环境说明
1) 本文档主要介绍 linux 环境下的 Qt 程序开发;
2) 主机 Qt 版本为 5.14.1;
主机 Qt 环境搭建及使用
Qt Creator 安装
将 qt-creator-opensource-linux-x86_64-4.10.1.run(Qt 实验源码\工具软件) 复制到 ubuntu 主机中,可以采用共享文件夹的方式也可以使用 tfp方式将文 件存入家目录下的 Downloads 目录。我们需要在终端中赋予安装程序可执行的权限
![](https://img-blog.csdnimg.cn/img_convert/beacad119ef8881cfd903c8f5d0fea2f.webp?x-oss-process=image/format,png)
我们可以使用图形化的文件管理器来查看
![](https://img-blog.csdnimg.cn/img_convert/18c39a90acff74bb36e467a3671cddfd.webp?x-oss-process=image/format,png)
双击“qt-creator-opensource-linux-x86_64-4.10.1.run”图标运行安装程序。
出现如下界面:
![](https://img-blog.csdnimg.cn/img_convert/37aea133cabfb58144bbf79de7594550.webp?x-oss-process=image/format,png)
等待程序验证完成后点击“Next”
![](https://img-blog.csdnimg.cn/img_convert/862fc203c307b9d9557b33675a9582c9.webp?x-oss-process=image/format,png)
这里我们需要登录或者注册一个账号,如果我们之前已经注册过直接登录就可以。如果没有注册过则需要新注册有一个账号后登录。这里笔者已经注册过账号,所以直接登录。
登录成功后出现如下界面,点击 Next
![](https://img-blog.csdnimg.cn/img_convert/c46818fcfb2637739b65e56826526fe8.webp?x-oss-process=image/format,png)
这里选择安装路径
![](https://img-blog.csdnimg.cn/img_convert/791d4b97f900d01053bf403b49e4ebc7.webp?x-oss-process=image/format,png)
可以直接默认,Next
![](https://img-blog.csdnimg.cn/img_convert/2365c19e93408c2db097dc69fc83802b.webp?x-oss-process=image/format,png)
这路选择安装的组件,直接默认即可
![](https://img-blog.csdnimg.cn/img_convert/00564cf3c301ffff00de7499f76f466b.webp?x-oss-process=image/format,png)
这里我们需要同意用户协议
![](https://img-blog.csdnimg.cn/img_convert/8db7847a87ece585c8c07076db41ecf6.webp?x-oss-process=image/format,png)
这个界面告诉我们安装完成后需要占用的空间。点击”Install”按钮后开始安装。
![](https://img-blog.csdnimg.cn/img_convert/2a504c164f4cc35be1ce4fd00525477d.webp?x-oss-process=image/format,png)
安装完成后出现如下界面
![](https://img-blog.csdnimg.cn/img_convert/b2c2a35bd96a531167dc30e2ef85256b.webp?x-oss-process=image/format,png)
点击“Finish”按钮后将弹出 Qt Creator 主界面
![](https://img-blog.csdnimg.cn/img_convert/f731b0b7100cd8bc671f93e650805206.webp?x-oss-process=image/format,png)
点击“Cancel”按钮后即可正常使用
Qt5.14.1 安装
复制到 qt-opensource-linux-x64-5.14.1.run(Qt 实验源码\工具软件)到 ubuntu 主机中,可以采用共享文件夹的方式也可以使用 tfp 方式将文件存入家目录下的 Downloads 目录。进入所在文件夹,先给执行权限输入命令
chmod +x ./qt-opensource-linux-x64-5.14.1.run
安装在命令行输入
./qt-opensource-linux-x64-5.14.1.run
会有可视化引导安装,一直 next 就行了
在选择安装组件的时候要是不知道选择那些就全选了 大概有 4 个 G 左右
下载 gcc 和 g++
sudo apt-get install gcc g++
下载 cmake
sudo apt-get install cmake
下载链接库
sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev
Qt Creator 配置
1)配置 GCC
运行 QtCreator 后,依次点击"Tool"->"Options",出现选项对话框,在左侧点击"Kits",右 边选择"Compilers"标签。 检查有没有下图标注的 C++和 C ,一般按上面步骤执行后都会有
![](https://img-blog.csdnimg.cn/img_convert/c5681a4a5349d91f26f11e9602644a95.webp?x-oss-process=image/format,png)
点击右侧"Add"按钮,弹出下拉列表后,选择"GCC"的"C"
![](https://img-blog.csdnimg.cn/img_convert/7578999f510b9c3218714afbbcd45b08.webp?x-oss-process=image/format,png)
填写信息如下,"Name"为"Auto-GCC","Compiler path"点击旁边的"Browse.."按钮选择编译器的路径,例子中的路径是 “/usr/bin/gcc”
![](https://img-blog.csdnimg.cn/img_convert/0f772356fecb067eec3aedfe44ccedec.webp?x-oss-process=image/format,png)
2)配置 G++
点击右侧"Add"按钮,弹出下拉列表后,选择"GCC"的"C++",下面的文本框填写"Name" 为"Auto-G++","Compiler path"点击旁边的"Browse.."按钮选择编译器的路径,例子中的路径是" /usr/bin/g++"。
![](https://img-blog.csdnimg.cn/img_convert/1a92942a3c80cf05639b26fd2cee3821.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/981e78ef8d0bb6469a0d83c1b301176c.webp?x-oss-process=image/format,png)
填写完成后,点击"Apply"。
3)配置 qmake
选择"Qt Versions"标签,如果有下面红框中的文本,可以跳过下面步骤
![](https://img-blog.csdnimg.cn/img_convert/c95f163278223cbbf07b99d39b358055.webp?x-oss-process=image/format,png)
如果没有,在右侧点击"Add..."
![](https://img-blog.csdnimg.cn/img_convert/850cecb2b8b86bb31e438c596eacdfcd.webp?x-oss-process=image/format,png)
会弹出 qmake 路径选择对话框,这里以"/home/linux/Qt5.14.1/5.14.1/gcc_64/bin/qmake"为例子。 选择”qmake”文件后,击"Open"按钮
![](https://img-blog.csdnimg.cn/img_convert/150322a3750d353ff7d12d4fdecfa765.webp?x-oss-process=image/format,png)
"Version name"改为" Qt %{Qt:Version} GCC"。然后点击"Apply"按钮。
4)配置 Kits
点击左侧"Kits",右侧选择"Kits"标签。检查有没有下图红框选中的文本,如果有可以跳过下面步骤
![](https://img-blog.csdnimg.cn/img_convert/8f6a1222b42f31e81814fe0af7cd1bc2.webp?x-oss-process=image/format,png)
然后没有,点击 Add:
在弹出的对话框中"Name"为"Desktop","Device Type"选择"Desktop"选项, "Sysroot"选择目标设备的系统目录,"Compiler"选择之前配置的名称
"Auto-GCC"和"Auto-G++","Qt version"选择之前配 置的名称"Qt 5.14.1
GCC",其它默认即可,最后点击"Apply"和"OK"按钮
Qt Creator 新建工程
注意:工程路径最好不要包含中文、特殊字符、空格等
我们可以新建一个“qt”文件夹,该文件夹用作我们以后存放源代码。
![](https://img-blog.csdnimg.cn/img_convert/1a251daca947fb9d78608e12c22b4827.webp?x-oss-process=image/format,png)
打开 Qt Creator,在欢迎页面点击 “New”按钮,来新建一个工程。
![](https://img-blog.csdnimg.cn/img_convert/1b03afc9b5e10e2c68f487cea3eb2991.webp?x-oss-process=image/format,png)
在出现的新建项目窗口中,我们选则“Application”->“Qt Widgets
Application”,然后点击右下方“Choose…”按钮,来创建一个桌面 Qt 应用。
![](https://img-blog.csdnimg.cn/img_convert/ca6afe74951aface61ab92f5b914500c.webp?x-oss-process=image/format,png)
我们在这里设置项目介绍和源码位置,我们这里创建一个名为
“HelloWorld”的示例项目,设置完成之后点击 next
![](https://img-blog.csdnimg.cn/img_convert/777d480e1e1da832af9a52bf4a9328cb.webp?x-oss-process=image/format,png)
直接点击 next
![](https://img-blog.csdnimg.cn/img_convert/0fba78f51cc740146364a95fac984324.webp?x-oss-process=image/format,png)
随后进行细节设置,主要设置要创建的源码文件的基本类信息,包括类名等。这里我们可以根据自己的项目特点进行设置。需要说明的一点就是基类的选择,这里基类有 QMainWindow、QWidget、QDialog 三种,它们的不同之处如下:
⚫ QMainWindow 类提供一个带有菜单条,工具条和一个状态条的主应用程序窗口。主窗口通常提供一个大的中央窗口部件,以及周围菜单,工具条,和一个状态栏。QMainWindow 窗口经常被继承,使得封装中央部件,菜单,工具条,状态栏等都变得很容易,当用户点击它的时候,相应的槽就会被调用;
⚫ QWidget 类是所有用户界面对象的基类,窗口部件是用户界面的一个基本单元,它从窗口系统接收鼠标,键盘和其他消息,并在屏幕上绘制自己。一个窗口部件可以被他的父窗口或者是其他窗口挡住一部分;
⚫ QDialog 类是对话框窗口的基类,对话框窗口主要用于短期任务和用户进行短期通讯的顶级窗口,QDialog 可以是模态对话框或者是非模态对话框。QDialog 支持扩展并带有返回值,他们可以带有默认值;我们在这里选择 QDialog 类即可,点击 next 完成类信息设置
![](https://img-blog.csdnimg.cn/img_convert/62658aeeaf7777656ade6cf27c29e6c6.webp?x-oss-process=image/format,png)
直接点击 next 按钮即可。
![](https://img-blog.csdnimg.cn/img_convert/cc60d67502baea3deee3399314bdefdc.webp?x-oss-process=image/format,png)
然后进行工具选择,该页面可以选择我们创建的工程可以使用的工具,选择想要使用的编译器模块,例如下图 。点击 next
![](https://img-blog.csdnimg.cn/img_convert/7f6875243a86878813bfcae1617349f2.webp?x-oss-process=image/format,png)
最后我们设置汇总信息,如果不需要版本控制等功能,直接点击完成finish 即可
![](https://img-blog.csdnimg.cn/img_convert/ea1494e9ab6b9e44dfc01507fd395dd3.webp?x-oss-process=image/format,png)
随后我们就进入到了主界面,这时候 Qt 已经帮我们做好了一些准备工作,包括创建了一些文件,写好了一些前置代码等等。
我们可以点击左边 protect 栏,来查看我们的编译选项
![](https://img-blog.csdnimg.cn/img_convert/f4d6586db0105ff92023904e1482aea7.webp?x-oss-process=image/format,png)
我们可以在左下角选择编译 Debug 版或者 Release 版,即调试版或发行版。
![](https://img-blog.csdnimg.cn/img_convert/d2de43a67edea0771f1874144c236f46.webp?x-oss-process=image/format,png)
左下角绿色剪头是编译并运行,锤子是仅编译,我们可以直接点击绿色小箭头将我们导入的工程编译并运行起来。
![](https://img-blog.csdnimg.cn/img_convert/c54b7e01b71420cb2b35c052bf5cf438.webp?x-oss-process=image/format,png)
点击运行按钮后,我们可以看到 HelloWorld 窗口运行起来了。
![](https://img-blog.csdnimg.cn/img_convert/c22384e77453a6c864cb68a57ea3ff6c.webp?x-oss-process=image/format,png)
导入工程
我们可以将已存在的 Qt 程序项目直接打开,这里以上一章节的HelloWorld 程序为例。首先我们确定源码存在的位置,如 HelloWorld 程序源码在 /home/linux/qt/helloworld 路径下。
点击欢迎页面的“Open” 按钮可以打开已有的工程。
![](https://img-blog.csdnimg.cn/img_convert/b6ffa945b2d26a0daad405520982fd31.webp?x-oss-process=image/format,png)
找到我们刚才解压好的源码,选择“helloworld.pro”文件并点击打开
![](https://img-blog.csdnimg.cn/img_convert/baf72f4ed2cf3b335947b8766f97c717.webp?x-oss-process=image/format,png)
接下来我们就可以进入到代码编辑界面了。
![](https://img-blog.csdnimg.cn/img_convert/01615379325eefe832cda08d04e899e7.webp?x-oss-process=image/format,png)
左上角是项目栏,点击项目名称左边的小箭头可以展开项目目录。
![](https://img-blog.csdnimg.cn/img_convert/634254b4c30ef17befc05739a92dfc9d.webp?x-oss-process=image/format,png)
我们可以点击左边项目栏,来查看我们的编译选项。需注意的是构建设置中的路径应与工程路径处于同级目录下。
![](https://img-blog.csdnimg.cn/img_convert/b5fcb7ea8ec51477b5a575d902a9e75c.webp?x-oss-process=image/format,png)
我们可以在左下角选择编译 Debug 版或者 Release 版,即调试版或发行版。
![](https://img-blog.csdnimg.cn/img_convert/05ef5226ed823dc083bad601ef420c45.webp?x-oss-process=image/format,png)
左下角绿色剪头是编译并运行,锤子是仅编译,我们可以直接点击绿色小箭头将我们导入的工程编译并运行起来。
![](https://img-blog.csdnimg.cn/img_convert/01ee39c14bb3f18a96d3e7d6f33257e4.webp?x-oss-process=image/format,png)
点击运行按钮后,我们可以看到 HelloWorld 窗口运行起来了。
![](https://img-blog.csdnimg.cn/img_convert/39c19bc987de762fb139641f81c9415a.webp?x-oss-process=image/format,png)
文件说明
通过上面两个章节,我们学习到了 Qt 程序的新建与导入的方法,也知道了Qt 会帮我们做一些基础工作,比如帮我们建立了一些文件,那么这些文件都是干什么用的呢?我们以HelloWorld 程序来说明一下。
![](https://img-blog.csdnimg.cn/img_convert/5845c02b7efde740b24b13a2e703ddf7.webp?x-oss-process=image/format,png)
以“.pro”为后缀名的文件,为 Qt 的项目管理文件,存储项目设置的文件;
![](https://img-blog.csdnimg.cn/img_convert/bbcd0196415b228fa2ec22553a049e72.webp?x-oss-process=image/format,png)
“Qt += core gui”表示项目中加入 core gui 模块。core gui 是 Qt 用于GUI 设计的类库模块,如果创建的是控制台(console)应用程序,就不需要添加 core gui。
Qt 类库以模块的形式组织各种功能的类,根据项目涉及的功能需求,在项目中添加适当的类库模块支持。例如,如果项目中使用到了涉及数据库操作的类就需要用到 sql(数据库)模块,在 pro 文件中需要在后面加上 sql:
1 Qt += core gui sql
“greaterThan(QT_MAJOR_VERSION, 4): QT += widgets”,这是个条件执行语句,表示当 Qt 主版本大于 4 时,才加入 widgets 模块。
“TARGET = HelloWorld”表示生成的目标可执行文件的名称,即编译后生成的可执行文件是 HelloWorld.exe。
“TEMPLATE = app”表示项目使用的模板是 app,是一般的应用程序。
后面的 SOURCES、HEADERS、FORMS 记录了项目中包含的源程序文件、头文件和窗体文件(.ui 文件)的名称。这些文件列表是 Qt Creator 自动添加到项目管理文件里面的,用户不需要手动修改。当添加一个文件到项目,或从项目里删除一个文件时,项目管理文件里的条目会自动修改。
文件夹“Header”中,存放的是所设计的窗体类的头文件;
文件夹“Sources”中,存放着源码文件。main.cpp 是实现 main()函数的程序文件,HelloWorld.cpp 是 widget.h 里定义类的实现文件。C++中,任何窗体或界面组件都是用类封装的,一个类一般有一个头文件(.h 文件)和一个源程序文件(.cpp 文件);
文件夹“Forms”中,存放着界面设计文件,“.ui”文件是一个 XML 格式存储的窗体上的元件及其布局的文件,双击项目文件目录树中的文件 ui,会打开一个集成在 Qt Creator 中的 Qt Designer 对窗体进行可视化设计;
![](https://img-blog.csdnimg.cn/img_convert/b109b280c4992c1afadd9df5f216ce9d.webp?x-oss-process=image/format,png)
UI 设计器有以下一些功能区域:
组件面板:窗口左侧是界面设计组件面板,分为多个组,如 Layouts、Buttons、Display Widgets 等,界面设计的常见组件都可以在组件面板里找到。
中间主要区域是待设计的窗体。如果要将某个组件放置到窗体上时,从组件面板上拖放一个组件到窗体上即可。
Signals 和 Slots 编辑器与 Action 编辑器是位于待设计窗体下方的两个编辑器。Signals 和 Slots 编辑器用于可视化地进行信号与槽的关联,Action 编辑器用于可视化设计 Action。
布局和界面设计工具栏:窗口上方的一个工具栏,工具栏上的按钮主要实现布局和界面设计。
对象浏览器(Object Inspector):窗口右上方是 Object Inspector,用树状视图显示窗体上各组件之间的布局包含关系,视图有两列,显示每个组件的对象名称(ObjectName)和类名称。
属性编辑器(Property Editor):窗口右下方是属性编辑器,是界面设计时最常用到的编辑器。属性编辑器显示某个选中的组件或窗体的各种属性及其取值,可以在属性编辑器里修改这些属性的值。属性编辑器的内容分为两列,左侧为属性的名称,右侧为属性的值。属性又分为多个组,实际上表示了类的继承关系,位于下方的类属性组继承自位于上方的类属性组;如果我们需要新建资源文件、源码文件等,可以在项目文件夹出点击鼠标右键,选择 Add New;如果我们有新的文件需要添加,可以在项目文件夹出点击鼠标右键,选择 Add Existing Files。
![](https://img-blog.csdnimg.cn/img_convert/60e087f97686191055326dd7d0335481.webp?x-oss-process=image/format,png)
帮助文档
Qt 的帮助文档是伴随我们学习 Qt 开发的好伙伴。在 Qt 开发过程中,我们会面临图形接口使用的问题,它不像 C 语言那样就那么几个函数接口,图形接口的接口数量可以用海量来形容,常用的我们可能能记住,其它的就没有必要去记了,用到什么就去帮助文档查看用法是比较方便的。我们可以按 F1 按键,或通过上方导航栏的“help->contects”来进入帮助文档。
![](https://img-blog.csdnimg.cn/img_convert/2c6858ec2b30db74b02b185cb8359aec.webp?x-oss-process=image/format,png)
上方的前进后退按钮方便我们查看文档,如返回到上一步,返回到下一步。
![](https://img-blog.csdnimg.cn/img_convert/c8bd53e3d2b8fb4941665f8346e096bb.webp?x-oss-process=image/format,png)
我们可以通过帮助文档来查看以下几个部分:
类使用的相关介绍;
查看相关类的使用介绍,我们可以先进入到帮助文档,然后在左上角选择“Search”。笔者这里以 QWidget 类为例,输入我们想要查找的类的名字,然后双击查找结果来查看说明
![](https://img-blog.csdnimg.cn/img_convert/62a4c12e7575b395865dfdeccc3d61bb.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/734b1a1e7767a25b48baec5169e0e73c.webp?x-oss-process=image/format,png)
也可以先将鼠标移动到想要查询的类的位置,如图所示,将鼠标移动至“QWidget”处,然后按“F1”键,即可跳转到相应的帮助文档
![](https://img-blog.csdnimg.cn/img_convert/bcf9d9f62fd0dbf61ead9cd36adb4c12.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/1c98e43a7f5269d97665d902aa7582f3.webp?x-oss-process=image/format,png)
我们可以通过再按一次“F1”键来全窗口查看帮助文档,按“Esc”键可以退出。
![](https://img-blog.csdnimg.cn/img_convert/d90fc4515fabb713b22b8fa1df997a9e.webp?x-oss-process=image/format,png)
部分常用的成员元素包括以下几项:
⚫ 公有成员函数:操作部件属性的相关函数;
⚫ 公有槽函数:Qt 类中已经定义好的槽函数,直接可与信号相连接;
⚫ 信号:软中断,如按下按钮触发 pressed() 信号等;
⚫ 保护成员函数:通常事件所对应的虚函数放在此处;
⚫ 事件:常用事件,如操作鼠标触发的鼠标事件;
滚动鼠标滚轮,向下即可看到“Qwdget Class”类的相关说明了。
![](https://img-blog.csdnimg.cn/img_convert/fbefe9df4a5872cc461bc22e6a2f2892.webp?x-oss-process=image/format,png)
1) 查看所用的部件的相应成员函数。
我们可以查找到该类所用部件的相应成员函数的使用方法、功能、参数、
返回值等等,我们以“按钮”控件,即“QPushButton Class”类为例,我们通
过索引搜索的方式,来找到这个类
![](https://img-blog.csdnimg.cn/img_convert/b6df40e6956cd5a13014ccca9d605b07.webp?x-oss-process=image/format,png)
我们可以通过点击“Public Functions” 来查看“QPushButton”这个类中的成员函数。
![](https://img-blog.csdnimg.cn/img_convert/c19ebcb8f7e257161818fa6ebd9a6303.webp?x-oss-process=image/format,png)
这里以“QPushButton(const QString &text, QWidget *parent =Q_NULLPTR)”为例,我们点击函数名字可以进入到函数详情中。我们可以看到相应的描述为:以“text”为显示内容,以“parent”为父对象,构造一个push 按钮。“text”“parent”为函数参数,由于是构造函数,所以此函数没有返回值。
![](https://img-blog.csdnimg.cn/img_convert/89df53a1e7a9dfc3cfb2a841e547e8b9.webp?x-oss-process=image/format,png)
还有一些函数是继承自其它类的,例如“Public Functions”中有 21 个继承自“QAbstractButton”类的函数,我们点击“QAbstractButton”即可查看。
点击“QAbstractButton”即可查看。
![](https://img-blog.csdnimg.cn/img_convert/06a57207cf66f9b9e74fdac83607fa3d.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/ac3713628d39a36ae10163790f6c9ced.webp?x-oss-process=image/format,png)
同样我们可以点击相应的函数进入查看详情。如查看“void setText(const QString &text)”。
![](https://img-blog.csdnimg.cn/img_convert/955065b39fc6acb0b4d595bbb314c273.webp?x-oss-process=image/format,png)
2) 查看所用的部件的信号。
我们这里还是以“PushButton”为例,我们点击“Public Slots”。
![](https://img-blog.csdnimg.cn/img_convert/5b466146b86b421d545f372ad6845a58.webp?x-oss-process=image/format,png)
可以看到“PushButton”本身有一个“void showMenu()”的信号,并且有很多继承自其他类的信号。
![](https://img-blog.csdnimg.cn/img_convert/71a0a97fc32e93ab33230aa9ed8df964.png)
一般来说我们用的“PushButton”的信号,最多的是用到其继承自基类“
QAbstractButton”中的几个信号,分别是点击(按下后抬起)、按压(单按下)、释放(单抬起)等。
![](https://img-blog.csdnimg.cn/img_convert/ba5eb467e98eee0378c18b25dd419630.webp?x-oss-process=image/format,png)
我们可以点击相应信号查看详情
![](https://img-blog.csdnimg.cn/img_convert/28e3b5dc2f8e452527da93bb60085627.webp?x-oss-process=image/format,png)
3) 查看所用的部件的事件(所对应的虚函数如何编写)。部件常用事件主要在 “QWidget”中声明,选择“Events”即可查看相关说明。
每个事件都对应着事件函数。
![](https://img-blog.csdnimg.cn/img_convert/3c9aa8a7227d3f64038bddf39884d85e.webp?x-oss-process=image/format,png)
点击事件函数可查看详情
![](https://img-blog.csdnimg.cn/img_convert/9cfada1eea9cdbe37f36ed259eaf78cb.webp?x-oss-process=image/format,png)
微信小程序开发环境搭建
微信小程序开发工具简介
微信小程序是小程序中的一种,英文名 Wechat Mini Program,是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。全面开放申请后,主体类型为企业、政府、媒体、其他组织或个人的开发者,均可申请注册小程序。微信小程序、微信订阅号、微信服务号、微信企业号是并行的体系。以下是小程序所涉及的技术概括:
⚫ JSON
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它是基于 ECMAScript(w3c 制定的 js 规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。易于人的阅读和编写,同时也易于机器解析和生成,并有效提升网络传输效率。
⚫ XML
XML(Extensible Markup Language),中文名为可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。
⚫ CSS
层叠样式表(Cascading Style Sheets)是一种用来表现 HTML(标准通用标记语言的一个应用)或 XML(标准通用标记语言的一个子集)等文件样式的计算机语言。CSS 不仅可以静态的修饰网页,还可以配合各种脚本语言动态的对网页各元素进行格式化。
⚫ JavaScript
JavaScript,是一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为 JavaScript 引擎,是浏览器的一部分,广泛用于客户端的脚本语言。
申请微信小程序
登录微信公众平台,注册账号,选择小程序。https://mp.weixin.qq.com/
![](https://img-blog.csdnimg.cn/img_convert/83292053568fe0a4b4b56a7d3f090cc2.webp?x-oss-process=image/format,png)
按照步骤依次注册,输入邮箱,密码,验证码等,同意协议进行注册。
![](https://img-blog.csdnimg.cn/img_convert/8ab03502376fa42b56fbb8437d38171f.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/4d7ac310374d3929d8f909290e4c85d2.webp?x-oss-process=image/format,png)
然后登录自己的邮箱,查阅邮件,点击链接进行激活。进入步骤 3,信息登记,按照网页要求,依次输入信息,身份信息,管理员微信信息,即可激活成功。
返回微信公众平台,输入刚刚注册的账户密码,会需要用管理员微信扫码登录,登录后,下载普通小程序开发者工具。
![](https://img-blog.csdnimg.cn/img_convert/58b02c884912b48da17f1330ead9ccfc.webp?x-oss-process=image/format,png)
点击开发,选择开发设置,获取小程序 ID,以备后续开发需求。
![](https://img-blog.csdnimg.cn/img_convert/132d35d92bd3f992423277351d961253.png)
微信小程序开发工具下载完成后,进行默认安装即可
创建新项目工程
打开微信小程序开发者工具,点击创建新工程,填写自己的 APPID,选择默认模板,语言选择 JavaScript,点击新建。
![](https://img-blog.csdnimg.cn/img_convert/14a63d1fc1fe9f98a92b4c9fdba69d6c.webp?x-oss-process=image/format,png)
新建工程完成为如下界面:
![](https://img-blog.csdnimg.cn/img_convert/7d817f94c57c4bd1de63142d122ca028.webp?x-oss-process=image/format,png)
基本环境配置
打开主界面的右上角的详情按钮,找到本地设置,将“增强编译”和“不校验合法域名”这两个选项进行勾选,因为在小程序的开发阶段,尽量把这两勾选上。
![](https://img-blog.csdnimg.cn/img_convert/4e4c10c53cfffe5f17822b0c50b28de0.png)
编译、调试
打开 app.json 文件,可以更改微信小程序的标题,改为“工程 demo”。然后按下 Ctrl + S 快捷键进行保存,即可完成编译,调试输出。查看现象。
![](https://img-blog.csdnimg.cn/img_convert/8dfa566e53dde1126b174110e6877c81.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/be2d2f7c78b057be6dc8e495d1a4bd2c.webp?x-oss-process=image/format,png)
项目总体设计介绍
总体框架
智慧家庭系统的设计基于物联网的思想,物联网是新一代信息技术的重要组成部分,其英文名称是“The Internet of things”。其基本思想是以互联网为媒介,实现远程监督、控制。它在各个领域有着非常广泛的应用。
总体框架如下:
![](https://img-blog.csdnimg.cn/img_convert/41b8b8931afc8c989fc8940a090d771a.webp?x-oss-process=image/format,png)
该项目分为 WIFI 连接模块、智能门禁模块、数据采集模块、智能检测模块、设备控制模块、天气预报模块、与百度云交互模块:下面具体介绍几个模块的功能。
WIFI 连接模块
该模块实现的原理是使用 wpa_supplicant 工具对无线网络进行管理和控制的功能。wpa_supplicant 是一个开源项目,已经被移植到 Linux,Windows 以及很多嵌入式系统上。它是 WPA 的应用层认证客户端,负责完成认证相关的登录、加密等工作。
wpa_supplicant 工具包含 wpa_supplicant 和 wpa_cli 这 2 个程序,其中wpa_supplicant 程序作为服务端在后台运行,服务 wpa_cli 客户端的请求,从而实现 WiFi 的配置连接。下面是通过 shell 命令去进行 WIFI 的配置及连接。
1.打开 wlan0 接口:
root@fsmp1c:~# ifconfig wlan0 up
2.启动 wpa_supplicant 进程并在后台运行
root@fsmp1c:~# wpa_supplicant -D nl80211 -i wlan0 -c /etc/wpa_supplicant.conf-B
3.扫描周边 WiFi 热点:
wpa_cli -i wlan0 scan
4.查看扫描结果:
root@fsmp1c:~# wpa_cli -i wlan0 scan_results
5.添加一个网络连接
root@fsmp1c:~# wpa_cli -i wlan0 add_network
6.配置 WiFi 热点的名称 ssid:
root@fsmp1c:~# wpa_cli -i wlan0 set_network 1 ssid '"FARSIGHT"'
7.配置 WiFi 热点的密码 psk:
root@fsmp1c:~# wpa_cli -i wlan0 set_network 1 psk '"fs123456"'
8.列举所有保存的连接
root@fsmp1c:~# wpa_cli -i wlan0 list_network
9.连接第 1 个保存的连接
root@fsmp1c:~# wpa_cli -i wlan0 select_network 1
10.启动 wpa_supplicant 应用
root@fsmp1c:~#wpa_supplicant -B -c wifi.conf -i wlan0
11.使用 udhcpc 命令动态获取 IP
root@fsmp1c:~#udhcpc -i wlan0
智能门禁模块
该模块是根据关电开关智能识别有没有人来,当有人触发光电开关,会自动弹出登陆界面,输入用户名和密码,程序会自动匹配数据库,如果用户名密码错误超过三次,会自动报警;如果输入正确,则开门。
数据采集模块
另开一个线程,实时去读取温湿度驱动设备文件的数据,进行计算得出温湿度的数值同样的方式得到光照的数值,通过信号传参的方式传给主线程,将数据设置到 ui 界面上。
智能检测模块
开启智能检测后,程序会根据你设定的阈值进行检测,假如温度超过你设定的温度阈值,会自动开启风扇;或者当光照低于你设定的阈值,会开灯提高照明的亮度。
设备控制模块
通过 ui 界面的按键去开关灯或者风扇,以及蜂鸣器。
天气预报模块
连接 WIFI 之后,通过 get 方法从网上获取信息,得到 Json 类型的数据,对这个数据进行解析,将解析到的数据设置到 ui 界面上面。
百度云交互模块
在 ui 界面输入在百度云创建设备时的 IoTCoreld、DeviceKey、DeviceSecret(三元组)通过组合生成 addr 和用户名,使用 MD5 加密算法计算得到密码,用于连接,连接成功后开启定时器,自动向指定好的 topic 发布采集到的温度湿度和光照,并且订阅云端控制设备的 topic,用户向云端发布json 数据,开发板接收到云端转发的 json 数据会做出响应。如{“led1”,1},开发板收到数据后 led1 会亮
源码分析
WIFI 连接模块
在 Qt 程序里使用 system 函数来执行 5.2 的命令来实现 WIFI 的配置
![](https://img-blog.csdnimg.cn/img_convert/b9d702f827b7c2a1829a9608bacfb9bc.webp?x-oss-process=image/format,png)
建立刷新按钮信号槽连接,实现点击刷新按钮界面显示附近 wifi。实现原理是启动 wpa_supplicant 进程并在后台运行,扫描周边 WiFi 热点;
使用wpa_cli -i wlan0 scan_results 命令查看扫描结果,并将扫描结果重定向到wifilist 文件中,对 wifilist 文件进行读操作,将读到的数据直接显示到 ui界面上面。核心代码如下
……
system("wpa_supplicant -D nl80211 -i wlan0 -c /etc/wpa_supplicant.conf -
B");
system("wpa_cli -i wlan0 scan");
system("wpa_cli -i wlan0 scan_results > ./wifilist");
usleep(50000);
QString fileName = "./wifilist";
QFile file(fileName);
int f = 0;
j = 0;
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QMessageBox::warning(this,"Warnning","can't
open",QMessageBox::Yes);
}
QTextStream in(&file);
QString str;
while (!((str = in.readLine()).isEmpty ()))
……
选中刷新后显示的 wifi 名后,点击连接会进入二级界面。二级界面输入密码正确后点击连接,等待几 s 后即可完成连接。这里输入框使用自定义类MylineEdit,继承自 QlineEdit,增加了鼠标点击事件,当触摸屏点击输入框的时候,会弹出软键盘,用来输入密码
![](https://img-blog.csdnimg.cn/img_convert/62f6f59189e1196a2e1747eaa8f00c66.webp?x-oss-process=image/format,png)
这里连接 wifi 的核心代码如下:
……
sprintf(set_ssid,"wpa_cli -i wlan0 set_network %d ssid
'\"%s\"'",i,wifiName.toLatin1().data());
sprintf(set_password, "wpa_cli -i wlan0 set_network %d psk '\"%s\"' >
TorF.ini",i,password_edit->text().toLatin1().data());
sprintf(select_wlan, "wpa_cli -i wlan0 select_network %d ",i);
system(set_ssid);
system(set_password);
system("wpa_cli -i wlan0 list_network");
system(select_wlan);
system("wpa_supplicant -B -c wifi.conf -i wlan0");
qDebug()<< get_TorF().data()->toUpper();
if(get_TorF().data()->toUpper()=="F")
{
QMessageBox::warning(this,tr("Connect information"), tr("密码
错误"));
return ;
}
system("udhcpc -i wlan0 -B");
char echo_1[64];
char echo_2[64];
sprintf(echo_1,"echo \"nameserver 114.114.114.114\" >
/etc/resolv.conf");
system(echo_1);
sprintf(echo_2,"echo \"nameserver 8.8.8.8\" > /etc/resolv.conf");
system(echo_2);
close();
QMessageBox::information(this,tr("Connect information"), tr("连接成
功"));
}
……
智能门禁模块
![](https://img-blog.csdnimg.cn/img_convert/60fd75211373352a34c3dfaa9604bee8.webp?x-oss-process=image/format,png)
PanGu 开发板有多个 GPIO 组,查看 GPIO 组信息,可以使用 gpiodetect 命令。
# gpiodetect
gpiochip0 [GPIOA] (16 lines)
gpiochip1 [GPIOB] (16 lines)
gpiochip10 [GPIOK] (16 lines)
gpiochip11 [GPIOZ] (16 lines)
gpiochip2 [GPIOC] (16 lines)
gpiochip3 [GPIOD] (16 lines)
gpiochip4 [GPIOE] (16 lines)
gpiochip5 [GPIOF] (16 lines)
gpiochip6 [GPIOG] (16 lines)
gpiochip7 [GPIOH] (16 lines)
gpiochip8 [GPIOI] (16 lines)
gpiochip9 [GPIOJ] (16 lines)
通过查扩展板的原理图可以看到 :光电开关的 GPIO 管脚是 PE15
读取 PE15 的状态
# gpioget gpiochip4 15
当有东西遮挡光电开关时,执行以上命令得到的是 0;没有东西遮挡。得
到的是 1.
所以我们开一个线程让它一直去读取光 PE15 的状态,当读到为 0 时,说明
有人,发送信号给主线程,使主线程开启登陆界面。
void ReadPE15Thread::run()
{
system("touch pe15.txt");
system("gpioget gpiochip4 15 > pe15.txt");
int fd;
char buf[32];
while (1) {
fd = open("./pe15.txt",O_RDONLY);
system("gpioget gpiochip4 15 > pe15.txt");
read(fd,buf,sizeof(buf));
if(strcmp(buf,"1\n")==0)
{
qDebug()<<tr("login !!");
emit pesig();
}
sleep(1);
close(fd);
}
}
建立信号槽连接
void MainWindow::loginSlot()
{
disconnect(&pe15thread,SIGNAL(pesig()),this,SLOT(loginSlot()));
login->show();
connect(login,SIGNAL(loginsuccess()),this,SLOT(loginsuccessSlot()));
connect(login,SIGNAL(loginfailed()),this,SLOT(loginfailedSlot()));
connect(login,SIGNAL(loginclose()),this,SLOT(logincloseSlot()));
}
登陆界面的编写
编译 UI 界面。
创建 usr.db 数据库文件
# sqlit3 usr.db
创建表
sqlite> CREATE TABLE usr(
usrname TEXT PRIMARY KEY ,
password NOT NULL
);
向数据库中添加用户名和密码
sqlite> INSERT INTO usr values(“usr”,“123”);
login.cpp
打开数据库 验证密码
bool Login::openDb()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("usr.db");
if(!db.open())
{
QMessageBox::warning(0, tr("Warning"), db.lastError().text());
return false;
}
QSqlQuery query(db);
if(!query.exec("select usrname,password from usr"))
{
db.close();
return false;
}
while(query.next())
{
QString UserName = query.value(0).toString();
QString Password = query.value(1).toString();
// qDebug()<< UserName;
// qDebug()<< Password;
if(UserName == usr_edit->text() &&Password ==password_edit->text())
return true;
}
db.close();
return false;
}
登陆成功发送成功信号,登陆失败发送失败信号
void MainWindow::loginSlot()
{
disconnect(&pe15thread,SIGNAL(pesig()),this,SLOT(loginSlot()));
login->show();
connect(login,SIGNAL(loginsuccess()),this,SLOT(loginsuccessSlot()));
connect(login,SIGNAL(loginfailed()),this,SLOT(loginfailedSlot()));
connect(login,SIGNAL(loginclose()),this,SLOT(logincloseSlot()));
}
void MainWindow::loginsuccessSlot()
{
QMessageBox::information(this, tr("information"),"密码正确,门锁已打开");
// 重置错误次数
Numberoferrors =3;
login->close();
beepunring();
connect(&pe15thread,SIGNAL(pesig()),this,SLOT(loginSlot()));
}
void MainWindow::loginfailedSlot()
{
QString info;
Numberoferrors--;
switch(Numberoferrors)
{
case 2:
info ="密码错误,还有 3 次机会";
break;
case 1:
info ="密码错误,还有 2 次机会";
break;
case 0:
info ="密码错误,还有 1 次机会";
break;
default:
info ="即将报警";
break;
}
QMessageBox::warning(this, tr("warning"),info);
if(Numberoferrors <0)
{
qDebug() << Numberoferrors;
beepring();
}
}
验证密码错误三次后蜂鸣器报警
数据采集和智能检测模块
系统启动后可以查看目录/sys/bus/iio/devices/
root@fsmp1c:~# ls /sys/bus/iio/devices/ iio:device0
如果系统中有多个 iio 设备,这里可能会有很多个 iio 目录,确定哪个
目录是我们的设备对应目录,可以通过查看
/sys/bus/iio/devices/iio\:device0/name 信息确认:
root@fsmp1c:~# cat /sys/bus/iio/devices/iio\:device0/name
0-0040
由显示信息每个驱动对应设备可能有所不同,当前显示内容为设备的物理
地址,与设备树中地址一致,可以确认 iio:device0 是当前设备对应目录查看
当目录下内容:
root@fsmp1c:~# ls -l /sys/bus/iio/devices/iio\:device0/
total 0
-r--r--r-- 1 root root 4096 Feb 7 15:51 dev
-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_offset
-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_raw
-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_scale
-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_offset
-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_raw
-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_scale
-r--r--r-- 1 root root 4096 Feb 7 15:51 name
drwxr-xr-x 2 root root 0 Feb 7 15:51 power
lrwxrwxrwx 1 root root 0 Feb 7 15:50 subsystem -> ../../../../../../../bus/iio
-rw-r--r-- 1 root root 4096 Feb 7 15:50 uevent
文件说明:
文件 in_ temp_scale 为温度标尺,计算公式如下,公式来自与驱动对应代码:
![](https://img-blog.csdnimg.cn/img_convert/d3a15c1479e79a0560e23ef00769fe9a.webp?x-oss-process=image/format,png)
𝑆𝑐𝑎𝑙𝑒𝑡𝑒𝑚𝑝 =
175.72 × 1000 × 4
65535 = 10.725097656
in_ temp_offset 为数据偏移,计算公式如下,公式来自于驱动对应代码
![](https://img-blog.csdnimg.cn/img_convert/529c08b000fb0a7480605d17e2ae1921.webp?x-oss-process=image/format,png)
𝑜𝑓𝑓𝑠𝑒𝑡𝑡𝑒𝑚𝑝 =
−46.85 × 65536
4 × 175.72 = −4368
in_ temp_raw 为原始数据,计算公式如下,公式来自于驱动对应代码:
![](https://img-blog.csdnimg.cn/img_convert/10541e082895fc9865463ea43f2636c3.webp?x-oss-process=image/format,png)
𝑅𝑎𝑤𝑡𝑒𝑚𝑝 =
𝐶𝑜𝑑𝑒𝑡𝑒𝑚𝑝
4
阅读 SI7006 芯片手册可以看到温度的计算公式为:
𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒(℃) =
175.72 × 𝐶𝑜𝑑𝑒𝑡𝑒𝑚𝑝
65535 − 46.85
上述公式与驱动返回值看不出直接对应关系,所以我们按照驱动提供的 scale、
offset 及 Raw 的计算公式对公式进行处理,得到最终公式计算过程如下:
𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒(℃) × 65536 = 175.72 × 𝐶𝑜𝑑𝑒𝑡𝑒𝑚𝑝 − 46.85 × 65536
𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒(℃) × 65536 = 175.72 × (𝐶𝑜𝑑𝑒𝑡𝑒𝑚𝑝+
−46.85 × 65536
175.72 )
𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒(℃) =
175.72
65536 × (𝐶𝑜𝑑𝑒𝑡𝑒𝑚𝑝+
−46.85 × 65536
175.72 )
𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒(℃) =
175.72 × 1000
65536 × (𝐶𝑜𝑑𝑒𝑡𝑒𝑚𝑝+
−46.85 × 65536
175.72 ) ÷ 1000
𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒(℃) =
175.72 × 1000 × 4
65536 × (
𝐶𝑜𝑑𝑒𝑡𝑒𝑚𝑝
4
+
−46.85 × 65536
175.72 × 4
) ÷ 1000
最终确定温度的计算公式为:
𝑇𝑒𝑚𝑝𝑒𝑟𝑎𝑡𝑢𝑟𝑒(℃) = (𝑅𝑎𝑤𝑡𝑒𝑚𝑝 + 𝑂𝑓𝑓𝑠𝑒𝑡𝑡𝑒𝑚𝑝) × 𝑆𝑐𝑎𝑙𝑒𝑡𝑒𝑚𝑝 ÷ 1000
同理湿度的计算公式为:
𝐻𝑢𝑚𝑖𝑑𝑖𝑡𝑦(%𝑅𝐻) = (𝑅𝑎𝑤ℎ𝑢𝑚 + 𝑂𝑓𝑓𝑠𝑒𝑡ℎ𝑢𝑚) × 𝑆𝑐𝑎𝑙𝑒ℎ𝑢𝑚 ÷ 1000
主要代码如下:
开一个线程去读取设备文件的信息并计算,得到温度湿度光照,通过信号传参的方式
传给主线程。
子线程:
……
void CollentdataThread::run()
{
int temp_raw = 0;
int temp_offset = 0;
float temp_scale = 0;
int hum_raw = 0;
int hum_offset = 0;
float hum_scale = 0;
float tem_float =0;
float hum_float =0;
float ill_float =0;
QString hum;
QString tem;
QString ill;
const char *device1 ="iio:device0";//温湿度
const char *device2 ="iio:device1";//光照
while (1)
{
/*read temp data*/
read_sysfs_int(device1, "in_temp_raw", &temp_raw);
read_sysfs_int(device1, "in_temp_offset", &temp_offset);
read_sysfs_float(device1, "in_temp_scale", &temp_scale);
tem_float =(temp_raw + temp_offset) * temp_scale / 1000;
tem =QString::number(tem_float,'f', 2);
read_sysfs_int(device1, "in_humidityrelative_raw", &hum_raw);
read_sysfs_int(device1, "in_humidityrelative_offset", &hum_offset);
read_sysfs_float(device1, "in_humidityrelative_scale", &hum_scale);
hum_float = (hum_raw + hum_offset) * hum_scale / 1000;
hum =QString::number(hum_float,'f', 2);
read_sysfs_float(device2, "in_illuminance_input", &ill_float);
ill =QString::number(ill_float,'f', 2);
emit send(tem,hum,ill);
sleep(2);
}
}
int CollentdataThread::read_sysfs_float(const char *device, const char *filename, float
*val)
{
int ret = 0;
FILE *sysfsfp;
char temp[128];
memset(temp, '0', 128);
ret = sprintf(temp, "/sys/bus/iio/devices/%s/%s", device, filename);
if (ret < 0)
goto error;
sysfsfp = fopen(temp, "r");
if (!sysfsfp)
{
ret = -errno;
goto error;
}
errno = 0;
if (fscanf(sysfsfp, "%f\n", val) != 1)
{
ret = errno ? -errno : -ENODATA;
if (fclose(sysfsfp))
perror("read_sysfs_float(): Failed to close dir");
goto error;
}
if (fclose(sysfsfp))
ret = -errno;
error:
return ret;
}
int CollentdataThread::read_sysfs_int(const char *device, const char *filename, int *val)
{
int ret = 0;
FILE *sysfsfp;
char temp[128];
memset(temp, '0', 128);
ret = sprintf(temp, "/sys/bus/iio/devices/%s/%s", device, filename);
if (ret < 0)
goto error;
sysfsfp = fopen(temp, "r");
if (!sysfsfp)
{
ret = -errno;
goto error;
}
errno = 0;
if (fscanf(sysfsfp, "%d\n", val) != 1)
{
ret = errno ? -errno : -ENODATA;
if (fclose(sysfsfp))
perror("read_sysfs_float(): Failed to close dir");
goto error;
}
if (fclose(sysfsfp))
ret = -errno;
error:
return ret;
}
主线程:
信号槽连接
connect(&thread_collentdata,SIGNAL(send(QString,QString,QString)),this,SLOT(set_
humAdte
mAdill(QString,QString,QString)));
槽函数:
void MainWindow::set_humAdtemAdill(QString tem,QString hum,QString ill)
{
// 将线程采集的数据赋值给成员变量
this->tem =tem;
this->hum =hum;
this->ill =ill;
/************** 异常处理 ********/
if(abnormalSwitch == true)
{
if(this->tem.toFloat() >tem_max.toFloat())
{
system("echo 255 > /sys/class/hwmon/hwmon1/pwm1");
}
else
system("echo 0 > /sys/class/hwmon/hwmon1/pwm1");
if(this->ill.toFloat() < ill_lv1.toFloat()&&this->ill.toFloat() > ill_lv2.toFloat())
{
system("echo 1 > /sys/class/leds/led1/brightness");
}
else if(this->ill.toFloat() < ill_lv2.toFloat()&&this->ill.toFloat() > ill_lv3.toFloat())
{
system("echo 1 > /sys/class/leds/led1/brightness");
system("echo 1 > /sys/class/leds/led2/brightness");
}
else if(this->ill.toFloat() < ill_lv3.toFloat())
{
system("echo 1 > /sys/class/leds/led1/brightness");
system("echo 1 > /sys/class/leds/led2/brightness");
system("echo 1 > /sys/class/leds/led3/brightness");
}
else
{
system("echo 0 > /sys/class/leds/led1/brightness");
system("echo 0 > /sys/class/leds/led2/brightness");
system("echo 0 > /sys/class/leds/led3/brightness");
}
}
else
{
/*************************************************************************/
tem.append("℃");
hum.append("%");
ill.append("Candela");
ui->illTextBrowser->setText(ill);
ui->humTextBrowser_2->setText(hum);
ui->temTextBrowser_2->setText(tem);
}
}
//开启/关闭智能检测
void MainWindow::abn_pushbutton_ONSlot()
{
abnormalSwitch = true;
QMessageBox::information(this, tr("information"),"开启智能检测成功");
}
void MainWindow::abn_pushbutton_OFFSlot()
{
abnormalSwitch =false;
QMessageBox::information(this, tr("information"),"关闭智能检测成功");
}
其中 abnormalSwitch 这个变量是全局变量,通过两个按钮来改变这个变量的值,如果关
闭按钮点击这个值变成 false
![](https://img-blog.csdnimg.cn/img_convert/00f479f66189c43e6e3cbeb732bcee4c.webp?x-oss-process=image/format,png)
If 语句就不会执行。直接将信息显示到 ui 界面
设备控制模块
这个模块很简单,直接上命令。
Led1 亮/灭
Led2 亮/灭
Led3 亮/灭
root@fsmp1c:~# echo 1 > /sys/class/leds/user1/brightness
root@fsmp1c:~# echo 0 > /sys/class/leds/ user1/brightness
root@fsmp1c:~# echo 1> /sys/class/leds/ user2/brightness
root@fsmp1c:~# echo 0 > /sys/class/leds/ user2/brightness
root@fsmp1c:~# echo 1 > /sys/class/leds/ user3/brightness
root@fsmp1c:~# echo 0 > /sys/class/leds/ user3/brightness
蜂鸣器响
void MainWindow::beepring()
{
int fd;
struct input_event event;
struct timeval time;
fd = open("/dev/input/by-path/platform-beeper-event", O_RDWR);
event.type = EV_SND;
event.code = SND_TONE;
event.value = 1000;
time.tv_sec = 1;
time.tv_usec = 0;
event.time = time;
write(fd, &event, sizeof(struct input_event));
}
让蜂鸣器关的话把 event.value 的值置成 0,将结构体对象再写到
/dev/input/by-path/platform-beeper-event 文件里
天气预报模块
使用 QNetworkAccessManager 类定义一个请求句柄;使用 QNetworkRequest 类
定义一个操作请求。
QNetworkAccessManager *manager;
QNetworkRequest quest;
QNetworkAccessManager 是用来协调网络操作的,即用来操作 QNetworkRequest
请求的 。QNetworkRequest 是 Network Access API 的一部分,是在网络上保
存着发送一个请求的必要信息.它包含一个 URL 和一些辅助信息,可以被用来去
修改请求。打个比方: 你想要带一些水给朋友,这个水就好比
QNetWorkRequest 你的请求,而装水的容器就是 QNetworkAccessManager.。有
了容器你才可以带走水,同理你想要发送请求就需要
QNetworkAccessManager。
这里,在 ui 界面的 comboBox 选择要查看的城市,填充到 QnetWorkRequest
里,使用 get 方法发送请求。
/*********************** 天气模块 *********************/
//点击查询请求天气数据
void MainWindow::weather_cilcked_Slot()
{
QString local_city = ui->comboBox->currentText().trimmed(); //获得需要查询
天气的城市名称
sendQuest(local_city);
}
//get 方法获取信息
void MainWindow::sendQuest(QString cityStr)
{
char quest_array[256] = "http://wthrcdn.etouch.cn/weather_mini?city=";
QNetworkRequest quest;
sprintf(quest_array, "%s%s", quest_array, cityStr.toUtf8().data());
quest.setUrl(QUrl(quest_array));
quest.setHeader(QNetworkRequest::UserAgentHeader, "RT-Thread ART");
manager->get(quest); /*发送 get 网络请求*/
}
建立信号槽连接
connect(manager, SIGNAL(finished(QNetworkReply*)), this,
SLOT(replyFinished(QNetworkReply*)));
当发送请求后,会收到网络的回复信号,只需要使用 QJsonDocument 解析 Json
类型的数据就好了。具体代码如下。
//天气数据处理槽函数
void MainWindow::replyFinished(QNetworkReply *reply)
{
QString all = reply->readAll();
QJsonParseError err;
//解析 json 对象
QJsonDocument json_recv = QJsonDocument::fromJson(all.toUtf8(), &err);
qDebug() << "recv weather data! error:"<< err.error;
if (!json_recv.isNull())
{
QJsonObject object = json_recv.object();
if (object.contains("data"))
{
QJsonValue value = object.value("data"); // 获取指定 key 对应的 value
if (value.isObject())
{
QJsonObject object_data = value.toObject();
if (object_data.contains("forecast"))
{
QJsonValue value = object_data.value("forecast");
if (value.isArray())
{
QJsonObject today_weather = value.toArray().at(0).toObject();
QString weather_type = today_weather.value("type").toString();
QString tuijian = object.value("data").toObject().value("ganmao").toString();
QString low = today_weather.value("low").toString();
QString high = today_weather.value("high").toString();
QString wendu = low.mid(low.length() - 4, 4) + "~" + high.mid(high.length()
- 4, 4);
QString strength = today_weather.value("fengli").toString();
strength.remove(0, 8);
strength.remove(strength.length() - 2, 2);
QString fengli = today_weather.value("fengxiang").toString() + strength;
ui->label_weather_2->setText(weather_type); //显示天气类型
ui->label_temperature_2->setText(wendu);
ui->label_wind_2->setText(fengli);
ui->label_recommend_2->setText(tuijian);
}
}
}
}
}
else
ui->label_recommend_2->setText( "json_recv is NULL or is not a object !");
reply->deleteLater(); //销毁请求对象
}
百度云交互模块
![](https://img-blog.csdnimg.cn/img_convert/a33048b505ec6bb665a6b94819333a96.webp?x-oss-process=image/format,png)
通过 ui 界面输入建立云端设备时生成的 ioTcoreid、DeviceKety、DeviceSecret。使用代码进行组合生成 brokerAddr、以及用户名密码,再使用
MD5 加密算法对密码进行加密得到连接需要的信息。点击计算按钮就将计算的结果复制给成员变量,便于连接时使用。
void MainWindow::pushButton_calculateSlot()
{
username.clear();
password.clear();
password_md5.clear();
brokerAddr.clear();
ioTCoreld = lineEdit_coreid->text();
deviceKey = lineEdit_devkey->text();
deviceSecret = lineEdit_devsecret->text();
brokerPort = "1883";
if(ui->comboBox_city->currentText() =="广州")
brokerAddr =
brokerAddr.append(ioTCoreld).append(".iot.").append("gz").append(".baidubce.com"
);
else
brokerAddr =
brokerAddr.append(ioTCoreld).append(".iot.").append("bj").append(".baidubce.com");
clientId = "zhjt123";
username =
username.append("thingidp").append("@").append(ioTCoreld).append("|").append(de
viceKey)\
.append("|").append(currentTimestamp).append("|").append("MD5");
password =
password.append(deviceKey).append("&").append(currentTimestamp).append("&").a
ppend("MD5")\
.append(deviceSecret);
QByteArray password_md5result;
QCryptographicHash md(QCryptographicHash::Md5);
md.addData(password.toUtf8());
password_md5result = md.result();
password_md5.append(password_md5result.toHex());
}
在 pro 文件里加入
QT += mqtt
然后在 mainwindow.h 加入头文件
#include <QtMqtt/qmqttclient.h>
使用 QmqttClient 定义 m_client 对象用于连接。
QMqttClient *m_client;//mqtt client 指针
将上一步计算好的信息设置到 m_client 对象中,使用
m_client->connectToHost();
进行连接。
void MainWindow::pushButton_connectmqttSlot()
{
//未连接服务器则连接
if (m_client->state() == QMqttClient::Disconnected) {
ui->pushButton_connectmqtt->setText(tr("断开"));
m_client->setHostname(brokerAddr);
m_client->setPort(brokerPort.toInt());
m_client->setUsername(username);
m_client->setPassword(password_md5);
m_client->connectToHost();
// 定时器初始化
InitTimer();
connect(m_client,SIGNAL(connected()),this,SLOT(mqttconnectSlot()));
connect(m_client, SIGNAL(messageReceived(const QByteArray, const
QMqttTopicName)),
this, SLOT(messageReceived(const QByteArray, const
QMqttTopicName)));
} else {//断开连接
ui->pushButton_connectmqtt->setText(tr("连接"));
m_client->disconnectFromHost();
}
}
发布信息
使用定时器设置定时时间,这里设置的是 5s,每隔 5s 会向云端指定的
topic 发布温湿度的数据。
void MainWindow::InitTimer()
{
m_timer = new QTimer;
//设置定时器是否为单次触发。默认为 false 多次触发
m_timer->setSingleShot(false);
//启动或重启定时器, 并设置定时器时间:毫秒
m_timer->start(5000);
//定时器触发信号槽
connect(m_timer,SIGNAL(timeout()),this,SLOT(TimertimeOut()));
}
void MainWindow::TimertimeOut()
{
mqttTopic ="$iot/zhjt/user/fortest";
mqttMessage.clear();
mqttMessage.append("{\"temperature").append("\"").append(":").append(tem)\
.append(",").append("\"humidity").append("\"").append(":").append(hu
m)\
.append(",").append("\"illumination").append("\"").append(":").append
(ill)\
.append("}");
//执行定时器触发时需要处理的业务
// 发布
if(m_client->publish(mqttTopic,mqttMessage.toUtf8()) == -1)
{
QMessageBox::critical(this,"Error","连接断开或输入的 topic 有误,无法
发布",QMessageBox::Yes);
m_timer->stop(); //停止定时器
}
}
订阅信息
订阅 Topic 为 $iot/zhjt/user/control
当有客户端发送 Topic 为 $iot/zhjt/user/control 的数据时,会接收到
数据并进行处理。
void MainWindow::mqttconnectSlot()
{
QString subScribeTopic ="$iot/zhjt/user/control";
m_client->subscribe(subScribeTopic);
}
void MainWindow::messageReceived(const QByteArray &message, const
QMqttTopicName &topic)
{
qDebug()<<"messageReceived:"<<topic.name()<<QString(message);
QJsonObject json_object = QJsonDocument::fromJson(message).object();
if(json_object.value("led1").toInt() ==1)
system("echo 1 > /sys/class/leds/led1/brightness");
else
system("echo 0 > /sys/class/leds/led1/brightness");
if(json_object.value("led2").toInt() ==1)
system("echo 1 > /sys/class/leds/led2/brightness");
else
system("echo 0 > /sys/class/leds/led2/brightness");
if(json_object.value("led3").toInt() ==1)
system("echo 1 > /sys/class/leds/led3/brightness");
else
system("echo 0 > /sys/class/leds/led3/brightness");
if(json_object.value("fan").toInt() ==0)
system("echo 0 > /sys/class/hwmon/hwmon1/pwm1");
else
{
char fan_on[64];
sprintf(fan_on,"echo %d >
/sys/class/hwmon/hwmon1/pwm1",json_object.value("fan").toInt());
system(fan_on);
}
if(json_object.value("beep").toInt() ==1)
beepring();
else
beepunring();
}
微信小程序设计
创建新项目
打开微信开发者工具,新建项目,填写自己的 AppID,新建。
![](https://img-blog.csdnimg.cn/img_convert/8f481c284156f130b17bc51df5500d0b.webp?x-oss-process=image/format,png)
注意:创建工程的路径一定不能有中文。
准备图片
下载几个图标,可以去阿里巴巴矢量图标库进行下载。下载好的图片放在
pages 文件夹下得 image 文件夹中。(没有可以自己新建一个文件夹)
https://www.iconfont.cn/home/index?spm=a313x.7781069.1998910419.2
![](https://img-blog.csdnimg.cn/img_convert/0a79e4e6091c291d79dd3fa284081246.webp?x-oss-process=image/format,png)
修改微信小程序代码
修改 app.json 文件
![](https://img-blog.csdnimg.cn/img_convert/2e968922711f9d4a64db5fe3760e0d96.png)
下载支持 MQTT 协议和 sha1 加密的 js 库
下载 mqtt.js https://github.com/mqttjs/MQTT.js
下载 hex_hmac_sha1.js https://github.com/xihu-fm/aliyun-iotclient-sdk/tree/master/lib
将这两个文件存放到 utils 目录下
![](https://img-blog.csdnimg.cn/img_convert/bdcd9b331b8c3497ebdccfedc0fafb4c.png)
编写 index.wxml,这个文件是用来编写页面的布局。
![](https://img-blog.csdnimg.cn/img_convert/215c8883534b663b46ea3de6c4cd210c.png)
编写 index.wxss,这个文件是用来配置页面的属性。
![](https://img-blog.csdnimg.cn/img_convert/900acfc72ffd712f8cf20b4b1672beb5.png)
编写 index.js,修改设备信息三元组。这个文件用来主要逻辑的编写
![](https://img-blog.csdnimg.cn/img_convert/7903885619203b5a58540437e69db2e5.png)
修改 socket 合法域名
所有的程序编写完成之后,进入调试窗口,就会看到下面这种情况:
![](https://img-blog.csdnimg.cn/img_convert/c25d1e017fd68795245d6bf8fa21c176.png)
说明没有在微信小程序的开发管理中添加这个域名所导致的。
打开小程序开发网页:
https://mp.weixin.qq.com/wxamp/devprofile/get_profile?token=58461
2979&lang=zh_CN
打开开发管理->开发设置->服务器域名,修改 socket 合法域名,添加这个域名即可。
![](https://img-blog.csdnimg.cn/img_convert/9c448b56977f35f25df4f77792fe8089.png)
实验源码
源码路径【4_智慧家庭\实验源码\4_zhjt】
【4_智慧家庭\实验源码\WeChat_zhjt_pro】
注意事项
1.在开发板运行时,需要导入中文字库,否则会因为识别不了中文。
将【4_智慧家庭\工具软件\wqy-zenhei-0.9.47-nightlybuild.tar.gz 或 wqyzenhei-0.8.38-1.tar.gz】复制到 ubuntu 下。并使用 scp 命令将文件拷贝到开发板
的 usr/share/fonts 目录下,使用 tar 命令解压后即可。
linux@ubuntu:~$ scp wqy-zenhei-0.8.38-1.tar.gz
root@192.168.10.128:/usr/share/fonts/
![](https://img-blog.csdnimg.cn/img_convert/ee48f3ba0d8b5892db2cd7e41658332f.png)
2.如果使用 mipi 五寸屏运行此项目,需要进行屏幕旋转以适应屏幕,具体
步骤如下:
在/etc/profile.d/qt-eglfs.sh 添加环境变量如下:
![](https://img-blog.csdnimg.cn/img_convert/0b869b64e5dce3c21a12320a1eead190.png)
![](https://img-blog.csdnimg.cn/img_convert/e47c505dc121fdfa4276473201b47e8e.png)
下面变量的 event0 设备需要填实际的触摸屏设备
![](https://img-blog.csdnimg.cn/img_convert/69485de25b6f8f3e9bf034e0ce97a697.png)
这里即填 event0
export QT_QPA_EGLFS_ROTATION=90
export QT_QPA_EGLFS_NO_LIBINPUT=1
export
QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/event0:rotate=90
时间显示的时候 ARM 系统的时间要和当前时间进行同步需要使用 ntp 服务。
ntpd
ntpd 是一个时间服务。采用柔性时间调整策略,让时间的变化和调整尽量减少对业务的影响。
ntpd 不盲目相信远端时钟,服务器时间和远端时钟超过恐慌阈值(默认 1000 秒),ntpd 甚至会停止时间同步。
ntpd 自己会思考。它相信本地时间可能不对,但是不会忽快忽慢甚至停滞。ntpd 通过多次收发包选择权威稳定的时间源,算出双方间的网络延迟,然后才会采信新的远端时钟进行时间同步。
ntpd 在和时间服务器的同步过程中,会把 BIOS 计时器的振荡频率偏差——或者说 Local Clock 的自然漂移 (drift) ——记录下来。这样即使网络有问题,本机仍然能维持一个相当精确的走时。
在 ubuntu 主机下载安装 ntp 服务
linux@ubuntu:~$ sudo apt-get install ntp
linux@ubuntu:~$ vi /etc/ntp.conf
将里面的文本复制下来,修改 ntpd 配置文件
root@fsmp1c:~# vi /etc/ntp.conf
将刚才复制的粘贴到这个文件下,重启 ntpd 服务
root@fsmp1c:~# systemctl restart ntpd.service
为开发板增加时区,在开发板创建文件夹
root@fsmp1c:~# mkdir /usr/share/zoneinfo
root@fsmp1c:~# mkdir /usr/share/zoneinfo/Asia
进入 ubuntu 时区目录
linux@ubuntu:~$ cd /usr/share/zoneinfo/Asia/
拷贝当前目录下的 shanghai 文件到开发板的/usr/share/zoneinfo/Asia/
linux@ubuntu:~$ scp Shanghai root@192.168.10.105:/usr/share/zoneinfo/Asia/
root@fsmp1c:~#ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
相关文章
- 为 Linux Deepin 添加屏幕保护
- QT-Qt图片按钮类
- QT-Qt界面居中显示
- 每天一个linux命令(24):Linux文件类型与扩展名
- Ubuntu16下编译linux内核,报"mkimage" command not found错的解决
- s3c2410上搭建QT/Embedded4.8.5开发环境(二)--安装arm平台qt库qt-everywhere-opensource-src-4.8.5
- Linux top命令的用法详细详解
- Linux基础之windows linux双系统
- Linux基础之linux服务器服务器间拷贝文件
- Qt音视频开发38-USB摄像头解码linux方案
- Linux四剑客详解——find
- Linux用户和用户组详解
- Linux 命令:pwd、touch、ll、wget
- linux常见命令汇总
- [转]❲阮一峰❳Linux 守护进程的启动方法
- 【Qt】在ubuntu上为可执行程序qt创建图标(亲测)
- 【Qt】在ubuntu上安装qt的mqtt模块
- 9、QT基础——Qt消息机制和事件
- 2、QT基础——创建Qt项目
- [手游新项目历程]-34- linux-127.0.0.1
- 【华为OD机试 2023最新 】 Linux发行版的数量(C++ 100%)
- 解决Ubuntu系统安装QT出错的问题:qt.qpa.plugin: Could not load the Qt platform plugin “xcb“ in ““ even though it
- 【Linux 内核】进程优先级与调度策略 ② ( 获取调度策略对应的进程优先级函数 | sched_get_priority_max 函数 | sched_get_priority_min 函数 )
- 【Linux 内核】调度器 ② ( sched_class 调度类结构体源码 | 源码路径 linux-5.6.18kernelschedsched.h )
- L59.linux命令每日一练 -- 第九章 Linux进程管理命令 -- killall和pkill
- L55.linux命令每日一练 -- 第八章 Linux磁盘与文件系统管理命令 -- mkswap和swapon
- L51.linux命令每日一练 -- 第八章 Linux磁盘与文件系统管理命令 -- mkfs和dumpe2fs
- L33.linux命令每日一练 -- 第五章 Linux信息显示与搜索文件命令 -- du和date
- Linux系统shell脚本之while循环实践1
- 嵌入式linux开发,Linux下访问PHY芯片寄存器,获取phyID号,获取phy的link状态
- 02 从头开始atac项目 ubuntu20 install r4.2 Linux系统环境配置 服务器版本的rstudio r install in linux /ubuntu/centos
- Linux特殊权限