语音识别模块,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;
}
工厂模式(Factory Pattern)是最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口(API)来指向新创建的对象。
创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口(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
};
首先在选定位置新建一个文件夹,然后在文件夹里面新建dog.c、animal.h、cat.c和mainPro.c 然后再新建一个文件夹存放sourceInsight的工程文件(有关sourceInsight的用法),如下图所示:
先写下4个分文件,animal.h 、cat.c 、dog.c 、person.c(一个功能一个文件) 然后将对应的代码写入对应的文件,比如:dog.c这个文件就是存放dog这个对象的相关行为,并且提供让主程序调用的函数API将dog这个对象添加到链表中去(这个就像是以后的智能家居为实现整个控制系统,需要添加的各个功能模块,一个供能模块就是一个文件),putdogInLink
函数是将dog对象插入进链表的API接口,这里采用头插法进行插入,即:先插入的在后边。
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;
}
他们之间的关联就是链表一样,一个文件加入链表,要加功能(就是加文件)就把文件当成元素插入链表中。
把文件中的cat ,dog, person 换成 卧室灯,餐厅灯,客厅灯,不就是功能性文件嘛, 要在来个卫生间灯,就在加一个文件。
XXX.c
,再在animal.h
中把struct Animal * putXXXInLink(struct Animal *phead);
(接口)暴露出来,暴露出来目的是给mainPro
添加,加入功能链表中。首先创建两个链表
(指令工厂、控制工厂),然后接下来创建两个线程
(一个是语音的、一个是客户端的),在每个线程里面在接受到指令后去控制工厂里面去查找对应的控制设备然后进行一系列操作
。以上是智能家居开发的软件框架构建准备工作,接下来开始代码的编写。
#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.c
和mainPro.c
文件的编写。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)
gcc *.c -o test -lwiringPi
进行编译。浴室灯调试
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;
}
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;
}
fire.c
文件代码,代码框架和浴室灯代码框架相同,不同的是这个文件里面要有读取引脚状态的函数,同时引脚也要设置为输入模式,当检测到或火灾是火灾传感器的引脚会被拉为低电平。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;
}
}
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;
}
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
这个函数因为在这里写后,会对后面多线程的处理不是特别的方便,计划的是连接进来一个客户端然后起一个线程去对接,但是结果实现客户端发送完一条消息后,需要断开连接然后重新连接,因为代码里面采用的是点对点的方式。#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;
}
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;
}
}
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
函数里面还涉及到线程的创建,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)
使用指令:gcc *.c -o test -lpthread -lwiringPi
进行编译,然后执行可以看到下图:(在语音没有指令的时候打印nodata from voice)
然后进行串口接收信息功能的测试,使用串口前按照这篇博文进行串口的设置,我这里使用的是USB转ttl连接电脑进行测试测试结果如下:(测试成功)【由于我树莓派串口坏了我是用的两个ttl充当串口用,两个ttl连接后一端插入电脑一端树莓派,设备名改为/dev/ttyAMA0
】
然后进行socket客户端连接的测试,在电脑端使用网络调试助手,输入IP地址和端口号进行连接。下图是发送和接受的结果显示。
mjpg-streamer
,树莓派利用pimjpg-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
#进行安装,结果如图:
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请求。在之后的文章里面会有如何使用智能云平台。