智能家居 (6) ——人脸识别控制开关电磁锁

1、前序文章:

【智能家居 (1) ——工厂模式继电器控制灯】

【智能家居 (2) ——工厂模式火焰报警器】

【智能家居 (3) ——语音识别控制端线程】

【智能家居 (4) ——网络控制端线程】

【智能家居 (5) ——前四章内容整合】

【基于 Libcurl 通过 https 访问翔云 OCR 实现人脸识别】

【树莓派安装mjpg-streamer使用摄像头】

2、main.c 函数:

通过语音控制开启人脸识别,摄像头亮灯2秒拍照

此 main.c 函数仅用于 “人脸识别控制开关电磁锁” 功能调试

#include 
#include 
#include "equipment.h"
#include "command.h"
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

char buf[1024] = {'\0'};		//用于存放翔云返回的数据

struct Equipment *findEquipByName(char *name,struct Equipment *phead);		//一些函数声明
struct Command *findCommandByName(char *name,struct Command *phead);
void *voiceControlThread(void *data);
char *getPicBase64FromFile(char *filePath);
size_t readData(void *ptr, size_t size, size_t nmemb, void *stream);
unsigned int postUrl();

struct Equipment *equiphead = NULL;			//设备工厂链表头节点
struct Command *cmdhead = NULL;				//指令控制工厂链表节点头

int main()
{
	if(wiringPiSetup() == -1){					//使用wiringPi库需要初始化
		printf("wiringPiSetup failed!\n");
		return -1; 
	}

	equiphead = addEleLockToLink(equiphead);			//各设备加入设备工厂
	
	cmdhead = addVoiceControlToLink(cmdhead);		//各指令控制加入指令控制工厂

	struct Equipment *tmpequiphead = equiphead;
	while(tmpequiphead != NULL){						//设备工厂所有设备初始化
		tmpequiphead->Init(tmpequiphead->pinNum);
		tmpequiphead = tmpequiphead->next;
	}

	pthread_t voiceControl_thread;
	pthread_create(&voiceControl_thread,NULL,voiceControlThread,NULL);		//创建线程:语音控制
	
	pthread_join(voiceControl_thread, NULL);		//主函数等待线程退出

	return 0;
}

void *voiceControlThread(void *data)			//“语音控制线程”执行的函数
{
	int nread;
	char *temName = NULL;
	struct Command *voiceHandler = NULL;
	struct Equipment *linkHandler;


	voiceHandler = findCommandByName("voiceControl",cmdhead);		//寻找“语音控制”所在节点,返回给voiceHandler
	if(voiceHandler == NULL){
		printf("find voiceHandler error\n");
		pthread_exit(NULL);
	}
	if(voiceHandler->Init(voiceHandler) < 0){				//“语音控制”功能初始化
		printf("voiceControl init error\n");
		pthread_exit(NULL);
	}

	while(1){
		nread = voiceHandler->getCommand(voiceHandler);			//获取指令
		if(nread == 0){											//没有获取到指令
			printf("No command received\n");
		}else{													//获取到指令
			printf("Get voice command:%s\n",voiceHandler->command);

												//以下为根据不同指令执行相应操作

			if(strstr("OpLock\r\n",voiceHandler->command) != NULL){
				linkHandler = findEquipByName("eleLock",equiphead);


				system("raspistill -o image.jpg");						//拍摄照片命名为 image.jpg
				system("convert -resize 400 image.jpg face2.jpg");		//降低拍摄的照片的大小,命名为 face2.jpg
				system("rm image.jpg");									//删除 image.jpg 临时照片
				// face1.jpg 为对比人脸照片,face2.jpg 为即拍人脸照片
				postUrl();
				system("rm face2.jpg");
				if(strstr(buf,"是") != NULL){							//如果返回对比数据是同一个人
						linkHandler->open(linkHandler->pinNum);			//开锁
						delay(10000);									//延时10000毫秒=10秒
						linkHandler->close(linkHandler->pinNum);		//关锁
				}
			}
		}
	}
}

unsigned int postUrl()
{
		CURL *curl;
        CURLcode res;
        char *key = "xxxxxxxxxxxxxxxxxxxxxxxx";						//用户 OCR Key 值
        char *secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";			//用户 OCR Secret 值
        int typeId = 21;											//识别类型 21
        char *format = "xml";										//设置返回格式 "xml"

        char *postString;


        char *bufPic1 = getPicBase64FromFile("./face1.jpg");		//获取图片 1 的 base64流
        char *bufPic2 = getPicBase64FromFile("./face2.jpg");		//获取图片 2 的 base64流

        int len = strlen(key)+strlen(secret)+strlen(bufPic1)+strlen(bufPic2)+124;			//计算所需传参字符串大小
        postString = (char *)malloc(len);													//为传参字符串创建空间
        memset(postString,'\0',len);														//初始化传参字符串空间
        sprintf(postString,"&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s",
                        bufPic1,bufPic2,key,secret,typeId,format);							//拼接平台要求的传参字符串

        curl = curl_easy_init();
        if (curl)
        {
                curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);    					//指定 post 内容
                curl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do");	//指定 url
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData);					//接收到数据,调用回调函数
                res = curl_easy_perform(curl);
				curl_easy_cleanup(curl);													//清除 curl
        }
        return 1;
}

char *getPicBase64FromFile(char *filePath)
{
		int fd;
        int filelen;
        char cmd[128];
        char *bufPic;

        sprintf(cmd,"base64 %s > tmpFile",filePath);		//拼接系统调用字符串
        system(cmd);										//系统调用获取图片 base64流

        fd = open("./tmpFile",O_RDWR);						//存放图片 base64 流的临时文件
        filelen = lseek(fd,0,SEEK_END);						//计算文件字符数
        lseek(fd,0,SEEK_SET);								//指针回到文件头部

        bufPic = (char *)malloc(filelen+2);					//创建空间存放图片 base64流
        memset(bufPic,'\0',filelen+2);						//初始化空间
        read(fd,bufPic,filelen);							//文件内容读取到空间
        close(fd);											//关闭文件描述符

        system("rm -f tmpFile");							//忽略提示关闭临时文件

        return bufPic;										//返回图片 base64流
}

size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)		//回调函数
{
        strncpy(buf,ptr,1024);
}

struct Equipment *findEquipByName(char *name,struct Equipment *phead)		//根据名字寻找设备工厂链表链节函数,并返回链节
{
	struct Equipment *tmp = phead;

	if(phead == NULL){
		return NULL;
	}

	while(tmp != NULL){
		if(strcmp(name,tmp->equipName) == 0){
			return tmp;
		}
		tmp = tmp->next;
	}
	return NULL;
}

struct Command *findCommandByName(char *name,struct Command *phead)			//根据名字寻找指令控制工厂链表链节函数,并返回链节
{
	struct Command *tmp = phead;

	if(phead == NULL){
		return NULL;
	}

	while(tmp != NULL){
		if(strcmp(name,tmp->commandName) == 0){
			return tmp;
		}
		tmp = tmp->next;
	}
	return NULL;
}

3、分文件

(1)equipment.h 文件(设备类头文件):

#include 				//wiringPi库
#include 
#include 

struct Equipment								//设备工厂链表节点定义
{
	char equipName[128];						//设备名
	int pinNum;									//引脚号
	int status;									//“初始化设备”函数指针
	int (*Init)(int pinNum);					//“打开设备”函数指针
	int (*open)(int pinNum);					//“关闭设备”函数指针
	int (*close)(int pinNum);

	int (*readStatus)(int pinNum);				//“读取设备状态”函数指针
	int (*changeStatus)(int status);			//“改变设备状态函数指针”

	struct Equipment *next;
};

struct Equipment *addEleLockToLink(struct Equipment *phead);				//“电磁锁”设备节点加入设备工厂链表函数声明

(2)electromagneticLock.c 文件:

#include "equipment.h"

int eleLockInit(int pinNum);				//一些函数声明
int eleLockOpen(int pinNum);
int eleLockClose(int pinNum);
struct Equipment *addEleLockToLink(struct Equipment *phead);


struct Equipment eleLock = {			//“电磁锁”设备链表节点
	.equipName = "eleLock",
	.pinNum = 1,						//树莓派gpio引脚 1
	.Init = eleLockInit,
	.open = eleLockOpen,
	.close = eleLockClose,
};


int eleLockInit(int pinNum)			//初始化函数
{
	pinMode(pinNum,OUTPUT);					//配置引脚为输出引脚
	digitalWrite(pinNum,HIGH);				//引脚输出高电平,即默认为关闭状态
}

int eleLockOpen(int pinNum)			//打开函数
{
	digitalWrite(pinNum,LOW);
}

int eleLockClose(int pinNum)			//关闭函数
{
	digitalWrite(pinNum,HIGH);
}


struct Equipment *addEleLockToLink(struct Equipment *phead)		//头插法将设备节点加入设备工厂链表函数
{
	if(phead == NULL){
		return &eleLock;
	}else{
		eleLock.next = phead;
		phead = &eleLock;
		return phead;
	}
}

(3)command.h 文件(指令控制类头文件):

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct Command									//指令控制工厂链表节点定义
{
	char commandName[128];						//“控制方式”名字
	char deviceFilesName[128];					//存放初始化功能需要打开的文件的路径
	char command[32];							//存放指令
	int fd;										//存放文件描述符
	int (*Init)(struct Command *file);			//“初始化”函数指针
	int s_fd;									//存放套接字描述符
	char ipAdress[32];							//存放IP地址
	char port[12];								//存放端口号
	int (*getCommand)(struct Command *cmd);		//“获取指令”函数指针
	char log[1024];								//日志(暂未使用)

	struct Command *next;
};

struct Command *addVoiceControlToLink(struct Command *phead);		//“语音控制”加入指令控制工厂链表函数声明

(4)voiceControl.c 文件(语音控制):

#include "command.h"
#include 

int voiceControlInit(struct Command *file);							//“语音控制”功能初始化函数声明
int voiceControlGetCommand(struct Command *cmd);					//“获取指令”函数声明
struct Command *addVoiceControlToLink(struct Command *phead);		//“语音控制”加入指令控制工厂链表函数声明


struct Command voiceControl = {				//“语音控制”链表节点
	.commandName = "voiceControl",
	.deviceFilesName = "/dev/ttyAMA0",
	.command = {'\0'},
	.Init = voiceControlInit,
	.getCommand = voiceControlGetCommand,
	.log = {'\0'},
};


int voiceControlInit(struct Command *file)
{
	int fd;
	if((fd = serialOpen(file->deviceFilesName,9600)) == -1){		//打开树莓派串口,波特率为9600
		exit(-1);
	}
	file->fd = fd;				//打开串口文件成功,返回“文件描述符”到“语音控制”链表节点中
}


int voiceControlGetCommand(struct Command *cmd)					//“获取指令”函数
{
	int nread = 0;
	memset(cmd->command,'\0',sizeof(cmd->command));					//读取串口
	nread = read(cmd->fd,cmd->command,sizeof(cmd->command));		//返回读取到数据的字节数
	return nread;
}


struct Command *addVoiceControlToLink(struct Command *phead)		//头插法将“语音控制”链表节点加入指令控制工厂链表函数
{
	if(phead == NULL){
		return &voiceControl;
	}else{
		voiceControl.next = phead;
		phead = &voiceControl;
		return phead;
	}
}

4、编译

在树莓派中,先安装好相应的库:

sudo apt install libssl-dev libcurl4-openssl-dev 

然后编译:

gcc main.c electromagneticLock.c voiceControl.c -lcurl -lwiringPi -lpthread -lssl

你可能感兴趣的:(树莓派智能家居,智能家居,raspberry,pi,树莓派)