一、项目概述
•项目功能
通过语音控制客厅灯、卧室灯、风扇、人脸识别开门等,可以进行火灾险情监测,可以并且实现Sockect发送指令远程控制各类家电等
•项目描述
全志H616通过串口连接各模块硬件,检测语音的识别结果,分析语音识别的结果来对家电设备进行控制。摄像头拍摄到人脸之后通过阿里云平台人脸识别方案对比阿里云人脸数据库来进行人脸识别开锁。该项目采用设计模式中的简单工厂模式,让设备端和控制端都采用链表管理,提高了项目代码的稳定性和拓展性。
二、系统框图
三、硬件搭建
语音模块及人脸识别方案配置参考: 语音模块配置 人脸识别方案
四、软件框架
整个项目开启4个监听线程, 分别是:
1. 语音监听线程:用于监听语音指令, 当有语音指令过来后, 通过消息队列的方式给消息处理线程发送指令
2. 网络监听线程:用于监听网络指令,当有网络指令过来后, 通过消息队列的方式给消息处理线程发送指令
3. 火灾检测线程:当存在煤气泄漏或者火灾闲情时, 发送警报指令给消息处理线程
4. 消息监听线程: 用于处理以上3个线程发过来的指令,并根据指令要求配置GPIO引脚状态,OLED屏显示、语音播报,还有人脸识别开门
上述四个线程采用统一个对外接口接口,同时添加到监听链表中。
统一的监听模块接口如下:
struct control
{
char control_name[128]; //监听模块名称
int (*init)(void); //初始化函数
void (*final)(void);//结束释放函数
void *(*get)(void *arg);//监听函数,如语音监听
void *(*set)(void *arg); //设置函数,如语音播报
struct control *next;
};
struct control *add_device_to_ctrl_list(struct control *phead, struct control *device);
统一的设备类接口如下:
struct gdevice
{
char dev_name[128]; //设备名称
int key; //key值,用于匹配控制指令的值
int gpio_pin; //控制的gpio引脚
int gpio_mode; //输入输出模式
int gpio_status; //高低电平状态
int check_face_status; //是否进行人脸检测状态
int voice_set_status; //是否语音语音播报
struct gdevice *next;
};
struct gdevice *add_device_to_gdevice_list(struct gdevice *phead, struct gdevice *device);
struct gdevice *find_gdevice_by_key(struct gdevice *pdev, unsigned char key);
int set_gpio_gdevice_status(struct gdevice *pdev);
五、项目代码
本项目代码量较大,仅展示部分源代码,项目开源地址:https://gitee.com/GeekerGao/smart-home
(一)
监听模块接口加入链表
control.c
#include <stdio.h>
#include "control.h"
struct control *add_interface_to_control_list(struct control *phead,struct control *control_interface)
{
//头插法添加节点
if(NULL == phead)
{
phead = control_interface;
}
else
{
control_interface->next = phead;
phead = control_interface;
}
return phead;
};
control.h
#ifndef __CONTROL_H__
#define __CONTROL_H__
struct control
{
char control_name[128]; //监听模块名称
int (*init)(void); //初始化函数
void (*final)(void);//结束释放函数
void *(*get)(void *arg);//监听函数,如语音监听
void *(*set)(void *arg); //设置函数,如语音播报
struct control *next;
};
struct control *add_interface_to_control_list(struct control *phead,struct control *control_interface);
#endif
消息队列的接口
msg_queue.c
#include <stdio.h>
#include "msg_queue.h"
// 定义消息队列的名称
#define QUEQUE_NAME "/test_queue"
// 创建一个新的消息队列
mqd_t msg_queue_create(void)
{
// 初始化mqd_t变量mqd为-1,表示未成功打开消息队列
mqd_t mqd = -1;
// 定义并初始化消息队列的属性结构体
struct mq_attr attr;
// 设置消息队列属性:默认为阻塞模式
attr.mq_flags = 0;
// 消息队列最大消息数量为10
attr.mq_maxmsg = 10;
// 每条消息的最大字节数为256
attr.mq_msgsize = 256;
// 当前消息队列中消息数量初始化为0
attr.mq_curmsgs = 0;
// 使用mq_open函数创建消息队列,如果不存在则根据属性创建(O_CREAT),并设置读写权限(O_RDWR)
// 第三个参数是权限位,0666表示所有用户都有读写权限,实际权限还需参考umask值
// 第四个参数是指向消息队列属性的指针
mqd = mq_open(QUEQUE_NAME, O_CREAT | O_RDWR, 0666, &attr);
// 打印消息队列描述符,用于调试
printf("%s | %s | %d:mqd = %d\n", __FILE__,__func__,__LINE__,mqd);
// 返回创建的消息队列描述符
return mqd;
}
// 清理并关闭消息队列
void msg_queue_fianl(mqd_t mqd)
{
// 如果消息队列描述符有效,则关闭消息队列
if(-1 != mqd)
mq_close(mqd);
// 删除消息队列
mq_unlink(QUEQUE_NAME);
// 保险起见,再次将mqd设为-1
mqd = -1;
}
// 向消息队列发送消息
int send_message(mqd_t mqd,void *msg,int msg_len)
{
int byte_send = -1;
// 使用mq_send函数发送消息到消息队列,第四个参数0为消息的优先级,默认情况下优先级不影响消息的发送顺序
byte_send = mq_send(mqd, (char *)msg, msg_len, 0);
// 返回发送的字节数,成功时应等于msg_len,失败时返回-1
return byte_send;
}
msg_queue.h
#ifndef __MSG_QUEUE_H__
#define __MSG_QUEUE_H__
#include <mqueue.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
mqd_t msg_queue_create(void);
void msg_queue_fianl(mqd_t mqd);
int send_message(mqd_t mqd,void *msg,int msg_len);
#endif
语音监听接口
global.h
#ifndef __GLOBAL_H__
#define __GLOBAL_H__
typedef struct {
mqd_t mqd;
struct control *ctrl_phead;
}ctrl_info_t;
#endif
voice_interface.c
#include <stdio.h>
#include <pthread.h> // 线程库
#include "voice_interface.h" // 语音接口头文件
#include "uartTool.h" // 串口操作工具库
#include "msg_queue.h" // 消息队列操作库
#include "global.h" // 全局变量和宏定义头文件
// 串口文件描述符,初始设为-1
static int serial_fd = -1;
// 初始化语音模块
static int voice_init(void)
{
// 打开指定的串口设备,BAUD为波特率宏定义
serial_fd = myserialOpen(SERIAL_DEV, BAUD);
// 打印日志,显示串口文件描述符
printf("%s|%s|%d:serial_fd = %d\n", __FILE__, __FUNCTION__, __LINE__, serial_fd);
return serial_fd;
}
// 释放语音模块资源
static void voice_final(void)
{
if(-1 != serial_fd) // 如果串口已打开
{
// 关闭串口
close(serial_fd);
// 重置串口文件描述符为-1
serial_fd = -1;
}
}
// 线程函数,用于接收语音指令
static void *voice_get(void *arg)
{
unsigned char buffer[6] = {0}; // 初始化缓冲区
int len; // 接收长度
mqd_t mqd