设计文档、备份

基于OpenHarmony和语音助手的智能家居系统

摘要

随着智能家居逐渐融入日常生活,智能家居的控制方法与智能话手段也在不断革新。传统的使用手机软件点击操控控制的方法,复杂而不实用。同时当前智能家居体验割裂严重,由于智能家居设备的高度相互独立性,即在单个家居产品足够智能化的同时多个家居的联动体验较差,经常需要在手机上下载不同的软件以适应不同品牌产品的差异。
同时对生产厂家而言消除这样的差异也并不容易,由于信息差、智能家居产品功能性能要求各不相同、其内核、芯片和设计底层架构不同,难以实现同步设备差异以达到目标效果
在此有以下的解决方案:如米家通过全线生产智能家居产品实现良好的互联效果;如(未来的)鸿蒙通过类似电脑的操作系统,针对不同资源受限的设备选用合适的OS内核,内核抽象层KAL通过屏蔽多内核差异,对上层提供极差的内核能力,通过HDF驱动框架提供统一的外设访问能力和驱动开发,管理框架。
本作品基于OpenHarmony开源鸿蒙操作系统对智能家居的构想实现了一个简单的通过系统进行差异化的资源分配然后对上层提供统一的基础内核能力的智能家居互联产品。其功能如下:

  • 设备之间数据访问和分布式调度管理
  • 通过终端访问数据与数据交换
  • 语音控制电器开关

关键词:智能家居 语音控制 Openharmony

第一章 绪论

1.1 研究背景及意义

1.1.1 当前全屋智能的实现方案

性能要求:

  • 可定制性。在此不是重点故略。
  • 相互独立性。由于系统没有同意标准的通信方式,因此对于多种通信协议需要采用相关技术屏蔽不同之处,从而对系统更高效的设计。
  • 高扩展性

功能分析:

  • 照明系统
  • 安防监控系统
  • 电器控制系统
  • 场景自定义

系统方案设计

  • 终端设备
  • 网关
  • 服务器
  • 控制终端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2EKWLntK-1653230470807)(/img/1.png)]

1.1.2 通过操作系统整合能力的优势

1.2 研究现状(以国内产品为例)

1.2.1 米家智能家居产品

2016年3月,小米正式发布米家品牌,以承载小米生态链公司的智能家居产品。通过米家APP可以控制家庭的智能设备,并能够实现智能设备之间的互联互通。
米家产品主要核心在于联动与自家有关的设备(米家家居设备),同时打造价格实惠,配网方便简单,操作简单的亲民产品

1.2.2 华为全屋智能

以华为全屋智能为例,多条件动态预判、智能布防撤防、精细化控制管理、
华为智慧生活提供的是一个平台,供各个终端共同联动,理论上可以接入所有支持openharmony/harmonyos的设备,即除了自身生态链产品外,也提供了美的,格力和海尔等第三方品牌接入,与华为智能家居联动

1.3 本文主要工作和论文安排

本文依据OpenHarmony技术特性简单打造了一个基于Openharmony的智能家居微型系统。通过借助手机自带的语音助手,实现对智能家居的电器控制(本作品以点亮led灯为例),极大的方便了传统上通过手机软件控制的繁杂方法。
其本质依然是通过语音助手执行脚本访问url,从而实现远程控制。作品使用支持搭载OpenHarmony操作系统和WIFI模块的Neptune开发板,实现在智能家居场景下的简单控制。

第二章 OpenHarmony技术特性介绍

2.1 技术架构

OpenHarmony整体遵从分层设计,从下向上依次为:内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 组件”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的组件。OpenHarmony技术架构如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u7ul3QVo-1653230470808)(…/img/2.png)]

内核层

  • 内核子系统:采用多内核(Linux内核或者LiteOS)设计,支持针对不同资源受限设备选用适合的OS内核。内核抽象层(KAL,Kernel Abstract Layer)通过屏蔽多内核差异,对上层提供基础的内核能力,包括进程/线程管理、内存管理、文件系统、网络管理和外设管理等。

  • 驱动子系统:驱动框架(HDF)是系统硬件生态开放的基础,提供统一外设访问能力和驱动开发、管理框架。

系统服务层

系统服务层是OpenHarmony的核心能力集合,通过框架层对应用程序提供服务。该层包含以下几个部分:

  • 系统基本能力子系统集:为分布式应用在多设备上的运行、调度、迁移等操作提供了基础能力,由分布式软总线、分布式数据管理、分布式任务调度、公共基础库、多模输入、图形、安全、AI等子系统组成。

  • 基础软件服务子系统集:提供公共的、通用的软件服务,由事件通知、电话、多媒体、DFX(Design For X) 等子系统组成。

  • 增强软件服务子系统集:提供针对不同设备的、差异化的能力增强型软件服务,由智慧屏专有业务、穿戴专有业务、IoT专有业务等子系统组成。

  • 硬件服务子系统集:提供硬件服务,由位置服务、用户IAM、穿戴专有硬件服务、IoT专有硬件服务等子系统组成。

根据不同设备形态的部署环境,基础软件服务子系统集、增强软件服务子系统集、硬件服务子系统集内部可以按子系统粒度裁剪,每个子系统内部又可以按功能粒度裁剪。

框架层

框架层为应用开发提供了C/C++/JS等多语言的用户程序框架和Ability框架,适用于JS语言的ArkUI框架,以及各种软硬件服务对外开放的多语言框架API。根据系统的组件化裁剪程度,设备支持的API也会有所不同。

应用层

应用层包括系统应用和第三方非系统应用。应用由一个或多个FA(Feature Ability)或PA(Particle Ability)组成。其中,FA有UI界面,提供与用户交互的能力;而PA无UI界面,提供后台运行任务的能力以及统一的数据访问抽象。基于FA/PA开发的应用,能够实现特定的业务功能,支持跨设备调度与分发,为用户提供一致、高效的应用体验。

2.2 技术特性

2.2.1硬件互助,资源共享

主要通过下列模块达成

分布式软总线

  • 分布式软总线是多设备终端的统一基座,为设备间的无缝互联提供了统一的分布式通信能力,能够快速发现并连接设备,高效地传输任务和数据。

分布式数据管理

  • 分布式数据管理位于基于分布式软总线之上的能力,实现了应用程序数据和用户数据的分布式管理。用户数据不再与单一物理设备绑定,业务逻辑与数据存储分离,应用跨设备运行时数据无缝衔接,为打造一致、流畅的用户体验创造了基础条件

分布式任务调度

  • 分布式任务调度基于分布式软总线、分布式数据管理、分布式Profile等技术特性,构建统一的分布式服务管理(发现、同步、注册、调用)机制,支持对跨设备的应用进行远程启动、远程调用、绑定/解绑、以及迁移等操作,能够根据不同设备的能力、位置、业务运行状态、资源使用情况并结合用户的习惯和意图,选择最合适的设备运行分布式任务

设备虚拟化

  • 分布式设备虚拟化平台可以实现不同设备的资源融合、设备管理、数据处理,将周边设备作为手机能力的延伸,共同形成一个超级虚拟终端。

2.2.2一次开发,多端部署

OpenHarmony提供用户程序框架、Ability框架以及UI框架,能够保证开发的应用在多终端运行时保证一致性。一次开发、多端部署。

多终端软件平台API具备一致性,确保用户程序的运行兼容性。

  • 支持在开发过程中预览终端的能力适配情况(CPU/内存/外设/软件资源等)。
  • 支持根据用户程序与软件平台的兼容性来调度用户呈现。

2.2.3 统一OS,弹性部署

OpenHarmony通过组件化和组件弹性化等设计方法,做到硬件资源的可大可小,在多种终端设备间,按需弹性部署,全面覆盖了ARM、RISC-V、x86等各种CPU,从百KiB到GiB级别的RAM。

2.3 系统类型

OpenHarmony支持如下几种系统类型:

轻量系统(mini system)

  • 面向MCU类处理器例如Arm Cortex-M、RISC-V 32位的设备,硬件资源极其有限,支持的设备最小内存为128KiB,可以提供多种轻量级网络协议,轻量级的图形框架,以及丰富的IOT总线读写部件等。可支撑的产品如智能家居领域的连接类模组、传感器设备、穿戴类设备等。

小型系统(small system)

  • 面向应用处理器例如Arm Cortex-A的设备,支持的设备最小内存为1MiB,可以提供更高的安全能力、标准的图形框架、视频编解码的多媒体能力。可支撑的产品如智能家居领域的IP Camera、电子猫眼、路由器以及智慧出行域的行车记录仪等。

标准系统(standard system)

  • 面向应用处理器例如Arm Cortex-A的设备,支持的设备最小内存为128MiB,可以提供增强的交互能力、3D GPU以及硬件合成能力、更多控件以及动效更丰富的图形能力、完整的应用框架。可支撑的产品如高端的冰箱显示屏。

根据设备能力不同,总共分为L0-L5级别,它们所代表的领域,如下:

  • L0 128K-1M 手环,智能IoT
  • L1 1M-64M,VR眼镜,智能配件
  • L2 64M-512M 音箱、摄像头,行车记录仪Linux内核
  • L3 512M-2G 智能表
  • L4 2G-4G 电视
  • L5 大于4G手机、平板、电视、车机

第三章 智能家居模拟系统的整体设计方案

3.1功能设计

本作品基于真实的家居环境使用基于Lite-OS子系统的Neptune开发板:(模拟微型无显示屏外设,具备简单控制和运算能力)
实现功能: 通过终端使用语音助手识别发出的语音指令,与开发板建立tcp连接并驱动开发板输出低电平,点亮led灯

3.2控制设计分析

系统架构如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RaaJdrpl-1653230470808)(…/img/3.png)]

3.3相关技术分析

3.3.1 tcp/ip协议简介

TCP/IP 是用于因特网 (Internet) 的通信协议。 TCP/IP 通信协议是对计算机必须遵守的规则的描述,只有遵守这些规则,计算机之间才能进行通信。

TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复 用。通过面向连接、端到端和可靠的数据包发送。通俗说,它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送;而UDP则不为IP提供可靠性、 流控或差错恢复功能。一般来说,TCP对应的是可靠性要求高的应用,而UDP对应的则是可靠性要求低、传输经济的应用。 TCP支持的应用协议主要有:Telnet、FTP、SMTP等; UDP支持的应用层协议主要有:NFS(网络文件系统)、SNMP(简单网络管理协议)、DNS(主域名称系统)、TFTP(通用文件传输协议)等。 TCP/IP协议与低层的数据链路层和物理层无关,这也是TCP/IP的重要特点

TCP(Transimision Control Protocal)特点:

  • 传输控制协议
  • 可靠的、面向连接的协议
  • 传输效率低(相比于UDP)

协议的分层
网络协议通常分不同层次进行开发,每一层分别负责不同的通信功能。一个协议族,比如TCP/IP,是一组不同层次上的多个协议的组合。 传统上来说 TCP/IP 被认为是一个四层协议, 而ISO(国际标准化组织),制定了一个国际标准OSI七层协议模型,OSI协议以OSI参考模型为基础界定了每个阶层的协议和每个阶层之间接口相关的标准。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BWyruMpM-1653230470809)(…/img/4.jpg)]
分层的作用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-glWZcfYY-1653230470809)(…/img/5.jpg)]

3.3.2 socket技术分析

tcp属于传输层协议,socket属于传输层与应用层之间的抽象层
本作品使用tcp服务端与tcp客户端之间进行socket通信。
简单的说,socket是tcpip的具体实现流程,是把TCP协议层的数据发送、接收等的封装。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2goW1PpJ-1653230470810)(…/img/6.jpg)]

socket简介

socket技术是以TCP/IP协议为基础的一组接口函数,应用于通信环节中,在协议中TCP需要建立三次握手的过程建立连接。其中socket定义了三次握手:

在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

socket原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l0Yt3adi-1653230470810)(…/img/7.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WgGKaNY0-1653230470810)(…/img/8.jpg)]

1. 使用socket()函数创建套接字
int socket(int af, int type, int protocol);

af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
大家需要记住127.0.0.1,它是一个特殊IP地址,表示本机地址,后面的教程会经常用到。
type 为数据传输方式,常用的有 SOCK_STREAM 和 SOCK_DGRAM
protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议

3. 使用bind()和connect()函数

  • socket() 函数用来创建套接字,确定套接字的各种属性,然后服务器端要用 bind() 函数将套接字与特定的IP地址和端口绑定起来,只有这样,流经该IP地址和端口的数据才能交给套接字处理;而客户端要用 connect() 函数建立连接
    int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
  • sock 为 socket 文件描述符,addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出
  • connect() 函数用来建立连接,它的原型为:
    int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);

4. 使用listen()和accept()函数

  • 对于服务器端程序,使用 bind() 绑定套接字后,还需要使用 listen() 函数让套接字进入被动监听状态,再调用 accept() 函数,就可以随时响应客户端的请求了。
  • 通过 listen() 函数可以让套接字进入被动监听状态,它的原型为:
    int listen(int sock, int backlog);
  • sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
    所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。

5.请求队列

  • 当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)。

  • 缓冲区的长度(能存放多少个客户端请求)可以通过 listen() 函数的 backlog 参数指定,但究竟为多少并没有什么标准,可以根据你的需求来定,并发量小的话可以是10或者20。

  • 如果将 backlog 的值设置为 SOMAXCONN,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。

  • 当请求队列满时,就不再接收新的请求,对于 Linux,客户端会收到 ECONNREFUSED 错误

注意:listen() 只是让套接字处于监听状态,并没有接收请求。接收请求需要使用 accept() 函数。

  • 当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。它的原型为:
    int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);

  • 它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。

  • accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字,大家注意区分。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。

  • 最后需要说明的是:listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。

6.socket数据的接收和发送

Linux下数据的接收和发送

  • Linux 不区分套接字文件和普通文件,使用 write() 可以向套接字中写入数据,使用 read() 可以从套接字中读取数据。

  • 前面我们说过,两台计算机之间的通信相当于两个套接字之间的通信,在服务器端用 write() 向套接字写入数据,客户端就能收到,然后再使用 read() 从套接字中读取出来,就完成了一次通信。

  • write() 的原型为:
    ssize_t write(int fd, const void *buf, size_t nbytes);
    fd 为要写入的文件的描述符,buf 为要写入的数据的缓冲区地址,nbytes 为要写入的数据的字节数。

  • write() 函数会将缓冲区 buf 中的 nbytes 个字节写入文件 fd,成功则返回写入的字节数,失败则返回 -1。
    read() 的原型为:
    ssize_t read(int fd, void *buf, size_t nbytes);

  • fd 为要读取的文件的描述符,buf 为要接收数据的缓冲区地址,nbytes 为要读取的数据的字节数。

  • read() 函数会从 fd 文件中读取 nbytes 个字节并保存到缓冲区 buf,成功则返回读取到的字节数(但遇到文件结尾则返回0),失败则返回 -1。

7.socket缓冲区以及阻塞模式
每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

缓冲区

  • write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。

  • TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。

  • read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fKFpengV-1653230470810)(…/img/9.png)]

这些I/O缓冲区特性可整理如下:

  • I/O缓冲区在每个TCP套接字中单独存在;
  • I/O缓冲区在创建套接字时自动生成;
  • 即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
  • 关闭套接字将丢失输入缓冲区中的数据。
    输入输出缓冲区的默认大小一般都是 8K,可以通过 getsockopt() 函数获取:
unsigned optVal;
int optLen = sizeof(int);
getsockopt(servSock, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen);
printf("Buffer length: %d\n", optVal);

阻塞模式
对于TCP套接字(默认情况下),当使用 write()/send() 发送数据时:

  1. 首先会检查缓冲区,如果缓冲区的可用空间长度小于要发送的数据,那么 write()/send() 会被阻塞(暂停执行),直到缓冲区中的数据被发送到目标机器,腾出足够的空间,才唤醒 write()/send() 函数继续写入数据。
  2. 如果TCP协议正在向网络发送数据,那么输出缓冲区会被锁定,不允许写入,write()/send() 也会被阻塞,直到数据发送完毕缓冲区解锁,write()/send() 才会被唤醒。
  3. 如果要写入的数据大于缓冲区的最大长度,那么将分批写入。
  4. 直到所有数据被写入缓冲区 write()/send() 才能返回。

当使用 read()/recv() 读取数据时:

  1. 首先会检查缓冲区,如果缓冲区中有数据,那么就读取,否则函数会被阻塞,直到网络上有数据到来。
  2. 如果要读取的数据长度小于缓冲区中的数据长度,那么就不能一次性将缓冲区中的所有数据读出,剩余数据将不断积压,直到有 read()/recv() 函数再次读取。
  3. 直到读取到数据后 read()/recv() 函数才会返回,否则就一直被阻塞。
    这就是TCP套接字的阻塞模式。所谓阻塞,就是上一步动作没有完成,下一步动作将暂停,直到上一步动作完成后才能继续,以保持同步性。

TCP套接字默认情况下是阻塞模式

第四章 智能家居硬件系统

4.1 系统硬件概述/选型与论证

本作品选用集合WIFI与蓝牙双模的OpenHarmony开发板Neptune

4.2 硬件模块选型与论证

基于选用Openharmony操作系统的开发板思路,我们选择以下开发板:
Neptune是一款基于w800芯片的开发板,w800是由“北京联盛德微电子有限责任公司(Winner Micro)”推出的一款芯片,基于C-SKY架构的用于物联网的32位芯片。
开发板具有代表性:成本低,纯内核层操作,适合智能家居小型系统,适用广泛。

4.3 硬件系统架构

硬件原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jba7yGdL-1653230470811)(…/img/10.png)]

第五章 智能家居软件系统

5.1 系统软件概述

软件部分分为:
操作系统的编译与烧录
网页设计
语音助手的脚本编写

5.2 单片机程序设计

核心内容为:驱动GPIO输出低电平

enum LedState {
    LED_ON = 0,
    LED_OFF,
    LED_SPARK,
};

enum LedState g_ledState = LED_SPARK;

static void* GpioTask(const char* arg)
{
    printf("****************");
    printf("led load success");
    printf("****************");
    (void)arg;
    while (1) {
        switch (g_ledState) {
            case LED_ON:
                printf(" LED_ON! \n");
                GpioSetOutputVal(WIFI_IOT_GPIO_PB_08, WIFI_IOT_GPIO_VALUE0);
                osDelay(500);
                break;
            case LED_OFF:
                printf(" LED_OFF! \n");
                GpioSetOutputVal(WIFI_IOT_GPIO_PB_08, WIFI_IOT_GPIO_VALUE1);
                osDelay(500);
                break;
            case LED_SPARK:
                printf(" LED_SPARK! \n");
                GpioSetOutputVal(WIFI_IOT_GPIO_PB_08, WIFI_IOT_GPIO_VALUE0);
                osDelay(100);
                GpioSetOutputVal(WIFI_IOT_GPIO_PB_08, WIFI_IOT_GPIO_VALUE1);
                osDelay(100);
                break;
            default:
                osDelay(500);
                break;
        }
    }

    return NULL;
}

static void GpioIsr(char* arg)
{
    (void)arg;

    enum LedState nextState = LED_SPARK;

    printf(" GpioIsr entry\n");

    GpioSetIsrMask(WIFI_IOT_GPIO_PB_09, 0);
    switch (g_ledState) {
        case LED_ON:
            nextState = LED_OFF;
            break;
        case LED_OFF:
            nextState = LED_ON;
            break;
        case LED_SPARK:
            nextState = LED_OFF;
            break;
        default:
            break;
    }

    g_ledState = nextState;
}

static void GpioExampleEntry(void)
{
    osThreadAttr_t attr;

    GpioInit();
    GpioSetDir(WIFI_IOT_GPIO_PB_08, WIFI_IOT_GPIO_DIR_OUTPUT); // output is 0 PB08 control led

    GpioSetDir(WIFI_IOT_GPIO_PB_09, WIFI_IOT_GPIO_DIR_INPUT); // input is PB09
    IoSetPull(WIFI_IOT_GPIO_PB_09, WIFI_IOT_GPIO_ATTR_PULLHIGH);
    GpioRegisterIsrFunc(WIFI_IOT_GPIO_PB_09, WIFI_IOT_INT_TYPE_EDGE, WIFI_IOT_GPIO_EDGE_FALL_LEVEL_LOW, GpioIsr, NULL);

    attr.name = "GpioTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = LED_TASK_STACK_SIZE;
    attr.priority = LED_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)GpioTask, NULL, &attr) == NULL) {
        printf("[GpioExample] Falied to create GpioTask!\n");
    }
}

SYS_RUN(GpioExampleEntry); // if test add it

5.3 网络通信协议的实现

Neptune服务端(核心代码)

static char request[128] = "";
void TcpServerTest(unsigned short port)
{
    printf("*********************TCP server test *****************");
    int retval = 0;
    int backlog = 1;
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socket
    int connfd = -1;

    struct sockaddr_in clientAddr = {0};
    socklen_t clientAddrLen = sizeof(clientAddr);
    struct sockaddr_in serverAddr = {0};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);  // 端口号,从主机字节序转为网络字节序
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 允许任意主机接入, 0.0.0.0

    retval = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); // 绑定端口
    if (retval < 0) {
        printf("bind failed, %ld!\r\n", retval);
        goto do_cleanup;
    }
    printf("bind to port %d success!\r\n", port);

    retval = listen(sockfd, backlog); // 开始监听
    if (retval < 0) {
        printf("listen failed!\r\n");
        goto do_cleanup;
    }
    printf("listen with %d backlog success!\r\n", backlog);

    // 接受客户端连接,成功会返回一个表示连接的 socket , clientAddr 参数将会携带客户端主机和端口信息 ;失败返回 -1
    // 此后的 收、发 都在 表示连接的 socket 上进行;之后 sockfd 依然可以继续接受其他客户端的连接,
    //  UNIX系统上经典的并发模型是“每个连接一个进程”——创建子进程处理连接,父进程继续接受其他客户端的连接
    //  鸿蒙liteos-a内核之上,可以使用UNIX的“每个连接一个进程”的并发模型
    //     liteos-m内核之上,可以使用“每个连接一个线程”的并发模型
    connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen);
    if (connfd < 0) {
        printf("accept failed, %d, %d\r\n", connfd, errno);
        goto do_cleanup;
    }
    printf("accept success, connfd = %d!\r\n", connfd);
    printf("client addr info: host = %s, port = %d\r\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

    // 后续 收、发 都在 表示连接的 socket 上进行;
    retval = recv(connfd, request, sizeof(request), 0);
    if (retval < 0) {
        printf("recv request failed, %ld!\r\n", retval);
        goto do_disconnect;
    }
    printf("recv request{%s} from client done!\r\n", request);

    retval = send(connfd, request, strlen(request), 0);
    if (retval <= 0) {
        printf("send response failed, %ld!\r\n", retval);
        goto do_disconnect;
    }
    printf("send response{%s} to client done!\r\n", request);

do_disconnect:
    osDelay(500);
    close(connfd);
    osDelay(500);; // for debug

do_cleanup:
    printf("do_cleanup...\r\n");

    close(sockfd);
}

SERVER_TEST_DEMO(TcpServerTest);

第六章 系统功能调试

6.1 系统硬件调试

作品通过WINNER MICRO官方提供的烧录工具Upgrade_Tools进行烧录和串口调试,串口打印如下:

Begin to run the OHOS SYSTEM
Begin to run the user main

 user task:17:20:53:May 17 2022 
ble_npl_eventq_init:bigger queue 64
load our  sec; addr=0x1e 0xa2 0x3c 0x3d 0x1a 0x4c  peer_addr_type=0 ediv=0 rand=0 authenticated=0 ltk=0xca 0xc0 0x77 0x3a 0xaa 0x1b 0x14 0x9e 0x5f 0xed 0x9e 0xbe 0x53 0x0f 0xb5 0xc6  
load peer sec; addr=0x1e 0xa2 0x3c 0x3d 0x1a 0x4c  peer_addr_type=0 ediv=0 rand=0 authenticated=0 ltk=0xca 0xc0 0x77 0x3a 0xaa 0x1b 0x14 0x9e 0x5f 0xed 0x9e 0xbe 0x53 0x0f 0xb5 0xc6  irk=0x9f 0x1d 0x65 0xd3 0xba 0x87 0xb8 0x1e 0x58 0xb1 0xdd 0x62 0x6e 0xb7 0xdb 0x6b  
load our cccd; addr=0x1e 0xa2 0x3c 0x3d 0x1a 0x4c  peer_addr_type=0 chr_val_handle=3(0x0003) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x1e 0xa2 0x3c 0x3d 0x1a 0x4c  peer_addr_type=0 chr_val_handle=35(0x0023) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x1e 0xa2 0x3c 0x3d 0x1a 0x4c  peer_addr_type=0 chr_val_handle=39(0x0027) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x1e 0xa2 0x3c 0x3d 0x1a 0x4c  peer_addr_type=0 chr_val_handle=46(0x002e) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x1e 0xa2 0x3c 0x3d 0x1a 0x4c  peer_addr_type=0 chr_val_handle=50(0x0032) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x1e 0xa2 0x3c 0x3d 0x1a 0x4c  peer_addr_type=0 chr_val_handle=55(0x0037) flags=1(0x0001) value_changed=0(0x0000) 
load our  sec; addr=0x06 0xcc 0x1d 0x36 0x56 0x9c  peer_addr_type=0 ediv=0 rand=0 authenticated=0 ltk=0x5b 0x52 0xb8 0x93 0xa3 0xd7 0x05 0xbd 0xe8 0x50 0x09 0x6b 0x52 0x88 0x7d 0x58  
load peer sec; addr=0x06 0xcc 0x1d 0x36 0x56 0x9c  peer_addr_type=0 ediv=0 rand=0 authenticated=0 ltk=0x5b 0x52 0xb8 0x93 0xa3 0xd7 0x05 0xbd 0xe8 0x50 0x09 0x6b 0x52 0x88 0x7d 0x58  irk=0x8c 0x94 0x10 0xd8 0xd9 0x3d 0x27 0x5b 0xf3 0x3d 0x02 0xf5 0x55 0x24 0x2d 0x7a  
load our cccd; addr=0x06 0xcc 0x1d 0x36 0x56 0x9c  peer_addr_type=0 chr_val_handle=35(0x0023) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x06 0xcc 0x1d 0x36 0x56 0x9c  peer_addr_type=0 chr_val_handle=39(0x0027) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x06 0xcc 0x1d 0x36 0x56 0x9c  peer_addr_type=0 chr_val_handle=46(0x002e) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x06 0xcc 0x1d 0x36 0x56 0x9c  peer_addr_type=0 chr_val_handle=50(0x0032) flags=1(0x0001) value_changed=0(0x0000) 
load our cccd; addr=0x06 0xcc 0x1d 0x36 0x56 0x9c  peer_addr_type=0 chr_val_handle=55(0x0037) flags=1(0x0001) value_changed=0(0x0000) 
00 00:00:00 0 92 D 0/HIVIEW: hilog init success.
00 00:00:00 0 92 D 0/HIVIEW: log limit init success.
00 00:00:00 0 92 I 1/SAMGR: Bootstrap core services(count:3).
00 00:00:00 0 92 I 1/SAMGR: Init service:0x81782bf TaskPool:0x20014d10
00 00:00:00 0 92 I 1/SAMGR: Init service:0x81782e1 TaskPool:0x20015380
00 00:00:00 0 92 I 1/SAMGR: Init service:0x81783e9 TaskPool:0x20015540
00 00:00:00 0 244 I 1/SAMGR: RegisterWifiEvent: 0
EnableWifi: 0
AddDeviceConfig: 0
InitWifiConfig, disconnect wifi...
Init service 0x81782e1  success!
00 00:00:00 0 160 I 1/SAMGR: Init service 0x81782bf  success!
00 00:00:00 0 72 D 0/HIVIEW: hiview init success.
00 00:00:00 0 72 I 1/SAMGR: Init service 0x81783e9  success!
00 00:00:00 0 72 I 1/SAMGR: Initialized all core system services!
00 00:00:00 0 160 I 1/SAMGR: Bootstrap system and application services(count:0).
00 00:00:00 0 160 I 1/SAMGR: Initialized all system and application services!
00 00:00:00 0 160 I 1/SAMGR: Bootstrap dynamic registered services(count:0).
WifiEventCallback status = WIFI_JOIN_SUCCESS
OnWifiConnectionChanged 56, state = 1, info = 
bssid: E2:E5:18:B7:8B:96, rssi: 40, connState: 1, reason: 0, ssid: FsrLab
Connect to FsrLab done, status = 1!
ConnectTo(0): 0
g_connected: 1
WifiEventCallback status = NETIF_IP_NET_UP
[101]: name = 6E 02 0
[0]: ip = 192.168.43.254
[0]: gw = 192.168.43.1
[0]: netmask = 255.255.255.0
[101]: name = 6E 01 0
[1]: ip = 0.0.0.0
[1]: gw = 0.0.0.0
[1]: netmask = 0.0.0.0
[108]: name = 6F 00 0
[2]: ip = 127.0.0.1
[2]: gw = 127.0.0.1
[2]: netmask = 255.0.0.0
After 9 seconds, I will start TcpServerTest test!
After 8 seconds, I will start TcpServerTest test!
After 7 seconds, I will start TcpServerTest test!
After 6 seconds, I will start TcpServerTest test!
After 5 seconds, I will start TcpServerTest test!
After 4 seconds, I will start TcpServerTest test!
After 3 seconds, I will start TcpServerTest test!
data length changed...251, 27
After 2 seconds, I will start TcpServerTest test!
After 1 seconds, I will start TcpServerTest test!
After 0 seconds, I will start TcpServerTest test!
TcpServerTest start
*********************TCP server test *****************
bind to port 5678 success!
listen with 1 backlog success!
accept success, connfd = 1!
client addr info: host = 192.168.43.1, port = 38328
recv request{GET / HTTP/1.1
Host: 192.168.43.254:5678
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux;} from client done!
send response{GET / HTTP/1.1
Host: 192.168.43.254:5678
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux;} to client done!
do_cleanup...
TcpServerTest done!
disconnect to AP ...
Disconnect: -9
UnRegisterWifiEvent: 0
RemoveDevice: 0
DisableWifi: 0
disconnect to AP done!

6.2 系统各功能模块编译程序的调试

编译前需要修改build.gn文件,如下:

生成静态库,gn编译流程的第一部

static_library("demo") {
    sources = [
        "my_test_1.c"
    ]

    include_dirs = [
        "//utils/native/lite/include",
        "//kernel/liteos_m/components/cmsis/2.0",
        "//foundation/communication/interfaces/kits/wifi_lite/wifiservice",
        "//base/iot_hardware/interfaces/kits/wifiiot_lite",
    ]

    if (board_name == "w800" || board_name == "hi3861v100") {
        sources += ["wifi_connecter.c", "net_demo_ohos.c"]
    }

    if (board_name == "w800") {
        include_dirs += [
            "//vendor/winnermicro/w800/src/network/lwip2.0.3/include/",
            "//vendor/winnermicro/w800/include/arch/xt804/csi_core",
            "//vendor/winnermicro/w800/include/arch/xt804",
            "//vendor/winnermicro/w800/include/platform",
            "//vendor/winnermicro/w800/include/os",
            "//vendor/winnermicro/w800/include/net",
            "//vendor/winnermicro/w800/include/app",
            "//vendor/winnermicro/w800/include/wifi",
            "//vendor/winnermicro/w800/include",
        ]
    }
}

编译文件,需要引用待编译的静态库

import("//build/lite/config/component/lite_component.gni")

lite_component("app") {
    features = [
        #"iothardware:gpio_example",
        #"my_app:my_app",
        #"wifitest:wifi_test",
        #"uart_sample:uart_demo",
        #"demolink:example_demolink",
        #"my_app_gpio:my_app_gpio",
        #"network:net_demo",
        "my_demo:demo",
    ]
}

6.3 系统功能验证测试

见演示视频,系统各功能配合完好。串口打印正常,通信延时较短

参考文献

[1]孙威. 基于物联网技术的智能家居控制系统研究[D].吉林建筑大学,2021.DOI:10.27714/d.cnki.gjljs.2021.000055.
[2]杨诗宇. 基于人体行为识别算法的智能家居系统研究[D].贵州大学,2021.DOI:10.27047/d.cnki.ggudu.2021.000130.

你可能感兴趣的:(harmonyos)