一、功能介绍
硬件:树莓派3B、LD3320语音识别模块、pi 摄像头、继电器组、小灯、火焰传感器、蜂鸣器、电 磁锁
项目框架:
采用了简单工厂模式的一个设计方式。稳定,拓展性更强,在C语言中,因为没有接口、类这一说法,所以这里采用了结构体来“等效替换”。有四个灯,所以我创建了四个灯控制.c程序。每一个程序文件中,都有一个设备结构体,每个程序文件的函数实现方法不同,当有新设备进入只需要在创建一个.c文件,改变函数实现方法即可。初始化的时候,通过链表将各个模块连接起来(头插法)。在要使用某个模块时,只需要使用链表遍历,找到所需模块去调用功能
具体功能是:
1、可通过ld3320语音模块的口令模式,口令+具体控制,通过串口把控制指令传给树莓派,来控 制客厅、餐厅、二楼、浴室的灯,以及 人脸识别功能。
2、也可以通过socket客户端来发指令来控制灯的开关,电磁锁
3、火灾报警,当火焰传感器检测到火焰的时候,蜂鸣器会报警。
4、视频监控采用开源mjpg-Streamer来实现的,程序执行时创建一个视频监控的线程,用system函数调用启动脚本运行,监控画面可在http://172.20.10.8:8080去看到
5、人脸识别开锁,人脸识别功能是使用的翔云平台的人脸识别解决方案,需要安装libcurl 和 openSSl库来支持https协议,通过系统调用wget +http://172.20.10.8:8080/?action=snapshot -O ./huyu1.jpg 指令到树莓派的监控页面"去截取一帧保存到本地,获取图片的base64编码,工程文件夹下也有一张照片,huyu.jpg格式,相当于采集的人脸。也是获取图片的base64编码,通过sprintf函数将访问翔云需要的两张图片的base64编码与Key、secret、typeId、format拼接在一起,通过https协议去访问翔云平台, 识别成功后会将识别结果返回,通过回调函数readData将返回的字符串读到readBuff里,通过strstr去readbuff里找有没有字符’是’,如果识别成功就去控制电磁锁打开。
二、设计框图
control Device
#include
#include
#include
struct Devices
{
char name[128];
int status;
int pinName;
int (*open)(int pinName);
int (*close)(int pinName);
int (*deviceInit)(int pinName);
void (*justDoOnce)();
char* (*getFace)();
char* (*getPicFromOCRBase64)();
int (*readStaus)(int pinName);
int (*changeStatus)(int status);
struct Devices* next;
};
struct Devices* addbathroomLink(struct Devices* head);
struct Devices* addupstairLink(struct Devices* head);
struct Devices* addrestaurantLink(struct Devices* head);
struct Devices* addlivingroomLink(struct Devices* head);
struct Devices* addcameraToDeviceLink(struct Devices *head);
struct Devices* addfiretoLink(struct Devices* head);
struct Devices* addBeepToDeviceLink(struct Devices *phead) ;
inoutcommand
#include
#include
struct inputcommander{
char commandName[128];
char deviceName[128];
char command[32];
int (*init)(struct inputcommander*voicer ,char* ipAddress,char* port);
int (*getCommand)(struct inputcommander* voicer);
char log[1024];
int fd;
char port[12];
char ipAddress[32];
int sfd;
struct inputcommander*next;
};
struct inputcommander* addvoiceControlInputLink(struct inputcommander* phead);
struct inputcommander* addsockControlLink(struct inputcommander* phead);
bathroom
#include "controDevice.h"
int bathroomLightopen(int pinName){
digitalWrite(pinName,LOW);
}
int bathroomLightclose(int pinName){
digitalWrite(pinName,HIGH);
}
int bathroomLightInit(int pinName){
pinMode(pinName,OUTPUT);
digitalWrite(pinName,HIGH);
}
struct Devices bathroomLight = {
.name="bathroomLight",
.pinName=22,
.open=bathroomLightopen,
.close=bathroomLightclose,
.deviceInit=bathroomLightInit
};
struct Devices* addbathroomLink(struct Devices* head){
if(head==NULL){
return &bathroomLight;
}
else
{
bathroomLight.next=head;
head=&bathroomLight;
return head;
}
}
livinglight
#include "controDevice.h"
int livingroomLightopen(int pinName){
digitalWrite(pinName,LOW);
}
int livingroomLightclose(int pinName){
digitalWrite(pinName,HIGH);
}
int livingroomLightInit(int pinName){
pinMode(pinName,OUTPUT);
digitalWrite(pinName,HIGH);
}
int livingroomLightChangestatus(int status){
}
struct Devices livingroomLight = {
.name="livingroomLight",
.pinName=24,
.open=livingroomLightopen,
.close=livingroomLightclose,
.deviceInit=livingroomLightInit,
.changeStatus=livingroomLightChangestatus
};
struct Devices* addlivingroomLink(struct Devices* head){
if(head==NULL){
return &livingroomLight;
}
else
{
livingroomLight.next=head;
head=&livingroomLight;
return head;
}
}
restraut light
#include "controDevice.h"
int restaurantLightopen(int pinName){
digitalWrite(pinName,LOW);
}
int restaurantLightclose(int pinName){
digitalWrite(pinName,HIGH);
}
int restaurantLighttInit(int pinName){
pinMode(pinName,OUTPUT);
digitalWrite(pinName,HIGH);
}
int restaurantLightChangestatus(int status){
}
struct Devices restaurantLight = {
.name="restaurantLight",
.pinName=23,
.open=restaurantLightopen,
.close=restaurantLightclose,
.deviceInit=restaurantLighttInit,
.changeStatus=restaurantLightChangestatus
};
struct Devices* addrestaurantLink(struct Devices* head){
if(head==NULL){
return &restaurantLight;
}
else
{
restaurantLight.next=head;
head=&restaurantLight;
return head;
}
}
upstair light
#include "controDevice.h"
int upstairLightopen(int pinName){
digitalWrite(pinName,LOW);
}
int upstairLightclose(int pinName){
digitalWrite(pinName,HIGH);
}
int upstairLightInit(int pinName){
pinMode(pinName,OUTPUT);
digitalWrite(pinName,HIGH);
}
int upstairLightChangestatus(int status){
}
struct Devices upstairLight = {
.name="upstairLight",
.pinName=21,
.open=upstairLightopen,
.close=upstairLightclose,
.deviceInit=upstairLightInit,
.changeStatus=upstairLightChangestatus
};
struct Devices* addupstairLink(struct Devices* head){
if(head==NULL){
return &upstairLight;
}
else
{
upstairLight.next=head;
head=&upstairLight;
return head;
}
}
filre
#include "controDevice.h"
int firetoInit(int pinName){ //初始化函数
pinMode(pinName,INPUT); //配置引脚为输入引脚
digitalWrite(pinName,HIGH); //引脚输出高电平,即默认为关闭状态
}
int firetostatusread(int pinName){ //读取火焰传感器状态函数
return digitalRead(pinName); //读取高低电平,返回0或1
}
struct Devices fireto = { //火焰传感器设备链表节点
.name="fire",
.pinName=25,
.deviceInit=firetoInit,
.readStaus=firetostatusread
};
struct Devices* addfiretoLink(struct Devices* head){ //头插法将设备节点加入设备工厂链表函数
if(head==NULL){
return &fireto;
}
else
{
fireto.next=head;
head=&fireto;
return head;
}
}
bee
#include "controDevice.h"
int beepInit(int pinName) //初始化函数
{
pinMode(pinName,OUTPUT); //配置引脚为输出引脚
digitalWrite(pinName,HIGH); //引脚输出高电平,即默认为关闭状态
}
int beepOpen(int pinName) //打开蜂鸣器函数
{
digitalWrite(pinName,LOW);
}
int beepClose(int pinName) //关闭蜂鸣器函数
{
digitalWrite(pinName,HIGH);
}
struct Devices beep = { //蜂鸣器设备链表节点
.name = "beep",
.pinName = 7, //树莓派gpio引脚29
.deviceInit = beepInit,
.open = beepOpen,
.close = beepClose
};
struct Devices* addBeepToDeviceLink(struct Devices *phead) //头插法将设备节点加入设备工厂链表函数
{
if(phead == NULL){
return &beep;
}else{
beep.next = phead;
phead = &beep;
return phead;
}
}
camera
#include "controDevice.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SWITCH1 21
char ocrRetBuf[1024] = {'\0'};//全局变量,用来接收从OCR后台返回的数据
size_t readData1(void *ptr, size_t size, size_t nmemb, void *stream)
//回调函数,把从后台的数据拷贝给ocrRetBuf
{
strncpy(ocrRetBuf,ptr,1024);
printf("data reviver\n");
}
char *getPicFromOCRBase641(char *Filepath)
{
int fd;
int filelen;
char cmd[128]={'\0'};
sprintf(cmd,"base64 %s > tmpFile",Filepath);
system(cmd);
fd=open("./tmpFile",O_RDWR);
filelen=lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
char *bufpic=(char *)malloc(filelen+2);
memset(bufpic,'\0',filelen+2);
read(fd,bufpic,filelen+128);
system("rm -rf tmpFile");
close(fd);
return bufpic;
}
char*getFace1(){
printf("pai zhao zhong\n");
system("raspistill -q 5 -t 1 -o pic.jpg");
while(access("./pic.jpg",F_OK) != 0); //判断是否拍照完毕
printf("paizhao wan bi\n");
char* base64BufFaceRec = getPicFromOCRBase641("./pic.jpg");
system("rm pic.jpg");
return base64BufFaceRec; //返回刚才拍照的base64
}
void postUrl()
{
CURL *curl;
CURLcode res;
//分开定义,然后字符串拼接
char* key = "P5bruv7dU4YRH7JHNxuCeb"; //翔云平台购买人脸识别后的key
char* secret = "0c4c02a1161e43bf9de539d6487260c8"; //翔云平台购买人脸识别后的secret
int typeId = 21;
char* format = "xml";
char* base64BufPic1 = getFace1();
char* base64BufPic2 = getPicFromOCRBase641("PYD.jpg");
int len = strlen(key)+strlen(secret)+strlen(base64BufPic1)+strlen(base64BufPic2)+128;//分配空间不够会导致栈溢出
char* postString = (char* )malloc(len);
memset(postString,'\0',len);//因为postString是一个指针,不能用sizeof来计算其指向的大小
sprintf(postString,"img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s",base64BufPic1,base64BufPic2,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,readData1); //回调函数readDate读取返回值
res = curl_easy_perform(curl); //类似于状态码
printf("OK:%d\n",res);
if(strstr(ocrRetBuf,"是") != NULL){ //判断翔云后台返回的字符串中有没有“是”
printf("the same person\n");
pinMode(SWITCH1,OUTPUT);
digitalWrite(SWITCH1,LOW);
}
else{
printf("different person\n");
digitalWrite(SWITCH1,HIGH);
}
curl_easy_cleanup(curl);
}
}
struct Devices camera = {
.name = "camera",
.justDoOnce = postUrl,
.getFace = getFace1,
.getPicFromOCRBase64 = getPicFromOCRBase641,
//.readData = readData1
};
struct Devices* addcameraToDeviceLink(struct Devices *head)
{
if(head == NULL){
return &camera;
}
else{
camera.next = head;
head = &camera;
}
}
socket
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "inoutCommand.h"
int SocketInit(struct inputcommander* Socketlnits,char*ipAddress,char*port){
int s_fd;
struct sockaddr_in s_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
s_fd = socket(AF_INET, SOCK_STREAM,0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family =AF_INET;
s_addr.sin_port = htons(atoi(Socketlnits->port));
inet_aton(Socketlnits->ipAddress , &s_addr.sin_addr);
bind(s_fd , (struct sockaddr*)&s_addr , sizeof(struct sockaddr_in));
listen(s_fd,10);
printf("socket server Listening >>>\n");
Socketlnits->sfd=s_fd;
return s_fd;
}
struct inputcommander socketControl = {
.commandName="socketserver",
.command={'\0'},
.port = "8124",
.ipAddress ="192.168.43.165",
.init = SocketInit,
.log = {'\0'},
.next = NULL
};
struct inputcommander* addsockControlLink(struct inputcommander* phead){
if(phead==NULL){
return &socketControl;
}
else
{
socketControl.next=phead;
phead=&socketControl;
return phead;
}
}
voice control
#include "inoutCommand.h"
#include
#include
#include
#include
#include
#include
int voicegetCommand(struct inputcommander* voicer){
int nread =0;
memset(voicer->command,'\0',sizeof(voicer->command));
nread = read(voicer->fd, voicer->command, sizeof(voicer->command));
if(nread==0){
printf(" voice device read over times\n");
}
else{
return nread;
}
}
int voiceInit(struct inputcommander* voicer,char*ipAddress,char*port){
int fd;
if((fd = serialOpen(voicer->deviceName,9600))<0){
printf("voicelnit open error\n");
return (-1);
}
printf("voicelnit...contun...\n");
voicer->fd=fd;
return fd;
}
struct inputcommander voiceControl = {
.commandName="voice",
.deviceName="/dev/ttyAMA0",
.command={'\0'},
.init = voiceInit,
.getCommand = voicegetCommand,
.log = {'\0'},
.next = NULL
};
struct inputcommander* addvoiceControlInputLink(struct inputcommander* phead){
if(phead==NULL){
return &voiceControl;
}
else
{
voiceControl.next=phead;
phead=&voiceControl;
return phead;
}
}
#include
#include
#include
#include "inoutCommand.h"
#include "controDevice.h"
#include
#include
#include
#include
#include
#include
#include
#include
#define SWITCH1 21 //四盏灯对应的引脚
#define SWITCH2 22
#define SWITCH3 23
#define SWITCH4 24
#define SWITCH5 25
struct Devices* tem=NULL;
struct inputcommander* commandhead=NULL;
struct inputcommander*socketHeadler = NULL;
int c_fd;
struct Devices* findDeviceByName(char* name,struct Devices* phead){
struct Devices*tmp=phead;
if(phead==NULL){
return NULL;
}
else{
while(tmp!=NULL){
if(strcmp(tmp->name,name)==0){
return tmp;
}
tmp=tmp->next;
}
return NULL;
}
}
struct inputcommander* findcommandByName(char* name,struct inputcommander* phead){
struct inputcommander*tmp=phead;
if(phead==NULL){
return NULL;
}
else{
while(tmp!=NULL){
if(strcmp(tmp->commandName,name)==0){
return tmp;
}
tmp=tmp->next;
}
return NULL;
}
}
void *fireAlarmThread(void *data) //“火灾报警器线程”执行的函数
{
int status;
struct Devices *firetmp = NULL;
struct Devices *buztmp = NULL;
firetmp = findDeviceByName("fire",tem); //寻找“火焰传感器”链表节点,返回给firetmp
buztmp = findDeviceByName("beep",tem); //寻找“蜂鸣器”链表节点,返回给buztmp
while(1){
status = firetmp->readStaus(firetmp->pinName); //读取“火焰传感器”状态
buztmp->deviceInit(buztmp->pinName);
if(status == 0){ //检测到火焰或强光源
printf("have fire\n");
buztmp->open(buztmp->pinName); //打开蜂鸣器
delay(1000); //延时1000毫秒=1秒
}
if(status == 1){ //未检测到火焰、强光源或解除警报
buztmp->close(buztmp->pinName); //关闭蜂鸣器
}
}
}
void *cameraThread_func(void* data)//起线程的函数有格式要求
{
struct Devices *cameraTemp;
cameraTemp = findDeviceByName("camera", tem); //设备都要从工厂里面取出来
if(cameraTemp == NULL){ //防止段错误的必需判断,当给指针赋值是,一定要考虑NULL的情况,否则后续操作都是空谈
printf("find camera error\n");
pthread_exit(NULL); //在线程中不用return
}
printf("222\n");
cameraTemp->justDoOnce(); //调用postUrl函数
}
void* read_Thread(void* datas){
int n_read;
memset(socketHeadler->command,'\0',sizeof(socketHeadler->command));
n_read = read(c_fd,socketHeadler->command,sizeof(socketHeadler->command));
if(n_read == -1){
perror("read");
}
else if(n_read>0){
printf("\n socker read number:%d , contixt:%s\n",n_read,socketHeadler->command);
if(strstr(socketHeadler->command,"KS") != NULL){
printf("open lock\n");
pinMode(SWITCH1,OUTPUT);
digitalWrite(SWITCH1,LOW);
}
if(strstr(socketHeadler->command,"KYS") != NULL){
pinMode(SWITCH2,OUTPUT);
digitalWrite(SWITCH2,LOW);
}
if(strstr(socketHeadler->command,"GYS") != NULL){
digitalWrite(SWITCH2,HIGH);
}
if(strstr(socketHeadler->command,"KKT") != NULL){ //对socket收到的指令进行分析,并执行对应的操作
pinMode(SWITCH4,OUTPUT);
digitalWrite(SWITCH4,LOW);
}
if(strstr(socketHeadler->command,"GKT") != NULL){
digitalWrite(SWITCH4,HIGH);
}
if(strstr(socketHeadler->command,"KCT") != NULL){ //对socket收到的指令进行分析,并执行对应的操作
pinMode(SWITCH3,OUTPUT);
digitalWrite(SWITCH3,LOW);
}
if(strstr(socketHeadler->command,"GCT") != NULL){
digitalWrite(SWITCH3,HIGH);
}
if(strstr(socketHeadler->command,"GS") != NULL){
digitalWrite(SWITCH1,HIGH);
}
else{
printf("Input error! \n");
}
}
}
void* voiceThread(void* datas){
int nread;
struct inputcommander* voiceHead=findcommandByName("voice",commandhead);
if(voiceHead==NULL){
printf(" no voice \n");
pthread_exit(NULL);
}
else{
printf("%s find voice \n",voiceHead->commandName);
if(voiceHead->init(voiceHead, NULL ,NULL)<0){
printf("voice init error!!!\n");
pthread_exit(NULL);
}
else{
printf(" %s init successful!\n",voiceHead->commandName);
}
while(1){
nread = voiceHead->getCommand(voiceHead);
if(nread == 0){
printf("waiting...\n");
}
else{
printf("do divece control : %s\n",voiceHead->command);
if(strstr(voiceHead->command,"XJ") != NULL){ //一级指令,
printf("收到:\n");
}else if(strstr(voiceHead->command,"KYSD") != NULL){
pinMode(SWITCH2,OUTPUT);
digitalWrite(SWITCH2,LOW);
}else if(strstr(voiceHead->command,"GYSD") != NULL){
digitalWrite(SWITCH2,HIGH);
}else if(strstr(voiceHead->command,"KCTD") != NULL){
pinMode(SWITCH3,OUTPUT);
digitalWrite(SWITCH3,LOW);
}else if(strstr(voiceHead->command,"GCTD") != NULL){
digitalWrite(SWITCH3,HIGH);
}else if(strstr(voiceHead->command,"KKTD") != NULL){
pinMode(SWITCH4,OUTPUT);
digitalWrite(SWITCH4,LOW);
}else if(strstr(voiceHead->command,"GKTD") != NULL){
digitalWrite(SWITCH4,HIGH);
}else if(strstr(voiceHead->command,"KS") != NULL){
pthread_t cameraThread;
printf("1111\n");
system("sudo killall -TERM motion");
delay(3000);
pthread_create(&cameraThread,NULL,cameraThread_func,NULL);
}
}
}
}
}
void* socketThread(void* datas){
int n_read = 0;
pthread_t readThread;
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
int clen = sizeof(struct sockaddr_in);
socketHeadler=findcommandByName("socketserver",commandhead);
if(socketHeadler ==NULL){
printf("NO find socketserver!\n");
pthread_exit(NULL);
}
else{
printf("find socketserver!\n");
}
socketHeadler->init(socketHeadler,NULL,NULL);
while(1){
c_fd=accept(socketHeadler->sfd,(struct sockaddr*)&c_addr,&clen);
pthread_create(&readThread,NULL,read_Thread,NULL);
}
}
void * video_thread(void *datas){
system("sudo motion");
printf(" chest ... \n");
//pthread_exit(NULL);
}
int main(){
if(wiringPiSetup() == -1){
printf("wiringPiSetup failed!\n");
return -1;
}
char name[128];
pthread_t voice_thread;
pthread_t socket_thread;
pthread_t fireAlarm_thread;
pthread_t videoThread;
//设备工厂初始化
tem=addbathroomLink(tem);
tem=addupstairLink(tem);
tem=addrestaurantLink(tem);
tem=addlivingroomLink(tem);
tem= addfiretoLink(tem);
tem=addBeepToDeviceLink(tem);
tem=addcameraToDeviceLink(tem);
commandhead=addvoiceControlInputLink(commandhead);
commandhead=addsockControlLink(commandhead);
pthread_create(&voice_thread , NULL , voiceThread , NULL);
pthread_create(&socket_thread , NULL , socketThread , NULL);
pthread_create(&fireAlarm_thread,NULL,fireAlarmThread,NULL);
pthread_create(&videoThread, NULL, video_thread, NULL);
pthread_join(voice_thread,NULL);
pthread_join(socket_thread,NULL);
pthread_join(fireAlarm_thread,NULL);
pthread_join(videoThread,NULL);
return 0;
}