linux智能家居产品
1 | - 驱动开发与设备树配置: |
项目介绍
项目名称: 基于 IMX6ULL PRO 的智能家居控制系统
项目概述:本项目开发一套简易智能家居控制系统,利用 IMX6ULL PRO 开发板作为核心硬件平台,搭载 Linux 实时操作系统。项目通过移植和开发各类驱动及应用程序,实现了对家居环境的有效监测与智能控制。
关键技术与实现:
- 驱动开发与设备树配置:
- 成功移植并实现了 DHT11 温湿度传感器、SR501 红外人体感应器、AP3216C 环境光与接近传感器以及 SG90 伺服电机控制器等外设的 Linux 驱动程序。
- 通过对设备树(Device Tree)的精确配置,确保了硬件资源的有效管理和外设的正确识别。
- QT 图形界面移植与开发:
- 使用 Qt 框架开发了用于设备控制和状态显示的图形用户界面,并成功移植到 ARM 架构的开发板上运行。
- MQTT 协议栈移植:
- 移植了 MQTT 协议栈到 ARM 开发板上,确保设备与阿里云服务器之间的稳定数据传输。
- GPIO、I2C、PWM 和中断管理:
- 利用 GPIO(通用输入输出接口)、I2C(串行总线协议)、PWM(脉宽调制)等接口的管理,为外设提供了灵活的控制手段。
- 利用高效的中断处理机制,保证了系统可靠性。
- LCD 显示与 Input 子系统:
- 利用 LCD 显示屏,支持显示图形界面和其他相关信息。
- 开发板集成了 Input 子系统,用于处理来自触摸屏或其他输入设备的用户输入。
- Pinctrl 子系统与同步机制:
- 应用了 Pinctrl(引脚控制)子系统来动态配置引脚功能,提高了系统的灵活性。
- 实现了多任务间的同步与互斥机制,确保了多线程操作的一致性和稳定性。
系统架构与功能:
- 通过集成开发板内置的 Wi-Fi 模块,实现了设备与互联网的无缝连接。
- 移植 MQTT 协议栈,确保了与阿里云服务器之间的高效数据交换。
- 开发了网页端交互界面,支持远程控制家居自动化模块,使得用户可以通过任何联网设备轻松管理家庭环境。
关键技术:驱动开发、应用开发,设备树配置、QT 移植与应用开发、MQTT 移植,gpio、i2c、pwm、Interrupt, lcd,Input 子系统,Pinctrl 子系统,同步与互斥
这是一个基于嵌入式Linux的智能家居项目,目标是在开发板上实现一个可以通过本地界面和微信小程序控制的设备。主要功能包括控制LED灯、显示温湿度值等,并通过MQTT(paho mqtt)协议与云平台通信。
该项目涵盖了硬件设计、嵌入式系统开发、网络通信、图形用户界面设计以及云平台对接等多个领域,涉及的技术栈丰富多样,包括但不限于Linux内核定制、驱动程序开发、Qt框架应用、JsonRPC远程调用、MQTT协议等。整个项目从需求分析、系统设计、代码实现到测试部署,历经多个阶段,历时数月完成,充分体现了我在嵌入式系统开发领域的综合能力。
项目成果与创新点:
- 成功实现了通过本地QT界面以及微信小程序远程控制LED灯开关、显示温湿度值等功能,为用户提供了灵活多样的操作方式。
- 在开发过程中,采用了JsonRPC实现前后台分离的架构设计,提高了系统的可维护性和可扩展性,降低了前后台程序之间的耦合度。
- 利用MQTT协议实现了设备与云平台之间的高效通信,使得设备能够实时上传数据并接收来自云端的控制指令,为后续的智能家居设备互联互通和智能化管理奠定了基础。
- 自主设计并实现了一套完整的智能家居设备控制协议,确保了不同设备之间的兼容性和互操作性,为后续产品的升级和扩展提供了便利。
项目影响力与应用前景:本项目的成功实施不仅为智能家居领域提供了一种可行的技术解决方案,也为相关产品的研发和推广提供了有益的借鉴。通过将嵌入式Linux技术与物联网技术相结合,可以广泛应用于家庭自动化、智能安防、能源管理等多个场景,具有广阔的市场应用前景和商业价值。
关于项目技术细节
1.你在项目中使用了JsonRPC实现前后台分离,具体是如何实现的?为什么要选择这种架构?
具体实现方式:在项目中,前后台程序通过 JsonRPC 进行通信。前台程序负责图形用户界面(GUI)的显示和用户交互,后台程序负责处理硬件操作和业务逻辑。前后台程序分别运行在不同的进程中,通过网络通信(TCP/UDP)进行数据交换。前台程序通过 JsonRPC 客户端向后台程序发送请求,后台程序通过 JsonRPC 服务器接收请求并返回结果。
选择这种架构的原因:
- 降低耦合度:前后台程序分别独立开发,降低了相互之间的依赖,便于维护和扩展。
- 提高可维护性:前后台程序的职责明确,便于单独进行功能测试和优化。
- 支持多平台:前后台程序可以通过网络通信在不同的设备上运行,提高了系统的灵活性和可扩展性。
2.MQTT协议在项目中起到了什么作用?你是如何处理MQTT消息的订阅和发布的?
作用:MQTT 协议在项目中用于实现设备与云平台之间的通信,支持一对多的消息发布和订阅。通过 MQTT 协议,设备可以将数据上传到云平台,同时云平台也可以向设备发送指令。
消息处理:
订阅消息:设备端程序通过 MQTT 客户端订阅云平台的特定主题,如
$thing/down/property/{ProductID}/{DeviceName}
,用于接收来自云平台的指令。发布消息:设备端程序通过 MQTT 客户端向云平台的特定主题发布数据,如
$thing/up/property/{ProductID}/{DeviceName}
,用于上传设备的状态数据。消息格式:消息内容通常为 JSON 格式,便于解析和处理。例如,上传温湿度数据的消息格式如下:
1
2
3
4
5
6
7
8
9{
"method": "report",
"clientToken": "v2530526688yDDou::d791c1a6-1a4b-4a44-b313-f911d704d765",
"timestamp": 1628646783,
"params": {
"temp_value": 28,
"humi_value": 98
}
}
3.在开发嵌入式Linux应用时,你是如何进行系统调试和优化的?有没有遇到什么技术难题,是如何解决的?
调试方法:
日志记录:在关键位置添加日志记录,记录程序的运行状态和错误信息,便于问题排查。
断点调试:使用 GDB 等调试工具设置断点,逐步跟踪程序的执行流程,定位问题。
网络抓包:使用 Wireshark 等工具抓取网络数据包,分析网络通信过程中的问题。
优化方法:
- 代码优化:通过代码分析工具(如 Valgrind)检测内存泄漏和性能瓶颈,优化代码。
- 硬件优化:合理配置硬件资源,如调整 GPIO 引脚的配置,优化驱动程序。
遇到的技术难题及解决方法:
- 问题:在开发过程中,遇到了跨平台兼容性问题,不同开发板的硬件资源和驱动程序存在差异。
- 解决方法:通过编写跨平台的代码,使用条件编译(如
#ifdef
)来处理不同平台的差异。同时,为每个开发板编写详细的硬件资源文档,确保代码的可移植性。
4.你对Qt框架的掌握程度如何?在项目中是如何利用Qt开发图形用户界面的?
- 掌握程度:对 Qt 框架有深入的了解,熟悉 Qt 的主要组件和功能,如 QWidget、QLabel、QPushButton 等。能够使用 Qt Designer 设计图形用户界面,并通过代码实现界面的动态交互。
- GUI 开发:
- 界面设计:使用 Qt Designer 设计界面布局,添加控件(如按钮、标签等)。
- 代码实现:通过 Qt 框架提供的类和方法,实现控件的事件处理和数据绑定。例如,为按钮添加点击事件处理函数,更新标签显示的内容。
关于项目设计与规划
1.项目的整体架构是怎样的?你是如何进行模块划分和功能设计的?
架构设计:项目采用分层架构,分为硬件层、驱动层、后台服务层和前台界面层。
- 硬件层:负责与硬件设备进行直接交互,如 GPIO 控制、温湿度传感器读取等。
- 驱动层:提供硬件设备的驱动程序,封装硬件操作的细节,为上层提供统一的接口。
- 后台服务层:处理业务逻辑,如数据处理、网络通信等。使用 JsonRPC 和 MQTT 协议与前台界面层进行通信。
- 前台界面层:负责图形用户界面的显示和用户交互,通过 JsonRPC 调用后台服务层的接口。
. 模块划分和功能设计
模块划分:
硬件控制模块:负责控制 LED 灯、读取温湿度传感器数据等。
网络通信模块:负责与云平台的 MQTT 通信,处理订阅和发布消息。
数据处理模块:负责处理从硬件设备获取的数据,并将其格式化为 JSON 格式。
用户界面模块:负责显示温湿度数据和控制 LED 灯的开关。
功能设计:
- 本地控制:通过图形用户界面控制 LED 灯的开关,显示温湿度数据。
- 远程控制:通过微信小程序远程控制 LED 灯的开关,获取设备状态数据。
- 数据上传:将设备的温湿度数据上传到云平台。
- 指令接收:接收云平台下发的控制指令,如开关 LED 灯。
2.在项目开发过程中,你是如何进行需求分析和需求变更管理的?
- 需求分析:
- 用户调研:通过与用户沟通,了解用户的需求和期望。
- 功能规划:根据用户需求,规划项目的功能模块和开发计划。
- 技术选型:选择适合的技术栈和工具,确保项目的可行性。
- 需求变更管理:
- 变更记录:记录需求变更的详细信息,包括变更原因、内容和影响范围。
- 影响评估:评估需求变更对项目进度和质量的影响。
- 沟通协调:与团队成员和用户进行沟通,确保需求变更的顺利实施。
3.你如何确保项目的进度和质量?有没有使用过项目管理工具或方法?
- 进度管理:
- 制定计划:根据项目需求,制定详细的开发计划,包括各个阶段的里程碑和时间表。
- 定期检查:定期检查项目进度,及时发现和解决问题。
- 调整计划:根据实际情况,适时调整项目计划,确保项目按时完成。
- 项目管理工具:使用 Trello、Jira 等项目管理工具,跟踪项目进度和任务状态。
1.2.2 软件结构
使用文件 IO 操作硬件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd, ret;
char buf[2];
/* 2. 打开文件 */
fd = open("/dev/100ask_led", O_RDWR);
if (fd == -1) {
printf("can not open file /dev/100ask_led\n");
return -1;
}
if (argc == 3) {
/* write */
buf[0] = strtol(argv[1], NULL, 0); // 哪个LED
if (strcmp(argv[2], "on") == 0)
buf[1] = 0; // 状态
else
buf[1] = 1;
ret = write(fd, buf, 2);
}
close(fd);
return 0;
}
这段代码的主要功能是通过文件操作来控制一个LED灯的开关状态。代码首先尝试以读写模式打开一个名为/dev/100ask_led
的设备文件,这个文件通常代表一个硬件设备,可能是一个LED灯。如果文件打开失败,程序会打印一条错误信息并返回-1,表示程序执行失败。如果文件成功打开,程序会检查命令行参数的数量是否为3。这里期望有两个额外的参数,第一个参数用于指定要控制的LED灯编号,第二个参数用于指定LED灯的状态(开或关)。代码使用strtol
函数将第一个参数转换为长整型数,并存储在buf[0]
中。然后,程序比较第二个参数是否为字符串”on”,如果是,则将buf[1]
设置为0,表示LED灯关闭;如果不是,则将buf[1]
设置为1,表示LED灯打开。最后,程序使用write
函数将buf
数组写入到文件描述符fd
中,以此来控制LED灯的状态。
使用 JsonRPC 实现前后台
把程序拆分为前后台
为何要拆分?
对于比较复杂的程序,前台界面显示、后台程序由不同的团队进行开发,双方定义好交互的接口即可。这样,前台、后台程序可以分别独立开发,降低相互之间的依赖。
比如:
① 当更改硬件,比如更换LED引脚时,前台程序无需改变,只需要修改后台程序
② 想调整界面时,只需要修改前台程序,无需修改后台程序
降低耦合性:前后台程序分别由不同的团队开发,通过定义接口,降低相互依赖。
便于扩展和维护:更改硬件或调整界面时,只需修改对应的部分,无需改动另一部分。
RPC 是远程过程调用(Remote Procedure Call)的意思,而 json 是比较流行的传递信息的格式。
如何拆分?
- 定义接口:前后台程序通过网络通信实现交互,使用JsonRPC远程调用。
- 实现前后台程序:前台程序负责界面显示,后台程序负责具体功能实现,通过JsonRPC进行通信。
网络传输中的 2 个对象: server 和 client
我们经常访问网站,这涉及 2 个对象:网站服务器,浏览器。网站服务器平时安静地呆着,浏览器主动发起数据请求。网站服务器、浏览器可以抽象成 2 个软件的概念: server 程序、 client 程序。
两种传输方式: TCP/UDP
应用层(Application Layer):
- 这是用户直接交互的层,负责处理特定的应用程序细节,如电子邮件、文件传输和网页浏览。它定义了用于应用程序间通信的协议和标准。
运输层(Transport Layer):
负责在网络中的两个节点之间提供可靠的数据传输。它确保数据的完整性和顺序性,并且处理端到端的通信。常见的运输层协议包括TCP(传输控制协议)和UDP(用户数据报协议)。
运输层主要使用以下两种协议:
① 传输控制协议 TCP(Transmission Control Protocol):面向连接的,数据传输的单位是报文段,能够提供可靠的交付。
② 用户数据包协议 UDP(User Datagram Protocol):无连接的,数据传输的单位是用户数据报,不保证提供可靠的交付,只能提供“尽最大努力交付”。
网络层(Network Layer):
- 负责将被称为数据包(datagram)的网络层分组从一台主机移动到另一台主机。
链路层(Data Link Layer):
- 因特网的网络层通过源和目的地之间的一系列路由器路由数据报
物理层(Physical Layer):
- 在物理层上所传数据的单位是比特。物理层的任务就是透明地传送比特流
我们需要使用“运输层”编写应用程序,我们的应用程序位于“应用层”。
使用“运输层”时,可以选择 TCP 协议,也可以选择 UDP 协议。
TCP 和 UDP 原理上的区别
TCP 向它的应用程序提供了面向连接的服务。这种服务有 2 个特点:可靠传输、流量控制(即发送方/接收方速率匹配)。它包括了应用层报文划分为短报文,并提供拥塞控制机制。
UDP 协议向它的应用程序提供无连接服务。它没有可靠性,没有流量控制,也没有拥塞控制。
为何存在 UDP 协议
既然 TCP 提供了可靠数据传输服务,而 UDP 不能提供,那么 TCP 是否总是首选呢?
答案是否定的,因为有许多应用更适合用 UDP,举个例子:视频通话时,使用 UDP,偶尔的丢包、偶尔的花屏时可以忍受的;如果使用 TCP,每个数据包都要确保可靠传输,当它出错时就重传,这会导致后续的数据包被阻滞,视频效果反而不好。
网络编程主要函数介绍
socket函数
此函数用于创建一个套接字。
socket本来也是用于同主机的进程间通信的
后来又了TCP/IP协议族的加入,才能实现网络通信
socket是什么?
是一个编程接口
是一种特殊的文件描述符 (everything in Unix is a file)
并不仅限于TCP/IP协议
并不仅限于linux
面向连接 (Transmission Control Protocol - TCP/IP)
无连接 (User Datagram Protocol -UDP 和 Inter-network Packet Exchange - IPX)
socket是内核给我们提供的函数,将复杂的网络通的过程转换成了我们熟悉的IO操作
TCP/IP协议被集成到操作系统的内核中,引入了新型的“I/O”操作,我们操作套接字,
只需要通过传参的方式,来指定想使用的协议即可。
对套接字的read操作,就是在套接字上接收数据
对套接字的write操作,就是向套接字上发送数据
套接字的类型:
流式套接字(SOCK_STREAM)—-给TCP使用
提供了一个面向连接、可靠的数据传输服务,数据无差
错、无重复的发送且按发送顺序接收。内设置流量控制,
避免数据流淹没慢的接收方。数据被看作是字节流,
无长度限制。
数据报套接字(SOCK_DGRAM)—-给UDP使用
提供无连接服务。数据包以独立数据包的形式被发送,
不提供无差错保证,数据可能丢失或重复,顺序发送,
可能乱序接收。
原始套接字(SOCK_RAW)
可以对较低层次协议如IP、ICMP直接访问。
1 |
|
bind函数
从函数用于将地址绑定到一个套接字。
1 | #include <sys/types.h> |
listen函数
1 | #include <sys/types.h> |
accept函数
服务器使用此函数获得连接请求,并且建立连接。
1 | #include <sys/types.h> |
connect函数
可以用connect建立一个连接,在connect中所指定的地址是想与之通信的服务器的地址
1 | #include <sys/types.h> |
JSON-RPC 示例与情景分析
JSON 是什么
JSON(JavaScript Object Notation, JavaScript 对象表示法)是基于 ECMAScript 的一个子集设计的,是一种开放标准的文件格式和数据交换格式,它易于人阅读和编写,同时也易于机器解析和生成。 JSON 独立于语言设计,很多编程语言都支持 JSON 格式的数据交换。 JSON 是一种常用的数据格式,在电子数据交换中有多种用途,包括与服务器之间的Web 应用程序的数据交换。其简洁和清晰的层次结构有效地提升了网络传输效率,使其成为理想的数据交换语言。其文件通常使用扩展名.json。
基于 JSON-RPC 操作硬件
程序A:调用rpc_led_control发起远程调用,让程序B去控制LED。
程序A:调用rpc_dht11_read发起远程调用,让程序B去读取温湿度的值
在 Ubuntu 中交叉编译程序。先编译 libev 库,再编译 jsonrpc 库,最后编译、执行测试程序测试程序。
编译 libev 库 编译 jsonrpc 库
线程的概念
线程(LWP)是轻量级的进程,进程是资源分配的最小单位,线程是系统调度的最小单位。
线程不会分配内存空间,一个进程内至少有一个线程,叫做主线程,
也可以有多个线程,这多个线程共用进程的内存空间(0-3G)。
多线程没有对进程安全,多线程的效率比较高。
多线程的函数是使用第三方的库函数
编程时,需要包含头文件 #include <pthread.h>
编译时,需要链接线程的函数库 -lpthread
① MQTT 网络技术
MQTT 概述
Paho MQTT:
- Paho MQTT 是 Eclipse Paho 项目的一部分,是一个开源的 MQTT 客户端库,支持多种编程语言,包括 C、C++、Java、Python、JavaScript 等。它的目标是为开发者提供一个简单易用的 MQTT 客户端库,以便在各种平台和设备上实现 MQTT 协议。
MQTT 协议全称是 Message Queuing Telemetry Transport, 即“消息队列遥测传输协议” ,它是物联网常用的应用层协议,运行在 TCP/IP 中的应用层中,依赖 TCP 协议,因此它具有非常高的可靠性。 同时它是基于 TCP 协议的“客户端-服务器” 模型发布/订阅主题消息的轻量级协议。
MQTT 协议提供一对多的消息发布,可以降低应用程序的耦合性,用户只需要编写极少量的应用代码就能完成一对多的消息发布与订阅,该协议是基于“客户端-服务器” 模型,在协议中主要有三种身份:发布者( Publisher)、服务器( Broker)以及订阅者(Subscriber)。其中, MQTT 消息的发布者和订阅者都是客户端,服务器只是作为一个中转的存在,将发布者发布的消息进行转发给所有订阅该主题的订阅者;发布者可以发布在其权限之内的所有主题,并且消息发布者可以同时是订阅者,实现了生产者与消费者的脱耦,发布的消息可以同时被多个订阅者订阅。
MQTT 通信模型示意图如下
MQTT 客户端的功能:
① 发布消息给其它相关的客户端。
② 订阅主题请求接收相关的应用消息。
③ 取消订阅主题请求移除接收应用消息。
④ 从服务端终止连接。
MQTT 服务器常被称为 Broker(消息代理),以是一个应用程序或一台设备,它一般为云服务器,比如 BTA 三巨头的一些物联网平台就是常使用 MQTT 协议,它是位于消息发布者和订阅者之间,以便用于接收消息并发送到订阅者之中,它的功能有:
① 接受来自客户端的网络连接请求。
② 接受客户端发布的应用消息。
③ 处理客户端的订阅和取消订阅请求。
④ 转发应用消息给符合条件的已订阅客户端(包括发布者自身)。
MQTT 概述2
1. MQTT协议概述
- 定义:MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息传输协议,常用于物联网(IoT)领域。它基于TCP/IP协议,支持低带宽、高延迟或不可靠的网络环境。
- 特点:
- 轻量级:协议简单,数据包小,适合在带宽受限的网络中使用。
- 高可靠性:基于TCP协议,确保消息的可靠传输。
- 一对多通信:支持发布/订阅模型,一个发布者可以向多个订阅者发送消息。
- 低功耗:适合电池供电的设备,减少网络通信的能耗。
2. MQTT通信模型
- 发布者(Publisher):负责发布消息到特定主题(Topic)。
- 订阅者(Subscriber):订阅特定主题,接收发布者发送的消息。
- Broker(消息代理):作为中间服务器,接收发布者的消息并转发给订阅者。常见的Broker有EMQX、Mosquitto等。
3. MQTT消息类型
- CONNECT:客户端连接到Broker的请求。
- CONNACK:Broker对连接请求的响应。
- PUBLISH:发布消息到特定主题。
- SUBSCRIBE:订阅特定主题。
- SUBACK:Broker对订阅请求的响应。
- UNSUBSCRIBE:取消订阅特定主题。
- UNSUBACK:Broker对取消订阅请求的响应。
- PINGREQ:客户端发送的心跳检测消息。
- PINGRESP:Broker对心跳检测消息的响应。
- DISCONNECT:客户端断开连接的请求。
4. MQTT QoS(服务质量)
- QoS 0:最多一次,消息可能丢失。
- QoS 1:至少一次,消息可能重复。
- QoS 2:恰好一次,确保消息只被接收一次。
5. 应用场景
- 智能家居:设备之间通过MQTT协议通信,实现远程控制和状态上报。
- 工业物联网:传感器设备通过MQTT将数据发送到服务器,服务器进行数据分析和处理。
- 车联网:车辆与服务器之间通过MQTT协议进行数据交互,实现远程监控和故障诊断。
② 配置文件处理
1**. 配置文件的作用**
- 存储参数:用于存储程序运行时需要的参数,如网络配置、设备信息、用户设置等。
- 灵活性:通过修改配置文件,可以在不修改代码的情况下调整程序的行为。
- 可维护性:便于程序的维护和升级,减少硬编码带来的问题。
2. 配置文件的格式
INI格式:
1
2
3
4
5
6[section1]
key1=value1
key2=value2
[section2]
key3=value3JSON格式:
1
2
3
4
5
6
7
8
9{
"section1": {
"key1": "value1",
"key2": "value2"
},
"section2": {
"key3": "value3"
}
}XML格式:
1
2
3
4
5
6
7
8
9<config>
<section1>
<key1>value1</key1>
<key2>value2</key2>
</section1>
<section2>
<key3>value3</key3>
</section2>
</config>
③ RPC 远程调用
1. RPC概述
- 定义:RPC(Remote Procedure Call)是一种允许一个程序调用另一个程序中的函数或方法的协议,就好像调用本地函数一样。
- 特点:
- 透明性:客户端调用远程函数时,就像调用本地函数一样,无需关心底层的通信细节。
- 异构性:支持不同平台、不同语言之间的通信。
- 效率:减少网络通信的开销,提高通信效率。
2. JSON-RPC协议
定义:JSON-RPC是一种基于JSON格式的RPC协议,使用JSON作为数据交换格式。
消息结构:
请求消息:
JSON
复制
1
2
3
4
5
6{
"jsonrpc": "2.0",
"method": "methodName",
"params": [param1, param2],
"id": 1
}响应消息:
JSON
复制
1
2
3
4
5{
"jsonrpc": "2.0",
"result": "resultValue",
"id": 1
}
错误处理:
JSON
复制
1
2
3
4
5
6
7
8{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found"
},
"id": 1
}
3. RPC在项目中的应用
- 前后台分离:通过RPC实现前后台程序的分离,前台程序负责用户界面,后台程序负责硬件操作和业务逻辑。
- 远程控制:客户端通过RPC调用服务器端的函数,实现远程控制设备的功能。