智能家居代码构建编写简单工厂模式树莓派摄像头视频监控功能实现
Posted FHNCSDN
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能家居代码构建编写简单工厂模式树莓派摄像头视频监控功能实现相关的知识,希望对你有一定的参考价值。
根据上一节内容的代码框架开始编写代码:
首先编写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,波特率115200
printf("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智能家居 (11) ——树莓派摄像头捕捉人脸并识别