一.项目分析
1.1 实现的功能
- 控制端
- 支持客户端发送指令
- 支持语音识别,发送指令
- 外设端
- 支持人脸识别开锁
- 支持监控
- 支持火灾报警
- 支持所有的灯光控制
- 支持窗帘和泳池灯的遥控
- 支持电风扇,电视机,空调,小夜灯的遥控
1.2硬件
- 主控芯片:树莓派:
优点:
1. 支持串口,无线蓝牙,WiFi等多种通信方式
2. GPIO的引脚比较多(大概可用的GPIO引脚有42个,可以满足我们的需求),可以编程自己配置驱动和引脚功能
3. 支持摄像头,可以用来实现监控和人脸识别解锁
4. 可以远程登录SSH界面操作
5. 学习过成中用的多,比较熟悉
缺点:
1. 响应速度可能没有单片机那么快,特别是语音识别那块
2. 串口只有一对,我后面的433M射频模块和红外编解码模块都需要串口 - 辅助外设:
1. WemosD1:扩充了串口和GPIO引脚
2. 语音模块YS-LDV7:语音识别,传送指令给服务器
3. USB转串口模块:用于WemosD1和语音模块程序的烧写和调试
4. 433M射频编解码模块:用于泳池灯和窗帘的遥控
5. 红外编解码模块:用于破解电风扇,电视机,空调,小夜灯的红外信号,实现自己遥控
6. 蜂鸣器
7. 电磁锁
8. 热感应设备,热敏电阻或者可以反馈高低电平的那种装置
1.3大概思路
- 指令接收部分,使用客户端服务器模型。
- 服务器调用线程处理语音模块和客户端的指令,然后给继电器引脚配置高低电平,或者给WemosD1发送信号指令。
- WemosD1一旦识别到有效指令,会通过串口,继续向433M和红外模块传递指令,进而使家用设备动作。
- 摄像头单独调用一个线程,不断通过辅助平台比对摄像头的图像,一旦识别成功,会开启电磁锁。
- 火灾报警根据选用设备的不同有两种思路:
(1)热敏电阻:可以设计一个继电器控制电路,一旦感应温度超高,会接通控制电路,进而接通蜂鸣器报警。这样就只要提供一个控制电源即可,个人感觉更简单
(2)当然,如果有感应装置会反馈高低电平,也可以调用线程,配置输出引脚,同样可以控制蜂鸣器。
二.程序结构设计
2.1采用工厂设计模式:
-
什么是设计模式:
1. 设计模式,描述了一组相互紧密作用的类和对象
2. C : 面向过程,一门面向对象不友好的语言
Java:面向对象
3. 建筑设计领域引入到计算机科学来的
4. 一共23中,代码容易被他人理解,保证代码的可靠性,程序的重要性 -
什么是类和对象
1. 类就相当于,C语言中的结构体类型,对象就是类的具象化
2. 举例:
#include<stdio.h>
struct animals {
char *name;
char *sex;
void (*doing)();
void (*eat)();
};
void dogdoing(char *name){
printf("%s看家护院\n",name);
}
void pdoing(char *name){
printf("%s不干人事\n",name);
}
int main()
{
struct animals dog ={
.name = "小黄",
.doing = dogdoing//给函数指针赋值,即给函数执行的首地址赋值
};
struct animals p ={
.name = "老王",
.doing = pdoing
};
dog.doing(dog.name);//调用函数
p.doing(p.name);
return 0;
}
ztj@ubuntu:~$ ./a.out
小黄看家护院
老王不干人事
ztj@ubuntu:~$ ^C
- 引入 工厂设计模式
- 可以看到,我们这种编码方式,还是显得很乱,并且暴露了代码逻辑,为了合理化代码的结构,简化开发过程,就引入了工厂模式
- 什么是工厂模式?
工厂模式是 最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
2.2 具体如何实现工厂模式?
- 根据智能家居项目,大致可以将其分为命令输入模块和输出模块
- 因此,可以将项目划分出两大 工厂
- 每个工厂都有若干设备,我们主要是对这些设备进行操作,以实现功能
- 因此,设计出两个结构体,用来分别储存两边设备的一些信息,以及功能实现的函数指针
- 使用链表的形式,将两边的结构体各自联系到一起
- 当我们需要对某个设备进行操作时,只需要知道其链表头节点和设备名或者其他一些信息,就可以匹配到相关设备
- 这样做的好处是,将项目面向对象编程,需要什么,就查找什么
- 同时,后期想添加一些功能,必然会添加一些设备,就简单了许多,因为将项目的实现,完全划分了各自区域,互不影响。
- 我么只需要将新功能涉及的设备直接添加到链表离去。功能的具体实现,就留到main函数中调用线程管理
2.3 模拟一下过程
- 指令处理的过程
- 链表的创建并返回头节点,头节点因为可能线程和函数中会用到,直接设为全局变量。
- 设备的初始化设置
- 命令输入进模块中,通过模块或者socket得到相应的命令,储存在buf中
- 通过字符命令匹配到相应的设备,并将字符命令一并储存在输出设备中去
- 通过字符串命令和设备内部的指令编码
- 根据指令,执行设备内部的指令处理函数,做引脚输入或输出,又或者执行相关的操作
- main函数部分
- 依次插入设备,构建输入和输出两个链表
- 初始化设置
- 创建线程,处理不同部分的指令:语音线程,server线程,摄像头线程,火灾线程
三.代码
3.1 两个头文件
- 这里主要是,对针对两个结构体,根据功能需求,进行了构思,可能有些部分后期没有用到,有些是后期做到具体部分有补充添加的,这里我都保留了。当然还有一些函数和结构体的声明
-controlDevice.h
#include<wiringPi.h>
#include<string.h>
#include<stdio.h>
struct device {
char deviceName[32];//设备名
char fileName[32];//文件名
int writePinNum;//输出引脚
int readPinNum;//写入引脚
char key[32];//指令编码
char commandBuf[32];//设备接收的指令
int status;//设备状态码,检测摄像头是否打开,或者火灾是否触发
FILE *fp;//摄像头返回的文件流
void (*open)(struct device *p);//打开设备
void (*close)(struct device *p);//关闭设备
void (*commandhandle)(struct device *p);//执行指令
void (*deviceInit)(struct device *p);//初始化设备
int (*readStatus)(struct device *p);//读取火灾报警器状态
char (*createFile)(char *fileName);//创建文件,人脸识别备用
struct device *next;//下一个设备节点
};
// 客厅有关声明
struct device *addLivingRoomLighttoLink(struct device *phead);
struct device livingRoomLight;
//餐厅有关声明
struct device *addDiningRoomLighttoLink(struct device *phead);
struct device diningRoomLight;
//二楼灯有关声明
struct device *addUpstairRoomLighttoLink(struct device *phead);
struct device upstairRoomLight;
//卫生间灯有关声明
struct device *addBathRoomLighttoLink(struct device *phead);
struct device bathRoomLight;
//人脸锁相关声明
struct device *addFaceLocktoLink(struct device *phead);
struct device faceLock;
//火灾报警有关声明
struct device *addFireAlarmtoLink(struct device *phead);
struct device fireAlarm;
//摄像头相关
struct device *addCameratoLink(struct device *phead);
struct device camera;
-inputCommand.h
#include<wiringPi.h>
struct inputCommand{
char commandName[32];//输入指令的名字
char command[32];//用于指令的操作
char inPutDeviceName[32];//输入设备
char outPutDevicename[32];//输出设备
int s_fd;//服务器句柄
int c_fd;//客户端句柄
int fd;//串口句柄
int (*Init)(struct inputCommand *p,char *ipAddress,char *port);//指令初始化
int (*getInputCommand)(struct inputCommand *p);//获取指令
struct inputCommand *next ;
};
//语音设备相关声明
struct inputCommand *addVoicetoLink(struct inputCommand *phead);
struct inputCommand voice;
//Server相关声明
struct inputCommand *addServertoLink(struct inputCommand *phead);
struct inputCommand server;
3.2 第一个设备文件作为模板
- 前面说了,工厂模式的好处,有利于后期的添加新模块,和代码的可读性,这一点在编写的过程中,也是有体现的,我只要做出一个模板,那么其他的部分就可以套用,简化了开发过程
- 这里我直接调用了wiringPi的库,进行了引脚的操作,实际上也可以自己编译一个引脚的驱动出来,但调库肯定是快一些的,实用为王
- 如何写IO字符驱动
- bathroomlight.c
#include"controlDevice.h"
#include"inputCommand.h"
#include<stdio.h>
#include<stdlib.h>
void bathRoomLightOpen(struct device *p){
digitalWrite(p->writePinNum,LOW);
}
void bathRoomLightClose(struct device *p){
digitalWrite(p->writePinNum,HIGH);
}
void bathRoomLightInit(struct device *p){
pinMode(p->writePinNum,OUTPUT);
digitalWrite(p->writePinNum,HIGH);
}
void bathRoomLightCommandHandle(struct device *p){
char *buf = p->commandBuf;
if(strcmp(buf,"031") == 0){
p->open(p);
}else if(strcmp(buf,"032") == 0){
p->close(p);
}else{
printf("bathRoomLightCommandHandle fail\n");
}
}
struct device bathRoomLight = {
.deviceName = "bathRoomLight",
.writePinNum = 24,
.key ="031&032",
.open = bathRoomLightOpen,
.close = bathRoomLightClose,
.commandhandle = bathRoomLightCommandHandle,
.deviceInit = bathRoomLightInit
};
struct device *addBathRoomLighttoLink(struct device *phead){
struct device *p;
if(phead == NULL){
phead = &bathRoomLight;
phead->next = NULL;
return phead;
}else{
p = &bathRoomLight;
p->next = phead;
phead = p;
return phead;
}
}
3.3 语音输入设备
- 这部分是基于ld_v7(基于ld_3320)板子的一个二次开发,厂家为我提供了开发资料,不需要关心LD3320这块芯片如何处理声音,只需要修改模板,主要是对源码的阅读,然后修改其中的关键字个词条
- 语音模块是通过串口连接的,烧写时通过TTL转USB连接电脑,用stc-isp工具烧写。
- 下面是和芯片对接的语音控制部分代码,和第一个设备模板差不多,稍作修改
#include"controlDevice.h"
#include"inputCommand.h"
#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <wiringSerial.h>
#include<string.h>
int voiceInit(struct inputCommand *voice,char *ipAddress,char *port){
int fd;
if((fd = serialOpen(voice->inPutDeviceName,9600) ) == -1){
exit(-1);
}
voice->fd = fd;
return fd;
}
int voiceGetCommand(struct inputCommand *voice){
int nread;
memset(voice->command,'\0',32);
nread = read(voice->fd,voice->command,sizeof(voice->command));
if(nread == 0){
printf("uart for voice over time\n");
}else{
return nread;
}
}
struct inputCommand voice = {
.commandName = "voice",
.command = {'\0'},
.inPutDeviceName = "/dev/ttyAMA0",
.outPutDevicename = {'\0'},
.Init = voiceInit,
.getInputCommand = voiceGetCommand,
.next = NULL
};
struct inputCommand *addVoicetoLink(struct inputCommand *phead){
struct inputCommand *p;
if(phead == NULL){
phead = &voice;
phead->next = NULL;
return phead;
}else{
p = &voice;
p->next = phead;
phead = p;
return phead;
}
}
3.4 server/client控制设备部分
- 这部分就是把socket抽象一个输入设备,主要是套接字,并返回描述符
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include<wiringPi.h>
#include <wiringSerial.h>
#include"inputCommand.h"
#include"controlDevice.h"
int serverInit(struct inputCommand *server,char *ipAddress,char *port){
int s_fd;
struct sockaddr_in s_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
//1.socket创建套接字
s_fd = socket(AF_INET,SOCK_STREAM, 0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
//2.bind(为套接字绑定地址和端口号)
s_addr.sin_family = AF_INET;//IPV4 因特网
s_addr.sin_port = htons( atoi(port) );//小端变大端
inet_aton(ipAddress,&s_addr.sin_addr) ;//字符串变网络形式
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
server->s_fd = s_fd;
printf("socket init success\n");
return s_fd;
}
int serverGetCommand(struct inputCommand *server){
/* int nread;
memset(server->command,'\0',32);
nread = read(server->c_fd,server->command,strlen(server->command));
return nread;*/
return 0;
}
struct inputCommand server = {
.commandName = "server",
.inPutDeviceName = {'\0'},
.outPutDevicename = {'\0'},
.command = {'\0'},
.Init = serverInit,
.getInputCommand = serverGetCommand,
.next = NULL
};
struct inputCommand *addServertoLink(struct inputCommand *phead){
struct inputCommand *p;
if(phead == NULL){
phead = &server;
phead->next = NULL;
return phead;
}else{
p = &server;
p->next = phead;
phead = p;
return phead;
}
}
3.5 main 函数部分
- 这部分是对各功能模块的一个汇总,主要就是四大线程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"inputCommand.h"
#include"controlDevice.h"
#include <unistd.h>
#include <wiringSerial.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
struct inputCommand *commandHead = NULL;//指令输入工厂头节点
struct device *deviceHead = NULL;//设备工厂头节点
struct inputCommand *sockethandle1 = NULL;//socket线程工具
struct device *sockethandle2 = NULL;
struct inputCommand *voicethandle1 = NULL;//voice线程工具
struct device *voicethandle2 = NULL;
struct device *camerahandle1 = NULL;//camera线程工具
struct device *camerahandle2 = NULL;
char *p = NULL;//客户端地址
//根据设备名字查找设备
struct device *findDeviceByName(char *name,struct device *deviceHead){
struct device *p;
p = deviceHead;
while(p != NULL){
if(strcmp(name,p->deviceName ) == 0 ){
return p;
}
p = p->next;
}
return NULL;
}
//通过命令类型查找设备输入设备
struct inputCommand *findInputByName(char *name,struct inputCommand *commandHead){
struct inputCommand *p;
p = commandHead;
while(p != NULL){
if(strcmp(name,p->commandName ) == 0 ){
return p;
}
p = p->next;
}
return NULL;
}
struct device *findDeviceByCommand(char *command,struct device *deviceHead){
struct device *p;
p = deviceHead;
while(p != NULL){
if(strstr(p->key,command ) != NULL ){
return p;
}
p = p->next;
}
return NULL;
}
void *voicePthread(){
int nread;
voicethandle1 = findInputByName("voice",commandHead);
if(voicethandle1 == NULL){
printf("find voicehandle error\n");
pthread_exit(NULL);
}else{
if( voicethandle1->Init(voicethandle1,NULL,NULL) < 0){
printf("voice init error\n");
pthread_exit(NULL);
}
while(1){
nread = voicethandle1->getInputCommand(voicethandle1);
if(nread == 0){
printf("voice no data\n");
}else{
printf("readvoice %s,%dByte \n",voicethandle1->command,nread);
voicethandle2 = findDeviceByCommand(voicethandle1->command,deviceHead);//找设备
if(voicethandle2 != NULL){//找到设备
strcpy(voicethandle2->commandBuf,voicethandle1->command);//传递指令到设备
voicethandle2->commandhandle(voicethandle2);//设备执行指令
}
}
}
}
return NULL;
}
void *readPthread(){
int nread;
while(1){
memset(sockethandle1->command,'\0',32);
nread = read(sockethandle1->c_fd,sockethandle1->command,sizeof(sockethandle1->command));
if(nread == -1){
perror("read");
}else if(nread > 0){
printf("readclient %s,%dByte \n",sockethandle1->command,nread);
sockethandle2 = findDeviceByCommand(sockethandle1->command,deviceHead);//找设备
if(sockethandle2 != NULL){//找到设备
strcpy(sockethandle2->commandBuf,sockethandle1->command);//传递指令到设备
sockethandle2->commandhandle(sockethandle2);//设备执行指令
}
}else{
printf("%s client quit\n",p);
pthread_exit(NULL);
}
}
return NULL;
}
void *serverPthread(){
int nread = 0;
pthread_t read_thread;
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
int sizec_addr = sizeof(struct sockaddr_in);
sockethandle1 = findInputByName("server",commandHead);
if(sockethandle1 == NULL){
printf("find sockethandle error\n");
pthread_exit(NULL);
}
//socket初始化
sockethandle1->Init(sockethandle1,"192.168.0.16","8088");
// listen()监听
listen(sockethandle1->s_fd,10 );
printf("socket listen...\n");
while(1){
// 不断地接收客户端请求,接收到就创建一个线程
sockethandle1->c_fd = accept(sockethandle1->s_fd, (struct sockaddr *)&c_addr, &sizec_addr);
if( sockethandle1->c_fd != -1){
p = inet_ntoa(c_addr.sin_addr);
printf("client %s connect\n",p);
pthread_create(&read_thread,NULL,readPthread,NULL);
}
pthread_join(read_thread,NULL);
}
return NULL;
}
void *cameraPthread(){
camerahandle1 = findDeviceByName("camera",deviceHead);
camerahandle2 = findDeviceByName("faceLock",deviceHead);
while(1){
if( strcmp(camerahandle1->commandBuf ,"113") == 0 && camerahandle1->fp ==NULL && camerahandle1->status == 0 ){
system("fswebcam /dev/video0 ~/photo/tmp.jpg");
fflush(stdout);
chdir("/home/pi/httphandle");
fflush(stdout);
FILE* pp =popen("/home/pi/httphandle/post","r");
fflush(stdout);
if(pp != NULL){
char *retbuf =(char *)malloc(128);
memset(retbuf,'\0',128);
fread(retbuf,1,64,pp);
if(strstr(retbuf,"Face recognition successful") != NULL){
printf("\n======================sucsessful========================\n\n");
camerahandle2->open(camerahandle2);
}else{
printf("\n======================failed========================\n\n");
camerahandle2->close(camerahandle2);
}
free(retbuf);
pclose(pp);
memset(camerahandle1->commandBuf,'\0',32);
}
}else{
continue;
}
}
return NULL;
}
void *fireAlarmPthread(){
return NULL;
}
int main(){
pthread_t voice_thread;//语音线程
pthread_t server_thread;//服务器线程
pthread_t camera_thread;//摄像头线程
pthread_t fireAlarm_thread;//火灾线程
if(-1 == wiringPiSetup()){
return -1;
}
//1.工厂模式链表创建
deviceHead = addLivingRoomLighttoLink(deviceHead);
deviceHead = addDiningRoomLighttoLink(deviceHead);
deviceHead = addUpstairRoomLighttoLink(deviceHead);
deviceHead = addBathRoomLighttoLink(deviceHead);
deviceHead = addFaceLocktoLink(deviceHead);
deviceHead = addFireAlarmtoLink(deviceHead);
deviceHead = addCameratoLink(deviceHead);
commandHead = addVoicetoLink(commandHead);
commandHead = addServertoLink(commandHead);
//2.初始化
livingRoomLight.deviceInit(&livingRoomLight);
diningRoomLight.deviceInit(&diningRoomLight);
upstairRoomLight.deviceInit(&upstairRoomLight);
bathRoomLight.deviceInit(&bathRoomLight);
faceLock.deviceInit(&faceLock);
fireAlarm .deviceInit(&fireAlarm);
camera.deviceInit(&camera);
//3.线程池建立:
//3.1 语音线程
int ret1 = pthread_create(&voice_thread,NULL,voicePthread,NULL);//注意第4参数类型void *
if(ret1 != 0){
printf("creat voice_thread fail\n");
exit(-1);
}
//3.2 server线程
int ret2 = pthread_create(&server_thread,NULL,serverPthread,NULL);
if(ret2 != 0){
printf("creat server_thread fail\n");
exit(-1);
}
//3.3 摄像头线程
int ret3 = pthread_create(&camera_thread,NULL,cameraPthread,NULL);
if(ret3 != 0){
printf("creat camera_thread fail\n");
exit(-1);
}
//3.4 火灾线程
int ret4 = pthread_create(&fireAlarm_thread,NULL,fireAlarmPthread,NULL);
if(ret4 != 0){
printf("creat fireAlarm_thread fail\n");
exit(-1);
}
//4.阻塞主进程,等待线程退出
pthread_join(voice_thread,NULL);
pthread_join(server_thread,NULL);
pthread_join(camera_thread,NULL);
pthread_join(fireAlarm_thread,NULL);
return 0;
}
3.6 摄像头功能
- 摄像头和人脸识别,这两部分我是单独写的,由main函数去调用和关闭
- 首先是摄像头,由sci和usb两种摄像头接口,使用方法其实是一样的,依赖mjpg-streamer的库,我使用的就是usb接口
- 先下载一个mjpg-streamer的库,并且安装好。里面有READMED等详细的开发文档,配合网上的一些教程,安装好就行了
- 进到源码目录下,.先进入sudo raspi-config,确保打开camera服务,然后 /start.sh脚本文件启动摄像头,也可以打开start.sh编辑它,更改一些设置,比如使用的的接口模式,分辨率等
- 然后浏览器下登陆平台,http://+你的IP地址+:8080,选择streamer可以看到图像
- 8080是端口号
3.7 人脸识别功能
- 人脸识别,通过访问第三方平台(我访问的是翔云OCR平台),可以搜索翔云OCR
- 我们通过摄像头拍照。将两张图片上传到平台,再把结果接收回来,就可以完成人脸识别
- 采用的协议是HTTPS,post请求方式
- http相关博文
- 注意要购买人脸识别产品,有免费的次数,获取key 和secreat
- 注意先安装lcurl和openssl的库,支持https协议
#include <stdio.h>
#include <curl/curl.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define false 0
#define true 1
typedef unsigned int bool;
char buf[10240] = {'\0'};
size_t readData(void *ptr, size_t size, size_t nmemb,void *stream){
memset(buf,'\0',10240);
strcpy(buf,ptr);
// printf("===============================get data==================================\n");
// printf("%s\n",buf);
}
char *getImgBase64ByPath(char* path){
char *bufpic;
char cmd[128] = {'\0'};
sprintf(cmd,"base64 %s > tmpFile",path);
system(cmd);
int fd = open("./tmpFile",O_RDWR);
int filelen = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
bufpic = (char*)malloc(filelen+2);
memset(bufpic,'\0',filelen+2);
read(fd,bufpic,filelen);
close(fd);
system("rm -f tmpFile");
return bufpic;
}
bool postUrl()
{
CURL *curl; //提供curl库一个操作句柄
CURLcode res;//curl_easy_perform(curl)的返回值
char* poststring;//post请求主体内容
char *img1; //人脸识别静态照片,开始为jpg格式,要转化成base64流
char *img2;//人脸识别动态照片
char* key = "4VaSoqfU3wtZtpsHKinsdx";
char* secret ="f4dd11a61fc8441c95b1e4777f54c264";
int typeId = 21;
char* format ="xml";
img1 = getImgBase64ByPath("/home/pi/photo/ztj.jpg");
img2 = getImgBase64ByPath("/home/pi/photo/tmp.jpg");
int len = strlen(key)+strlen(secret)+strlen(img1)+strlen(img2)+248;
poststring = (char *)malloc(len);
memset(poststring,'\0',len);
sprintf(poststring,"img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s",img1,img2,key,secret,typeId,format);
curl_global_init(CURL_GLOBAL_ALL);
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);//将http头输出到fp所指向的文件
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
if(strstr(buf,"是") != NULL){
printf("=========================Face recognition successful===========================\n");
return true;
}else{
printf("===========================Face recognition failed==============================\n");
return false;
}
}
int main(void)
{
bool res;
//getUrl("/tmp/get.html");
res = postUrl();
return res;
}