这里的摄像头只是当作一个设备去用,目前实现通过串口指令然后system()进行拍照。然后翔云平台进行人脸对比,未实现自动人脸检测(不会py)。所以摄像头没有另创线程。但是做视频监控可以另创线程。
监控运动物体(人脸靠近)参考:树莓派摄像头使用Motion监测人物动作_行稳方能走远的博客-CSDN博客
这里使用fswebcam进行拍照。参考用户手册
首先在/smarthome拍照,命名为imageComp.jpg
sudo fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./imageComp.jpg
全局变量
char ocrRetBuf[1024] = {'\0'};//全局变量,用来接收从OCR后台返回的数据
camera.c
#include "controlDevice.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>
size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)//回调函数,把从后台的数据拷贝给ocrRetBuf
{
strncpy(ocrRetBuf,ptr,1024);
}
char* getBase64FromFile(char* filePath)
{
char* base64Buf = NULL;
char cmd[256] = {'\0'};
sprintf(cmd,"base64 %s > tmpFile",filePath);//图片的base64流导入到文件中
system(cmd);
int fd = open("./tmpFile",O_RDWR);
int fileLen = lseek(fd,0,SEEK_END); //计算文件大小
lseek(fd,0,SEEK_SET);
base64Buf = (char* )malloc(fileLen+8);
memset(base64Buf,'\0',fileLen+8);
read(fd,base64Buf,fileLen+8); //从文件中读取base64流到字符串
close(fd);
system("rm -f tmpFile");
return base64Buf;//指针变量随着子程序调用结束消失,但malloc开辟的空间地址还在,我拿到了这块地址,就能读
}
char* getFace()
{
printf("人脸数据采集中...\n");
system("sudo fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./image.jpg");
while(access("./image.jpg",F_OK) != 0); //判断是否拍照完毕
printf("数据采集完毕\n");
char* base64BufFaceRec = getBase64FromFile("./image.jpg");
system("rm image.jpg"); //采集完成删除,防止占内存
return base64BufFaceRec; //返回刚才拍照的base64
}
void postUrl() //根据文档,接口调用方法为post请求
{
CURL *curl;
CURLcode res;
//根据翔云平台的接口要求 分开定义,然后字符串拼接
char* base64BufPic1 = getFace();//图片base64流
char* base64BufPic2 = getBase64FromFile("./imageComp.jpg");
char* key = "Tnf7EDJaQ1qFZkow29xxxx";
char* secret = "66d49fdbfd4944ec93035f14ea14xxxx";
int typeId = 21;
char* format = "xml";
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_COOKIEFILE, "/tmp/cookie.txt");// 指定cookie缓存文件
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);//指定post传输内容,get请求将URL和postString一次性发送
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);
printf("OK:%d\n",res);
curl_easy_cleanup(curl);
}
}
struct Devices camera = {
.deviceName = "camera",
.justDoOnce = postUrl
};
struct Devices* addCameraToDeviceLink(struct Devices *phead)
{
if(phead == NULL){
return &camera;
}else{
camera.next = phead; //以前的头变成.next
phead = &camera; //更新头
return phead;
}
}
controlDevice.h
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h> //wiringPi库
extern char ocrRetBuf[1024];
struct Devices //设备类
{
char deviceName[128]; //设备名
int status; //状态
int pinNum; //引脚号
int (*Init)(int pinNum); //“初始化设备”函数指针
int (*open)(int pinNum); //“打开设备”函数指针
int (*close)(int pinNum); //“关闭设备”函数指针
int (*readStatus)(int pinNum); //“读取设备状态”函数指针 为火灾报警器准备
int (*changeStatus)(int status); //“改变设备状态”函数指针
void (*justDoOnce)();
struct Devices *next;
};
struct Devices* addBathroomLightToDeviceLink(struct Devices *phead); //“浴室灯”加入设备链表函数声明
struct Devices* addBedroomLightToDeviceLink(struct Devices *phead); //“卧室灯”加入设备链表函数声明
struct Devices* addRestaurantLightToDeviceLink(struct Devices *phead); //“餐厅灯”加入设备链表函数声明
struct Devices* addLivingroomLightToDeviceLink(struct Devices *phead); //“客厅灯”加入设备链表函数声明
struct Devices* addSmokeAlarmToDeviceLink(struct Devices *phead); //“烟雾报警器”加入设备链表函数声明
struct Devices* addBuzzerToDeviceLink(struct Devices *phead); //“蜂鸣器”加入设备链表函数声明
struct Devices* addCameraToDeviceLink(struct Devices *phead);
struct Devices* addLockToDeviceLink(struct Devices *phead);
在mainPro.c文件Command(struct InputCommand* CmdHandler)函数添加
if(strcmp("OCR",CmdHandler->command) == 0){
tmp = findDeviceByName("camera",pdeviceHead);
if(tmp != NULL){
tmp->justDoOnce();
if(strstr(ocrRetBuf,"否") != NULL){ //字符串检索 判断翔云后台返回的一大堆字符串中有没有“否”
printf("人脸比对失败\n");
}else{
printf("人脸比对成功\n");
tmp = findDeviceByName("lock",pdeviceHead);
if(tmp != NULL){
tmp->open(tmp->pinNum);
printf("已开门\n");
delay(3000);
tmp->close(tmp->pinNum);
}
}
}
}
这样当串口发送OCR时,实现人脸对比并开锁,所以没有用线程去做
当然,要把camera、lock设备加入设备工厂
编译运行
gcc *.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt -I ../https/curl-7.71.1/_install/include/ -L ../https/curl-7.71.1/_install/lib/ -lcurl
sudo ./a.out
运行结果
智能家居项目目录
智能家居(5) —— 智能家居项目整合(语音控制线程,网络控制线程、烟雾报警线程)
网络编程知识预备(2) —— 三次握手与四次挥手、半连接状态、2MSL
网络编程知识预备(3) —— TCP流量控制(滑动窗口)、拥塞控制
网络编程知识预备(4) —— SOCKET、TCP、HTTP之间的区别与联系
网络编程知识预备(5) —— 了解应用层的HTTP协议与HTTPS协议
网络编程知识预备(6) —— libcurl库简介及其编程访问百度首页
智能家居(7) —— 人脸识别 & 翔云平台编程使用(编译openSSL支持libcurl的https访问、安装SSL依赖库openSSL)