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

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

首先编写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请求。在之后的文章里面会有如何使用智能云平台。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/272130.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux中Shell的命令替换用法笔记

命令替换主要是指将命令的标准输出值赋值给某个变量。命令替换属于Shell编程中非常重要的功能&#xff0c;需要熟悉掌握。 命令替换的方式 1、反引号:命令 2、$()&#xff1a;$(命令) 用法说明&#xff1a;date1date #将date命令值给date1变量 date2$(date) #将date命令值给dat…

采样次数不同平均值不一样_网络推广采取的方式不同,效果也不一样

网络推广是基于互联网而进行&#xff0c;针对目标客户群体&#xff0c;加之以有效的网络链接形式使其形成迅速大的购买力的一种营销模式&#xff0c;是目前很多企业已经认识到的&#xff0c;但是在推广过程之中往往会出现不同的状况&#xff0c;比如说模式是完全不一样的。 …

rocketmq 同步刷盘和异步刷盘以及主从复制之同步复制和异步复制你理解了吗

同步刷盘、异步刷盘 RocketMQ的消息是存储到磁盘上的&#xff0c;这样既能保证断电后恢复&#xff0c;又可以让存储的消息量超出内存的限制。 RocketMQ为了提高性能&#xff0c;会尽可能地保证磁盘的顺序写。消息在通过Producer写入RocketMQ的时候&#xff0c;有两种 写磁盘方…

七层网络模型、TCP/IP四层模型、网络数据包、交换机路由器区别

七层网络模型&#xff08;简称OSI模型&#xff09;&#xff1a; OSI 模型(Open System Interconnection model)是一个由国际标准化组织提出的概念模型,试图供一个使各种不同的计算机和网络在世界范围内实现互联的标准框架。它将计算机网络体系结构划分为七层,每层都可以提供抽…

Linux中Shell的算数运算符和位运算符用法笔记

1、算数运算符 算数运算符主要是加、减、乘、除、余、幂等常见的算术运算&#xff0c;以及加等、减等、乘等、除等、余等复合算术运算。 注意&#xff1a;Shell只支持整数运算&#xff0c;小数部分会舍去。一般情况下算术运行需要个let命令一起来使用。注意除法运算&#xff0c…

TCP三次握手、四次挥手、socket,tcp,http三者之间的区别和原理

接着上一篇文章叙述&#xff1a; TCP/IP连接(在互联网的通信中&#xff0c;永远是客户端主动连接到服务端)&#xff1a; 手机能够使用联网功能是因为手机底层实现了TCP/IP协议&#xff0c;可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口&#xff0c;使…

NSUserDefaults的用法

NSUserDefaults适合存储轻量级的本地数据&#xff0c;比如要保存一个登陆界面的数据&#xff0c;用户名、密码之类的&#xff0c;个人觉得使用NSUserDefaults是首选。下次再登陆的时候就可以直接从NSUserDefaults里面读取上次登陆的信息咯。 因为如果使用自己建立的plist文件什…

apache 配置文件内使用 8080 端口_【SpringBoot 框架】- SpringBoot 配置文件

一、SpringBoot配置文件类型SpringBoot是基于约定的&#xff0c;所以很多配置都有默认值&#xff0c;但如果想使用自己的配置替换默认配置的话 &#xff0c;就可以自己编写配置文件进行相应配置&#xff0c;起步依赖spring-boot-starter-parent 中&#xff0c;有配置文件的引入…

Linux中while循环的用法笔记

Shell中可以采用while循环来实现需要进行循环的操作。 语法结构如下&#xff1a; while exp do command done 执行过程&#xff1a;while将测试exp的返回值&#xff0c;如果返回值为true则执行循环体的命令&#xff0c;返回值为false则不执行循环。循环完成后会进入下一次循环之…

libcurl库的安装和使用

libcurl简介: libcurl是一个跨平台的网络协议库&#xff0c;支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议。libcurl同样支持HTTPS证书授权&#xff0c;HTTP POST, HTTP PUT, FTP 上传, HTTP基本表单上传&#xff0c;代理&#xff0c;cookies,和用户认证。li…

android 微信缩小通话界面_安卓如何做出微信那样的界面仿微信“我”的界面2/5...

本系列目标通过安卓编程仿写微信“我”的界面,让大家也能做出类似微信界面.效果图如下:本文目标做出支付部分(其他部分在后续文章中逐步分享).效果图如下:实现方案通过截图工具或者下载一张微信支付照片,放到工程的src/main/res/drawable目录下,命名为pay.png;同样获取一张向右…

Shell脚本中函数的定义和调用笔记

Shell脚本函数的作用主要是把一些可以通用的功能封装起来、避免脚本中出现大量重复的脚本代码&#xff0c;同时可以大大增强脚本的可读性、和可维护性。Shell函数定义的语法格式&#xff1a;function FUNCTION_NAME(){command1command2command3}也可以省略function 关键字FUNCT…

opensll、libcurl库安装的使用,树莓派CSI摄像头的配置,树莓派调用智能API平台实现人脸识别、树莓派配置中文环境

接着上一节人工智能翔云平台介绍&#xff1a; 人工智能OCR识别&#xff1a; OCR 是英文Optical Character Recognition的缩写&#xff0c;意思是光学字符识别&#xff0c;也可简单地称为文字识别&#xff0c;是文字自动输入的一种方法。它通过 扫描 和摄像等光学输入方式获取纸…

Shell脚本中函数返回值的用法笔记

函数的返回值又可以称为函数的退出状态&#xff0c;实际上可以理解为一种通信方式。Shell脚本中函数可以使用返回值的方式把调用的结果信息反馈给调用者。便于调用者可以根据反馈的结果做相应处理。说明&#xff1a;函数的返回值主要使用 return 关键字来处理。这和很多编程语言…

python 怎么取对数_概率矩阵分解(PMF)及MovieLens上的Python代码

首先对Probabilistic Matrix Factorization这篇论文的核心公式进行讲解和推导&#xff1b;然后用Python代码在Movielens数据集上进行测试实验。一、 背景知识文中作者提到&#xff0c;传统的协同过滤算法有两个不足&#xff1a;1).不能很好地处理规模非常大的数据&#xff1b;2…