基于全志H616智能家居项目
文章目录
一、实现功能
- 实现Socket服务器远程控制客厅灯、厨房灯、泳池灯、卧室灯、卫生间灯的打开和关闭。
- 通过SU-03T语音模块控制客厅灯、厨房灯、泳池灯、卧室灯、卫生间灯的打开和关闭。
- 当检测到火焰的时候蜂鸣器报警
- 当检测到可燃气体泄露的时候蜂鸣器报警,并打开排风扇。
- 通过语音模块和Socket服务器控制红外发射模块,实现无线红外遥控控制加湿器打开与关闭
- 实现摄像头人脸识别开锁功能,和视频实时监控功能
二、项目开发环境&实现思路
2.1 开发环境:
- 开发板:OrangePi Zero 2
- 开发语言:C、Python
- 编程工具:VS Code
- SSH远程登录树莓派:MobaXterm
2.1 实现思路:
- 项目架构采用简单工厂模式来设计,将TCP服务器、语音识别、设计成链表的每个节点,形成控制工厂。灯光、门锁、火焰检测、烟雾检测、风扇、蜂鸣器等也设计成链表的每个节点,形成设备端工厂。基于这种架构添加新功能的时候只要添加一个链表节点文件就可以了,项目的稳定性和拓展性都做的不错。
工厂的类:
struct InputCommand { //指令工厂类 char commandName[128]; //设备名称 char deviceName[128]; char command[128]; //指令内容 int (*init)(struct InputCommand *voicer, char *ipAdress, char *port); //初始化函数 int (*getCommand)(struct InputCommand *voicer); //获取指令函数 int (*putOpenCommand)(struct InputCommand *voicer); //发送打开指令函数 int (*putCloseCommand)(struct InputCommand *voicer); //发送关闭指令函数 int fd; //文件描述符 char port[12]; //端口号 char ipAdress[32]; //IP地址 int s_fd; //套接字描述符 char log[1024]; //日志信息 struct InputCommand *next; //链表下一个节点 };
struct Devices { // 控制设备类 char devicesName[128]; // 设备名称 int devicesPin; // 设备引脚 int status; // 设备状态 int (*open)(int pin); // 打开设备 int (*close)(int pin); // 关闭设备 int (*devicesInit)(int pin); // 初始化设备 int (*readStatus)(int pin); // 读取设备状态 int (*changeStatus)(int status); // 修改设备状态 struct Devices *next; // 链表下一个节点 };
工厂的对象:
三、硬件模块和接线
3.1 硬件模块准备:
首先我们要准备我们智能家居的房子模型和用到的模块传感器,如下:
3.2 硬件模块接线:
四、香橙派使用USB摄像头
详细可参考《OrangePi_Zero2_H616用户手册v4.0.pdf》 中的3.13.6 USB摄像头测试章节。
4.1 将USB摄像头接入香橙派:
4.2 通过lsmod指令查看内核是否加载USB摄像头模块:
lsmod //列出当前加载的内核模块
lsmod
命令用于列出当前加载的内核模块。它会显示已加载模块的信息,包括模块的名称、大小、使用次数等- 每一行表示一个加载的模块,其中包括模块的名称、大小、以及使用该模块的其他模块等信息。
4.3 通过 v4l2-ctl 指令查看USB 摄像头的设备节点信息:
orangepi@orangepi:~$ sudo apt update
orangepi@orangepi:~$ sudo apt install -y v4l-utils
orangepi@orangepi:~$ v4l2-ctl --list-devices
USB 2.0 Camera (usb-sunxi-ehci-1):
/dev/video0
通过 v4l2-ctl 命令可以看到 USB 摄像头的设备节点信息为/dev/videox(x有可能是0 1或者2等数 字),所以我们需要测试一下:
4.4 使用 fswebcam 测试 USB 摄像头:
4.4.1 安装fswebcam:
orangepi@orangepi:~$ sudo apt update
orangepi@orangepi:~$ sudo apt-get install -y fswebcam
4.4.2 安装完 fswebcam 后可以使用下面的命令来拍照:
- -d 选项用于指定 USB 摄像头的设备节点
- –no-banner 用于去除照片的水印
- -r 选项用于指定照片的分辨率
- -S 选项用设置于跳过前面的帧数
- ./image.jpg 用于设置生成的照片的名字和路径
sudo fswebcam -d /dev/video1 --no-banner -r 1280x720 -S 5 ./test.jpg
在服务器版的 linux 系统中,拍完照后可以直接通过mobaxterm拖到电脑桌面看或者使用 scp 命令将拍好的图片传到Ubuntu PC 上镜像观看:
orangepi@orangepi:~$ scp image.jpg pg@192.168.1.55:/home/pg/ #根据自己的实际家目录修改pg
我们可以看到这就是我们的USB摄像头拍摄的一张照片,虽然像素不怎么滴!!!
4.5 使用 mjpg-streamer 测试 USB 摄像头:
4.5.1 下载 mjpg-streamer:
Github 的下载地址:
orangepi@orangepi:~$ git clone https://github.com/jacksonliam/mjpg-streamer
Gitee 的镜像下载地址为:
orangepi@orangepi:~$ git clone https://gitee.com/leeboby/mjpg-streamer
我们使用Gitee 的镜像下载方式。
4.5.2 安装依赖的软件包:
Ubuntu 系统:
orangepi@orangepi:~$ sudo apt-get install -y cmake libjpeg8-dev
Debian 系统:
orangepi@orangepi:~$ sudo apt-get install -y cmake libjpeg62-turbo-dev
4.5.3 编译安装 mjpg-streamer:
orangepi@orangepi:~$ cd mjpg-streamer/mjpg-streamer-experimental
orangepi@orangepi:~/mjpg-streamer/mjpg-streamer-experimental$ make -j4
orangepi@orangepi:~/mjpg-streamer/mjpg-streamer-experimental$ sudo make install
4.5.4 输入下面命令启动 mjpg_streamer:
注意,video 的序号不一定都是 video0,请以实际看到的为准。
orangepi@orangepi:~/mjpg-streamer/mjpg-streamer-experimental$ export LD_LIBRARY_PATH="$(pwd)"
orangepi@orangepi:~/mjpg-streamer/mjpg-streamer-experimental$ sudo ./mjpg_streamer -i "./input_uvc.so -d /dev/video1 -u -f 30" -o "./output_http.so -w ./www"
同时也可以根据自带的shell脚本来配置:
修改 start.sh脚本,将start.sh里的:
./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"
字段修改为:
./mjpg_streamer -i "./input_uvc.so -d /dev/video1 -u -f 30" -o "./output_http.so -w ./www"
/* 注意这里的video1需要根据实际情况修改 */
这样就可以通过执行./start.sh
运行摄像头了。
4.5.5 局域网设备通过开发板IP地址和端口号查看摄像头:
在和开发板同一局域网的 Ubuntu PC 或者 Windows PC 或者手机的浏览orange Pi器中输入【开发板的IP地址:8080】就能看到摄像头输出的视频了:
推荐使用 mjpg-streamer 来测试 USB 摄像头,比 motion 流畅很多,使用mjpg-streamer 感觉 不到任何卡顿
4.6 使用wget指令拍一张照片:
sudo fswebcam -d /dev/video1 --no-banner -r 1280x720 -S 5 /home/orangepi/tmp/test.jpg
wget http://192.168.31.249:8080/?action=snapshot -O /home/orangepi/tmp/shiyahaohome.jpg
4.7 设置摄像头服务每次开机自启动:
4.7.1 创建一个mjpg.sh脚本并赋予可执行的权限:
touch mjpg.sh
- 在这个shell脚本下添加以下内容:
# !/bin/bash //指定用bash执行
cd /home/orangepi/mjpg-streamer/mjpg-streamer-experimental //需要执行文件的路径
./start.sh //执行
- 给mjpg.sh赋予可执行的权限:
chmod +x mjpg.sh
4.7.2 设置开机自启动:
- 在**/etc/xdg/autostart/下创建mjpg.desktop** 文件输入以下内容,/etc/xdg/autostart这个文件夹下面的.desktop文件会在开机的时候自动启动
cd /etc/xdg /autostart/
sudo vi mjpg.desktop
[Desktop Entry]
Name=mjpg //名字
Exec=/home/orangepi/mjpg.sh //执行命令
Type=Application //类型是应用
NoDisplay=true //不显示
然后重启我们的系统查看服务有没有启动,由于我全志的ubtunu的版本装错了,所以在**/etc/xdg/目录下没有autostart**,所以我们在项目中手动的打开USB摄像头的服务。
五、阿里云人脸识别方案
5.1 接入阿里云:
在智能家居的项目中,我们采用阿里云视觉智能开发平台的接口来做人脸识别的识别方案,通过两张人脸1:1比对的方式进行人脸识别,以此来判断是不是本人
阿里云视觉智能开放平台网址:[通义实验室视觉智能开放平台 (aliyun.com)](https://vision.aliyun.com/)
https://vision.aliyun.com/experience/detail?tagName=facebody&children=CompareFace
我们先点击“技术文档”查看使用方法:
根据技术文档中的接入指引我们应该怎么做:
- 开通阿里云账号及人脸人体服务,用自己支付宝即可开通
- 创建并获取AccessKey ID和Secret
- 在Linux或开发板上安装所需的SDK
- 根据示例代码进行修改人脸比对识别
5.1.1 开通阿里云账号及人脸识别服务:
首先点击上面的“立即开通”,然后用自己的支付宝扫描二维码进行开通,然后同意下面的服务协议,然后开始实名认证,实名认证之后就开通成功了,点击进入:控制台
5.1.2 创建并获取AccessKey ID和Secret:
登录完成后,即可在产品控制台->点击获取Acce Token,获取对应的AccessKey ID和AccessKey Secret
当然,在第一次获取到AccessKey ID和AccessKey Secret,需要点击创建AccessKey, 然后最好把 AccessKey.csv下载下来备份,不然会找不到AccessKey Secret就需要重新创建。
5.1.3 安装图像识别SDK:
sudo apt install python3-pip
pip3.10 install alibabacloud_facebody20191230
我们可以看到人脸识别的SDK安装成功了
5.1.4 配置环境变量:
同时配置Linux环境变量,根据自己实际的ACCESS_KEY_ID和ACCESS_KEY_SECRET,下面的两行写入到家目录下的.bashrc中:
vi ~/.bashrc
//然后在末尾输入以下内容:
export ALIBABA_CLOUD_ACCESS_KEY_ID="XXX" //根据自己实际的ID填写
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="XXXX" //根据自己实际的SECRET填写
保存退出后,然后重启香橙派,然后输入export
验证:
可见,ACCESSKEY已经成功被配置成为环境变量!
5.1.5 获取阿里云人脸比对Python实例代码:
文件在本地或可访问的URL:
# -*- coding: utf-8 -*-
# 引入依赖包
# 最低SDK版本要求:facebody20191230的SDK版本需大于等于4.0.8
# 可以在此仓库地址中引用最新版本SDK:https://pypi.org/project/alibabacloud-facebody20191230/
# pip install alibabacloud_facebody20191230
import os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import CompareFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions
config = Config(
# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。
# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。
# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。
access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
# 访问的域名
endpoint='facebody.cn-shanghai.aliyuncs.com',
# 访问的域名对应的region
region_id='cn-shanghai'
)
runtime_option = RuntimeOptions()
compare_face_request = CompareFaceAdvanceRequest()
#场景一:文件在本地
streamA = open(r'/tmp/CompareFace-right.png', 'rb')
compare_face_request.image_urlaobject = streamA
#场景二,使用任意可访问的url
urlB = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/facebody/CompareFace/CompareFace-left1.png'
imgB = urlopen(urlB).read()
compare_face_request.image_urlbobject = io.BytesIO(imgB)
try:
# 初始化Client
client = Client(config)
response = client.compare_face_advance(compare_face_request, runtime_option)
# 获取整体结果
print(response.body)
except Exception as error:
# 获取整体报错信息
print(error)
# 获取单个字段
print(error.code)
# tips: 可通过error.__dict__查看属性名称
# 关闭流
streamA.close()
5.1.6 根据示例代码进行修改进行人脸比对1:1识别:
我们下载下来之后要对这个代码进行修改,修改如下:
# -*- coding: utf-8 -*-
# 引入依赖包
# 最低SDK版本要求:facebody20191230的SDK版本需大于等于4.0.8
# 可以在此仓库地址中引用最新版本SDK:https://pypi.org/project/alibabacloud-facebody20191230/
# pip install alibabacloud_facebody20191230
import os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import CompareFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions
def face_detect(): #定义成一个函数,方便调用!
config = Config(
# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。
# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。
# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。
access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
# 访问的域名
endpoint='facebody.cn-shanghai.aliyuncs.com',
# 访问的域名对应的region
region_id='cn-shanghai'
)
runtime_option = RuntimeOptions()
compare_face_request = CompareFaceAdvanceRequest()
#场景一:文件在本地
streamA = open(r'/home/orangepi/tmp/shiyahao.jpg', 'rb') #预存的照片
compare_face_request.image_urlaobject = streamA
streamB = open(r'/home/orangepi/tmp/shiyahaoTest.jpg', 'rb') #待测试的照片
compare_face_request.image_urlbobject = streamB
try:
# 初始化Client
client = Client(config)
response = client.compare_face_advance(compare_face_request, runtime_option)
# 获取整体结果
print(response.body)
except Exception as error:
# 获取整体报错信息
print(error)
# 获取单个字段
print(error.code)
# tips: 可通过error.__dict__查看属性名称
# 关闭流
streamA.close()
streamB.close()
if __name__ == '__main__': #写一个main调用face_detect函数来测试
face_detect()
我使用了自己的照片作为标准,并使用了两张照片进行测试,第一张是我本人的另一张照片,第二张是我女朋友的照片:
三张照片放到了我们的根目录下的/tmp路径下,下面我们就进行人脸比对测试:
我们可以看到当我使用自己照片的时候,置信度约为:90%,使用我女朋友的照片的时候,置信度仅为:9%
5.16.7 相似度的提取和转化:
我们在上面确实能够进行人脸的比对,但是返回的是一个非常复杂的数据体,所以我们将相似度单独返回,并且只保留整数部分:
# 获取单个字段
confidence = response.body.to_map()['Data']['Confidence'] #to_map()函数很重要,不要忘记
score = int(confidence)
print("相似度分数为:", score)
return score
现在就可以返回相似度的整数部分了,而在之后的项目代码中我们就可以根据这个整数score进行判断了
5.2 C语言调用阿里云Python接口实现人脸识别:
我们在上面的人脸识别测试中,写出了调用阿里云人脸识别的Python程序,现在我们就要用C语言来调用Python的接口进行人脸识别!
5.2.1 C语言调用Python接口步骤:
5.2.2 封装人脸比对接口供项目使用:
face.py:
# -*- coding: utf-8 -*-
# 引入依赖包
# 最低SDK版本要求:facebody20191230的SDK版本需大于等于4.0.8
# 可以在此仓库地址中引用最新版本SDK:https://pypi.org/project/alibabacloud-facebody20191230/
# pip install alibabacloud_facebody20191230
import os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import CompareFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions
def face_detect(): #定义成一个函数,方便调用!
config = Config(
# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。
# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。
# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。
access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
# 访问的域名
endpoint='facebody.cn-shanghai.aliyuncs.com',
# 访问的域名对应的region
region_id='cn-shanghai'
)
runtime_option = RuntimeOptions()
compare_face_request = CompareFaceAdvanceRequest()
#场景一:文件在本地
streamA = open(r'/home/orangepi/tmp/shiyahao.jpg', 'rb') #预存的照片
compare_face_request.image_urlaobject = streamA
streamB = open(r'/home/orangepi/tmp/raonaibao.jpg', 'rb') #待测试的照片
compare_face_request.image_urlbobject = streamB
try:
# 初始化Client
client = Client(config)
response = client.compare_face_advance(compare_face_request, runtime_option)
# 获取整体结果
#print(response.body)
# 获取单个字段
confidence = response.body.to_map()['Data']['Confidence'] #to_map()函数很重要,不要忘记
score = int(confidence)
print("相似度分数为:", score)
return score
except Exception as error:
# 获取整体报错信息
print(error)
# 获取单个字段
print(error.code)
# tips: 可通过error.__dict__查看属性名称
# 关闭流
streamA.close()
streamB.close()
peopleFace.c:
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <Python.h>
void face_init(void) //初始化python环境
{
Py_Initialize();
PyObject *sys = PyImport_ImportModule("sys");
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Append(path, PyUnicode_FromString("."));
}
void face_final(void) //释放python环境
{
Py_Finalize();
}
int face_score(void) //python下face_detect函数返回的是已经经过提取和取证过的置信度score,是个int型
{
PyObject *pModule = PyImport_ImportModule("face"); //加载python文件
if (!pModule)
{
PyErr_Print();
printf("Error: failed to load module\n");
goto FAILED_MODULE; //goto的意思就是如果运行到这里就直接跳转到FAILED_MODULE
}
PyObject *pFunc = PyObject_GetAttrString(pModule, "face_detect"); //加载python文件中的对应函数
if (!pFunc)
{
PyErr_Print();
printf("Error: failed to load function\n");
goto FAILED_FUNC;
}
PyObject *pValue = PyObject_CallObject(pFunc, NULL);
if (!pValue)
{
PyErr_Print();
printf("Error: function call failed\n");
goto FAILED_VALUE;
}
int result = 0;
if (!PyArg_Parse(pValue, "i", &result)) //ace_detect函数返回的是已经经过提取和取证过的置信度score,是个int型,用‘i’表示
{
PyErr_Print();
printf("Error: parse failed");
goto FAILED_RESULT;
}
/* 如果函数返回的是字符串,上面的PyArg_Parse则需要用‘s’来表示,且下面注释的代码非常重要,因为字符串名代表了其首地址,所以不能直接复制而是需要使用strncpy函数!!!
category = (char *)malloc(sizeof(char) * (strlen(result) + 1) ); //开辟一个新的字符串常量。+1是为了留出空间给\0
memset(category, 0, (strlen(result) + 1)); //初始化字符串
strncpy(category, result, (strlen(result) + 1)); //将result的结果复制给新的字符串
*/
FAILED_RESULT:
Py_DECREF(pValue);
FAILED_VALUE:
Py_DECREF(pFunc);
FAILED_FUNC:
Py_DECREF(pModule);
FAILED_MODULE:
return result;
}
peopleFace.h:
#ifndef __PEOPLEFACE_H__
#define __PEOPLEFACE_H__
void face_init(void); //初始化人脸识别引擎
void face_final(void); //释放人脸识别引擎
int face_score(void); //人脸识别
#endif
peopleFaceTest.c:
#include <stdio.h>
#include "peopleFace.h"
int main()
{
int score = 0;
face_init(); //初始化人脸识别引擎
score = face_score(); //获取人脸识别得分
printf("score=%d\n", score); //打印得分
face_final(); //释放人脸识别引擎
return 0;
}
六、SU-03T语音模块的配置和烧录
进入语音模块官网 [智能公元/AI产品零代码平台 (smartpi.cn)](https://blog.csdn.net/weixin_54859557/article/details/141267233?spm=1001.2014.3001.5502) 配置词条和识别后的串口输入输出指令,如下:
6.1 创建产品:
6.2 PIN引脚设置:
6.3 唤醒词自定义:
6.4 命令词定义和控制详情:
6.5 发音人配置:
6.6 其他配置:
6.7 烧录固件:
6.8 测试语音模块:
语音模块的固件烧录完成之后,接下来我们就测试烧录的固件,首先先把我们的语音模块和USB转TTL模块交叉相连,然后打开串口助手,根据我们定义的识别命令进行呼喊,查看发过来的数据是否正确:
七、智能家居项目代码实现
7.1 智能家居项目工程目录:
代码文件 | 文件属性 |
---|---|
BEEP.c | 蜂鸣器设备 |
camera.c | 摄像头设备 |
ceSuoLight.c | 卫生间灯设备 |
chuFangLight.c | 厨房灯设备 |
contrlDevices.h | 设备工厂 |
face.py | 人脸识别接口 |
fan.c | 排风扇设备 |
fire.c | 火焰传感器设备 |
InputCommand.h | 指令工厂 |
keTingLight.c | 客厅灯设备 |
lock.c | 电磁锁设备 |
main.c | 主函数 |
MQ-5.c | 烟雾传感器设备 |
peopleFace.c | 人脸识别接口封装 |
peopleFace.h | 人脸识别接口封装 |
serialSu03t.c | 串口指令 |
Socket.c | 网络指令 |
woShiLight.c | 卧室灯设备 |
yongChiLight.c | 泳池灯设备 |
7.2 项目代码:
- 指令工厂相关代码:
/* InputCommand.h */
#ifndef __INPUTCOMMAND_H__
#define __INPUTCOMMAND_H__
#include <stdio.h> // 包含标准输入输出头文件
#include <sys/types.h> // 包含系统数据类型头文件
#include <sys/socket.h> // 包含系统套接字库的头文件
#include <stdlib.h> // 包含标准库头文件
#include <arpa/inet.h> // 包含网络地址转换头文件
#include <netinet/in.h> // 包含IPv4地址头文件
#include <string.h> // 包含字符串头文件
#include <unistd.h> // 包含unistd.h头文件
#include <wiringSerial.h> // 包含wiringSerial库头文件,用于串口通信
#include <wiringPi.h> // 包含wiringPi库头文件,用于GPIO控制
struct InputCommand { //指令工厂类
char commandName[128]; //设备名称
char deviceName[128];
char command[128]; //指令内容
int (*init)(struct InputCommand *voicer, char *ipAdress, char *port); //初始化函数
int (*getCommand)(struct InputCommand *voicer); //获取指令函数
int (*putOpenCommand)(struct InputCommand *voicer); //发送打开指令函数
int (*putCloseCommand)(struct InputCommand *voicer); //发送关闭指令函数
int fd; //文件描述符
char port[12]; //端口号
char ipAdress[32]; //IP地址
int s_fd; //套接字描述符
char log[1024]; //日志信息
struct InputCommand *next; //链表下一个节点
};
struct InputCommand* addsocketCommandToInputCommandLink(struct InputCommand *phead); //向输入指令链表中添加socket指令
struct InputCommand *addVoiceContrlToInputCommanderLink(struct InputCommand *phead); //向输入指令链表中添加语音模块指令
#endif
/* serialSu03t.c */
#include "InputCommand.h"
int getCommand(struct InputCommand *voicer) //获取语音指令
{
int nread = 0;
memset(voicer->command, '\0', sizeof(voicer->command)); //清空指令内容
nread = read(voicer->fd, voicer->command, sizeof(voicer->command)); //读取指令内容
printf("指令内容:%s, 长度:%d\n", voicer->command, nread); //打印指令内容
return nread;
}
int putOpenCommand(struct InputCommand *infrared) //发送打开加湿器指令
{
int len;
char infraredOpenCmd[5] = {0xA1, 0xF1, 0x00, 0x00, 0x19};
len = write(infrared->fd, infraredOpenCmd, sizeof(infraredOpenCmd)); //发送指令内容
return len;
}
int putCloseCommand(struct InputCommand *infrared) //发送关闭加湿器指令
{
int len;
char infraredCloseCmd[5] = {0xA1, 0xF1, 0x00, 0x00, 0x0d};
write(infrared->fd, infraredCloseCmd, sizeof(infraredCloseCmd)); //发送指令内容
return len;
}
int voiceInit(struct InputCommand *voicer, char *ipAdress, char *port) //初始化语音模块
{
int fd;
//打开语音模块串口,并设置波特率为9600
if ((fd = serialOpen(voicer->deviceName, 9600)) == -1){ //打开语音模块串口
printf("语音模块打开失败\n");
exit(-1);
}else{
printf("语音模块打开成功\n");
printf("fd = %d\n",fd);
}
voicer->fd = fd;
return fd;
}
struct InputCommand voiceContrl = { //语音模块指令工厂
.commandName = "voice", //设备名称
.deviceName = "/dev/ttyS5", //设备文件名
.command = {'\0'}, //指令内容
.init = voiceInit, //初始化函数
.getCommand = getCommand, //获取指令函数
.putOpenCommand = putOpenCommand, //发送打开指令函数
.putCloseCommand = putCloseCommand, //发送关闭指令函数
.log = {'\0'}, //日志内容
.next = NULL //下一个设备
};
struct InputCommand *addVoiceContrlToInputCommanderLink(struct InputCommand *phead) //添加语音模块到输入指令链表
{
if (phead == NULL){
return &voiceContrl;
}else{
voiceContrl.next = phead;
phead = &voiceContrl;
}
return phead;
}
/* Socket.c */
#include "InputCommand.h"
int socketInit(struct InputCommand *socketMes, char *ipAdress, char *port) //初始化socket服务器
{
int s_fd; // 套接字文件描述符
struct sockaddr_in server_addr; // 服务器地址结构体
memset(&server_addr, 0, sizeof(server_addr)); // 服务器地址结构体清零
//初始化套接字
s_fd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP/IP套接字
if(s_fd == -1){
printf("创建套接字失败!\n");
perror("socket"); // 输出错误信息
exit(-1);
}
server_addr.sin_family = AF_INET; // 设置服务器地址族为IPv4
server_addr.sin_port = htons(atoi(socketMes->port)); // 设置服务器端口号
inet_aton(socketMes->ipAdress, &server_addr.sin_addr); // 设置服务器IP地址
//绑定IP地址和端口
int ret = bind(s_fd,(struct sockaddr *)&server_addr,sizeof(server_addr)); // 绑定套接字到服务器地址
if(ret == -1){
printf("绑定服务器地址失败\n");
perror("bind"); // 输出错误信息
exit(-1);
}
//开始监听
ret = listen(s_fd, 10); // 监听套接字
if(ret == -1){
printf("监听套接字失败\n");
perror("listen"); // 输出错误信息
exit(-1);
}
printf("服务器启动成功!\n");
socketMes->s_fd = s_fd; // 保存套接字文件描述符
return s_fd; // 返回套接字文件描述符
}
int socketGetCommand(struct InputCommand *socketMes) //获取socket客户端指令
{
int c_fd; // 客户端套接字文件描述符
int n_read; // 读取字节数
struct sockaddr_in client_addr; // 客户端地址结构体
memset(&client_addr, 0, sizeof(client_addr)); // 客户端地址结构体清零
int client_addr_len = sizeof(client_addr); // 客户端地址长度
printf("等待客户端连接...\n");
c_fd = accept(socketMes->s_fd, (struct sockaddr *)&client_addr, &client_addr_len); // 接受客户端连接请求
if(c_fd == -1){
printf("接受客户端连接请求失败\n");
}
printf("客户端连接成功!\n"); // 输出客户端连接信息
printf("客户端地址:%s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
n_read = read(c_fd, socketMes->command, sizeof(socketMes->command)); // 读取客户端发送的指令
if(n_read == -1){
printf("读取数据失败\n");
perror("read"); // 输出错误信息
}else{
printf("接收字节数:%d\n",n_read); // 输出接收到的数据
printf("接收数据:%s\n", socketMes->command); // 输出接收到的数据
}
return n_read; // 返回读取字节数
}
struct InputCommand socketCommand = { //socket客户端指令工厂
.commandName = "socketServer", //设备名称
.port = "8848", //端口号
.ipAdress = "192.168.31.249", //IP地址
.command = {'\0'}, //指令内容
.init = socketInit, //初始化指令
.getCommand = socketGetCommand, //获取指令内容
.log = {'\0'}, //日志信息
.next = NULL //链表下一个节点
};
struct InputCommand* addsocketCommandToInputCommandLink(struct InputCommand *phead) //向输入指令链表中添加socket指令
{
if(phead == NULL){
phead = &socketCommand;
}else{
socketCommand.next = phead;
phead = &socketCommand;
}
return phead;
}
- 设备工厂相关代码:
/* contrlDevices.h */
#ifndef __CONTROL_DEVICES_H__
#define __CONTROL_DEVICES_H__
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <string.h>
#include <unistd.h>
struct Devices { // 控制设备类
char devicesName[128]; // 设备名称
int devicesPin; // 设备引脚
int status; // 设备状态
int (*open)(int pin); // 打开设备
int (*close)(int pin); // 关闭设备
int (*devicesInit)(int pin); // 初始化设备
int (*readStatus)(int pin); // 读取设备状态
int (*changeStatus)(int status); // 修改设备状态
struct Devices *next; // 链表下一个节点
};
struct Devices* addChuFangLightToDevicesLink(struct Devices *phead); // 向设备链表中添加厨房灯
struct Devices* addKeTingLightToDevicesLink(struct Devices *phead); // 向设备链表中添加客厅灯
struct Devices* addceSuoLightToDevicesLink(struct Devices *phead); // 向设备链表中添加卫生间灯
struct Devices* addwoShiLightToDevicesLink(struct Devices *phead); // 向设备链表中添加卧室灯
struct Devices* addyongChiLightToDevicesLink(struct Devices *phead); // 向设备链表中添加泳池灯
struct Devices* addfireToDevicesLink(struct Devices *phead); // 向设备链表中添加火焰传感器
struct Devices* addyanWuToDevicesLink(struct Devices *phead); // 向设备链表中添加烟雾传感器
struct Devices* addCameraToDevicesLink(struct Devices *phead); // 向设备链表中添加摄像头
struct Devices* addBeepToDevicesLink(struct Devices *phead); // 向设备链表中添加蜂鸣器设备
struct Devices* addFanToDevicesLink(struct Devices *phead); // 向设备链表中添加风扇设备
struct Devices* addLockToDevicesLink(struct Devices *phead); // 向设备链表中添加电磁锁设备
#endif
/* chuFangLight.c */
#include "contrlDevices.h" // 包含设备控制头文件
int chuFangLightOpen(int pin)
{
digitalWrite(pin, LOW); // 打开设备
}
int chuFangLightClose(int pin)
{
digitalWrite(pin, HIGH); // 关闭设备
}
int chuFangLightInit(int pin)
{
pinMode(pin, OUTPUT); // 设置引脚为输出模式
digitalWrite(pin, HIGH); // 关闭设备
}
struct Devices chuFangLight = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "chuFangLight", // 设备名称(厨房灯)
.devicesPin = 6, // 设备引脚号
.open = chuFangLightOpen, // 设备打开函数
.close = chuFangLightClose, // 设备关闭函数
.devicesInit = chuFangLightInit, // 设备初始化函数
};
struct Devices* addChuFangLightToDevicesLink(struct Devices *phead) // 向设备链表中添加厨房灯
{
if(phead == NULL){
return &chuFangLight;
}else{
chuFangLight.next = phead;
phead = &chuFangLight;
}
return phead;
}
/* keTingLight.c */
#include "contrlDevices.h" // 包含设备控制头文件
int keTingLightOpen(int pin)
{
digitalWrite(pin, LOW); // 打开设备
}
int keTingLightClose(int pin)
{
digitalWrite(pin, HIGH); // 关闭设备
}
int keTingLightInit(int pin)
{
pinMode(pin, OUTPUT); // 设置引脚为输出模式
digitalWrite(pin, HIGH); // 关闭设备
}
struct Devices keTingLight = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "keTingLight", // 设备名称(客厅灯)
.devicesPin = 9, // 设备引脚号
.open = keTingLightOpen, // 设备打开函数
.close = keTingLightClose, // 设备关闭函数
.devicesInit = keTingLightInit, // 设备初始化函数
};
struct Devices* addKeTingLightToDevicesLink(struct Devices *phead) // 向设备链表中添加客厅灯
{
if(phead == NULL){
phead = &keTingLight;
}else{
keTingLight.next = phead;
phead = &keTingLight;
}
return phead;
}
/* ceSuoLight.c */
#include "contrlDevices.h" // 包含设备控制头文件
int ceSuoLightOpen(int pin)
{
digitalWrite(pin, LOW); // 打开设备
}
int ceSuoLightClose(int pin)
{
digitalWrite(pin, HIGH); // 关闭设备
}
int ceSuoLightInit(int pin)
{
pinMode(pin, OUTPUT); // 设置引脚为输出模式
digitalWrite(pin, HIGH); // 关闭设备
}
struct Devices ceSuoLight = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "ceSuoLight", // 设备名称(卫生间灯)
.devicesPin = 10, // 设备引脚号
.open = ceSuoLightOpen, // 设备打开函数
.close = ceSuoLightClose, // 设备关闭函数
.devicesInit = ceSuoLightInit, // 设备初始化函数
};
struct Devices* addceSuoLightToDevicesLink(struct Devices *phead) // 向设备链表中添加卫生间灯
{
if(phead == NULL){
phead = &ceSuoLight;
}else{
ceSuoLight.next = phead;
phead = &ceSuoLight;
}
return phead;
}
/* woShiLight.c */
#include "contrlDevices.h" // 包含设备控制头文件
int woShiLightOpen(int pin)
{
digitalWrite(pin, LOW); // 打开设备
}
int woShiLightClose(int pin)
{
digitalWrite(pin, HIGH); // 关闭设备
}
int woShiLightInit(int pin)
{
pinMode(pin, OUTPUT); // 设置引脚为输出模式
digitalWrite(pin, HIGH); // 关闭设备
}
struct Devices woShiLight = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "woShiLight", // 设备名称(卧室灯)
.devicesPin = 13, // 设备引脚号
.open = woShiLightOpen, // 设备打开函数
.close = woShiLightClose, // 设备关闭函数
.devicesInit = woShiLightInit, // 设备初始化函数
};
struct Devices* addwoShiLightToDevicesLink(struct Devices *phead) // 向设备链表中添加卧室灯
{
if(phead == NULL){
phead = &woShiLight;
}else{
woShiLight.next = phead;
phead = &woShiLight;
}
return phead;
}
/* yongChiLight.c */
#include "contrlDevices.h" // 包含设备控制头文件
int yongChiLightOpen(int pin)
{
digitalWrite(pin, HIGH); // 打开设备
}
int yongChiLightClose(int pin)
{
digitalWrite(pin, LOW); // 关闭设备
}
int yongChiLightInit(int pin)
{
pinMode(pin, OUTPUT); // 设置引脚为输出模式
digitalWrite(pin, LOW); // 关闭设备
}
struct Devices yongChiLight = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "yongChiLight", // 设备名称(泳池灯)
.devicesPin = 16, // 设备引脚号
.open = yongChiLightOpen, // 设备打开函数
.close = yongChiLightClose, // 设备关闭函数
.devicesInit = yongChiLightInit, // 设备初始化函数
};
struct Devices* addyongChiLightToDevicesLink(struct Devices *phead) // 向设备链表中添加泳池灯
{
if(phead == NULL){
phead = &yongChiLight;
}else{
yongChiLight.next = phead;
phead = &yongChiLight;
}
return phead;
}
/* BEEP.c */
#include "contrlDevices.h" // 包含设备控制头文件
int beepOpen(int pin)
{
digitalWrite(pin, HIGH); // 打开设备
}
int beepClose(int pin)
{
digitalWrite(pin, LOW); // 关闭设备
}
int beepInit(int pin)
{
pinMode(pin, OUTPUT); // 设置引脚为输出模式
digitalWrite(pin, LOW); // 关闭设备
}
struct Devices beep = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "beep", // 设备名称(蜂鸣器)
.devicesPin = 8, // 设备引脚号
.open = beepOpen, // 设备打开函数
.close = beepClose, // 设备关闭函数
.devicesInit = beepInit, // 设备初始化函数
};
struct Devices* addBeepToDevicesLink(struct Devices *phead) // 向设备链表中添加蜂鸣器设备
{
if(phead == NULL){
phead = &beep;
}else{
beep.next = phead;
phead = &beep;
}
return phead;
}
/* fan.c */
#include "contrlDevices.h" // 包含设备控制头文件
int fanOpen(int pin)
{
digitalWrite(pin, HIGH); // 打开设备
}
int fanClose(int pin)
{
digitalWrite(pin, LOW); // 关闭设备
}
int fanInit(int pin)
{
pinMode(pin, OUTPUT); // 设置引脚为输出模式
digitalWrite(pin, LOW); // 关闭设备
}
struct Devices fan = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "fan", // 设备名称(风扇)
.devicesPin = 7, // 设备引脚号
.open = fanOpen, // 设备打开函数
.close = fanClose, // 设备关闭函数
.devicesInit = fanInit, // 设备初始化函数
};
struct Devices* addFanToDevicesLink(struct Devices *phead) // 向设备链表中添加风扇设备
{
if(phead == NULL){
phead = &fan;
}else{
fan.next = phead;
phead = &fan;
}
return phead;
}
/* fire.c */
#include "contrlDevices.h" // 包含设备控制头文件
int fireInit(int pin)
{
pinMode(pin, INPUT); // 设置引脚为输入模式
digitalWrite(pin, HIGH); // 初始化火焰传感器为高电平
}
int fireReadStatus(int pin) // 读取设备状态
{
if(digitalRead(pin) == LOW){ // 读取引脚电平,LOW表示火焰燃烧
return 1;
}else{
return 0;
}
}
struct Devices fire = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "fire", // 设备名称(火焰传感器)
.devicesPin = 2, // 设备引脚号
.devicesInit = fireInit, // 设备初始化函数
.readStatus = fireReadStatus // 设备状态读取函数
};
struct Devices* addfireToDevicesLink(struct Devices *phead) // 向设备链表中添加火焰传感器
{
if(phead == NULL){
phead = &fire;
}else{
fire.next = phead;
phead = &fire;
}
return phead;
}
/* MQ-5.c */
#include "contrlDevices.h" // 包含设备控制头文件
int yanWuInit(int pin)
{
pinMode(pin, INPUT); // 设置引脚为输入模式
digitalWrite(pin, HIGH); // 初始化烟雾传感器为高电平
}
int yanWuReadStatus(int pin) // 读取设备状态
{
if(digitalRead(pin) == LOW){ // 读取引脚电平,LOW表示火焰燃烧
return 1;
}else{
return 0;
}
}
struct Devices yanWu = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "yanWu", // 设备名称(烟雾传感器)
.devicesPin = 5, // 设备引脚号
.devicesInit = yanWuInit, // 设备初始化函数
.readStatus = yanWuReadStatus // 设备状态读取函数
};
struct Devices* addyanWuToDevicesLink(struct Devices *phead) // 向设备链表中添加烟雾传感器
{
if(phead == NULL){
phead = &yanWu;
}else{
yanWu.next = phead;
phead = &yanWu;
}
return phead;
}
/* lock.c */
#include "contrlDevices.h" // 包含设备控制头文件
int lockOpen(int pin)
{
digitalWrite(pin, LOW); // 打开设备
}
int lockClose(int pin)
{
digitalWrite(pin, HIGH); // 关闭设备
}
int lockInit(int pin)
{
pinMode(pin, OUTPUT); // 设置引脚为输出模式
digitalWrite(pin, HIGH); // 关闭设备
}
struct Devices lock = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "lock", // 设备名称(电磁锁)
.devicesPin = 17, // 设备引脚号
.open = lockOpen, // 设备打开函数
.close = lockClose, // 设备关闭函数
.devicesInit = lockInit, // 设备初始化函数
};
struct Devices* addLockToDevicesLink(struct Devices *phead) // 向设备链表中添加电磁锁
{
if(phead == NULL){
return &lock;
}else{
lock.next = phead;
phead = &lock;
}
return phead;
}
/* camera.c */
#include "contrlDevices.h" // 包含设备控制头文件
#include "peopleFace.h" // 包含人脸识别头文件
int cameraOpen(int pin) // 打开摄像头函数
{
system("wget http://192.168.31.249:8080/?action=snapshot -O /home/orangepi/tmp/shiyahaoTest.jpg"); //拍照并保存到指定目录
delay(100); // 延时10ms
if(access("/home/orangepi/tmp/shiyahaoTest.jpg", F_OK) == 0){ // 判断文件是否存在
return 1; // 存在则返回1
}else{
return 0; // 不存在则返回0
}
}
int cameraClear(int pin) // 清除摄像头函数
{
remove("/home/orangepi/tmp/shiyahaoTest.jpg");
}
struct Devices camera = { // 定义一个结构体变量,用于保存设备信息
.devicesName = "camera", // 设备名称(摄像头人脸识别)
.open = cameraOpen, // 设备打开函数
.close = cameraClear // 设备关闭函数
};
struct Devices* addCameraToDevicesLink(struct Devices *phead) // 向设备链表中添加摄像头
{
if(phead == NULL){
return &camera;
}else{
camera.next = phead;
phead = &camera;
}
return phead;
}
- 人脸识别相关代码:
# face.py
# -*- coding: utf-8 -*-
# 引入依赖包
# 最低SDK版本要求:facebody20191230的SDK版本需大于等于4.0.8
# 可以在此仓库地址中引用最新版本SDK:https://pypi.org/project/alibabacloud-facebody20191230/
# pip install alibabacloud_facebody20191230
import os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import CompareFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions
def face_detect(): #定义成一个函数,方便调用!
config = Config(
# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。
# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。
# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。
access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
# 访问的域名
endpoint='facebody.cn-shanghai.aliyuncs.com',
# 访问的域名对应的region
region_id='cn-shanghai'
)
runtime_option = RuntimeOptions()
compare_face_request = CompareFaceAdvanceRequest()
#场景一:文件在本地
streamA = open(r'/home/orangepi/tmp/shiyahao.jpg', 'rb') #预存的照片
compare_face_request.image_urlaobject = streamA
streamB = open(r'/home/orangepi/tmp/shiyahaoTest.jpg', 'rb') #待测试的照片
compare_face_request.image_urlbobject = streamB
try:
# 初始化Client
client = Client(config)
response = client.compare_face_advance(compare_face_request, runtime_option)
# 获取整体结果
#print(response.body)
# 获取单个字段
confidence = response.body.to_map()['Data']['Confidence'] #to_map()函数很重要,不要忘记
score = int(confidence)
print("相似度分数为:", score)
return score
except Exception as error:
# 获取整体报错信息
print(error)
# 获取单个字段
print(error.code)
# tips: 可通过error.__dict__查看属性名称
# 关闭流
streamA.close()
streamB.close()
/* peopleFace.c */
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <Python.h>
void face_init(void) //初始化python环境
{
Py_Initialize();
PyObject *sys = PyImport_ImportModule("sys");
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Append(path, PyUnicode_FromString("."));
}
void face_final(void) //释放python环境
{
Py_Finalize();
}
int face_score(void) //python下face_detect函数返回的是已经经过提取和取证过的置信度score,是个int型
{
PyObject *pModule = PyImport_ImportModule("face"); //加载python文件
if (!pModule)
{
PyErr_Print();
printf("Error: failed to load module\n");
goto FAILED_MODULE; //goto的意思就是如果运行到这里就直接跳转到FAILED_MODULE
}
PyObject *pFunc = PyObject_GetAttrString(pModule, "face_detect"); //加载python文件中的对应函数
if (!pFunc)
{
PyErr_Print();
printf("Error: failed to load function\n");
goto FAILED_FUNC;
}
PyObject *pValue = PyObject_CallObject(pFunc, NULL);
if (!pValue)
{
PyErr_Print();
printf("Error: function call failed\n");
goto FAILED_VALUE;
}
int result = 0;
if (!PyArg_Parse(pValue, "i", &result)) //ace_detect函数返回的是已经经过提取和取证过的置信度score,是个int型,用‘i’表示
{
PyErr_Print();
printf("Error: parse failed");
goto FAILED_RESULT;
}
/* 如果函数返回的是字符串,上面的PyArg_Parse则需要用‘s’来表示,且下面注释的代码非常重要,因为字符串名代表了其首地址,所以不能直接复制而是需要使用strncpy函数!!!
category = (char *)malloc(sizeof(char) * (strlen(result) + 1) ); //开辟一个新的字符串常量。+1是为了留出空间给\0
memset(category, 0, (strlen(result) + 1)); //初始化字符串
strncpy(category, result, (strlen(result) + 1)); //将result的结果复制给新的字符串
*/
FAILED_RESULT:
Py_DECREF(pValue);
FAILED_VALUE:
Py_DECREF(pFunc);
FAILED_FUNC:
Py_DECREF(pModule);
FAILED_MODULE:
return result;
}
/* peopleFace.h */
#ifndef __PEOPLEFACE_H__
#define __PEOPLEFACE_H__
void face_init(void); //初始化人脸识别引擎
void face_final(void); //释放人脸识别引擎
int face_score(void); //人脸识别
#endif
- 主程序框架:
#include "contrlDevices.h" // 包含设备控制头文件
#include "InputCommand.h" // 包含指令头文件
#include <unistd.h> // 包含unistd.h头文件
#include <pthread.h> // 包含pthread.h头文件
#include "peopleFace.h" // 包含人脸识别头文件
pthread_mutex_t mutex; // 互斥锁,用于线程之间的互斥访问
struct Devices *pDevicesHead = NULL; // 设备链表头指针
struct InputCommand *pCommandHead = NULL; // 指令链表头指针
struct InputCommand *SU03THandler; // 语音模块对象
struct InputCommand *socketHandler; // Socket对象
int c_fd; // 客户端套接字文件描述符
struct Devices *cmd = NULL; // 指令对象
struct Devices* getDevicesByName(struct Devices *pDevicesHead, char *name) // 根据设备名称获取设备对象
{
struct Devices *p = pDevicesHead;
if(pDevicesHead == NULL){ // 设备链表为空
return NULL;
}else{
while(p != NULL){ // 遍历设备链表
if(strcmp(p->devicesName, name) == 0){ // 找到设备
return p; // 返回设备对象
}
p = p->next;
}
return NULL; // 没有找到设备
}
}
struct InputCommand* getCommandByName(struct InputCommand *pDevicesHead, char *name) // 根据指令名称获取指令对象
{
struct InputCommand *p = pDevicesHead;
if(pDevicesHead == NULL){ // 设备链表为空
return NULL;
}else{
while(p != NULL){ // 遍历设备链表
if(strcmp(p->commandName, name) == 0){ // 找到设备
return p; // 返回设备对象
}
p = p->next;
}
return NULL; // 没有找到设备
}
}
void* SU03T_thread_func(void *arg) // 语音线程函数
{
int n_read;
int score = 0; // 人脸识别得分
face_init(); // 初始化人脸识别引擎
SU03THandler = getCommandByName(pCommandHead, "voice"); // 获取语音模块对象
if(SU03THandler == NULL){
printf("获取语音模块对象失败\n");
pthread_exit(NULL); // 退出线程
}else{
if(SU03THandler->init(SU03THandler,NULL,NULL) < 0){ // 语音模块初始化
printf("语音模块初始化失败\n");
pthread_exit(NULL); // 退出线程
}else{
printf("语音模块初始化成功\n");
}
while(1){
n_read = SU03THandler->getCommand(SU03THandler); // 获取指令
if(n_read == 0){
printf("超时!\n");
}
printf("获取指令成功: %s\n", SU03THandler->command); // 打印指令
pthread_mutex_lock(&mutex); // 加互斥锁
if(strstr(SU03THandler->command, "pcf") != NULL){
printf("获取打开厨房灯指令\n");
cmd = getDevicesByName(pDevicesHead, "chuFangLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "lcf") != NULL){
printf("获取关闭厨房灯指令\n");
cmd = getDevicesByName(pDevicesHead, "chuFangLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "pkt") != NULL){
printf("获取打开客厅灯指令\n");
cmd = getDevicesByName(pDevicesHead, "keTingLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "lkt") != NULL){
printf("获取关闭客厅灯指令\n");
cmd = getDevicesByName(pDevicesHead, "keTingLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "pcs") != NULL){
printf("获取打开卫生间灯指令\n");
cmd = getDevicesByName(pDevicesHead, "ceSuoLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "lcs") != NULL){
printf("获取关闭卫生间灯指令\n");
cmd = getDevicesByName(pDevicesHead, "ceSuoLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "pws") != NULL){
printf("获取打开卧室灯指令\n");
cmd = getDevicesByName(pDevicesHead, "woShiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "lws") != NULL){
printf("获取关闭卧室灯指令\n");
cmd = getDevicesByName(pDevicesHead, "woShiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "pyc") != NULL){
printf("获取打开泳池灯指令\n");
cmd = getDevicesByName(pDevicesHead, "yongChiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "lyc") != NULL){
printf("获取关闭泳池灯指令\n");
cmd = getDevicesByName(pDevicesHead, "yongChiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "psy") != NULL){
printf("获取打开所有灯指令\n");
cmd = getDevicesByName(pDevicesHead, "chuFangLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "keTingLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "ceSuoLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "woShiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "yongChiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "lsy") != NULL){
printf("获取关闭所有灯指令\n");
cmd = getDevicesByName(pDevicesHead, "chuFangLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "keTingLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "ceSuoLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "woShiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "yongChiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(SU03THandler->command, "pjs") != NULL){
printf("获取打开加湿器指令\n");
if( SU03THandler->putOpenCommand(SU03THandler) < 0){
printf("发送打开加湿器指令失败!\n");
}else{
printf("发送打开加湿器指令成功!\n");
}
}else if(strstr(SU03THandler->command, "ljs") != NULL){
printf("获取关闭加湿器指令\n");
if( SU03THandler->putCloseCommand(SU03THandler) < 0){
printf("发送关闭加湿器指令失败!\n");
}else{
printf("发送关闭加湿器指令成功!\n");
}
}else if(strstr(SU03THandler->command, "ace") != NULL){
printf("获取人脸识别指令\n");
cmd = getDevicesByName(pDevicesHead, "camera"); //获取摄像头对象
if(cmd != NULL){
int ret = cmd->open(0); //拍照
if(ret == 1){
score = face_score(); //人脸识别
if(score > 80){
printf("识别成功,得分:%d\n", score);
cmd = getDevicesByName(pDevicesHead, "lock");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
printf("门锁打开成功!\n");
sleep(3); //延时3秒
cmd->close(cmd->devicesPin);
printf("门锁关闭成功!\n");
}
}else{
printf("识别失败,得分:%d\n", score);
}
cmd->close(0); //清除摄像头照片
}else{
printf("拍照失败!\n");
}
score = 0; //清空得分
}
}
else{
printf("指令不支持!\n");
}
pthread_mutex_unlock(&mutex); // 解互斥锁
}
}
face_final(); //释放人脸识别引擎
pthread_exit(NULL); // 退出线程
}
void* read_thread_func(void *arg) // 读线程函数
{
int n_read;
memset(socketHandler->command, '\0', sizeof(socketHandler->command)); // 清空指令缓冲区
while(1){
pthread_mutex_lock(&mutex); // 加互斥锁
n_read = read(c_fd, socketHandler->command, sizeof(socketHandler->command));// 读取客户端发送的指令
if(n_read == -1){
printf("读取数据失败\n");
perror("read"); // 输出错误信息
}else{
//printf("接收字节数:%d\n",n_read); // 输出接收到的数据
printf("接收数据:%s\n", socketHandler->command); // 输出接收到的数据
if(strstr(socketHandler->command, "opencf") != NULL){
printf("获取打开厨房灯指令\n");
cmd = getDevicesByName(pDevicesHead, "chuFangLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "closecf") != NULL){
printf("获取关闭厨房灯指令\n");
cmd = getDevicesByName(pDevicesHead, "chuFangLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "openkt") != NULL){
printf("获取打开客厅灯指令\n");
cmd = getDevicesByName(pDevicesHead, "keTingLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "closekt") != NULL){
printf("获取关闭客厅灯指令\n");
cmd = getDevicesByName(pDevicesHead, "keTingLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "opencs") != NULL){
printf("获取打开卫生间灯指令\n");
cmd = getDevicesByName(pDevicesHead, "ceSuoLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "closecs") != NULL){
printf("获取关闭卫生间灯指令\n");
cmd = getDevicesByName(pDevicesHead, "ceSuoLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "openws") != NULL){
printf("获取打开卧室灯指令\n");
cmd = getDevicesByName(pDevicesHead, "woShiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "closews") != NULL){
printf("获取关闭卧室灯指令\n");
cmd = getDevicesByName(pDevicesHead, "woShiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "openyc") != NULL){
printf("获取打开泳池灯指令\n");
cmd = getDevicesByName(pDevicesHead, "yongChiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "closeyc") != NULL){
printf("获取关闭泳池灯指令\n");
cmd = getDevicesByName(pDevicesHead, "yongChiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "openfs") != NULL){
printf("获取打开排风扇指令\n");
cmd = getDevicesByName(pDevicesHead, "fan");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "closefs") != NULL){
printf("获取关闭排风扇指令\n");
cmd = getDevicesByName(pDevicesHead, "fan");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "opensy") != NULL){
printf("获取打开所有灯指令\n");
cmd = getDevicesByName(pDevicesHead, "chuFangLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "keTingLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "ceSuoLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "woShiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "yongChiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "closesy") != NULL){
printf("获取关闭所有灯指令\n");
cmd = getDevicesByName(pDevicesHead, "chuFangLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "keTingLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "ceSuoLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "woShiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "yongChiLight");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}else if(strstr(socketHandler->command, "openjs") != NULL){
printf("获取打开加湿器指令\n");
SU03THandler = getCommandByName(pCommandHead, "voice");
if( SU03THandler->putOpenCommand(SU03THandler) < 0){
printf("发送打开加湿器指令失败!\n");
}else{
printf("发送打开加湿器指令成功!\n");
}
}else if(strstr(socketHandler->command, "closejs") != NULL){
printf("获取关闭加湿器指令\n");
SU03THandler = getCommandByName(pCommandHead, "voice");
if( SU03THandler->putCloseCommand(SU03THandler) < 0){
printf("发送关闭加湿器指令失败!\n");
}else{
printf("发送关闭加湿器指令成功!\n");
}
}else if(strstr(socketHandler->command, "exit") != NULL){
printf("退出程序指令\n");
pthread_mutex_unlock(&mutex); // 解互斥锁
pthread_exit(NULL); // 退出线程
}else{
printf("请发送正确的指令!\n");
}
}
pthread_mutex_unlock(&mutex); // 解互斥锁
}
pthread_exit(NULL); // 退出线程
}
void* socket_thread_func(void *arg) // Socket线程函数
{
pthread_t readThread; // 读线程
struct sockaddr_in client_addr; // 客户端地址结构体
memset(&client_addr, 0, sizeof(client_addr)); // 客户端地址结构体清零
int client_addr_len = sizeof(client_addr); // 客户端地址长度
socketHandler = getCommandByName(pCommandHead, "socketServer"); // 获取socket模块对象
if(socketHandler == NULL){
printf("获取Socket对象失败\n");
pthread_exit(NULL); // 退出线程
}
socketHandler->init(socketHandler,NULL,NULL); // Socket初始化
printf("等待客户端连接...\n");
while(1){
c_fd = accept(socketHandler->s_fd, (struct sockaddr *)&client_addr, &client_addr_len); // 接受客户端连接请求
if(c_fd == -1){
printf("接受客户端连接请求失败\n");
}
printf("客户端连接成功!\n"); // 输出客户端连接信息
printf("客户端地址:%s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
pthread_create(&readThread, NULL, read_thread_func, NULL); // 创建读线程
pthread_join(readThread, NULL); // 等待读线程退出
}
pthread_exit(NULL); // 退出线程
}
void* fire_thread_func(void *arg) // 火灾检测线程函数
{
printf("开始检测火灾...\n");
while(1){
pthread_mutex_lock(&mutex); // 加互斥锁
cmd = getDevicesByName(pDevicesHead, "fire"); // 获取火灾检测器对象
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
if(cmd->readStatus(cmd->devicesPin) == 1){
printf("火灾发生!\n");
cmd = getDevicesByName(pDevicesHead, "beep");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else{
printf("无火灾发生!\n");
cmd = getDevicesByName(pDevicesHead, "beep");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}
}
pthread_mutex_unlock(&mutex); // 解互斥锁
sleep(1); // 睡眠1秒
}
pthread_exit(NULL); // 退出线程
}
void* MQ5_thread_func(void *arg) // 烟雾传感器线程函数
{
printf("开始检测烟雾...\n");
while(1){
pthread_mutex_lock(&mutex); // 加互斥锁
cmd = getDevicesByName(pDevicesHead, "yanWu"); // 获取烟雾报警器对象
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
if(cmd->readStatus(cmd->devicesPin) == 1){
printf("烟雾报警!\n");
cmd = getDevicesByName(pDevicesHead, "beep");
if(cmd != NULL){
printf("找到厨房灯!\n");
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "fan");
if(cmd != NULL){
printf("找到风扇!\n");
cmd->devicesInit(cmd->devicesPin);
cmd->open(cmd->devicesPin);
}
}else{
printf("无烟雾报警!\n");
cmd = getDevicesByName(pDevicesHead, "beep");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
cmd = getDevicesByName(pDevicesHead, "fan");
if(cmd != NULL){
cmd->devicesInit(cmd->devicesPin);
cmd->close(cmd->devicesPin);
}
}
}
pthread_mutex_unlock(&mutex); // 解互斥锁
sleep(1); // 睡眠1秒
}
pthread_exit(NULL); // 退出线程
}
int main()
{
pthread_t SU03T_thread; // 语音线程
pthread_t socket_thread; // Socket线程
pthread_t fire_thread; // 火灾检测线程
pthread_t MQ5_thread; // 烟雾传感器线程
if (wiringPiSetup() == -1) { //初始化wiringPi
printf("初始化wiringPi失败!\n");
return 1;
}
face_init(); //初始化人脸识别引擎
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
// 1.指令工厂初始化
pCommandHead = addsocketCommandToInputCommandLink(pCommandHead); // 添加Socket到指令链表
pCommandHead = addVoiceContrlToInputCommanderLink(pCommandHead); // 添加语音控制到指令链表
// 2.控制设备工厂初始化
pDevicesHead = addChuFangLightToDevicesLink(pDevicesHead); // 添加厨房灯到设备链表
pDevicesHead = addKeTingLightToDevicesLink(pDevicesHead); // 添加客厅灯到设备链表
pDevicesHead = addceSuoLightToDevicesLink(pDevicesHead); // 添加卫生间灯到设备链表
pDevicesHead = addwoShiLightToDevicesLink(pDevicesHead); // 添加卧室灯到设备链表
pDevicesHead = addyongChiLightToDevicesLink(pDevicesHead); // 添加泳池灯到设备链表
pDevicesHead = addfireToDevicesLink(pDevicesHead); // 添加火灾检测器到设备链表
pDevicesHead = addyanWuToDevicesLink(pDevicesHead); // 添加烟雾报警器到设备链表
pDevicesHead = addCameraToDevicesLink(pDevicesHead); // 添加摄像头到设备链表
pDevicesHead = addBeepToDevicesLink(pDevicesHead); // 添加蜂鸣器到设备链表
pDevicesHead = addFanToDevicesLink(pDevicesHead); // 添加风扇到设备链表
pDevicesHead = addLockToDevicesLink(pDevicesHead); // 添加门锁到设备链表
// 3.线程池建立(语音、Socket、火灾、烟雾...)
pthread_create(&SU03T_thread, NULL, SU03T_thread_func, NULL); // 创建语音线程
pthread_create(&socket_thread, NULL, socket_thread_func, NULL); // 创建Socket线程
pthread_create(&fire_thread, NULL, fire_thread_func, NULL); // 创建火灾检测线程
pthread_create(&MQ5_thread, NULL, MQ5_thread_func, NULL); // 创建烟雾传感器线程
pthread_join(SU03T_thread, NULL); // 等待语音线程退出
pthread_join(socket_thread, NULL); // 等待Socket线程退出
pthread_join(fire_thread, NULL); // 等待火灾检测线程退出
pthread_join(MQ5_thread, NULL); // 等待烟雾传感器线程退出
while(1){
}
pthread_mutex_destroy(&mutex); // 销毁互斥锁
face_final(); //释放人脸识别引擎
return 0;
}
7.3 代码编译运行:
gcc *.c -I /usr/include/python3.10 -l python3.10 -lwiringPi -lpthread //编译代码
sudo ./a.out //运行程序