智能家居 (5) ——智能家居项目整合(语音控制线程,网络控制线程、烟雾报警线程)
智能家居 (5) ——智能家居项目整合(语音控制线程,网络控制线程、烟雾报警线程)香橙派全志H616总结问题1:三线程同时运行,有时候无法正常进行开关灯操作。问题2:在前几篇文章中,在open\close操作前都有Init操作问题3: int (*changeStatus)(int status),记住设备的状态
·
目录
问题2:在前几篇文章中,在open\close操作前都有Init操作
问题3: int (*changeStatus)(int status),记住设备的状态

一、主函数
mianPro.c
#include <pthread.h>
#include "controlDevice.h"
#include "inputCommand.h"
struct InputCommand *pcommandHead = NULL; //定义指令工厂初始链表头
struct Devices *pdeviceHead = NULL; //定义设备工厂初始链表头
struct InputCommand *socketHandler = NULL;
struct Devices* findDeviceByName(char *name, struct Devices *phead)
{
struct Devices *tmp =phead;
if(phead == NULL){
return NULL;
}else{
while(tmp != NULL){
if(strcmp(tmp->deviceName,name)==0){
return tmp;
}
tmp = tmp->next;
}
return NULL;
}
}
struct InputCommand* findCommandByName(char *name, struct InputCommand *phead)
{
struct InputCommand *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 Command(struct InputCommand* CmdHandler)
{
struct Devices *tmp =NULL;
if(strcmp("CSHALL",CmdHandler->command) == 0){
tmp = findDeviceByName("smokeAlarm",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("buzzer",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("livingroomLight",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("restaurantLight",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("bedroomLight",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("bathroomLight",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
printf("设备已全部初始化\n");
}
if(strcmp("OL1",CmdHandler->command) == 0){
tmp = findDeviceByName("livingroomLight",pdeviceHead);
if(tmp != NULL){
tmp->open(tmp->pinNum);
printf("已打开客厅灯\n");
}
}
if(strcmp("CL1",CmdHandler->command) == 0){
tmp = findDeviceByName("livingroomLight",pdeviceHead);
if(tmp != NULL){
tmp->close(tmp->pinNum);
printf("已关闭客厅灯\n");
}
}
if(strcmp("OL2",CmdHandler->command) == 0){
tmp = findDeviceByName("restaurantLight",pdeviceHead);
if(tmp != NULL){
tmp->open(tmp->pinNum);
printf("已打开餐厅灯\n");
}
}
if(strcmp("CL2",CmdHandler->command) == 0){
tmp = findDeviceByName("restaurantLight",pdeviceHead);
if(tmp != NULL){
tmp->close(tmp->pinNum);
printf("已关闭餐厅灯\n");
}
}
if(strcmp("OL3",CmdHandler->command) == 0){
tmp = findDeviceByName("bedroomLight",pdeviceHead);
if(tmp != NULL){
tmp->open(tmp->pinNum);
printf("已打开卧室灯\n");
}
}
if(strcmp("CL3",CmdHandler->command) == 0){
tmp = findDeviceByName("bedroomLight",pdeviceHead);
if(tmp != NULL){
tmp->close(tmp->pinNum);
printf("已关闭卧室灯\n");
}
}
if(strcmp("OL4",CmdHandler->command) == 0){
tmp = findDeviceByName("bathroomLight",pdeviceHead);
if(tmp != NULL){
tmp->open(tmp->pinNum);
printf("已打开浴室灯\n");
}
}
if(strcmp("CL4",CmdHandler->command) == 0){
tmp = findDeviceByName("bathroomLight",pdeviceHead);
if(tmp != NULL){
tmp->close(tmp->pinNum);
printf("已关闭浴室灯\n");
}
}
if(strcmp("OLALL",CmdHandler->command) == 0){
tmp = findDeviceByName("livingroomLight",pdeviceHead);
if(tmp != NULL) tmp->open(tmp->pinNum);
tmp = findDeviceByName("restaurantLight",pdeviceHead);
if(tmp != NULL) tmp->open(tmp->pinNum);
tmp = findDeviceByName("bedroomLight",pdeviceHead);
if(tmp != NULL) tmp->open(tmp->pinNum);
tmp = findDeviceByName("bathroomLight",pdeviceHead);
if(tmp != NULL) tmp->open(tmp->pinNum);
printf("已打开所有灯\n");
}
if(strcmp("CLALL",CmdHandler->command) == 0){
tmp = findDeviceByName("livingroomLight",pdeviceHead);
if(tmp != NULL) tmp->close(tmp->pinNum);
tmp = findDeviceByName("restaurantLight",pdeviceHead);
if(tmp != NULL) tmp->close(tmp->pinNum);
tmp = findDeviceByName("bedroomLight",pdeviceHead);
if(tmp != NULL) tmp->close(tmp->pinNum);
tmp = findDeviceByName("bathroomLight",pdeviceHead);
if(tmp != NULL) tmp->close(tmp->pinNum);
printf("已关闭所有灯\n");
}
}
void *voiceControlThread(void *data) //“语音控制线程”执行函数
{
int nread;
struct InputCommand *voiceHandler = NULL;
voiceHandler = findCommandByName("voice", pcommandHead);
if(voiceHandler == NULL){
printf("find voiceHandler error\n");
pthread_exit(NULL);
}else{
if(voiceHandler->Init(voiceHandler) < 0){ //“语音控制”功能初始化
printf("voiceControl init error\n");
pthread_exit(NULL);
}else{
printf("voiceControl init success\n");
}
while(1){
memset(voiceHandler->command,'\0',sizeof(voiceHandler->command));
nread = voiceHandler->getCommand(voiceHandler);
if(nread == 0){ //串口没有获取到指令
printf("No voiceCommand received\n");
}else{ //获取到指令
printf("Get VoiceCommand -->%s\n",voiceHandler->command);
Command(voiceHandler);
}
}
}
}
void *socketReadThread(void *data) //“读取tcp端口指令线程”执行的函数
{
int n_read;
printf("Connect success\n");
while(1){
memset(socketHandler->command,'\0',sizeof(socketHandler->command));
n_read=read(socketHandler->fd,socketHandler->command,sizeof(socketHandler->command));
if(n_read == -1){
perror("read:");
}else{
printf("Get SocketCommand-->%s\n",socketHandler->command);
Command(socketHandler);
}
}
}
void *socketControlThread(void *data) //“网络控制线程”执行的函数
{
int c_fd; //文件描述符
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
int clen = sizeof(struct sockaddr_in);
pthread_t socketRead_thread; //线程里面套线程,网络连接后信息通信
socketHandler = findCommandByName("socket", pcommandHead);
if(socketHandler == NULL){
printf("find socketHandler error\n");
pthread_exit(NULL);
}
if(socketHandler->Init(socketHandler) < 0){ //“网络控制”功能初始化
printf("socketControl init error\n");
pthread_exit(NULL);
}else{
printf("socketControl init success\n");
}
while(1){
//4.accept
c_fd = accept(socketHandler->s_fd,(struct sockaddr *)&c_addr,&clen); //接收连接请求,阻塞至有客户端完成三次握手
socketHandler->fd = c_fd; //将套接字描述符返回给“网络控制”链表节点
pthread_create(&socketRead_thread,NULL,socketReadThread,NULL); //创建新线程:用于读取TCP端口指令
}
}
void *smokeAlarmThread(void *data) //“烟雾报警器线程”执行的函数
{
int smokeStatus; //存放“烟雾传感器”状态
struct Devices *tmp = NULL;
while(1){
tmp = findDeviceByName("smokeAlarm", pdeviceHead);
if(tmp != NULL){
smokeStatus = tmp->readStatus(tmp->pinNum);
delay(100);
tmp = findDeviceByName("buzzer", pdeviceHead);
if(tmp != NULL){
if( smokeStatus == 0 ){
tmp->open(tmp->pinNum);
}else{
tmp->close(tmp->pinNum);
}
}
}
}
}
int main()
{
if (wiringPiSetup () == -1) {
fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
return 1 ;
}
pthread_t voiceControl_thread;
pthread_t socketControl_thread;
pthread_t smokeAlarm_thread;
//1.指令工厂初始化
pcommandHead = addVoiceControlToInputCommandLink(pcommandHead);
pcommandHead = addSocketControlToInputCommandLink(pcommandHead);
//2.设备控制工厂初始化
pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead); //“浴室灯”加入设备链表
pdeviceHead = addBedroomLightToDeviceLink(pdeviceHead);
pdeviceHead = addRestaurantLightToDeviceLink(pdeviceHead);
pdeviceHead = addLivingroomLightToDeviceLink(pdeviceHead);
pdeviceHead = addSmokeAlarmToDeviceLink(pdeviceHead);
pdeviceHead = addBuzzerToDeviceLink(pdeviceHead);
//3.线程池建立 int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
//3.1 语音线程
pthread_create(&voiceControl_thread,NULL,voiceControlThread,NULL);
//3.2 socket线程
pthread_create(&socketControl_thread,NULL,socketControlThread,NULL);
//3.3 烟雾报警线程
pthread_create(&smokeAlarm_thread,NULL,smokeAlarmThread,NULL);
//3.4 摄像头线程
pthread_join(voiceControl_thread, NULL); //主函数等待线程退出
pthread_join(socketControl_thread, NULL); //主函数等待线程退出
pthread_join(smokeAlarm_thread, NULL); //主函数等待线程退出
return 0;
}
二、指令工厂
voiceControl.c
#include "inputCommand.h"
int voiceInit(struct InputCommand *voice)
{
int fd;
if ((fd = serialOpen (voice->deviceName, 115200)) < 0) {
fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
return 1 ;
}
voice->fd = fd;
return fd;
}
int voiceGetCommand(struct InputCommand *voice)
{
int nread = 0;
nread = read(voice->fd, voice->command, sizeof(voice->command)); //读取串口
return nread; //返回读取到数据的字节数,实际读取的指令放到了command里
}
struct InputCommand voiceControl = {
.commandName = "voice",
.deviceName = "/dev/ttyS5",
.command = '\0',
.Init = voiceInit,
.getCommand = voiceGetCommand,
.log = {'\0'},
.next = NULL
};
struct InputCommand* addVoiceControlToInputCommandLink(struct InputCommand *phead) //“语音控制”(对象)加入指令方式链表函数
{
if(phead == NULL){
return &voiceControl;
}else{
voiceControl.next = phead;
phead = &voiceControl;
return phead;
}
}
socketControl.c
#include "inputCommand.h"
int socketInit(struct InputCommand *socketMsg)
{
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); //创建套接字,ipv4 tcp协议
if(s_fd == -1){ //若创建套接字失败
perror("socket:");
exit(-1);
}
s_addr.sin_family = AF_INET; //ipv4
s_addr.sin_port = htons(atoi(socketMsg->port)); //端口号,选择5000以上。honts返回网络字节序,atoi(argv[2])防止端口被占用
inet_aton(socketMsg->ipAdress,&s_addr.sin_addr); //转换为网络能识别的格式
//2.bind
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen
listen(s_fd,10);//监听10个连接
printf("socket Server listening ...\n");
socketMsg->s_fd = s_fd; //发送套接字描述符
return s_fd;
}
struct InputCommand socketControl = {
.commandName = "socket",
.command = '\0',
.port = "8080",
.ipAdress = "192.168.3.77",
.Init = socketInit,
.log = {'\0'},
.next = NULL
};
struct InputCommand* addSocketControlToInputCommandLink(struct InputCommand *phead)
{
if(phead == NULL){
return &socketControl;
}else{
socketControl.next = phead;
phead = &socketControl;
return phead;
}
}
inputCommand.h
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <wiringPi.h>
#include <wiringSerial.h>
struct InputCommand
{
char commandName[128]; //“控制方式”名
char deviceName[128];
char command[32]; //存放指令
int fd; //存放文件描述符(串口/网络)
int s_fd; //存放服务器套接字描述符
char port[12]; //存放端口号
char ipAdress[32]; //存放 IP地址
char log[1024]; //日志
int (*Init)(struct InputCommand *voice); //“初始化”函数指针
int (*getCommand)(struct InputCommand *voice); //“获取指令”函数指针
struct InputCommand *next;
};
struct InputCommand* addVoiceControlToInputCommandLink(struct InputCommand *phead); //“语音控制”加入指令链表函数声明
struct InputCommand* addSocketControlToInputCommandLink(struct InputCommand *phead);
三、设备工厂
smokeAlarm.c
#include "controlDevice.h" //自定义设备类的文件
int smokeAlarmInit(int pinNum) //C语言必须要传参,JAVA不用,可直接访问变量的值
{
pinMode(pinNum,INPUT); //配置引脚为输入模式
//digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int smokeAlarmReadStatus(int pinNum)
{
return digitalRead(pinNum);
}
int smokeAlarmStatus(int status)
{
}
struct Devices smokeAlarm = { //定义烟雾报警器(对象)
.deviceName = "smokeAlarm", //名字
.pinNum = 6, //香橙派 6号(wPi)引脚
.Init = smokeAlarmInit, //指定初始化函数
.readStatus = smokeAlarmReadStatus,
.changeStatus = smokeAlarmStatus
};
struct Devices* addSmokeAlarmToDeviceLink(struct Devices *phead) //烟雾报警器(对象)加入设备链表函数
{
if(phead == NULL){
return &smokeAlarm;
}else{
smokeAlarm.next = phead; //以前的头变成.next
phead = &smokeAlarm; //更新头
return phead;
}
}
buzzer.c
#include "controlDevice.h" //自定义设备类的文件
int buzzerInit(int pinNum)
{
pinMode(pinNum,OUTPUT); //配置引脚为输出模式
digitalWrite(pinNum,HIGH); //引脚置高电平,蜂鸣器关闭
}
int buzzerOpen(int pinNum)
{
digitalWrite(pinNum,LOW); //引脚置低电平,蜂鸣器开启
}
int buzzerClose(int pinNum)
{
digitalWrite(pinNum,HIGH); //引脚置高电平,蜂鸣器关闭
}
struct Devices buzzer = { //定义蜂鸣器(对象)
.deviceName = "buzzer", //名字
.pinNum = 9, //香橙派 9号(wpi)引脚
.Init = buzzerInit, //指定初始化函数
.open = buzzerOpen, //指定“开启蜂鸣器”函数
.close = buzzerClose, //指定“关闭蜂鸣器”函数
};
struct Devices* addBuzzerToDeviceLink(struct Devices *phead) //蜂鸣器(对象)加入设备链表函数
{
if(phead == NULL){
return &buzzer;
}else{
buzzer.next = phead;
phead = &buzzer;
return phead;
}
}
livingroomLight.c
#include "controlDevice.h" //自定义设备类的文件
int livingroomLightInit(int pinNum) //C语言必须要传参,JAVA不用,可直接访问变量的值
{
pinMode(pinNum,OUTPUT); //配置引脚为输出模式
digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int livingroomLightOpen(int pinNum)
{
digitalWrite(pinNum,LOW); //引脚置低电平,闭合继电器
}
int livingroomLightClose(int pinNum)
{
digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int livingroomLightStatus(int status)
{
}
struct Devices livingroomLight = { //定义客厅灯(对象)
.deviceName = "livingroomLight", //名字
.pinNum = 16, //香橙派 16号(wPi)引脚
.Init = livingroomLightInit, //指定初始化函数
.open = livingroomLightOpen, //指定“打开灯”函数
.close = livingroomLightClose, //指定“关闭灯”函数
.changeStatus = livingroomLightStatus
};
struct Devices* addLivingroomLightToDeviceLink(struct Devices *phead) //客厅灯(对象)加入设备链表函数
{
if(phead == NULL){
return &livingroomLight;
}else{
livingroomLight.next = phead; //以前的头变成.next
phead = &livingroomLight; //更新头
return phead;
}
}
restaurantLight.c
#include "controlDevice.h" //自定义设备类的文件
int restaurantLightInit(int pinNum) //C语言必须要传参,JAVA不用,可直接访问变量的值
{
pinMode(pinNum,OUTPUT); //配置引脚为输出模式
digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int restaurantLightOpen(int pinNum)
{
digitalWrite(pinNum,LOW); //引脚置低电平,闭合继电器
}
int restaurantLightClose(int pinNum)
{
digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int restaurantLightStatus(int status)
{
}
struct Devices restaurantLight = { //定义餐厅灯(对象)
.deviceName = "restaurantLight", //名字
.pinNum = 13, //香橙派 13号(wPi)引脚
.Init = restaurantLightInit, //指定初始化函数
.open = restaurantLightOpen, //指定“打开灯”函数
.close = restaurantLightClose, //指定“关闭灯”函数
.changeStatus = restaurantLightStatus
};
struct Devices* addRestaurantLightToDeviceLink(struct Devices *phead) //餐厅灯(对象)加入设备链表函数
{
if(phead == NULL){
return &restaurantLight;
}else{
restaurantLight.next = phead; //以前的头变成.next
phead = &restaurantLight; //更新头
return phead;
}
}
bedroomLight.c
#include "controlDevice.h"
int bedroomLightInit(int pinNum) //C语言必须要传参,JAVA不用,可直接访问变量的值
{
pinMode(pinNum,OUTPUT); //配置引脚为输出模式
digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int bedroomLightOpen(int pinNum)
{
digitalWrite(pinNum,LOW); //引脚置低电平,闭合继电器
}
int bedroomLightClose(int pinNum)
{
digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int bedroomLightStatus(int status)
{
}
struct Devices bedroomLight = { //定义卧室灯(对象)
.deviceName = "bedroomLight", //名字
.pinNum = 8, //香橙派 8号(wPi)引脚
.Init = bedroomLightInit, //指定初始化函数
.open = bedroomLightOpen, //指定“打开灯”函数
.close = bedroomLightClose, //指定“关闭灯”函数
.changeStatus = bedroomLightStatus
};
struct Devices* addBedroomLightToDeviceLink(struct Devices *phead) //卧室灯(对象)加入设备链表函数
{
if(phead == NULL){
return &bedroomLight;
}else{
bedroomLight.next = phead; //以前的头变成.next
phead = &bedroomLight; //更新头
return phead;
}
}
bathroomLight.c
#include "controlDevice.h" //自定义设备类的文件
int bathroomLightInit(int pinNum) //C语言必须要传参,JAVA不用,可直接访问变量的值
{
pinMode(pinNum,OUTPUT); //配置引脚为输出模式
digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int bathroomLightOpen(int pinNum)
{
digitalWrite(pinNum,LOW); //引脚置低电平,闭合继电器
}
int bathroomLightClose(int pinNum)
{
digitalWrite(pinNum,HIGH); //引脚置高电平,断开继电器
}
int bathroomLightStatus(int status)
{
}
struct Devices bathroomLight = { //定义浴室灯(对象)
.deviceName = "bathroomLight", //名字
.pinNum = 2, //香橙派 2号(wPi)引脚
.Init = bathroomLightInit, //指定初始化函数
.open = bathroomLightOpen, //指定“打开灯”函数
.close = bathroomLightClose, //指定“关闭灯”函数
.changeStatus = bathroomLightStatus
};
struct Devices* addBathroomLightToDeviceLink(struct Devices *phead) //浴室灯(对象)加入设备链表函数
{
if(phead == NULL){
return &bathroomLight;
}else{
bathroomLight.next = phead; //以前的头变成.next
phead = &bathroomLight; //更新头
return phead;
}
}
controlDevice.h
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h> //wiringPi库
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); //“改变设备状态”函数指针
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); //“蜂鸣器”加入设备链表函数声明
四、总结
问题1:三线程同时运行,有时候无法正常进行开关灯操作。
初步排查:烟雾线程占用CPU100%,怀疑是死循环。
解决办法:加入延时函数。

问题2:在前几篇文章中,在open\close操作前都有Init操作

解决办法:通过指令进行初始化
if(strcmp("CSHALL",CmdHandler->command) == 0){
tmp = findDeviceByName("smokeAlarm",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("buzzer",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("livingroomLight",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("restaurantLight",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("bedroomLight",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
tmp = findDeviceByName("bathroomLight",pdeviceHead);
if(tmp != NULL) tmp->Init(tmp->pinNum);
printf("设备已全部初始化\n");
}
问题3: int (*changeStatus)(int status),记住设备的状态
记住设备的状态,已经执行的open、close、Init就不再执行,同时解决问题2。
待实现
智能家居项目目录
智能家居(5) —— 智能家居项目整合(语音控制线程,网络控制线程、烟雾报警线程)
网络编程知识预备(2) —— 三次握手与四次挥手、半连接状态、2MSL
网络编程知识预备(3) —— TCP流量控制(滑动窗口)、拥塞控制
网络编程知识预备(4) —— SOCKET、TCP、HTTP之间的区别与联系
网络编程知识预备(5) —— 了解应用层的HTTP协议与HTTPS协议
网络编程知识预备(6) —— libcurl库简介及其编程访问百度首页
智能家居(7) —— 人脸识别 & 翔云平台编程使用(编译openSSL支持libcurl的https访问、安装SSL依赖库openSSL)
更多推荐
所有评论(0)