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/,持续更新中。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第1张图片

可实战项目:14个Linux+Qt综合项目案例,8个MP1A物联网拓展项目

项目配套文档及源码,可在下方评论区留言索取~~

1、Linux+Qt综合项目案例:华清远见stm32mp157开发板优势特色部分,包括音乐播放器、智慧家庭、智能工业电表、智能出行助手、智能猫眼、环境监测、智能安防、智能语音识别等10余个项目案例,涉及家居、医疗、农业多种应用方向,在案例中使用了多种物联网和嵌入式技术,包括OT开发、linux应用开发、linux驱动开发、物联网云端接入、MQTT协议、json字符串等知识点。

基于Linux+Qt的智慧家庭项目

项目简介:

智慧家庭又可称为智慧家庭服务平台,是智慧城市的最小单元,是以家庭为载体,以家庭成员之间的亲情为纽带。智慧家庭综合利用物联网、云计算、移动互联网和大数据等新一代信息技术,结合自动控制技术,将家庭设备智能控制、家庭环境感知、家人健康感知、家居安全感知以及信息交流、消费服务等家居生活有效地结合起来,创造出健康、安全、舒适、低碳、便捷、个性化和充满关爱的家庭生活方式。

开发平台:

华清远见stm32mp157开发板豪华套餐(开发板+仿真器+五寸屏+摄像头+资源扩展板+tf卡+读卡器)

项目功能展示:

Wifi 模块

点击刷新按钮,可以实时更新附近的 wifi,选择要连接的 wifi,会弹出输入密码的页面,输入密码,点击连接即可连接成功。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第2张图片
STM32MP157开发板Linux+Qt项目实战:智慧家庭_第3张图片

选择要查看天气的城市,点击获取按钮

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第4张图片

环境监测模块

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第5张图片

设备控制模块

选择按钮即可完成设备的控制

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第6张图片

开启和关闭按钮控制智能监测的开启和关闭,提交按钮上的输入框输入的是智能监测触发的阈值。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第7张图片

连接百度云模块

输入百度云连接三元组,点击获取时间戳,点击计算、连接,即可实现向云端发送环境进而转发到微信小程序,并且通过微信小程序控制设备。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第8张图片

在微信小程序显示温湿度。并且控制开发板 led 灯

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第9张图片

遮挡光电开关,会自动弹出门禁系统。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第10张图片

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 目录。我们需要在终端中赋予安装程序可执行的权限

我们可以使用图形化的文件管理器来查看

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第11张图片

双击“qt-creator-opensource-linux-x86_64-4.10.1.run”图标运行安装程序。

出现如下界面:

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第12张图片

等待程序验证完成后点击“Next”

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第13张图片

这里我们需要登录或者注册一个账号,如果我们之前已经注册过直接登录就可以。如果没有注册过则需要新注册有一个账号后登录。这里笔者已经注册过账号,所以直接登录。

登录成功后出现如下界面,点击 Next

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第14张图片

这里选择安装路径

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第15张图片

可以直接默认,Next

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第16张图片

这路选择安装的组件,直接默认即可

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第17张图片

这里我们需要同意用户协议

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第18张图片

这个界面告诉我们安装完成后需要占用的空间。点击”Install”按钮后开始安装。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第19张图片

安装完成后出现如下界面

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第20张图片

点击“Finish”按钮后将弹出 Qt Creator 主界面

点击“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 ,一般按上面步骤执行后都会有

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第21张图片

点击右侧"Add"按钮,弹出下拉列表后,选择"GCC"的"C"

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第22张图片

填写信息如下,"Name"为"Auto-GCC","Compiler path"点击旁边的"Browse.."按钮选择编译器的路径,例子中的路径是 “/usr/bin/gcc”

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第23张图片

2)配置 G++

点击右侧"Add"按钮,弹出下拉列表后,选择"GCC"的"C++",下面的文本框填写"Name" 为"Auto-G++","Compiler path"点击旁边的"Browse.."按钮选择编译器的路径,例子中的路径是" /usr/bin/g++"。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第24张图片
STM32MP157开发板Linux+Qt项目实战:智慧家庭_第25张图片

填写完成后,点击"Apply"。

3)配置 qmake

选择"Qt Versions"标签,如果有下面红框中的文本,可以跳过下面步骤

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第26张图片

如果没有,在右侧点击"Add..."

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第27张图片

会弹出 qmake 路径选择对话框,这里以"/home/linux/Qt5.14.1/5.14.1/gcc_64/bin/qmake"为例子。 选择”qmake”文件后,击"Open"按钮

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第28张图片

"Version name"改为" Qt %{Qt:Version} GCC"。然后点击"Apply"按钮。

4)配置 Kits

点击左侧"Kits",右侧选择"Kits"标签。检查有没有下图红框选中的文本,如果有可以跳过下面步骤

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第29张图片

然后没有,点击 Add:

在弹出的对话框中"Name"为"Desktop","Device Type"选择"Desktop"选项, "Sysroot"选择目标设备的系统目录,"Compiler"选择之前配置的名称

"Auto-GCC"和"Auto-G++","Qt version"选择之前配 置的名称"Qt 5.14.1

GCC",其它默认即可,最后点击"Apply"和"OK"按钮

Qt Creator 新建工程

注意:工程路径最好不要包含中文、特殊字符、空格等

我们可以新建一个“qt”文件夹,该文件夹用作我们以后存放源代码。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第30张图片

打开 Qt Creator,在欢迎页面点击 “New”按钮,来新建一个工程。

在出现的新建项目窗口中,我们选则“Application”->“Qt Widgets

Application”,然后点击右下方“Choose…”按钮,来创建一个桌面 Qt 应用。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第31张图片

我们在这里设置项目介绍和源码位置,我们这里创建一个名为

“HelloWorld”的示例项目,设置完成之后点击 next

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第32张图片

直接点击 next

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第33张图片

随后进行细节设置,主要设置要创建的源码文件的基本类信息,包括类名等。这里我们可以根据自己的项目特点进行设置。需要说明的一点就是基类的选择,这里基类有 QMainWindow、QWidget、QDialog 三种,它们的不同之处如下:

⚫ QMainWindow 类提供一个带有菜单条,工具条和一个状态条的主应用程序窗口。主窗口通常提供一个大的中央窗口部件,以及周围菜单,工具条,和一个状态栏。QMainWindow 窗口经常被继承,使得封装中央部件,菜单,工具条,状态栏等都变得很容易,当用户点击它的时候,相应的槽就会被调用;

⚫ QWidget 类是所有用户界面对象的基类,窗口部件是用户界面的一个基本单元,它从窗口系统接收鼠标,键盘和其他消息,并在屏幕上绘制自己。一个窗口部件可以被他的父窗口或者是其他窗口挡住一部分;

⚫ QDialog 类是对话框窗口的基类,对话框窗口主要用于短期任务和用户进行短期通讯的顶级窗口,QDialog 可以是模态对话框或者是非模态对话框。QDialog 支持扩展并带有返回值,他们可以带有默认值;我们在这里选择 QDialog 类即可,点击 next 完成类信息设置

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第34张图片

直接点击 next 按钮即可。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第35张图片

然后进行工具选择,该页面可以选择我们创建的工程可以使用的工具,选择想要使用的编译器模块,例如下图 。点击 next

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第36张图片

最后我们设置汇总信息,如果不需要版本控制等功能,直接点击完成finish 即可

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第37张图片

随后我们就进入到了主界面,这时候 Qt 已经帮我们做好了一些准备工作,包括创建了一些文件,写好了一些前置代码等等。

我们可以点击左边 protect 栏,来查看我们的编译选项

我们可以在左下角选择编译 Debug 版或者 Release 版,即调试版或发行版。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第38张图片

左下角绿色剪头是编译并运行,锤子是仅编译,我们可以直接点击绿色小箭头将我们导入的工程编译并运行起来。

点击运行按钮后,我们可以看到 HelloWorld 窗口运行起来了。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第39张图片

导入工程

我们可以将已存在的 Qt 程序项目直接打开,这里以上一章节的HelloWorld 程序为例。首先我们确定源码存在的位置,如 HelloWorld 程序源码在 /home/linux/qt/helloworld 路径下。

点击欢迎页面的“Open” 按钮可以打开已有的工程。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第40张图片

找到我们刚才解压好的源码,选择“helloworld.pro”文件并点击打开

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第41张图片

接下来我们就可以进入到代码编辑界面了。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第42张图片

左上角是项目栏,点击项目名称左边的小箭头可以展开项目目录。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第43张图片

我们可以点击左边项目栏,来查看我们的编译选项。需注意的是构建设置中的路径应与工程路径处于同级目录下。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第44张图片

我们可以在左下角选择编译 Debug 版或者 Release 版,即调试版或发行版。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第45张图片

左下角绿色剪头是编译并运行,锤子是仅编译,我们可以直接点击绿色小箭头将我们导入的工程编译并运行起来。

点击运行按钮后,我们可以看到 HelloWorld 窗口运行起来了。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第46张图片

文件说明

通过上面两个章节,我们学习到了 Qt 程序的新建与导入的方法,也知道了Qt 会帮我们做一些基础工作,比如帮我们建立了一些文件,那么这些文件都是干什么用的呢?我们以HelloWorld 程序来说明一下。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第47张图片

以“.pro”为后缀名的文件,为 Qt 的项目管理文件,存储项目设置的文件;

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第48张图片

“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 对窗体进行可视化设计;

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第49张图片

UI 设计器有以下一些功能区域:

组件面板:窗口左侧是界面设计组件面板,分为多个组,如 Layouts、Buttons、Display Widgets 等,界面设计的常见组件都可以在组件面板里找到。

中间主要区域是待设计的窗体。如果要将某个组件放置到窗体上时,从组件面板上拖放一个组件到窗体上即可。

Signals 和 Slots 编辑器与 Action 编辑器是位于待设计窗体下方的两个编辑器。Signals 和 Slots 编辑器用于可视化地进行信号与槽的关联,Action 编辑器用于可视化设计 Action。

布局和界面设计工具栏:窗口上方的一个工具栏,工具栏上的按钮主要实现布局和界面设计。

对象浏览器(Object Inspector):窗口右上方是 Object Inspector,用树状视图显示窗体上各组件之间的布局包含关系,视图有两列,显示每个组件的对象名称(ObjectName)和类名称。

属性编辑器(Property Editor):窗口右下方是属性编辑器,是界面设计时最常用到的编辑器。属性编辑器显示某个选中的组件或窗体的各种属性及其取值,可以在属性编辑器里修改这些属性的值。属性编辑器的内容分为两列,左侧为属性的名称,右侧为属性的值。属性又分为多个组,实际上表示了类的继承关系,位于下方的类属性组继承自位于上方的类属性组;如果我们需要新建资源文件、源码文件等,可以在项目文件夹出点击鼠标右键,选择 Add New;如果我们有新的文件需要添加,可以在项目文件夹出点击鼠标右键,选择 Add Existing Files。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第50张图片

帮助文档

Qt 的帮助文档是伴随我们学习 Qt 开发的好伙伴。在 Qt 开发过程中,我们会面临图形接口使用的问题,它不像 C 语言那样就那么几个函数接口,图形接口的接口数量可以用海量来形容,常用的我们可能能记住,其它的就没有必要去记了,用到什么就去帮助文档查看用法是比较方便的。我们可以按 F1 按键,或通过上方导航栏的“help->contects”来进入帮助文档。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第51张图片

上方的前进后退按钮方便我们查看文档,如返回到上一步,返回到下一步。

我们可以通过帮助文档来查看以下几个部分:

类使用的相关介绍;

查看相关类的使用介绍,我们可以先进入到帮助文档,然后在左上角选择“Search”。笔者这里以 QWidget 类为例,输入我们想要查找的类的名字,然后双击查找结果来查看说明

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第52张图片

也可以先将鼠标移动到想要查询的类的位置,如图所示,将鼠标移动至“QWidget”处,然后按“F1”键,即可跳转到相应的帮助文档

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第53张图片
STM32MP157开发板Linux+Qt项目实战:智慧家庭_第54张图片

我们可以通过再按一次“F1”键来全窗口查看帮助文档,按“Esc”键可以退出。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第55张图片

部分常用的成员元素包括以下几项:

⚫ 公有成员函数:操作部件属性的相关函数;

⚫ 公有槽函数:Qt 类中已经定义好的槽函数,直接可与信号相连接;

⚫ 信号:软中断,如按下按钮触发 pressed() 信号等;

⚫ 保护成员函数:通常事件所对应的虚函数放在此处;

⚫ 事件:常用事件,如操作鼠标触发的鼠标事件;

滚动鼠标滚轮,向下即可看到“Qwdget Class”类的相关说明了。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第56张图片

1) 查看所用的部件的相应成员函数。

我们可以查找到该类所用部件的相应成员函数的使用方法、功能、参数、

返回值等等,我们以“按钮”控件,即“QPushButton Class”类为例,我们通

过索引搜索的方式,来找到这个类

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第57张图片

我们可以通过点击“Public Functions” 来查看“QPushButton”这个类中的成员函数。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第58张图片

这里以“QPushButton(const QString &text, QWidget *parent =Q_NULLPTR)”为例,我们点击函数名字可以进入到函数详情中。我们可以看到相应的描述为:以“text”为显示内容,以“parent”为父对象,构造一个push 按钮。“text”“parent”为函数参数,由于是构造函数,所以此函数没有返回值。

还有一些函数是继承自其它类的,例如“Public Functions”中有 21 个继承自“QAbstractButton”类的函数,我们点击“QAbstractButton”即可查看。

点击“QAbstractButton”即可查看。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第59张图片
STM32MP157开发板Linux+Qt项目实战:智慧家庭_第60张图片

同样我们可以点击相应的函数进入查看详情。如查看“void setText(const QString &text)”。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第61张图片

2) 查看所用的部件的信号。

我们这里还是以“PushButton”为例,我们点击“Public Slots”。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第62张图片

可以看到“PushButton”本身有一个“void showMenu()”的信号,并且有很多继承自其他类的信号。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第63张图片

一般来说我们用的“PushButton”的信号,最多的是用到其继承自基类“

QAbstractButton”中的几个信号,分别是点击(按下后抬起)、按压(单按下)、释放(单抬起)等。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第64张图片

我们可以点击相应信号查看详情

3) 查看所用的部件的事件(所对应的虚函数如何编写)。部件常用事件主要在 “QWidget”中声明,选择“Events”即可查看相关说明。

每个事件都对应着事件函数。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第65张图片

点击事件函数可查看详情

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第66张图片

微信小程序开发环境搭建

微信小程序开发工具简介

微信小程序是小程序中的一种,英文名 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/

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第67张图片

按照步骤依次注册,输入邮箱,密码,验证码等,同意协议进行注册。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第68张图片
STM32MP157开发板Linux+Qt项目实战:智慧家庭_第69张图片

然后登录自己的邮箱,查阅邮件,点击链接进行激活。进入步骤 3,信息登记,按照网页要求,依次输入信息,身份信息,管理员微信信息,即可激活成功。

返回微信公众平台,输入刚刚注册的账户密码,会需要用管理员微信扫码登录,登录后,下载普通小程序开发者工具。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第70张图片

点击开发,选择开发设置,获取小程序 ID,以备后续开发需求。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第71张图片

微信小程序开发工具下载完成后,进行默认安装即可

创建新项目工程

打开微信小程序开发者工具,点击创建新工程,填写自己的 APPID,选择默认模板,语言选择 JavaScript,点击新建。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第72张图片

新建工程完成为如下界面:

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第73张图片

基本环境配置

打开主界面的右上角的详情按钮,找到本地设置,将“增强编译”和“不校验合法域名”这两个选项进行勾选,因为在小程序的开发阶段,尽量把这两勾选上。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第74张图片

编译、调试

打开 app.json 文件,可以更改微信小程序的标题,改为“工程 demo”。然后按下 Ctrl + S 快捷键进行保存,即可完成编译,调试输出。查看现象。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第75张图片
STM32MP157开发板Linux+Qt项目实战:智慧家庭_第76张图片

项目总体设计介绍

总体框架

智慧家庭系统的设计基于物联网的思想,物联网是新一代信息技术的重要组成部分,其英文名称是“The Internet of things”。其基本思想是以互联网为媒介,实现远程监督、控制。它在各个领域有着非常广泛的应用。

总体框架如下:

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第77张图片

该项目分为 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 的配置

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第78张图片

建立刷新按钮信号槽连接,实现点击刷新按钮界面显示附近 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,增加了鼠标点击事件,当触摸屏点击输入框的时候,会弹出软键盘,用来输入密码

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第79张图片

这里连接 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("连接成

功"));

}

……

智能门禁模块

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第80张图片

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()<

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 为温度标尺,计算公式如下,公式来自与驱动对应代码:

=

175.72 × 1000 × 4

65535 = 10.725097656

in_ temp_offset 为数据偏移,计算公式如下,公式来自于驱动对应代码

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第81张图片

=

−46.85 × 65536

4 × 175.72 = −4368

in_ temp_raw 为原始数据,计算公式如下,公式来自于驱动对应代码:

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第82张图片

=

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

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第83张图片

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(); //销毁请求对象

}

百度云交互模块

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第84张图片

通过 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

使用 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:"<

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,新建。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第85张图片

注意:创建工程的路径一定不能有中文。

准备图片

下载几个图标,可以去阿里巴巴矢量图标库进行下载。下载好的图片放在

pages 文件夹下得 image 文件夹中。(没有可以自己新建一个文件夹)

https://www.iconfont.cn/home/index?spm=a313x.7781069.1998910419.2

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第86张图片

修改微信小程序代码

修改 app.json 文件

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第87张图片

下载支持 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 目录下

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第88张图片

编写 index.wxml,这个文件是用来编写页面的布局。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第89张图片

编写 index.wxss,这个文件是用来配置页面的属性。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第90张图片

编写 index.js,修改设备信息三元组。这个文件用来主要逻辑的编写

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第91张图片

修改 socket 合法域名

所有的程序编写完成之后,进入调试窗口,就会看到下面这种情况:

说明没有在微信小程序的开发管理中添加这个域名所导致的。

打开小程序开发网页:

https://mp.weixin.qq.com/wxamp/devprofile/get_profile?token=58461

2979&lang=zh_CN

打开开发管理->开发设置->服务器域名,修改 socket 合法域名,添加这个域名即可。

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第92张图片

实验源码

源码路径【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

[email protected]:/usr/share/fonts/

2.如果使用 mipi 五寸屏运行此项目,需要进行屏幕旋转以适应屏幕,具体

步骤如下:

在/etc/profile.d/qt-eglfs.sh 添加环境变量如下:

STM32MP157开发板Linux+Qt项目实战:智慧家庭_第93张图片

下面变量的 event0 设备需要填实际的触摸屏设备

这里即填 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 [email protected]:/usr/share/zoneinfo/Asia/

root@fsmp1c:~#ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

你可能感兴趣的:(嵌入式linux+Qt项目实战,嵌入式linux开发,stm32mp157教程,stm32,linux,qt)