智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)

智能家居项目开发

  • 一、智能家居功能细节拆分
    • 控制区:
    • 外设区:
    • 面向对象类和对象的概念
    • 结构体新玩法
  • 二、工厂模式
    • 1. 工厂模式的概念
    • 2. 工厂模式的实现
    • 3. 工厂模式使用及功能验证
  • 三、智能家居项目框架设计
    • 1. 智能家居架构代码文件工程建立
    • 2. 主流程设计和浴室灯框架编写
  • 四、基于设备控制工厂—实现灯,火灾
    • 1. 浴室灯控制框架设计
    • 2. 浴室灯代码实现和测试
    • 3. 二楼灯代码实现和测试
    • 4. 继电器组实现四盏灯功能
    • 5. 火灾检测模块
  • 五、基于指令工厂—语音识别,Socket客户端
    • 1. 声音识别的串口读取功能
    • 2. 添加Socket服务器功能(框架封装)
    • 3. 主程序初步编写: 实现语音和网络线程
    • 4. 树莓派mjpg-streamer监控功能调试:

一、智能家居功能细节拆分

智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第1张图片

控制区:

语音识别模块,socket客户端

外设区:

继电器组控制灯,远程终端子系统控制灯,窗帘等,火灾报警,摄像头。

面向对象类和对象的概念

类:是一种用户定义的引用的数据类型,也称类类型,(结构体)
对象:类的一种具象

struct Animal {
      char name[128];
      int age;
      int sex;         //成员属性
      void *peat();
      void *pbeat();   //成员方法
};

对象是类的一种具象,如 struct Animal dog ;struct Animal cat;struct Animal person; dog 就是类的具体的对象,dog是Animal类的对象。

C语言面向对象的代码

#include
//类:抽象的
struct Animal {
      char name[128];
      int age;
      int sex;         //成员属性
      void (*peat)();
      void (*pbeat)();   //成员方法
};


void dogEat()
{
   printf("狗吃骨头\n");
}
void catEat()
{
   printf("猫吃鱼\n");
}
void personEat()
{
   printf("人吃饭\n");
}

void dongBeat()
{
  printf("咬人\n");
}

void dogBeat()
{
  printf("咬人\n");
}

void catBeat()
{
  printf("抓人\n");
}
void personBeat()
{
  printf("打人\n");
}

int main()
{
    struct Animal dog;
    struct Animal cat;
    struct Animal person;   //对象,事物的具象
    
    dog.peat =  dogEat;
    cat.peat =  catEat;
    person.peat =  personEat;

    dog.pbeat =  dogBeat;
    cat.pbeat=  catBeat;
    person.pbeat=  personBeat;

    dog.peat();
    cat.peat();
    person.peat();
   
    dog.pbeat();
    cat.pbeat();
    person.pbeat();
    
    return 0;
}

结构体新玩法

内核的方式给结构体赋值:

 struct Animal dog2 = {
    .name = "阿黄",
    .peat = dogEat,
    .pbeat = dogBeat
};

新玩法代码

#include
//类:抽象的
struct Animal {
      char name[128];
      int age;
      int sex;         //成员属性
      void (*peat)();
      void (*pbeat)();   //成员方法
};

void dogEat()
{
   printf("狗吃骨头\n");
}

void dongBeat()
{
  printf("咬人\n");
}

int main()
{
//以前我们写结构体赋值时,是这样按顺序怼进去
    struct Animal dog1 = {"阿黄",1,1,dogEat,dogBeat};
//有时候我们只是想给单个赋值,我们怎么写呢?
    struct Animal dog2 = {
    .name = "阿黄",
    .peat = dogEat,
    .pbeat = dogBeat
    };
 
    dog.peat();
    dog.pbeat();
    
    return 0;
}

二、工厂模式

1. 工厂模式的概念

工厂模式(Factory Pattern)是最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第2张图片

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口(API)来指向新创建的对象

2. 工厂模式的实现

创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口(API)来指向新创建的对象。

暴漏逻辑代码例如:

#include
//类:抽象的
struct Animal {
      char name[128];
      int age;
      int sex;         //成员属性
      void (*peat)();
      void (*pbeat)();   //成员方法
};

void dogEat()
{
   printf("狗吃骨头\n");
}

void dongBeat()
{
  printf("咬人\n");
}

int main()
{
    struct Animal dog= {
    .name = "阿黄",
    .peat = dogEat,
    .pbeat = dogBeat
    };
 
    dog.peat();
    dog.pbeat();
    
    return 0;
}

暴漏了逻辑

void dogEat()
{
   printf("狗吃骨头\n");
}

void dongBeat()
{
  printf("咬人\n");
}

    struct Animal dog2 = {
    .name = "阿黄",
    .peat = dogEat,
    .pbeat = dogBeat
    };

设计思路图
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第3张图片

  • 首先在选定位置新建一个文件夹,然后在文件夹里面新建dog.c、animal.h、cat.cmainPro.c 然后再新建一个文件夹存放sourceInsight的工程文件(有关sourceInsight的用法),如下图所示:
    智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第4张图片

  • 先写下4个分文件,animal.h 、cat.c 、dog.c 、person.c(一个功能一个文件) 然后将对应的代码写入对应的文件,比如:dog.c这个文件就是存放dog这个对象的相关行为,并且提供让主程序调用的函数API将dog这个对象添加到链表中去(这个就像是以后的智能家居为实现整个控制系统,需要添加的各个功能模块,一个供能模块就是一个文件),putdogInLink 函数是将dog对象插入进链表的API接口,这里采用头插法进行插入,即:先插入的在后边。

    智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第5张图片

  • mainPro.c 主程序要调用这三个文件组成链表。 最后就是编写mainpro.c主函数,下面函数还编写了一个可供用户输入的然后查找响应节点的函数findUtilByName,用户输入要查找的节点名称,找到后返回指向该节点的指针,通过指针就可以对该节点进行操作,就把它当做链表的一个节点即可

#include"animal.h"
#include
//查找响应节点的函数  功能:链表的遍历查找结点
struct Animal *findUtilByName(char *str,struct Animal *phead)
{
      struct Animal *tmp = phead;

      if(phead == NULL){
           return NULL;
      }
      else{
           while(tmp != NULL){
		   if(strcmp(tmp->name,str)==0){    //若s1、s2字符串相等,则返回零
		       return tmp;//返回指向要找节点的指针
		   }
		   tmp = tmp->next;  
	   }
	   return NULL;
      }
}

int main()
{

	char buf[128]={'\0'};
	struct Animal *phead = NULL;
    struct Animal *ptmp;
    
	//把文件都连接起来(链表)
	phead = putCatInLink(phead);
	phead = putDogInLink(phead);
	phead = putPersonInLink(phead);
    //业务代码
	while(1){
		puts("please input Tom ,pipi,CONG");
		scanf("%s",buf);
	    ptmp = findUtilByName(buf,phead);//查找响应节点的函数
		if(ptmp != NULL){
		ptmp->pbeat();
		ptmp->peat();
	        
		}
        memset(buf,'\0',sizeof(buf));
	}
	return 0;
}

他们之间的关联就是链表一样,一个文件加入链表,要加功能(就是加文件)就把文件当成元素插入链表中。

3. 工厂模式使用及功能验证

智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第6张图片
把文件中的cat ,dog, person 换成 卧室灯,餐厅灯,客厅灯,不就是功能性文件嘛, 要在来个卫生间灯,就在加一个文件。

  • 添加XXX功能文件:
    在目录下添加文件XXX.c ,再在animal.h 中把struct Animal * putXXXInLink(struct Animal *phead);(接口)暴露出来,暴露出来目的是给mainPro添加,加入功能链表中。

三、智能家居项目框架设计

智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第7张图片

  • 根据以上简单工厂模式,智能家居设计的时候,就可以设计为指令工厂、main函数、控制工厂,指令工厂面就存放指令(比如:语音指令、客户端指令等,将这些指令串为一个链表),控制工厂就是控制一些家庭设备(比如:各个房间的灯,门锁、串口等,创建一个链表,然后根据指令,去查找对应的控制结点),main函数里面 首先创建两个链表(指令工厂、控制工厂),然后接下来创建两个线程(一个是语音的、一个是客户端的),在每个线程里面在接受到指令后去控制工厂里面去查找对应的控制设备然后进行一系列操作

1. 智能家居架构代码文件工程建立

  • 根据上面的叙述,我们可以创建以下架构的代码文件工程,指令工场和控制工场的头文件就是以下图片中的两个头文件。然后将这些文件导入到sourceInsight里面进行代码的编写。智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第8张图片
    智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第9张图片

以上是智能家居开发的软件框架构建准备工作,接下来开始代码的编写。

2. 主流程设计和浴室灯框架编写

主流程设计框架
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第10张图片
指令工厂框架
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第11张图片
设备控制工厂框架
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第12张图片

四、基于设备控制工厂—实现灯,火灾

1. 浴室灯控制框架设计

#include"contrlDevice.h"
int bathroomLightOPen()
{}
int bathroomLightClose()
{}
int bathroomLightInit()
{}
int bathroomLightStatus(int status)
{}

//Devices类  bathroomLight对象
struct Devices bathroomLight = {  
    .name = "bathroomLight",
	.open = bathroomLightOPen,
	.close = bathroomLightClose,
	.deviceInit = bathroomLightInit,
	.changStatus = bathroomLightStatus
};
//(插入设备链表)函数
struct Devices * addBathroomLightToDeviceLink(struct Devices *phead)
{
    if(phead == NULL){
        return &bathroomLight;
	}
    else{
        bathroomLight.next = phead;
		phead = &bathroomLight;
	}
};

根据上面内容的代码框架开始编写代码:

  • 首先编写controlDevices.h这个头文件里面的代码,这个是设备工厂每一个结点的结构体类型,而且 还要在这个头文件里面进行函数的声明,也就是创建的那些设备.c文件里面的函数(为了将设备添加至设备链表的函数),其中这个头文件里面的结构体内容根据功能提前设定
  • 然后再编写 inputCommand.h这个头文件里面的内容,这个是指令工厂里面的头文件,也是指令链表里面的每一个结点的类型。编写完这两个头文件,
  • 然后再进行设备工厂设备文件xxxx.c、指令工厂指令文件xxxx.cmainPro.c文件的编写。

2. 浴室灯代码实现和测试

先写一些硬件测试代码,如继电器组的代码,来测试接线问题。
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第13张图片

  • controlDevices.h设备控制工厂头文件代码结点结构体的声明,这里面的东西不一定够用,可以先写上,等不够的时候在进行添加。
#include //包含wiringPi库
#include
#include

struct Devices
{
	int status;  //表示开关的状态
	int pinNum;  //引脚数
	
	char devicesName[128]; //存放设备的名称
	
	int (*open)(int pinNum);
	int (*close)(int pinNum);
	int (*deviceInit)(int pinNum);
	
	int (*readStatus)(int pinNum); //火灾报警等 状态
	int (*changStatus)(int status);  //改变状态
	
	struct Devices*next;
};
//以下几行将设备添加至设备链表的函数声明,便于以后的查找引用
struct Devices* addBathroomLightToDeviceLink(struct Devices* phead);

  • 首先编写设备工厂的设备文件bathroomLight.c浴室灯
#include"contrlDevices.h"    //包含头文件

int bathroomLightOPen(int pinNum)
{    
	 digitalWrite(pinNum, HIGH);  //将引脚电平拉高,点亮浴室灯
}

int bathroomLightClose(int pinNum)
{
     digitalWrite(pinNum, LOW); //将引脚电平拉低,熄灭浴室灯
}

int bathroomLightInit(int pinNum)
{
     pinMode(pinNum , OUTPUT);  //初始化引脚功能
	 digitalWrite(pinNum, HIGH);  
}


struct Devices bathroomLight = {

    .deviceName = "bathroomLight",  //通过这个设备名进行浴室灯结点的查找,然后再进行结构体函数的调用
	.pinNum = 22,     //浴室灯继电器控制IO口引脚  gpio 22
	.open = bathroomLightOPen, 
	.close = bathroomLightClose,
	.deviceInit = bathroomLightInit,

};

//将浴室灯结点插入到设备工厂链表里面,采用头插法
struct Devices * addBathroomLightToDeviceLink(struct Devices *phead)
{
    if(phead == NULL){
        return &bathroomLight;
	}
    else{
        bathroomLight.next = phead;
		phead = &bathroomLight;
	}
}
  • 最后mainPro.c主程序控制代码编写,main函数里面涉及到设备工厂、设备控制工厂头结点的插入和设备文件、设备文件浴室灯插入到设备链表。 同时还要有结点查找函数:设备结点查找函数,查找后返回结点指针然后对特定结点进行操作即可。
#include
#include"contrlDevices.h"
#include

//查找响应节点的函数  功能:链表的遍历查找结点
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;
   }
}

int main(){
   if(-1 == wiringPiSetup()){
        return -1;
   }
     
   char *name ="bathroomLight";
   
   struct Devices *pdeviceHead = NULL;
   pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead);
   
   struct Devices *tmp = findDeviceByName(name,pdeviceHead);
   if(tmp != NULL){
   	   tmp->deviceInit(tmp->pinNum);
   	   tmp->open(tmp->pinNum);
   }
   return 0;
}

代码编写完成后通过ftp工具传输到树莓派编译(我这里用的是FileIlla)
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第14张图片

  • 使用指令:gcc *.c -o test -lwiringPi进行编译。

浴室灯调试

3. 二楼灯代码实现和测试

  • 和浴室灯实现方式一样,编写设备工厂的设备文件upstairLight.c
#include"contrlDevices.h"

int upstairLightOPen(int pinNum)
{
	 digitalWrite(pinNum, LOW);
}

int upstairLightClose(int pinNum)
{
     digitalWrite(pinNum, HIGH);
}

int upstairLightInit(int pinNum)
{
     pinMode(pinNum , OUTPUT);
	 digitalWrite(pinNum, HIGH);
}

struct Devices upstairLight = {
    .deviceName = "upstairLight",
	.pinNum = 21,
	.open = upstairLightOPen,
	.close = upstairLightClose,
	.deviceInit = upstairLightInit,
};

struct Devices * addUpstairLightToDeviceLink(struct Devices *phead)
{
    if(phead == NULL){
        return &upstairLight;
	}
    else{
        upstairLight.next = phead;
		phead = &upstairLight;
	}
}
  • 最后mainPro.c主程序控制代码编写,main函数里面涉及到设备工厂、设备控制工厂头结点的插入和设备文件、设备文件二楼灯插入到设备链表
#include
#include"contrlDevices.h"
#include

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;
   }
}

int main(){
   if(-1 == wiringPiSetup()){
        return -1;
   }
     
   char *name ="upstairLight";
   
   struct Devices *pdeviceHead = NULL;
   pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead);
   pdeviceHead = addUpstairLightToDeviceLink(pdeviceHead);
   
   struct Devices *tmp = findDeviceByName(name,pdeviceHead);
   if(tmp != NULL){
   	   tmp->deviceInit(tmp->pinNum);
   	   tmp->open(tmp->pinNum);
   }
   return 0;
}

传到树莓派上进行编译,测试。
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第15张图片

4. 继电器组实现四盏灯功能

restaurantLight.c、upstairLight.c 、livingroomLight.c、bathroomLight.c 他们的实现方法都是一样的。

  • 修改controlDevices.h设备控制工厂头文件代码填写结点结构体的声明
#include
#include

struct Devices{
 
     char deviceName[128];
	 int status;
	 int pinNum;
	 
	 int (*open)(int pinNum);
	 int (*close)(int pinNum);
	 int (*deviceInit)(int pinNum);
	 int (*readStatus)();
	 int (*changStatus)(int status);

	 struct Devices *next;
};

struct Devices * addBathroomLightToDeviceLink(struct Devices *phead);
struct Devices * addUpstairLightToDeviceLink(struct Devices *phead);
struct Devices * addLivingroomLightToDeviceLink(struct Devices *phead);
struct Devices * addRestaurantLightToDeviceLink(struct Devices *phead);
  • 最后mainPro.c主程序控制代码编写,main函数里面涉及到设备工厂、设备控制工厂头结点的插入和设备文件、设备文件各种灯插入到设备链表。
#include
#include"contrlDevices.h"
#include

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;
   }
}

int main(){

   char name[128];
   struct Devices *tmp = NULL;

   //初始化树莓派硬件,这个只需要执行一次所以放在main函数里面即可
   if(-1 == wiringPiSetup()){
        return -1;
   }
     
   struct Devices *pdeviceHead = NULL;//将设备链表的头结点设置为全局变量
   //设备控制工厂初始化
   pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead); //插入浴室灯
   pdeviceHead = addUpstairLightToDeviceLink(pdeviceHead);//插入二楼灯
   pdeviceHead = addRestaurantLightToDeviceLink(pdeviceHead);//插入餐厅灯
   pdeviceHead = addLivingroomLightToDeviceLink(pdeviceHead);//插入客厅灯
   
   while(1){  //通过输入指令名字 来进行 比对 实现设备开灯操作
      printf("input:\n");
	  scanf("%s",name);
   
      tmp = findDeviceByName(name,pdeviceHead);
	  if(tmp != NULL){
   	      tmp->deviceInit(tmp->pinNum);
   	      tmp->open(tmp->pinNum);
      }
   	} 
   return 0;
}

实现继电器组控制四盏灯
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第16张图片

5. 火灾检测模块

  • fire.c文件代码,代码框架和浴室灯代码框架相同,不同的是这个文件里面要有读取引脚状态的函数,同时引脚也要设置为输入模式,当检测到或火灾是火灾传感器的引脚会被拉为低电平。
  • 火灾检测和上面的灯的设计模式一样。实现方式也是大同小异,在初始化时不同的是设计INPUT输入引脚,在火灾检测里返回状态,这里是和灯不同的点。
  • 最后mainPro.c主程序控制代码编写,main函数里面涉及到设备工厂、设备控制工厂头结点的插入和设备文件、设备文件插入到设备链表。
#include"contrlDevices.h"

int fireInit(int pinNum)
{
     pinMode(pinNum , INPUT);
	 digitalWrite(pinNum, HIGH);
}

int fireStatusRead(int pinNum)
{
	 return digitalRead(pinNum);
}


struct Devices fire = {

    .deviceName = "fire",
	.pinNum = 25,
	.readStatus = fireStatusRead,
	.deviceInit = fireInit

};

struct Devices * addFireToDeviceLink(struct Devices *phead)
{
    if(phead == NULL){
        return &fire;
	}
    else{
        fire.next = phead;
		phead = &fire;
	}
}

五、基于指令工厂—语音识别,Socket客户端

1. 声音识别的串口读取功能

  • inputCommand.h指令工厂头文件代码,里面有设备链表每一个结点的结构体类型的声明,和设备控制工厂头文件类似。
#include
#include

struct InputCommander
{
     char commandName[128];  //指令名
	 char deviceName[128];   //设备名
     char command[32];       //存放指令内容
	 int (*Init)(struct InputCommander *voicer , char *ipAdress, char *port);      //初始化(串口)信息 建立套接字等
     int (*getCommand)(struct InputCommander *voicer);
	 char log[1024];         //日志信息
	 int fd;                 //文件描述符
	 
	 struct InputCommander *next;     //链表指针
}

语音模块无非就是串口通信,忘记串口通信点击这里,回顾串口。

声音相关的读取代码,无非就是串口相关的操作

  • 语音相关的读取代码,这个文件里面的函数就要添加读取指令函数和初始化函数所谓的初始化函数就是将串口打开然后设置相应的波特率,读取指令函数需要注意的是在读取指令前需要将缓存区初始化防止有乱码,读指令函数主要调用read函数进行指令的读取,在没有指令到来的时候,输出读取时间超时,其实代码框架和设备工厂的框架基本类似只是文件里面包含的函数有所差异,但都有一个设备结点插入函数
#include
#include
#include
#include
#include
#include"InputCommand.h"

//串口的初始化
int voiceInit(struct InputCommander *voicer, char *ipAdress, char *port){
   int fd;
   //打开/dev/ttyAMA0设备(串口的地址,在Linux中就是设备所在的目录) ,波特率9600
   if((fd = serialOpen(voicer->deviceName,9600))<0)
   	{ 
         return (-1);
   	}   
   	//初始化获得fd
   return fd;
}
 
 //声音识别读取
int voiceGetCommand(struct InputCommander *voicer)
{
  int n_read = 0;
  //读fd ,把读到的内容放voicer。command里 
  n_read = read(voicer->fd ,voicer->command ,sizeof(voicer->command));

  if(n_read == 0){
      printf("usart for voice read over time\n");
  }else{
      //返回读到的字符大小
      return n_read; 
  }
}

struct InputCommander voiceContrl =  {
     .commandName = "voice",        //结构体名  
	 .deviceName = "/dev/ttyAMA0",  //对应设备名
     .command = {'\0'},             //指令初始化成空
	 .Init = voiceInit,             //串口的初始化
     .getCommand = voiceGetCommand,  
	 .log = {'\0'},
	 .next = NULL
};

语音识别代码总结

  • 最总完善一下代码,把指令工厂里的语音链进链表。指令文件头插入到指令链表

指令工厂InputCommand.h,包含结构体。

#include
#include

struct InputCommander
{
     char commandName[128];
	 char deviceName[128];   //存放串口设备名字
     char command[32];       //存放指令信息
	 int (*Init)(struct InputCommander *voicer , char *ipAdress, char *port);
     int (*getCommand)(struct InputCommander *voicer);
	 char log[1024];
	 int fd;
	 
	 struct InputCommander *next;
};
//将指令结点添加至指令链表中的函数声明
struct InputCommander * addvoiceContrlToInputCommandLink(struct InputCommander *phead);

语音识别 voiceContrl.c

#include
#include
#include
#include
#include
#include"InputCommand.h"

int voiceInit(struct InputCommander *voicer, char *ipAdress, char *port){
   int fd;
   if((fd = serialOpen(voicer->deviceName,9600))<0)
   	{ 
         return (-1);
   	}
   
   return fd;
}

int voiceGetCommand(struct InputCommander *voicer)
{
     int n_read = 0;
  
     n_read = read(voicer->fd ,voicer->command ,sizeof(voicer->command));

     if(n_read == 0){
         printf("usart for voice read over time\n");
     }else{
         return n_read; 
     }
}

struct InputCommander voiceContrl =  {

     .commandName = "voice",
	 .deviceName = "/dev/ttyAMA0",
     .command = {'\0'},
	 .Init = voiceInit,
     .getCommand = voiceGetCommand,  
	 .log = {'\0'},
	 .next = NULL

};

//(头插法插入指令工厂链表)函数
struct InputCommander * addvoiceContrlToInputCommandLink(struct InputCommander *phead)
{
    if(phead == NULL){
        return &voiceContrl;
	}
    else{
        voiceContrl.next = phead;
		phead = &voiceContrl;
	}
}
  • 最后mainPro.c主程序控制代码编写,main函数里面指令工厂头结点的插入和指令文件插入到指令链表。同时还要有结点查找函数:指令结点查找函数,查找后返回结点指针然后对特定结点进行操作即可。
#include
#include
#include"contrlDevices.h"
#include"InputCommand.h"


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;
   }
}

int main(){

   char name[128];
   //初始化树莓派引脚
   if(-1 == wiringPiSetup()){
        return -1;
   }
   
   //设备控制工厂初始化 
   struct Devices *tmp = NULL;
   struct Devices *pdeviceHead = NULL;
   pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead);
   pdeviceHead = addUpstairLightToDeviceLink(pdeviceHead);
   pdeviceHead = addRestaurantLightToDeviceLink(pdeviceHead);
   pdeviceHead = addLivingroomLightToDeviceLink(pdeviceHead);
   pdeviceHead = addFireToDeviceLink(pdeviceHead);

   //指令工厂初始化 
   struct InputCommander *pcommandHead = NULL;
   pcommandHead = addvoiceContrlToInputCommandLink(pcommandHead);
   
   while(1){
      printf("input:\n");
	  scanf("%s",name);
   
      tmp = findDeviceByName(name,pdeviceHead);

	  if(tmp != NULL){
   	      tmp->deviceInit(tmp->pinNum);
   	      tmp->open(tmp->pinNum);
      }
   	}
   
   return 0;
}

2. 添加Socket服务器功能(框架封装)

  • 如果忘记了Socket就先回顾一下 Socket知识回顾
  • inputCommand.h指令工厂头文件代码,里面有设备链表每一个结点的结构体类型的声明,和设备控制工厂头文件类似。
#include
#include


struct InputCommander
{
     char commandName[128];
	 char deviceName[128];
     char command[32];
	 int (*Init)(struct InputCommander *voicer , char *ipAdress, char *port);
     int (*getCommand)(struct InputCommander *voicer);
	 char log[1024];
	 int fd;

	 char port[12];       //端口号
	 char ipAddress[32];  //IP地址
	 int sfd;             //文件描述符
	 struct InputCommander *next;
};
//将指令结点添加至指令链表中的函数声明
struct InputCommander * addVoiceContrlToInputCommandLink(struct InputCommander *phead);
struct InputCommander * addSocketContrlToInputCommandLink(struct InputCommander *phead);

Socket服务器 SocketContrl.c 框架封装

  • 下面是socket指令文件代码,这个里面不需要getCommmand这个函数因为在这里写后,会对后面多线程的处理不是特别的方便,计划的是连接进来一个客户端然后起一个线程去对接,但是结果实现客户端发送完一条消息后,需要断开连接然后重新连接,因为代码里面采用的是点对点的方式。
  • 后面加入多线程会改变此bug。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include"InputCommand.h"

//对socket的初始化
int socketInit(struct InputCommander *socketMes, char *ipAdress, 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);
    }
	s_addr.sin_family = AF_INET;//协议
    s_addr.sin_port = htons(atoi(socketMes->port));
    inet_aton(socketMes->ipAddress, &s_addr.sin_addr);//把socketMes->ipAddress IP 转化成 网络地址结构体&s_addr.sin_addr 
//2.bind
    bind(s_fd,(struct sockaddr *)&s_addr ,sizeof(struct sockaddr_in));//绑定服务器IP地址和端口号
//3.listen
    listen(s_fd,10); //监听

    socketMes->sfd = s_fd;
    return s_fd;  
}
//个里面不需要getCommmand这个函数因为在这里写后,会对后面多线程的处理不是特别的方便
/*int socketGetCommand(struct InputCommander *socketMes)
{
    int c_fd;
	int n_read= 0;
	struct sockaddr_in c_addr;
	memset(&c_addr,0,sizeof(struct sockaddr_in));
	
	int clen = sizeof(struct sockaddr_in);
	
	c_fd = accept(socketMes->sfd,(struct sockaddr *)&c_addr,&clen);
	n_read = read(c_fd , socketMes->command ,sizeof(socketMes->command));
       if(n_read == -1){
            perror("read");
       }
       else if(n_read >0){
           printf("\n get:  %d\n",n_read);
       }

	   return n_read;
}*/

struct InputCommander socketContrl =  {
     .commandName = "socketServer",
     .command = {'\0'},
	 .Init = socketInit,
     .getCommand = socketGetCommand,  
	 .log = {'\0'},

	 .port = "8088",
	 .ipAddress = "192.168.0.104",
	 .next = NULL
};

struct InputCommander * addSocketContrlToInputCommandLink(struct InputCommander *phead)
{
    if(phead == NULL){
        return &socketContrl;
	}
    else{
        socketContrl.next = phead;
		phead = &socketContrl;
	}
}
  • 最后mainPro.c主程序控制代码编写,main函数里面指令工厂头结点的插入和指令文件插入到指令链表。同时还要有结点查找函数:指令结点查找函数,查找后返回结点指针然后对特定结点进行操作即可。
#include
#include
#include"contrlDevices.h"
#include"InputCommand.h"



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;
   }
}

int main(){

   char name[128];
   struct Devices *tmp = NULL;

   if(-1 == wiringPiSetup()){
        return -1;
   }
     
   
   struct Devices *pdeviceHead = NULL;
   pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead);
   pdeviceHead = addUpstairLightToDeviceLink(pdeviceHead);
   pdeviceHead = addRestaurantLightToDeviceLink(pdeviceHead);
   pdeviceHead = addLivingroomLightToDeviceLink(pdeviceHead);
   pdeviceHead = addFireToDeviceLink(pdeviceHead);


   struct InputCommander *pcommandHead = NULL;
   pcommandHead = addVoiceContrlToInputCommandLink(pcommandHead);
   pcommandHead = addSocketContrlToInputCommandLink(pcommandHead);
   
   while(1){
      printf("input:\n");
	  scanf("%s",name);
   
      tmp = findDeviceByName(name,pdeviceHead);

	  if(tmp != NULL){
   	      tmp->deviceInit(tmp->pinNum);
   	      tmp->open(tmp->pinNum);
      }
   	}
   
   return 0;
}

3. 主程序初步编写: 实现语音和网络线程

  • 下面是语音指令输入文件代码,这个文件里面的函数就要添加读取指令函数和初始化函数,所谓的初始化函数就是将串口打开然后设置相应的波特率,读取指令函数需要注意的是在读取指令前需要将缓存区初始化防止有乱码,读指令函数主要调用read函数进行指令的读取,在没有指令到来的时候,输出:usart for voice read over time,其实代码框架和设备工厂的框架基本类似,只是文件里面包含的函数有所差异,但都有一个设备结点插入函数。

语音识别指令文件 voiceContrl.c

#include
#include
#include
#include
#include
#include"InputCommand.h"
//就是对串口的初始化
int voiceInit(struct InputCommander *voicer, char *ipAdress, char *port){
   int fd;
    //open serial,波特率115200
   if((fd = serialOpen(voicer->deviceName,115200)) == -1)
   	{ 
   	     printf("usrat open fail\n");
         return (-1);
   	}
   voicer->fd = fd;
   return fd;
}

int voiceGetCommand(struct InputCommander *voicer)
{
  int n_read = 0;
  memset(voicer->command,'\0',sizeof(voicer->command));
  n_read = read(voicer->fd ,voicer->command ,sizeof(voicer->command));
 
  if(n_read == 0){
		   printf("voice read over time\n");	 
  }else{
          return n_read;
  	}
}

struct InputCommander voiceContrl =  {
     .commandName = "voice",
	 .deviceName = "/dev/ttyAMA0",
     .command = {'\0'},
	 .Init = voiceInit,
     .getCommand = voiceGetCommand,  
	 .log = {'\0'},
	 .next = NULL
};

struct InputCommander * addVoiceContrlToInputCommandLink(struct InputCommander *phead)
{
    if(phead == NULL){
        return &voiceContrl;
	}
    else{
        voiceContrl.next = phead;
		phead = &voiceContrl;
	}
}
  • 下面是socket指令文件代码,这个里面不需要getCommmand这个函数因为在这里写了,对后面多线程的处理不是特别的方便,计划的是连接进来一个客户端然后起一个线程去对接,但是客户端发送完一条消息后,需要断开连接然后重新连接,因为代码里面采用的是点对点的方式。《socket知识补充》
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"InputCommand.h"

int socketInit(struct InputCommander *socketMes, char *ipAdress, 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){
		   printf("socket create fail\n");
           perror("socket");
           exit(-1);
    }	
	s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(atoi(socketMes->port));
    inet_aton(socketMes->ipAddress, &s_addr.sin_addr);

//2.bind
    bind(s_fd,(struct sockaddr *)&s_addr ,sizeof(struct sockaddr_in));

//3.listen
    listen(s_fd,10);
    printf("socket Server listening ....\n");
    socketMes->sfd = s_fd;
    return s_fd;  
}

int socketGetCommand(struct InputCommander *socketMes)
{
    int c_fd;
	int n_read= 0;
	struct sockaddr_in c_addr;
	memset(&c_addr,0,sizeof(struct sockaddr_in));
	
	int clen = sizeof(struct sockaddr_in);
	
	c_fd = accept(socketMes->sfd,(struct sockaddr *)&c_addr,&clen);
	n_read = read(c_fd , socketMes->command ,sizeof(socketMes->command));
       if(n_read == -1){
            perror("read");
       }
       else if(n_read >0){
           printf("\n get:  %d\n",n_read);
       }

	   return n_read;
}

struct InputCommander socketContrl =  {

     .commandName = "socketServer",
     .command = {'\0'},
	 .Init = socketInit,
     .getCommand = socketGetCommand,  
	 .log = {'\0'},

	 .port = "8088",
	 .ipAddress = "192.168.0.105",
	 .next = NULL

};

struct InputCommander * addSocketContrlToInputCommandLink(struct InputCommander *phead)
{
    if(phead == NULL){
        return &socketContrl;
	}
    else{
        socketContrl.next = phead;
		phead = &socketContrl;
	}
}
  • 最后进行main函数代码的编写,main函数里面涉及到设备工厂、指令工厂头结点的插入和设备文件、指令文件分别插入到设备链表和指令链表。同时还要有结点查找函数:包括设备结点查找函数、指令结点查找函数,查找后返回结点指针然后对特定结点进行操作即可。同时main函数里面还涉及到线程的创建,socket_thread这个函数里面在有客户端接入的时候又进行了线程的创建,用来对接接入的客户端。《线程知识补充》
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"contrlDevices.h"
#include"InputCommand.h"

int c_fd; //表示已连接的套接字描述符
//将设备链表的头结点设置为全局变量
struct Devices *pdeviceHead = NULL;
//将指令链表的头结点设置为全局变量
struct InputCommander *pcommandHead = NULL;
//这个是查找到的的socket指令结点,将它设为全局变量是因为socket_thread这个函数有用到这个节点
//除此之外,socket_thread的子线程read_thread也有用到这个节点指针,虽然可以通过创建线程传参,但是不建议那么做。
struct InputCommander *socketHeadler = 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 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 * 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 get:  %d,%s\n",n_read,socketHeadler->command);
       }else{
             printf("client quit\n");
	     }      
}
//语音线程函数,用于等待语音指令,在这里设置为一个包含有while(1)的线程
void *voice_thread(void *datas)
{
   int n_read;
   struct InputCommander *voiceHeadler;
   voiceHeadler = findcommandByName("voice",pcommandHead);
   if(voiceHeadler == NULL){
         printf("Find voiceHeadler error\n");
		 pthread_exit(NULL); //查找指令工厂语音部分失败退出当前线程
   }
   else{
   	    printf("%s find successful\n",voiceHeadler->commandName);
        if(voiceHeadler->Init(voiceHeadler,NULL,NULL)< 0){
            printf("voice init error\n");
			pthread_exit(NULL); //初始化失败退出当前线程
			
		}
		else{
		    printf("%s init successful!\n",voiceHeadler->commandName);
		} 
		while(1){
		   n_read = voiceHeadler->getCommand(voiceHeadler);
           if(n_read == 0){
                  printf("nodata form voice\n\n");	   
           }else{
                  printf("dp divece contrl:%s\n",voiceHeadler->command);
		    }
		}
   	}
}

//socket线程,用于与客户端对接,这个是main函数里面创建线程函数
void *socket_thread(void *datas)
{ 
   
   int n_read= 0;
   pthread_t readThread; //内存单元被设置为新创建线程的线程ID
   
   struct sockaddr_in c_addr;//客户端信息
   memset(&c_addr,0,sizeof(struct sockaddr_in));
   int clen = sizeof(struct sockaddr_in);

   
   socketHeadler = findcommandByName("socketServer",pcommandHead);
   if(socketHeadler == NULL){
         printf("Find voiceHeadler error\n");
		 pthread_exit(NULL); //查找指令工厂socket部分失败退出当前线程       
   }else{
		    printf("%s init successful!\n",socketHeadler->commandName);
		} 
	//socket初始化
   socketHeadler->Init(socketHeadler,NULL,NULL);
   while(1){
       //不断去接收新客户端 ,接收到就创建线程去接待客户端
       c_fd = accept(socketHeadler->sfd,(struct sockaddr *)&c_addr,&clen);
       
        //读取(接待客户端)线程创建
	   pthread_create(&readThread,NULL,read_Thread,NULL); 
	    //参数1:指针指向readThread内存单元被设置为新创建线程的线程ID 
        //参数2:线程属性——NULL 默认属性
        //参数3:启动线程调用的 read_Thread函数
        //参数4:一个指针,作为read_Thread的参数NULL传入
   }
}

int main(){

   char name[128];

   pthread_t voicethread;  
   pthread_t socketthread;   

   struct Devices *tmp = NULL;
   //初始化树莓派硬件,这个只需要执行一次所以放在main函数里面即可
   if(-1 == wiringPiSetup()){
        return -1;
   }
     
 //设备控制工厂初始化
   pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead);
   pdeviceHead = addUpstairLightToDeviceLink(pdeviceHead);
   pdeviceHead = addRestaurantLightToDeviceLink(pdeviceHead);
   pdeviceHead = addLivingroomLightToDeviceLink(pdeviceHead);
   pdeviceHead = addFireToDeviceLink(pdeviceHead);

//指令工厂初始化
   pcommandHead = addVoiceContrlToInputCommandLink(pcommandHead);//插入语音指令结点
   pcommandHead = addSocketContrlToInputCommandLink(pcommandHead);//插入socket指令结点

   //线程池建立
   //建立语音线程
   pthread_create(&voicethread,NULL,voice_thread,NULL); 
   //建立socket线程
   pthread_create(&socketthread,NULL,socket_thread,NULL); 
   //等待指定线程退出
   pthread_join(voicethread,NULL);
   pthread_join(socketthread,NULL);
   
   return 0;
}

语音线程函数

//语音线程函数
void *voice_thread(void *datas)//语音线程函数,用于等待语音指令,在这里设置为一个包含有while(1)的线程
{
   int n_read;
   struct InputCommander *voiceHeadler;
   voiceHeadler = findDeviceByName("voice",pcommandHead);
   if(voiceHeadler == NULL){
         printf("Find voiceHeadler error\n");
		 pthread_exit(NULL);  //查找指令工厂语音部分失败退出当前线程
   }
   else{
        printf("%s find successful\n",voiceHandler->commandName);
        if(voiceHeadler->Init(voiceHeadler,NULL,NULL)< 0){
            printf("voice init error\n");
			pthread_exit(NULL);   //初始化失败退出当前线程
			//return NULL; //理论上线程里是不允许return的
        }else{
		printf("%s init successful!\n",voiceHandler->commandName);}
		while(1){
		   n_read = voiceHeadler->getCommand(voiceHeadler);
           if(n_read == 0){
                  printf("nodata form voice\n\n");	   
           }else{
                  printf("dp divece contrl:%s\n",voiceHeadler->command);
		    }
		}
   	}
}
  • 语音线程工作流程 语音线程创建以后去调用voice__thread函数,头指针,函数头指针,然后调用findDeviceByName函数按名字去指令工厂链表里,查找结点,若找到返回结点指针tmp就是语音结点voiceContrl)。voiceHeadler指向tmp就调用voiceContrl里初始化和读取等操作。

代码编写完成后通过ftp工具传输到树莓派编译(我这里用的是FileIlla)
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第17张图片

  • 使用指令:gcc *.c -o test -lpthread -lwiringPi进行编译,然后执行可以看到下图:(在语音没有指令的时候打印nodata from voice)
    智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第18张图片

  • 然后进行串口接收信息功能的测试,使用串口前按照这篇博文进行串口的设置,我这里使用的是USB转ttl连接电脑进行测试测试结果如下:(测试成功)【由于我树莓派串口坏了我是用的两个ttl充当串口用,两个ttl连接后一端插入电脑一端树莓派,设备名改为/dev/ttyAMA0
    智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第19张图片

  • 然后进行socket客户端连接的测试,在电脑端使用网络调试助手,输入IP地址和端口号进行连接。下图是发送和接受的结果显示。
    智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第20张图片
    智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第21张图片

4. 树莓派mjpg-streamer监控功能调试:

  • 使用监控功能使用树莓派现成的库mjpg-streamer,树莓派利用pi
    Camera模块
    ,通过mjpg-streamer软件获取视频,通过手机端或电脑端浏览实时视频。mjpg-streamer是一个开源的摄像头媒体流,通过本地获取摄像头的数据,再通过http通讯发出来,然后再通过浏览器访问树莓派的ip地址和对应的端口号就能看到对应的视频流
  • mjpg-streamer是一个比较好的软件框架,他用的是插件的思想,它将相应的功能编译成相应的.so库,然后通过代码里面的delsy.so库里面的API拿来用。

git clone https://github.com/jacksonliam/mjpg-streamer.git下载mjpg-streamer

下载前先下载一下几个工具和库:
sudo apt-get install libjpeg8-dev #JPEG支持库,图像处理库 **
sudo apt-get install imagemagick
sudo apt-get install libv4l-dev #4l是小写"L",这个是底层摄像头驱动的上层的一个
应用库,底层是value for linux,v4表示value 4, l表示linux**,这是一个开源的底层视频设备驱动的一个库。
sudo apt-get install cmake 下载编译工具

下载好在这个mjpg-streamer库之后cd mjpg-streamer/mjpg-streamer-experimental //进入下载目录后进入左侧路径,然后使用指令:make all #进行编译,出现下图错误,表示树莓派里面没有cmake编译工具,make指令会调用cmake的东西,sudo apt-get install cmake进行安装即可。
在这里插入图片描述
出现下图表示编译成功:
在这里插入图片描述
然后使用指令:sudo make install #进行安装,结果如图:
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第22张图片

  • 然后打开启动脚本start.sh,这里面有启动脚本,
  • 如下图所示:input_uvc使用uvc摄像头(也就是USB口的摄像头),output_http表示使用http输出。
  • 而实际上树莓派的应该使用input_raspicam.so,所依要进行修改。将input_uvc.so改为 input_raspicam.so即可 。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

然后使用指令:sudo raspi-config打开设置再将摄像头打开即可。
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第23张图片
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第24张图片
最后使用指令:./start.sh执行脚本即可,
通过浏览器输入 http://【树莓派IP地址】:8080,回车 显示如下页面,点击页面左侧,Stream栏,显示监视画面。
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第25张图片

智能家居人脸识别方案:

对于人脸识别这个功能的实现我采用人工智能开放平台——祥云平台,只要掌握了这一个平台后台API的开发,同样就可以使用其他平台的方案去开发车牌识别、人脸识别、图片识别等等功能。下面是翔云平台的产品:
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第26张图片
先试用一下人脸识别功能,注册登录后开始使用人脸识别功能,比对结果有JSON数据( JSON 是一种轻量级的传输数据格式 , 用于数据交互 ,json是一种与语言无关的数据交换的格式.),这种在网页上点击进行的识别是进行的BS(browser serve,就是浏览器服务)的识别,每一次网页访问都是BS模式,这种通用的协议是http的协议,人脸识别就是让代码完成刚才点击的一系列操作,就是让代码发起http请求,不一定要掉浏览器发起http请求,因为浏览器的后台也是通过http的请求来获取数据。既然要使用代码发起http请求就要了解linux如何使用C语言发起http请求。在之后的文章里面会有如何使用智能云平台。
智能家居项目开发: 设计模式(工厂模式)+ 线程池 + Socket (持续更新中)_第27张图片

你可能感兴趣的:(项目,c语言,智能家居,树莓派开发,物联网项目,ARM开发)