IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 智能家具项目 -> 正文阅读

[C++知识库]智能家具项目

本项目使用工厂模式来编写代码,其中包括指令工厂和设备工厂,使用工厂模式的目的是为了让各模块代码更加独立,并且让C语言具有所谓的面向对象的属性;
本项目使用树莓派作为主控芯片,开发环境选择ubuntu下的Linux,通过使用交叉编译工具链,将x86架构下的代码,交叉编译成为arm架构下可执行程序,置于树莓派中;
控制方案采用socket网络控制线程和串口控制线程,其中串口控制使用的是LD3320语音模块,通过对该模块的二次开发,使其成为能够在特定语音环境下实现串口通信的功能,与树莓派通过串口进行通信,在树莓派获得LD3320发送的数据后经过相应的处理,来对设备进行控制;socket网络控制采用TCP协议,通过IP地址和端口号与树莓派建立连接,通过向树莓派发送数据,树莓派接收到相应数据之后进行处理,实现对设备的控制;

指令工程头文件:inputCommand.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<arpa/inet.h>
#include <netinet/in.h>
#include<string.h>
#include<pthread.h>


struct InputCommand{
	char commandName[128];                  //命令控制名(串口控制/语音控制)
	char deviceName[128];                   //用于调用open函数传递设备名称
	char *command[32];         				//获得命令后存放于command中
	int (*init)(struct InputCommand *device ,char * ipAddress,char *port);           //对串口和socket进行初始化的init()函数  
	int (*getCommand)(struct InputCommand *device);   		//通过串口和socket获取命令函数
	int fd;													//用于socket()返回的文件描述符
	char port[12];											//用于socket连接时的端口号
	char ipAddress[32];										//用于socket连接时的IP地址
 	char log[1024];											//用于记录命令记录

	struct InputCommand *next;
};


struct InputCommand *addVoiceIntoCommandLink(struct InputCommand *head);    //将语音控制加入到指令工厂链表中,让main.c调用
struct InputCommand *addSocketIntoCommandLink(struct InputCommand *head);   //将socket命令加入到指令链表中,好让main.c调用

设备工厂头文件 controlDevice.h

#include <stdio.h>
#include <wiringPi.h>
struct Devices{
		char deviceName[128];       //设备名称
		int status;             	//谁被当前状态
		int pinNum;                 //设备连接到树莓派的引脚
		int (*open)(int pinNum);    //开启设备函数指针
		int (*close)(int pinNum);   //关闭设备函数指针
		int (*deviceinit)(int pinNum);   //初始化设备函数指针

		int (*readStatus)();      //获取当前设备状态指针
		int (*changeStatus)(int status,int pinNum);    //改变当前设备状态函数指针   

		struct Devices* next;
};


struct Devices *addBathRoomIntoLink(struct Devices *head);  //将设备放入到设备工厂中,方便后面main函数调用
struct Devices *addBedRoomIntoLink(struct Devices *head);
struct Devices *addlockIntoLink(struct Devices *head);
struct Devices *addtoiletIntoLink(struct Devices *head);
struct Devices *addFireIntoLink(struct Devices *head);

设备1源文件 :bathroom.c

#include "controlDevice.h"

int bathRoomInit(int pinNum)      //设备初始化函数
{
	pinMode(pinNum,OUTPUT);       //配置引脚为输出模式
	digitalWrite(pinNum,HIGH);    //初始化状态
	return 0;
}

int bathRoomopen(int pinNum)    //开启设备函数
{	
	digitalWrite(pinNum,LOW);   //将引脚置低
	return 0}

int bathRoomClose(int pinNum)   //关闭设备函数
{
	digitalWrite(pinNum,HIGH);  
	return 0;
}

int bathroomchange(int status,int pinNum)        //改变设备状态函数
{
	return 0;
}

struct Devices bathRoom = {
	.pinNum = 22,                    //设备引脚为22
	.deviceName = "bathRoomLight",   //设备名称
	.deviceinit = bathRoomInit,      //为设备初始化函数指针赋值
	.open = bathRoomopen,            //设备开启函数指针
	.close = bathRoomClose,          //设备关闭函数指针
	.changeStatus = bathroomchange   //设备状态改变函数指针

     .next = NULL;
};

struct Devices *addBathRoomIntoLink(struct Devices *head)    //将设备放入链表函数实现
{
		if(head == NULL){
			head = &bathRoom;
		}else{
			bathRoom.next = head;
			head = &bathRoom;
		}

		return head;
}

设备2源文件:bedroom.c

#include "controlDevice.h"

int bedRoomInit(int pinNum)     //设备初始化函数
{
	pinMode(pinNum,OUTPUT);
	digitalWrite(pinNum,HIGH);
	return 0;
}

int bedRoomopen(int pinNum)       //开启设备函数
{	
	digitalWrite(pinNum,LOW);
	return 0;
}

int bedRoomClose(int pinNum)      //关闭设备函数
{
	digitalWrite(pinNum,HIGH);
	return 0;
}

int bedroomchange(int status,int pinNum)    //改变状态函数
{
	return 0;
}

struct Devices bedRoom = {
	.pinNum = 23,                   //连接到树莓派的引脚
	.deviceName = "bedRoomLight",   //设备名
	.deviceinit = bedRoomInit,      //设备初始化
	.open = bedRoomopen,            //开启设备 
	.close = bedRoomClose,          //关闭设备
	.changeStatus = bedroomchange   //改变设备状态

     .next = NULL;
};

struct Devices *addBedRoomIntoLink(struct Devices *head)     //将设备放入到设备链表中
{
		if(head == NULL){
			head = &bedRoom;
		}else{
			bedRoom.next = head;
			head = &bedRoom;
		}

		return head;
}

设备3源文件 fire.c

#include "controlDevice.h"

int fireInit(int pinNum)   //设备初始化
{
	pinMode(pinNum,INPUT);
	digitalWrite(pinNum,HIGH);
}

int fireReadStatus(int pinNum)   //读取当前设备状态
{
	return digitalRead(pinNum);
}


struct Devices fire = {
	.pinNum = 4,                    //连接到树莓派引脚
	.deviceName = "fireDetect",     //设备名
	.deviceinit = fireInit,         //设备初始化
	.readStatus = fireReadStatus    //读取设备状态
	.next = NULL;
};

struct Devices *addFireIntoLink(struct Devices *head)              //将设备放入到设备链表中
{
		if(head == NULL){
			head = &fire;
		}else{
			fire.next = head;
			head = &fire;
		}

		return head;
}

设备4源文件:lock.c

#include "controlDevice.h"

int lockInit(int pinNum)                //初始化设备
{
	pinMode(pinNum,OUTPUT);
	digitalWrite(pinNum,HIGH);
}

int lockopen(int pinNum)               //打开设备
{	
	digitalWrite(pinNum,LOW);
}

int lockClose(int pinNum)              //关闭设备
{
	digitalWrite(pinNum,HIGH);
}

int lockchange(int status,int pinNum)     //改变设备状态
{

}

struct Devices lock = {
	.pinNum = 24,                  //连接到树莓派的引脚 
	.deviceName = "lock",          //设备名称
	.deviceinit = lockInit,        //初始化设备
	.open = lockopen,              //打开设备
	.close = lockClose,            //关闭设备
	.changeStatus = lockchange     //改变设备状态

	.next = NULL;
};

struct Devices *addlockIntoLink(struct Devices *head)                 //将设备放入到设备链表中
{
		if(head == NULL){
			head = &lock;
		}else{
			lock.next = head;
			head = &lock;
		}

		return head;
}

socket网络控制源文件 socketComand.c

#include "inputCommand.h"

int socketInit(struct InputCommand* socketMsg ,char * ipAddress,char *port)          //socket网络初始化,IP地址和端口号在结构体struct InputCommand* socketMsg中
{
	int s_fd;        //socket()函数的文件描述符
	int bind_ret;    //bind()函数的返回值
	struct sockaddr_in s_addr;   //用于定义连接信息的结构体
	
	memset(&s_addr,0,sizeof(struct sockaddr_in));   //将上述结构体先清空
	
	s_fd = socket(AF_INET,SOCK_STREAM,0);           //socket函数,定义网络协议
	if(s_fd ==-1){                                  //判断是否创建成功
		perror("socket");
		exit(-1);
	}
	
	s_addr.sin_family = AF_INET;            //选择协议簇为IPV4
	s_addr.sin_port = htons(atoi(socketMsg->port));      //设置端口号,由于结构体中是字符串,所以需要转换整型,并且将字节序转换为网络字节序 

	inet_aton(socketMsg->ipAddress,&s_addr.sin_addr);   //对结构体总的IP地址进行定义 
	
	bind_ret = bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));    //绑定
	if(bind_ret == -1){                              //判断是否绑定成功
		perror("bind");
		exit(-1);
	}

	int listen_ret;
	listen_ret = listen(s_fd,10);                  //监听

	if(listen_ret == -1){					       //判断监听是否成功
		perror("listen");
		exit(-1);
	}else{
		printf("listening....\n");
	}
	
	socketMsg->fd = s_fd;                         //将socket函数返回的文件描述符赋值给socket命令的结构体中,方便之后应用
	return s_fd;
}

int socketGetCommand(struct InputCommand* socketMsg)       //socket获取命令函数
{
	struct sockaddr_in c_addr;
	memset(&c_addr,0,sizeof(struct sockaddr_in));             
	
	int  c_addr_len = sizeof(struct sockaddr_in);
	int c_fd = accept(socketMsg->fd,(struct sockaddr *)&c_addr,&c_addr_len);     //接受完成三次握手的连接

	if(c_fd == -1){                     //判断是否接受成功

		perror("accept");
		exit(-1);
	}
	printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
	//read
	memset(socketMsg->command,'\0',sizeof(socketMsg->command));         //将结构体中的命令清空,防止上一次的命令影响下一次命令
	int nread = read(c_fd,socketMsg->command,sizeof(socketMsg->command));      //读取信息
	if(nread ==-1){
		printf("read failure\n");
	}else if(nread >0){
		printf("read from client  %d byte,context:%s",nread,sizeof(socketMsg->command));   //打印信息
	}else{
		printf("client quit!\n");
	}

	return nread;
}


struct InputCommand socketControl = {           //初始化socket命令结构体
	.commandName = "socketServer",              //命令名称
	.command = {'\0'},                          //调用getCommand函数得到的命令赋值给command
	.ipAddress = "192.168.43.80",               //树莓派IP地址
	.port = "8088",							    //端口号
	.init = socketInit,                         //socket初始化函数
	.getCommand = socketGetCommand,             //获取指令函数
	.log = {'\0'},                              //命令记录
	.next = NULL
};


struct InputCommand *addSocketIntoCommandLink(struct InputCommand *head)        //将指令放入到指令工厂中
{
	if(head == NULL){
		head = &socketControl;
	}else{
		socketControl.next = head;
		head = &socketControl;
	}
}

语音指令源文件voiceControl.c

#include "inputCommand.h"

int voiceInit(struct InputCommand* voice ,char * ipAddress,char *port)       //语音控制初始化
{
	int fd;                                                                 //串口打开文件描述符
	if((fd = serialOpen(voice->deviceName,9600)) == -1){                   //打开串口
			exit(-1);
	}
	voice->fd = fd;                                                        //将文件描述符赋值到语音控制结构体中的fd
	return fd;
}

int voiceGetCommand(struct InputCommand* voice)                              //语音获取命令函数
{
	int nread = 0;
	memset(voice->command,'\0',sizeof(voice->command));                     //这里将结构体中的命令清除是防止上一次的命令遗留,影响下一次命令
	nread = read(voice->fd,voice->command,sizeof(voice->command));          //读取命令
	return nread;
}


struct InputCommand voiceControl = {
	.commandName = "voiceControl",             //命令名
	.command = {'\0'},                         //调用getCommand函数获取的指令存放于这
	.deviceName = "/dev/ttyAMA0",		       //调用open打开串口是需要用的串口文件路径
	.init = voiceInit,                         //串口初始化函数
	.getCommand = voiceGetCommand,             //获取命令函数
	.log = {'\0'},                             //命令记录
	.next = NULL
};


struct InputCommand *addVoiceIntoCommandLink(struct InputCommand *head)       //将命令放入到指令工厂中
{
	if(head == NULL){
		head = &voiceControl;
	}else{
		voiceControl.next = head;
		head = &voiceControl;
	}
}


主函数main.c

#include "controlDevice.h"
#include "inputCommand.h"
#include <string.h>
struct Devices *deviceHead = NULL;      //因为函数中会用到这些变量,而且使用的是多线程,尽量避免传参,因此使用全局变量
struct InputCommand *commandHead = NULL;
struct InputCommand *socketHandler = NULL;
int c_fd;

struct Devices *findDeviceInLink(char *name,struct Devices *head)     //根据设备名从设备工厂链表中找到对应设备
{
	if(head == NULL){
		printf("the link is empty\n");
		return NULL;
	}
	while(head != NULL){
		if(strcmp(head->deviceName,name)==0){
			return head;
		}
		head = head->next;
	}

	return NULL;
}

struct InputCommand *findCommandDeviceInLink(char *name,struct InputCommand *head)    //从命令工厂中找到对应命令
{
	if(head == NULL){
		printf("the link is empty\n");
		return NULL;
	}
	while(head != NULL){
		if(strcmp(head->commandName,name)==0){
			return head;
		}
		head = head->next;
	}

	return NULL;
}

void controlDeviceDoCommand(char *command)   //语音线程和socket线程在接收到客户端的命令的时候调用这个函数,会对设备进行操作
{
	if(strstr(command,"bath") != NULL){       //判断是否为bathroom的命令
			struct Devices *BathRoom = findDeviceInLink("bathRoomLight",deviceHead);    //在设备工厂链表中找到该设备
			BathRoom->deviceinit(BathRoom->pinNum);   //初始化设备
			if(strstr(command,"open") != NULL){       //判断获得的指令是不是开
					BathRoom->open(BathRoom->pinNum);    //如果是开就调用设备open函数指针
			}else if(strstr(command,"close") != NULL){  
					BathRoom->close(BathRoom->pinNum);  //如果是关则调用设备close函数指针
			}
	}else if(strstr(command,"bed") != NULL){   //bedroom
			struct Devices *BedRoom = findDeviceInLink("bedRoomLight",deviceHead);
			BedRoom->deviceinit(BedRoom->pinNum);   //init  device;
			if(strstr(command,"open") != NULL){       //open command
					BedRoom->open(BedRoom->pinNum);
			}else if(strstr(command,"close") != NULL){  //close  command
					BedRoom->close(BedRoom->pinNum);
			}
	}else if(strstr(command,"lock") != NULL){   //bedroom
			struct Devices *Lock = findDeviceInLink("lock",deviceHead);
			Lock->deviceinit(Lock->pinNum);   //init  device;
			if(strstr(command,"open") != NULL){       //open command
					Lock->open(Lock->pinNum);
			}else if(strstr(command,"close") != NULL){  //close  command
					Lock->close(Lock->pinNum);
			}
	}else{                                 //如果收到的指令并没有对应的设备,则打印信息
		printf("no such device\n");
	}
}

void *voice_threadFunc(void *data)				//语音线程函数
{
	 struct InputCommand *voiceHandler = NULL;
	 int nread;
	 voiceHandler = findCommandDeviceInLink("voiceControl",commandHead);   //在指令工厂中寻找语音控制指令
	 if(voiceHandler == NULL){							//判断是否找到命令结构体
			printf("find voiceHandler error\n");
			pthread_exit(NULL);
	 }else{
		if(voiceHandler->init(voiceHandler,NULL,NULL)<0){       //找到结构体后对命令进行初始化
			printf("voice init error\n");
			pthread_exit(NULL);									//初始化不成功退出线程
		}else{
			printf("%s init success\n",voiceHandler->commandName);  //调试信息
		}
		
		while(1){                                               //为了能够一直读信息使用死循环                  
				nread = voiceHandler->getCommand(voiceHandler);    //读取指令
				if(nread == 0){
					printf("no data from voice\n");
				}else{
					printf("get command:%s\n",voiceHandler->command);   //打印读取的信息用于调试
					controlDeviceDoCommand(voiceHandler->command);      //获取到命令后调用控制设备的函数
				}
			}
	 }
}

void * socketReadFunc(void *data)
{	
	while(1){                     //为了让socket能够持续读取加一个死循环
		memset(socketHandler->command,'\0',sizeof(socketHandler->command));
		int nread = read(c_fd,socketHandler->command,sizeof(socketHandler->command));
		if(nread == 0){
				printf("no data from voice\n");
		}else if(nread<0){
				printf("socket read error\n");
				pthread_exit(NULL);
		}else{
				printf("get command:%s\n",socketHandler->command);
				controlDeviceDoCommand(socketHandler->command);       //获取到客户端的命令之后调用处理函数

		}
	}
}

void *socket_threadFunc(void *data)             //socket网络命令控制线程
{
	struct sockaddr_in c_addr;               
	memset(&c_addr,0,sizeof(struct sockaddr_in));
	
	int  c_addr_len = sizeof(struct sockaddr_in);
	 
	pthread_t socketRead;                 //socket读信息线程
	 socketHandler = findCommandDeviceInLink("socketServer",commandHead);     //在指令链表中按照指令明寻找到对应的命令
	 if(socketHandler == NULL){                          //判断是否找到socket命令
	 	printf("find socketHandler error\n");	
		pthread_exit(NULL);
	 }else{
		printf("%s init success\n",socketHandler->commandName);    //打印读取信息进行调试
	 }

	 int s_fd = socketHandler->init(socketHandler,NULL,NULL);		//初始化命令
	 while(1){
		c_fd = accept(socketHandler->fd,(struct sockaddr *)&c_addr,&c_addr_len);    //获取完成三次握手的连接
		pthread_create(&socketRead,NULL,socketReadFunc,NULL);		//线程读取信息函数创建
	 }
	
}

int main()
{
	if(wiringPiSetup() == -1){
		return -1;
	}

	pthread_t voice_thread;
	pthread_t socket_thread;
	pthread_t camera_thread;
	pthread_t fire_thread;
	
   char name[128];
  
   deviceHead = addBathRoomIntoLink(deviceHead);              //添加设备进设备工厂链表
   deviceHead = addBedRoomIntoLink(deviceHead);
   deviceHead = addtoiletIntoLink(deviceHead);
   deviceHead = addlockIntoLink(deviceHead);
   deviceHead = addFireIntoLink(deviceHead);

   commandHead = addVoiceIntoCommandLink(commandHead);         //添加指令进指令工厂链表
   commandHead = addSocketIntoCommandLink(commandHead);

   pthread_create(&voice_thread,NULL,voice_threadFunc,NULL);          //创建语音控制线程
   pthread_create(&socket_thread,NULL,socket_threadFunc,NULL);		  //创建网络控制线程
   
   pthread_join(voice_thread,NULL);                                //线程等待
   pthread_join(socket_thread,NULL);
   
	return 0;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-15 15:59:28  更:2021-07-15 16:02:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/5 16:03:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码