目录
一、功能需求
二、开发环境
1、硬件:
2、软件:
3、引脚分配:
三、关键点
1、设计模式之工厂模式
2、wiringPi库下的相关硬件操作函数调用
3、语音模块的串口通信
4、线程
5、摄像头的实时监控和拍照功能
6、人脸识别
四、编译和运行
五、视频功能展示
一、功能需求
- 火焰传感器检测到火焰时,蜂鸣器报警,直到无火焰时停止报警
- 语音控制多个LED灯的开和关(二楼灯、餐厅灯、客厅灯、卫生间灯)
- 语音开启摄像头,并在对应IP地址的网页上实时监控画面
- 语音控制摄像头拍摄照片,存在当前文件夹下(通过filezilla将照片传到PC上查看)
- 语言开启人脸识别功能,将拍摄照片与本人照片对比,识别成功蜂鸣器滴一声,失败滴四声
- 通过socket网络,实现开发板跑服务端,安卓机跑客户端,实现手机远程发送指令完成以上功能,要做app和学java(未实现)
二、开发环境
1、硬件:
Orangepi Zero2 全志H616开发板,语音模块SU-03T,摄像头模组OV9726,蜂鸣器,火焰传感器,4个LED等,4路继电器组,6v电源,若干杜邦线。
2、软件:
MobaXterm、VS Code、FileZilla
3、引脚分配:
在MobaXterm命令控制终端输入gpio readall可以查看开发板上的所有引脚。语音模块、蜂鸣器、火焰传感器和4路继电器组的引脚接线在下图框出。
三、关键点
1、设计模式之工厂模式
工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。本文通过使用工厂模式,将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象(链表)。
//1.指令工厂初始化
pCommandHead = addVoiceContrlToInputCommandLink(pCommandHead);//串口
pCommandHead = addSocketContrlToInputCommandLink(pCommandHead);
//2.设备控制工厂初始化
//四个LED+火灾+蜂鸣器+摄像头
pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead);
pdeviceHead = addUpstairLightToDeviceLink(pdeviceHead);
pdeviceHead = addRestaurantLightToDeviceLink(pdeviceHead);
pdeviceHead = addLivingroomLightToDeviceLink(pdeviceHead);
pdeviceHead = addFireToDeviceLink(pdeviceHead);
pdeviceHead = addBeepToDeviceLink(pdeviceHead);
pdeviceHead = addCameraToDeviceLink(pdeviceHead);
2、wiringPi库下的相关硬件操作函数调用
包括wiringPi库的初始化,蜂鸣器、火焰传感器、继电器组的输入输出引脚配置和高低电平设置。wiringP库下的串口配置和初始化。
3、语音模块的串口通信
本文用的是全志H616芯片中的串口设备/dev/ttyS5,波特率115200,实现与语音模块的串口通信。语音模块收到我们发出的命令词,将命令词转化为16进制数据通过串口发送到开发板,在程序中完成对串口数据接收、存储、判断是哪种命令、执行对应命令的操作,包括开关二楼灯、餐厅灯、客厅灯、卫生间灯、开启摄像头、拍一张照片和人脸识别。
语音模块SU-03T需要烧入对应命令的SDK,本文配置的SDK是在智能公元/AI产品零代码平台上完成,免费的。
int get_voice_type(char *cmd)
{
if(!strcmp("OLL", cmd)) return OLL;
if(!strcmp("ORL", cmd)) return ORL;
if(!strcmp("OUL", cmd)) return OUL;
if(!strcmp("OBL", cmd)) return OBL;
if(!strcmp("CLL", cmd)) return CLL;
if(!strcmp("CRL", cmd)) return CRL;
if(!strcmp("CUL", cmd)) return CUL;
if(!strcmp("CBL", cmd)) return CBL;
if(!strcmp("OC" , cmd)) return OC ;
if(!strcmp("TAP", cmd)) return TAP;
if(!strcmp("OFR", cmd)) return OFR;
perror("voice recognition failure");
}
void voiceContrlFunc(char *cmd){
switch(get_voice_type(cmd)){
case OLL://OLL ASCII对应的16进制4f 4c 4c
printf("open livingroom light\n");
struct Devices *tmpOpenLivingroomLight = findDeviceByName("livingroomLight", pdeviceHead);
tmpOpenLivingroomLight->open(tmpOpenLivingroomLight->pinNum);
break;
case ORL://ORL 4f 52 4c
printf("open restaurant light\n");
struct Devices *tmpOpenRestaurantLight = findDeviceByName("restaurantLight", pdeviceHead);
tmpOpenRestaurantLight->open(tmpOpenRestaurantLight->pinNum);
break;
case OUL://OUL 4f 55 4c
printf("open upstair light\n");
struct Devices *tmpOpenUpstairLight = findDeviceByName("upstairLight", pdeviceHead);
tmpOpenUpstairLight->open(tmpOpenUpstairLight->pinNum);
break;
case OBL://OBL 4f 42 4c
printf("open bathroom light\n");
struct Devices *tmpOpenBathroomLight = findDeviceByName("bathroomLight", pdeviceHead);
tmpOpenBathroomLight->open(tmpOpenBathroomLight->pinNum);
break;
case CLL://CLL 43 4c 4c
printf("close livingroom light\n");
struct Devices *tmpCloseLivingroomLight = findDeviceByName("livingroomLight", pdeviceHead);
tmpCloseLivingroomLight->close(tmpCloseLivingroomLight->pinNum);
break;
case CRL://CRL 43 52 4c
printf("close restaurant light\n");
struct Devices *tmpCloseRestaurantLight = findDeviceByName("restaurantLight", pdeviceHead);
tmpCloseRestaurantLight->close(tmpCloseRestaurantLight->pinNum);
break;
case CUL://CUL 43 55 4c
printf("close upstair light\n");
struct Devices *tmpCloseUpstairLight = findDeviceByName("upstairLight", pdeviceHead);
tmpCloseUpstairLight->close(tmpCloseUpstairLight->pinNum);
break;
case CBL://CBL 43 42 4c
printf("close bathroom light\n");
struct Devices *tmpCloseBathroomLight = findDeviceByName("bathroomLight", pdeviceHead);
tmpCloseBathroomLight->close(tmpCloseBathroomLight->pinNum);
break;
case OC://OC 4f 43
printf("open camera\n");
printf(" -------------------------------------------------------------------\n");
printf(" --\033[1;32m 已开启摄像头,请到指定网页观看画面 https//192.168.43.206:8081 \033[0m--\n");//黄色字体
printf(" -------------------------------------------------------------------\n");
printf("\n");
break;
case TAP://TAP 54 41 50
printf("take a picture\n");
struct Devices *tmpTakeAPictureCamera = findDeviceByName("camera", pdeviceHead);
tmpTakeAPictureCamera->takeAPicture();
printf(" --------------------------------------\n");
printf(" --\033[1;32m 已拍照,请在当前文件夹下查看照片 \033[0m--\n");//黄色字体
printf(" --------------------------------------\n");
printf("\n");
break;
case OFR://OFR 4f 46 52
printf("open face recognition\n");
struct Devices *tmpFaceRecCamera = findDeviceByName("camera", pdeviceHead);
tmpFaceRecCamera->faceRecognition();
break;
}
}
4、线程
主函数中创建了三个线程:语音线程、网络线程、火焰检测线程。
语音线程(voiceThread):完成串口的配置和初始化,在while循环里每隔0.3s检查串口是否有数据到来。
网络线程(socketThread):接收客户端指令,实现手机远程发送指令完成以上功能,要做app和学java(未实现)。
火焰检查线程(socketThread):while循环里每隔0.5s检测是否有火焰,有则蜂鸣器发出警报,知道无火焰。
5、摄像头的实时监控和拍照功能
参考这篇文章:(1031条消息) 香橙派Orange Pi Zero 2开发板配置USB摄像头的方法_阿龙还在写代码的博客-CSDN博客
6、人脸识别
通过命令词【人脸识别】让摄像头拍摄一张照片并与本地的本人照片做对比,成功则蜂鸣器滴一声,失败则滴四声。人脸识别的处理程序调用的是翔云OCR人脸识别 (netocr.com)的人脸识别API,该接口地址https://netocr.com/api/faceliu.do是https协议的地址,https协议是在http和tcp之间多添加了一层,进行身份验证和数据加密。
若要访问https协议的地址,则需要用到Libcurl这个跨平台的网络协议库,配合OpenSSL库,就可以访问https协议的接口。(编译OpenSSL支持Libcurl的https访问,如果直接编译Libcurl,只能访问http不能访问https,需要OpenSSL库才能访问https)
camera.c
#include "contrlDevices.h"
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
extern struct Devices *pdeviceHead;//extern表面变量或函数是定义在其他文件中,声明 为全局变量在该源文件使用
struct Devices* findDeviceByName(char *name, struct Devices *phead);
#define true 1
#define false 0
struct Devices camera;
char buf[1024] = {'\0'};//全局
void cameraTakeAPicture(){
system("(fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./image.jpg) > tmpFile");//照片存放在当前目录下
}
size_t readData(void *ptr, size_t size, size_t nmemb, void *stream){
strncpy(buf, ptr, 1024);
}
char *getPicBase64FromFile(char *filePath){
char *bufPic = NULL;
char cmd[128] = {'\0'};
sprintf(cmd, "base64 %s > tmpFile", filePath);
system(cmd);//图片的base64流数据存入tmpFile文件中
int fd = open("./tmpFile", O_RDWR);
int filelen = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);//重新让文件的光标回到初始位置
bufPic = (char *)malloc(filelen + 2);//+1也可以 多加点没毛病
memset(bufPic, '\0', filelen + 2);
read(fd, bufPic, filelen);
close(fd);
system("rm -f tmpFile");
return bufPic;
}
void cameraFaceRecognition(){
camera.takeAPicture();
CURL *curl;
CURLcode res;
char *postString;
char *img1;
char *img2;
char *key = "DYRrmZz2rTwYGywyWdhKzR";
char *secret = "56bc8e083a9b4d9fbf590413ddcb3a61";
int typeId = 21;
char *format = "xml";
char *bufPic1 = getPicBase64FromFile("./image.jpg");
char *bufPic2 = getPicBase64FromFile("./zyl.jpg");
int len = strlen(key) + strlen(secret) + strlen(bufPic1) + strlen(bufPic2) + 128;
postString = (char *)malloc(len);
memset(postString, '\0', len);//sizeof(postString)替换成len,因为postString是指针
sprintf(postString, "&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", bufPic1, bufPic2, key, secret, 21, format);//拼接字符串
curl = curl_easy_init();
if (curl){
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件
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);
struct Devices *beepHandler = findDeviceByName("beep", pdeviceHead);
if(strstr(buf, "是") != NULL){
beepHandler->open(beepHandler->pinNum); usleep(300000);
beepHandler->close(beepHandler->pinNum);
printf("\n");
printf(" -----------------------------------\n");
printf(" --\033[1;32m 人脸识别成功: the same person \033[0m--\n");//绿色字体
printf(" -----------------------------------\n");
printf("\n");
}
else{
int i = 4;
while(i--){
beepHandler->open(beepHandler->pinNum); usleep(200000);
beepHandler->close(beepHandler->pinNum); usleep(100000);
}
printf("\n");
printf(" ------------------------------------\n");
printf(" --\033[1;31m 人脸识别失败: different person \033[0m--\n");//红色字体
printf(" ------------------------------------\n");
printf("\n");
}
curl_easy_cleanup(curl);
}
}
//实例化对象
struct Devices camera = {
.deviceName = "camera",
.takeAPicture = cameraTakeAPicture,
.faceRecognition = cameraFaceRecognition
};
struct Devices* addCameraToDeviceLink(struct Devices *phead){
if(phead == NULL){
return &camera;
}
else{//头插
camera.next = phead;
phead = &camera;
return phead;
}
}
四、编译和运行
编译时需要用到一些库文件和该库文件里的头文件。
gcc bathroomLight.c livingroomLight.c restaurantLight.c upstairLight.c socketContrl.c voiceContrl.c fireDetection.c beep.c camera.c usartContrl.c main.c -I ../httpHandler/curl-7.71.1/_install/include/ -L ../httpHandler/curl-7.71.1/_install/lib/ -lcurl -lwiringPi -lpthread
运行
sudo ./a.out
五、视频功能展示
【linux项目】-智能家居