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

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

  • controlDevices.h是指令工厂头文件代码,结点结构体的声明,这里面的东西不一定够用,可以先写上,等不够的时候在进行添加。
#include<wiringPi.h> //包含wiringPi库
#include<string.h>
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);
struct Devices* addSecondFlootLightToDeviceLink(struct Devices* phead);
struct Devices* addRestaurantLightToDeviceLink(struct Devices* phead);
struct Devices* addLivingRoomLightToDeviceLink(struct Devices* phead);
struct Devices* addFireContrlToDeviceLink(struct Devices* phead);
  • inputCommand.h是设备工厂头文件代码,里面有设备链表每一个结点的结构体类型的声明,和指令工厂头文件类似。
#include<wiringPi.h>
#include<string.h>
#include<wiringSerial.h>
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
struct InputCommander
{int fd;int socketfd;char port[12];           //端口号char ipAdress[32];       //ip地址char command[32];      //存放指令信息char log[1024];          //存放日志信息char devicesname[128];   //存放串口设备名字char commandName[128];int (*getCommand)(struct InputCommander* voicer); //接收指令函数int (*Init)(struct InputCommander* voicer,char* ipAdress,char* port);struct InputCommander* next;
};
//将指令结点添加至指令链表中的函数声明
struct InputCommander* addVoiceToDeviceLink(struct InputCommander* phead);
struct InputCommander* addSocketToDeviceLink(struct InputCommander* phead);
  • 首先编写设备工厂的设备文件bathroomLight.c
#include"contrlDevices.h"  //包含头文件
int bathroomLightOpen(int pinNum)
{digitalWrite(pinNum,LOW);//将引脚电平拉低,点亮浴室灯
}
int bathroomLightClose(int pinNum)
{digitalWrite(pinNum,HIGH);//将引脚电平拉高,熄灭浴室灯
}
int bathroomLightInit(int pinNum)
{pinMode(pinNum,OUTPUT);digitalWrite(pinNum,HIGH);//初始化引脚功能
}
struct Devices bathroomLight={.pinNum=22,             //浴室灯继电器控制IO口引脚.devicesName="bathroomLight", //通过这个设备名进行浴室灯结点的查找,然后再进行结构体函数的调用.deviceInit=bathroomLightInit,.open=bathroomLightOpen,.close=bathroomLightClose,
};
struct Devices* addBathroomLightToDeviceLink(struct Devices* phead) //将浴室灯结点插入到设备工厂链表里面,采用头插法
{if (phead==NULL){return &bathroomLight;}else{bathroomLight.next=phead;phead=&bathroomLight;}
}
  • fire.c文件代码,代码框架和浴室灯代码框架相同,不同的是这个文件里面要有读取引脚状态的函数,同时引脚也要设置为输入模式,当检测到或火灾是火灾传感器的引脚会被拉为低电平。
#include"contrlDevices.h"
int fireContrlInit(int pinNum)
{pinMode(pinNum,INPUT);digitalWrite(pinNum,HIGH);
}
int fireReadstatus(int pinNum)
{int ret;ret=digitalRead(pinNum);return ret;
}
struct Devices fireContrl={.pinNum=25,             //火灾报警器输入IO口.devicesName="fire",.deviceInit=fireContrlInit,.readStatus=fireReadstatus
};
struct Devices* addFireContrlToDeviceLink(struct Devices* phead)
{if (phead==NULL){return &fireContrl;}else{fireContrl.next=phead;phead=&fireContrl;}
}
  • 由于其他几个灯光控制文件里面的代码和第一个浴室灯控制代码大同小异,所以这里不再赘述,下面是语音指令输入文件代码,这个文件里面的函数就要添加读取指令函数和初始化函数,所谓的初始化函数就是将串口打开然后设置相应的波特率,读取指令函数需要注意的是在读取指令前需要将缓存区初始化防止有乱码,读指令函数主要调用read函数进行指令的读取,在没有指令到来的时候,输出:usart for voice read over time,其实代码框架和设备工厂的框架基本类似,只是文件里面包含的函数有所差异,但都有一个设备结点插入函数。
#include "inputCommand.h"
int voiceInit(struct InputCommander* voicer)//就是对串口的初始化
{int fd;if((fd=serialOpen(voicer->devicesname,115200))==-1){   //open serial,波特率115200printf("usrat open fail\n");exit(-1);}voicer->fd=fd;return fd;
}
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("usart for voice read over time\n");}else{return nread;}
}
struct InputCommander voiceContrl={.commandName="voice",.command={'\0'},.devicesname="/dev/ttyAMA0",.next=NULL,.getCommand=voiceGetCommand,.Init=voiceInit,.log={'\0'}
};
struct InputCommander* addVoiceToDeviceLink(struct InputCommander* phead)
{if(phead==NULL){return &voiceContrl;}else{voiceContrl.next=phead;phead=&voiceContrl;}
}
  • 下面是socket指令文件代码,这个里面不需要getCommmand这个函数因为在这里写了,对后面多线程的处理不是特别的方便,计划的是连接进来一个客户端然后起一个线程去对接,但是客户端发送完一条消息后,需要断开连接然后重新连接,因为代码里面采用的是点对点的方式。《socket知识补充》
#include "inputCommand.h"
int socketInit(struct InputCommander* socketMes)//就是对socket的初始化
{int socketfd;int bindre;int listenre;int len=sizeof(struct sockaddr_in);struct sockaddr_in IP;memset(&IP,'\0',len);IP.sin_family=AF_INET; //协议IP.sin_port=htons(atoi(socketMes->port));IP.sin_addr.s_addr=inet_addr(socketMes->ipAdress);socketfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//建立套接字if(socketfd==-1){printf("socket create fail\n");perror("socket");exit(-1);}else{printf("socket create successful\n");}bindre=bind(socketfd,(struct sockaddr*)&IP,len); //绑定服务器IP地址和端口号listenre=listen(socketfd,10);   //监听printf("socket server listening.........\n");socketMes->socketfd=socketfd;return socketfd;
}
struct InputCommander socketContrl={.commandName="socketServer",.command={'\0'},.next=NULL,.Init=socketInit,               //socket初始化函数,建立套接字,然后绑定、监听、等待客户端的连接.log={'\0'},.port="8088",                 //服务器端口号.ipAdress="192.168.43.136"   //服务端IP地址
};
struct InputCommander* addSocketToDeviceLink(struct InputCommander* phead)
{if(phead==NULL){return &socketContrl;}else{socketContrl.next=phead;phead=&socketContrl;}
}
  • 最后进行main函数代码的编写,main函数里面涉及到设备工厂、指令工厂头结点的插入和设备文件、指令文件分别插入到设备链表和指令链表。同时还要有结点查找函数:包括设备结点查找函数、指令结点查找函数,查找后返回结点指针然后对特定结点进行操作即可。同时main函数里面还涉及到线程的创建,socket_thread这个函数里面在有客户端接入的时候又进行了线程的创建,用来对接接入的客户端。《线程知识补充》
#include<stdio.h>
#include<string.h>
#include <unistd.h>
#include <pthread.h>
#include "inputCommand.h"
#include"contrlDevices.h"
int newfd;
struct Devices *pdeviceHead=NULL;//将设备链表的头结点设置为全局变量
struct InputCommander* socketHandler=NULL; //这个是查找到的的socket指令结点,将它设为全局变量是因为socket_thread这个函数有用到这个节点//除此之外,socket_thread的子线程read_thread也有用到这个节点指针,虽然可以通过创建线程传参,但是不建议那么做。
struct InputCommander* pcommandHead=NULL;//将指令链表的头结点设置为全局变量
struct Devices *findDevicesByName(char*name,struct Devices*phead)//查找设备结点函数
{struct Devices *tmp=phead;if(phead==NULL){return NULL;}else{while(tmp!=NULL){if(strcmp(tmp->devicesName,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 *data)//当有新的客户端接入的时候,创建线程去对接,这个函数就是线程对接函数,用于读取客户端指令
{int n_read;memset(socketHandler->command,'\0',sizeof(socketHandler->command));n_read=read(newfd,socketHandler->command,sizeof(socketHandler->command));if(n_read==-1)perror("read");else if(n_read>0){printf("\n get:%d,%s\n",n_read,socketHandler->command);}else{printf("client quit\n");}
}
void* voice_thread(void*data)//语音线程函数,用于等待语音指令,在这里设置为一个包含有while(1)的线程
{int nread;struct InputCommander* voiceHandler;voiceHandler=findCommandByName("voice",pcommandHead);if(voiceHandler==NULL){printf("find voiceHandler error!\n");pthread_exit(NULL);   //查找指令工厂语音部分失败退出当前线程}else{printf("%s find successful\n",voiceHandler->commandName);if(voiceHandler->Init(voiceHandler)<0){printf("voice init error\n");pthread_exit(NULL);   //初始化失败退出当前线程}else{printf("%s init successful!\n",voiceHandler->commandName);}while(1){nread=voiceHandler->getCommand(voiceHandler);if(nread==0){printf("nodata from voice\n");}else{printf("do divece contrl:%s\n",voiceHandler->command);}}}
}
void* socket_thread(void*data)//socket线程,用于与客户端对接,这个是main函数里面创建线程函数
{pthread_t readthread; int len=sizeof(struct sockaddr_in);struct sockaddr_in CLI;//客户端信息memset(&CLI,'\0',len);socketHandler=findCommandByName("socketServer",pcommandHead);if(socketHandler==NULL){printf("find socketHandler error!\n");pthread_exit(NULL);   //查找指令工厂socket部分失败退出当前线程}else{printf("%s find successful!\n",socketHandler->commandName);}socketHandler->Init(socketHandler);while(1){newfd=accept(socketHandler->socketfd,(struct sockaddr*)&CLI,&len);pthread_create(&readthread,NULL,read_thread,NULL);}
}
int main()
{if(wiringPiSetup()==-1){return -1;}//初始化树莓派硬件,这个只需要执行一次所以放在main函数里面即可pthread_t voicetd;pthread_t sockettd;//指令工厂初始化pcommandHead=addVoiceToDeviceLink(pcommandHead);          //插入语音指令结点pcommandHead=addSocketToDeviceLink(pcommandHead);            //插入socket指令结点//设备控制工厂初始化pdeviceHead=addBathroomLightToDeviceLink(pdeviceHead);    //插入浴室灯pdeviceHead=addRestaurantLightToDeviceLink(pdeviceHead);  //插入餐厅灯pdeviceHead=addSecondFlootLightToDeviceLink(pdeviceHead); //插入二楼浴室灯pdeviceHead=addLivingRoomLightToDeviceLink(pdeviceHead);  //插入客厅灯pdeviceHead=addFireContrlToDeviceLink(pdeviceHead);       //插入火灾报警器控制//线程池建立、语音线程、socket线程pthread_create(&voicetd,NULL,voice_thread,NULL);pthread_create(&sockettd,NULL,socket_thread,NULL);pthread_join(voicetd,NULL);pthread_join(sockettd,NULL);//等待指定线程退出return 0;
}

代码编写完成后通过ftp工具传输到树莓派编译(我这里用的是FileIlla)

  • 使用指令:gcc *.c -o test -lpthread -lwiringPi进行编译,然后执行可以看到下图:(在语音没有指令的时候打印nodata from voice)
  • 然后进行串口接收信息功能的测试,使用串口前按照这篇博文进行串口的设置,我这里使用的是USB转ttl连接电脑进行测试测试结果如下:(测试成功)

  • 然后进行socket客户端连接的测试,在电脑端使用网络调试助手,输入IP地址和端口号进行连接。下图是发送和接受的结果显示。

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

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

  • 下载前先下载一下几个工具和库:
    sudo apt-get install libjpeg8-dev #JPEG支持库,图像处理库
    sudo apt-get install imagemagick
    sudo apt-get install libv4l-dev #4l是小写"L",这个是底层摄像头驱动的上层的一个应用库,底层是value for linuxv4表示value 4l表示linux,这是一个开源的底层视频设备驱动的一个库。
    sudo apt-get install cmake #下载编译工具
  • git clone https://github.com/jacksonliam/mjpg-streamer.git下载mjpg-streamer库,
  • 下载好在这个库之后cd mjpg-streamer/mjpg-streamer-experimental //进入下载目录后进入左侧路径,然后使用指令:make all #进行编译,出现下图错误,表示树莓派里面没有cmake编译工具,make指令会调用cmake的东西,sudo apt-get install cmake进行安装即可。

    出现下图表示编译成功:
  • 然后使用指令:sudo make install #进行安装,结果如图:
  • 然后打开启动脚本start.sh,这里面有启动脚本,如下图所示:input_uvc是使用uvc摄像头,也就是usb口的摄像头,output_http表示使用http输出。而实际上树莓派的应该使用input_raspicam.so,所依要进行修改。将input_uvc.so改为 input_raspicam.so即可 。
  • 然后使用指令:sudo raspi-config打开设置再将摄像头打开即可。

  • 最后使用指令:./start.sh执行脚本即可,然后通过浏览器输入 http://IP地址:8080,回车 显示如下页面,点击页面左侧,Stream栏,显示监视画面。

智能家居人脸识别方案:

  • 对于人脸识别这个功能的实现我采用人工智能开放平台——祥云平台,只要掌握了这一个平台后台API的开发,同样就可以使用其他平台的方案去开发车牌识别、人脸识别、图片识别等等功能。下面是翔云平台的产品:

  • 先试用一下人脸识别功能,注册登录后开始使用人脸识别功能,比对结果有JSON数据( JSON 是一种轻量级的传输数据格式 , 用于数据交互 ,json是一种与语言无关的数据交换的格式.),这种在网页上点击进行的识别是进行的BS(browser serve,就是浏览器服务)的识别,每一次网页访问都是BS模式,这种通用的协议是http的协议,人脸识别就是让代码完成刚才点击的一系列操作,就是让代码发起http请求,不一定要掉浏览器发起http请求,因为浏览器的后台也是通过http的请求来获取数据。既然要使用代码发起http请求就要了解linux如何使用C语言发起http请求。在之后的文章里面会有如何使用智能云平台。

智能家居代码构建编写、简单工厂模式、树莓派摄像头视频监控功能实现相关推荐

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

    智能家居项目开发 一.智能家居功能细节拆分 控制区: 外设区: 面向对象类和对象的概念 结构体新玩法 二.工厂模式 1. 工厂模式的概念 2. 工厂模式的实现 3. 工厂模式使用及功能验证 三.智能家 ...

  2. Java设计模式之简单工厂模式实验(软件工程综合实践课程第二周)

    实验目的 1 .理解软件设计的相关理论 : 2 .理解面向对象设计原则: 实验内容 1 .简单工厂模式实验: (1) 参考讲义上的代码,利用简单工厂模式完成计算器应用程序: (2) 画出计算器程序简单 ...

  3. 简单工厂模式,工厂方法模式,抽象工厂模式总结-java版

    文章目录 LOG:更新日志 一.简单工厂模式,工厂方法模式,抽象工厂模式定义 二.三种工厂模式的优缺点以及适用场景 三.名词解释 四.简单工厂模式.工厂方法模式与抽象工厂模式之间的区别 五.抽象工厂模 ...

  4. 设计模式:简单工厂模式(C++)【看不懂算我输】

    简单工厂模式介绍 简单工厂模式并不是Gof四人帮搞的23中设计模式中的一种,只不过大部分的设计模式书籍都会介绍它.所有我们还是来看看它,或许你觉得很简单.但是我们可以瞧瞧它有什么优点和缺点.如何进行改 ...

  5. 【Head First 设计模式】-简单工厂模式读后总结

    1 简单工厂模式定义和特点 1.1 什么是简单工厂模式 书中并没有对简单工厂进行定义,我觉得百度百科介绍还不错就摘抄拉过来: 简单工厂模式(Simple Factory Pattern)属于类的创新型 ...

  6. 简单工厂模式-工厂方法模式

    文章目录 1 简单工厂模式 1.1 类的结构图 1.2 代码实现 2 工厂方法模式 2.1 定义 2.2 类的结构图 2.3 代码实现 1 简单工厂模式 1.1 类的结构图 1.2 代码实现 pack ...

  7. 设计模式之工厂类模式总结对比、简单工厂模式、工厂方法模式、抽象工厂模式、带反射的工厂模式、例子代码分析、最详细

    1. 题目 假设某公司同时用SqlServer.MySql数据库,即会切换两数据库(不同数据库的sql语句有些许差异),同时,两数据库里均有对Users.Departments表的操作(sql代码不一 ...

  8. php设计一个盒子类代码_PHP设计模式之简单工厂模式(Simple Factory)代码实例大全(七)...

    目的[1] 简单工厂模式是一个精简版的工厂模式. 它与静态工厂模式最大的区别是它不是『静态』的.因为非静态,所以你可以拥有多个不同参数的工厂,你可以为其创建子类.甚至可以模拟(Mock)它,这对编写可 ...

  9. java工厂模式式代码_简单工厂模式及其简单Java案例代码实现

    说明:本文是<大话设计模式>的学习记录及结合网上相关信息编写,原书代码例子采用C#编写,本文采用Java稍加改写.若有不当,欢迎指正,共同进步.java 1.简单工厂模式概述:设计模式 简 ...

最新文章

  1. Linux初学者的感受
  2. 数据中心节能的13个有用小知识
  3. 基于.NET的WebService的实现
  4. Debug javascript inside jsp page 调试jsp嵌入的js
  5. DP Big Event in HDU
  6. oauth 使用令牌_使用OAuth2令牌的安全REST服务
  7. service层拼接XML
  8. 深入浅出MFC文档/视图架构之文档
  9. MAC地址和IP地址的关系
  10. php删除第一个字母,php – 正在上传的文件将第一个字母切断
  11. SIP系统怎么禁用?SIP系统完整性保护关闭方法(含M1)
  12. conda deactivate python3_conda进行python环境隔离
  13. Adaboost、RandomFrest、GBRT的区别
  14. 巅峰对决 竞逐百万大赛 | 2019数字中国创新大赛第二批赛题强势来袭
  15. Oracle表字段的增加、删除、修改和重命名
  16. [刷机教程] android系列 adb操作命令详解,常用adb操作命令详解
  17. hdu5855二分+最大流
  18. linux创建裸磁盘,Linux上如何创建裸设备
  19. 《三体》中的经典名句
  20. [MySQL光速入门]007 作业解答

热门文章

  1. PXI/PXIe控制器 4Link架构 16GB带宽 兼容主流PXIe机箱 设计文件
  2. php 拼音首字母大写字母,获取汉字拼音首字母大写
  3. Linux内核(10) - 内核中的链表
  4. python中 什么意思_Python里面的这几个梗,你能回答出来吗
  5. 上海擎标助力中国移动山东公司通过ISO22301业务连续性认证
  6. Medium上的文章
  7. 关于Cisco交换机接口模式的详细介绍
  8. 微型计算机的主要硬件以及技术指标,微型计算机的硬件组成.doc
  9. PageHelper 与 PageInfo 的坑
  10. 正视美国的科技竞争焦虑感