一、语音控制
1、指令结构体编写
这个结构体定义了一个命令输入的模型。在这个模型中,包含以下几个部分:
- cmdName:一个长度为128的字符串,用于存储命令名称。
- dvicesName:一个长度为128的字符串,用于存储设备名称。
- cmd:一个长度为32的字符串,用于存储具体的命令。
-
Init:一个函数指针,该函数接受三个参数:指向
InputCmd
结构体实例的指针(可以读取所有字段)、IP地址和端口号,并返回一个整数值。 -
getCmd:一个函数指针,该函数接受一个参指向
InputCmd
结构体实例的指针(可以读取所有字段)并返回一个整数值。 - log:一个长度为1024的字符数组,用于存储日志信息。
- fd:一个整数,文件描述符。
- next:一个指向结构体本身的指针,用于操控链表。
imputCmd.h
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
struct InputCmd{
char cmdName[128];
char devicesName[128];
char cmd[32];
int (*Init)(struct InputCmd *cmd,char *ipAdress,char *port);
int (*getCmd)(struct InputCmd *cdm);
char *log[1024];
int fd;
char port[12];
char ipAddress[32];
int s_fd;
struct InputCmd *next;
};
2、实例化对象
(1)结构体变量初始化
一个结构体变量初始化的例子,声明并初始化了一个`InputCmd`类型的变量`voice`,并将每个字段分别赋值。
- cmdName字段被赋值为字符串"voice"。
- devicesName字段被赋值为字符串"/dev/ttyAMA0"。
- cmd字段被赋值为空字符串。
- init字段被赋值为`voice_init`函数的地址。
- getCmd字段被赋值为`voice_getCmd`函数的地址。
- log字段被赋值为空字符串。
(2)对应字段函数编写
初始化串口,获取文件描述符
fd=serialOpen(voice->devicesName,9600)
它将尝试以9600波特率打开名为 voice->devicesName
的串行端口,并将返回的文件描述符(File Descriptor)赋值给变量 fd
。这里的 voice->devicesName
是指向一个字符串的指针,该字符串包含要打开的串行端口的名称或路径。例如,在Linux系统上,这可能是类似于 "/dev/ttyS0" 或 "/dev/ttyUSB0" 的设备文件路径。
9600
是波特率,它决定了数据传输的速度,表示每秒传输9600个数据位。如果 serialOpen
函数成功打开串行端口,它将返回一个非负整数。如果打开失败,函数通常会返回 -1
。
文章来源地址https://www.toymoban.com/news/detail-826760.html
读取指令
从voicer->fd
指向的串行端口读取数据,并将这些数据存储在voicer->command
缓冲区中。
插入链表等待被调用
Voice.c
#include "inputCmd.h"
int voice_init(struct InputCmd *voice,char *ipAdress,char *port){
int fd;
if((fd=serialOpen(voice->devicesName,9600))==-1){
exit(-1);
}
voice->fd=fd;
return fd;
}
int voice_getCmd(struct InputCmd *voice){
int nread=0;
nread=read(voice->fd,voice->cmd,sizeof(voice->cmd));
if(nread==0){
printf("usart for voice read over time\n");
}else
return nread;
}
struct InputCmd voice={
.cmdName="voice",
.devicesName="/dev/ttyAMA0",
.cmd={'\0'},
.Init=voice_init,
.getCmd=voice_getCmd,
.log={'\0'}
};
struct InputCmd* addVoiceToInputCmdLink(struct InputCmd *phead){
if(phead==NULL){
return &voice;
}else{
voice.next=phead;
phead=&voice;
}
}
3、将插入链表函数加入inputCmd.h等待被调用
4、main函数编写
插入链表等待被调用
mainPro.c
#include <stdio.h>
#include <string.h>
#include "controlDevices.h"
#include "inputCmd.h"
struct Devices *findDevicesName(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;
}
}
int main(){
if(wiringPiSetup()==-1){
return -1;
}
struct Devices *pdevicesHead=NULL;
struct InputCmd *pinputCmdHead=NULL;
pdevicesHead=addbathroomLightToDevicesLink(pdevicesHead);
pdevicesHead=addupstairLightToDevicesLink(pdevicesHead);
pdevicesHead=addlivingroomLightToDevicesLink(pdevicesHead);
pdevicesHead=addrestaurantLightToDevicesLink(pdevicesHead);
pdevicesHead=addFireToDevicesLink(pdevicesHead);
pinputCmdHead=addVoiceToInputCmdLink(pinputCmdHead);
char name[128]={'\0'};
struct Devices *tmp=NULL;
while(1){
printf("INPUT:\n");
scanf("%s",name);
tmp=findDevicesName(name,pdevicesHead);
if(tmp!=NULL){
tmp->devicesInit(tmp->pinNum);
tmp->open(tmp->pinNum);
tmp->readStatus(tmp->pinNum);
}
}
return 0;
}
二、网络控制
1、结构体变量初始化
2、对应字段函数编写
(1)socket_Init
1、创建一个服务器端的套接字并初始化服务器地址结构
- 定义两个socket描述符变量:
s_fd
为服务端socket描述符,c_fd
为客户端socket描述符。 - 定义两个
sockaddr_in
结构体变量:s_addr
存储服务器端网络地址信息,c_addr
存储客户端网络地址信息。 - 使用
memset()
函数清零这两个结构体变量,确保它们的所有成员初始化为0。 - 创建服务器端socket
调用 socket()
函数创建一个基于IPv4协议的流式(TCP)socket。如果返回值 s_fd
为-1,则表示socket创建失败,程序通过 perror("socket")
输出错误信息,并使用 exit(-1)
终止程序执行。
- 初始化服务器地址结构体:
s_addr.sin_family = AF_INET
:设置地址族为Internet(IPv4)。
s_addr.sin_port = htons(atoi(socketInit->port)):
将字符串形式的端口号转换为整型数字,然后使用 htons()
函数将端口号从主机字节序转换为网络字节序。
inet_aton(socketInit->ipaddress, &s_addr.sin_addr)
: 将字符串形式的IP地址转换为二进制格式,并存入到s_addr.sin_addr
中。这样就完成了服务器地址的配置,接下来可以调用bind()函数绑定这个地址和端口到刚创建的socket上。(需要包含头文件#include <arpa/inet.h>)
2、将创建的socket与特定的IP地址和端口号绑定
-
s_fd
是之前通过socket()
函数创建的socket描述符。 -
(struct sockaddr *)&s_addr
指向已经初始化好的服务器地址结构体s_addr
。由于bind()
函数的第二个参数要求指向一个通用套接字地址结构(struct sockaddr *
),所以我们需要类型转换。
如果 bind()
调用成功,那么该socket就会监听指定的IP地址和端口上的连接请求。如果失败,则会返回一个错误码,通常需要通过 perror()
或检查 errno
来获取错误信息并进行相应的处理。
3、 将socket设置为监听模式
-
s_fd
是之前创建并绑定到特定IP地址和端口的socket描述符。 - 第二个参数(这里是10)表示backlog,即在服务器开始拒绝连接请求前,可以排队等待处理的最大连接数。当这个数目达到后,新的连接请求将会被拒绝,直到服务器完成对已有连接的处理。
将已经初始化并设置为监听状态的socket描述符 s_fd
赋值给结构体 socketInit
的成员变量 s_fd
(2) socket_getCmd
1、声明变量与初始化c_addr结构体
int c_fd;
:声明一个整型变量 c_fd
,用于存储接受到的客户端socket描述符。
int n_read = 0;
:声明一个整型变量 n_read
,用于记录从客户端socket读取数据的数量。
struct sockaddr_in c_addr;
:定义一个结构体变量 c_addr
,用于存储客户端的网络地址信息。
memset(&c_addr, 0, sizeof(struct sockaddr_in));
:清零 c_addr
结构体,确保所有成员初始化为0。
int len = sizeof(struct sockaddr_in);
:声明并初始化一个整型变量 len
,表示要获取的客户端地址结构体的大小。
2、等待并接受来自客户端的连接请求
c_fd = accept(socketGet.s_fd, (struct sockaddr *)&c_addr, &len);
:使用 accept()
函数等待并接受来自客户端的连接请求。
- 第一个参数是服务端监听的socket描述符(即之前创建并调用
listen()
的s_fd
)。 - 第二个参数是一个指向
sockaddr_in
结构体的指针,用于接收已连接客户端的地址信息。 - 第三个参数是一个指向整数的指针,用于接收实际填充到地址结构体中的字节数。
3、从已连接的客户端socket (c_fd
) 读取数据
n_read = read(c_fd, socketGet->command, sizeof(socket_command));
:使用 read()
函数从已连接的客户端socket (c_fd
) 读取数据,并将数据存入 socketGet
结构体中 command
成员指定的缓冲区。第二个参数是缓冲区的起始地址,第三个参数是期望读取的最大字节数。
对于 read()
函数的返回值进行判断:
- 如果
n_read == -1
,说明读取过程中发生错误,通过perror("read")
输出错误信息。 - 如果
n_read > 0
,则打印出读取到的数据长度。 - 如果
n_read == 0
,通常意味着客户端已经关闭了连接,此时输出 "client quit!",然后可能执行相应的退出循环操作(这里没有显示完整的上下文,所以无法确定是否有break语句所在的循环)。
最后返回 n_read
,代表本次从客户端读取数据的实际字节数。
(3)插入链表等待被调用
socket.c
#include "inputCmd.h"
int socket_init(struct InputCmd *socketInit,char *ipAdress,char *port){
int s_fd,c_fd;//s_fd:服务(service)端socket描述符;c_fd:客户(client)端socket描述符
struct sockaddr_in s_addr;//s_addr:存储服务器端网络地址信息
struct sockaddr_in c_addr;//c_addr:存储客户端网络地址信息
memset(&s_addr,0,sizeof(struct sockaddr_in));//初始化结构体
memset(&c_addr,0,sizeof(struct sockaddr_in));
//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(socketInit->port));
inet_aton(socketInit->ipAddress,&s_addr.sin_addr);
//bind
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//listen
listen(s_fd,10);
socketInit->s_fd=s_fd;
return s_fd;
}
int socket_getCmd(struct InputCmd *Socket){
int c_fd;
int n_read=0;
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
int len=sizeof(struct sockaddr_in);
c_fd=accept(Socket->s_fd,(struct sockaddr *)&c_addr,&len);
n_read=read(c_fd,Socket->cmd,sizeof(Socket->cmd));
if(n_read==-1){
perror("read");
}else if(n_read>0){
printf("\n");
printf("get:%d\n",n_read);
}else{
printf("client quit!\n");
}
return n_read;
}
struct InputCmd Socket={
.cmdName="socket",
.cmd={'\0'},
.Init=socket_init,
.getCmd=socket_getCmd,
.log={'\0'},
.port="8080",
.ipAddress="172.20.10.9"
};
struct InputCmd* addSocketToInputCmdLink(struct InputCmd *phead){
if(phead==NULL){
return &Socket;
}else{
Socket.next=phead;
phead=&Socket;
}
}
3、将插入链表函数加入inputCmd.h等待被调用
#include <stdio.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
struct InputCmd{
char cmdName[128];
char devicesName[128];
char cmd[32];
int (*Init)(struct InputCmd *cmd,char *ipAdress,char *port);
int (*getCmd)(struct InputCmd *cdm);
char *log[1024];
int fd;
char port[12];
char ipAddress[32];
int s_fd;
struct InputCmd *next;
};
struct InputCmd* addVoiceToInputCmdLink(struct InputCmd *phead);
struct InputCmd* addSocketToInputCmdLink(struct InputCmd *phead);
4、main函数编写
mainPro.c
#include <stdio.h>
#include <string.h>
#include "controlDevices.h"
#include "inputCmd.h"
struct Devices *findDevicesName(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;
}
}
int main(){
if(wiringPiSetup()==-1){
return -1;
}
struct Devices *pdevicesHead=NULL;
struct InputCmd *pinputCmdHead=NULL;
pdevicesHead=addbathroomLightToDevicesLink(pdevicesHead);
pdevicesHead=addupstairLightToDevicesLink(pdevicesHead);
pdevicesHead=addlivingroomLightToDevicesLink(pdevicesHead);
pdevicesHead=addrestaurantLightToDevicesLink(pdevicesHead);
pdevicesHead=addFireToDevicesLink(pdevicesHead);
pinputCmdHead=addVoiceToInputCmdLink(pinputCmdHead);
pinputCmdHead=addSocketToInputCmdLink(pinputCmdHead);
char name[128]={'\0'};
struct Devices *tmp=NULL;
while(1){
printf("INPUT:\n");
scanf("%s",name);
tmp=findDevicesName(name,pdevicesHead);
if(tmp!=NULL){
tmp->devicesInit(tmp->pinNum);
tmp->open(tmp->pinNum);
tmp->readStatus(tmp->pinNum);
}
}
return 0;
}
三、测试
将工程放入树莓派中,编译通过,无错误无警告
四、当前阶段源代码
链接:https://pan.baidu.com/s/1vh8smf725jrxSUlDOHFOfg
提取码:sail文章来源:https://www.toymoban.com/news/detail-826760.html
到了这里,关于【智能家居】6、语音控制及网络控制代码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!