[ARM+Linux]智能家居项目

本文介绍了一个采用简单工厂设计模式的智能家居项目,通过树莓派、语音模块和Wemos D1实现设备控制。项目利用多线程实现指令控制,通过socket和语音模块进行交互,包括灯光、蜂鸣器、火焰传感器和摄像头等设备的管理。在实施过程中,作者遇到了段错误问题,并通过调试解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本次智能家居项目采用的是简单工厂设计模式简化main函数代码,便于阅读。所有控制以及外设的设备都做成一个个对象,分别将命令控制的连成一个控制链表,外设设备做成一个外设设备的链表,这样做是为了方便以后功能模块的添加。其中为了能分别做好控制,我们采用多线程来实现。

指令工厂:

socket、语音控制(由于树莓派只有一个串口,所以我将Wemos D1作为服务器,树莓派作为客户端通过socket连接,实现指令控制)

设备工厂:

   树莓派、语音模块、wemos D1、继电器组、摄像头、火焰传感器、电磁锁、蜂鸣器。

树莓派通过串口连接各语音模块LD2330,检测语音的识别结果,分析语音识别的结果来对家电设备进行控制。树莓派摄像头拍摄到人脸之后通过HTTPS访问翔云平台的人脸识别方案对比照片的base64编码来进行人脸识别开锁。由于语音模块占用了树莓派唯一的串口位,为了保留语音控制,所以,我将Wemos D1做成了服务器,让树莓派变为客户端使用socket和wemos进行连接。wemos通过读取树莓派作为客户端发送过来的数据来控制家电设备,完成了对于树莓派的接口拓展,以便控制更多的设备。

每个模块实现的步骤:

将所有设备和指令模块分别通过链表连接起来,以便于随意添加设备,指令工厂发出指令(通过语音模块----->串口\socket------->网络通信),会创建并进入相应的线程,线程中通过比较设备名来判断发出的指令是什么以及要控制哪个设备,再通过寻找设备名函数来定位设备,从而调用相应封装好的函数来实现相应的功能(我们所写的代码都是在用户态,需要在虚拟文件系统中实现系统调用sys_open,通过sys_call透过虚拟文件系统调动内核从而实现对硬件的控制) 

 mainPro.c


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
 
#include <sys/types.h>          
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
 
#include "contrlDevices.h"
#include "inputCommand.h"
 
pthread_t voiceThread; 	//注意:定义线程不使用指针以免空指针异常
pthread_t socketThread;	//注意:不建议线程传参(链表头)所以定为全局变量
pthread_t fireThread;
pthread_t cameraThread;
pthread_t clientWemosThread;
 
struct InputCommander *pCommandHead = NULL;  
struct Devices		  *pdeviceHead = NULL;
struct InputCommander *socketHandler = NULL; 
struct InputCommander *clientHandler = NULL;
 
pthread_mutex_t mutex;  //定义互斥量(锁)
//pthread_cond_t  cond;   //条件 
 
int c_fd;			    //注意:涉及到多线程不要轻易的去传参
 
//摄像头相关,改变返回值命名,因为C语言中没有这样的返回值
#define true 1
#define false 0
typedef unsigned int bool;
char buf[1024] = {'\0'};
 
struct Devices *findDeviceByName(char *name,struct Devices *phead)    //查询设备
{
	if(phead == NULL){
		return NULL;
	}
	while(phead != NULL){
		if(strstr(phead->deviceName,name) != NULL){
			return phead;
		}
		phead = phead->next;
	}
	
	return NULL;
}
 
struct InputCommander *findCommandByName(char *name,struct InputCommander *phead)  //查询控制
{
	if(phead == NULL){
		return NULL;
	}
	while(phead != NULL){
		if(strcmp(phead->commandName,name) == 0){
			return phead;
		}
		phead = phead->next;
	}
	return NULL;
}
 
 
void *voice_thread(void *arg)    //语音线程
{
	int i = 0;
	int nread;
	struct InputCommander *voiceHandler = NULL;
	struct Devices *deviceTmp = NULL;
	
	voiceHandler = findCommandByName("voice",pCommandHead);    //在控制工厂找到语音模块
	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);
		} //语音初始化完成
 
pthread_mutex_lock(&mutex);    //加锁【有待研究】   
                               //为什么加这个锁呢,我的想法是在语音读取一级指令的时候,为了避免其它线程对于 紧接着读取二级指令的干扰
 
		while(1){			
			memset(voiceHandler->comand,'\0',sizeof(voiceHandler->comand));
		
			nread = voiceHandler->getCommand(voiceHandler);	    //读取来自语音模块的串口数据
			if(nread == 0){
				//printf("noData from voice,please say again\n");
			}else if(strstr(voiceHandler->comand,"all") != NULL){
				printf("close all light\n");
				deviceTmp = findDeviceByName("yu",pdeviceHead);
				deviceTmp->close(deviceTmp->pinNum);
				deviceTmp = findDeviceByName("ke",pdeviceHead);
				deviceTmp->close(deviceTmp->pinNum);
				deviceTmp = findDeviceByName("chu",pdeviceHead);
				deviceTmp->close(deviceTmp->pinNum);
				deviceTmp = findDeviceByName("shui",pdeviceHead);
				deviceTmp->close(deviceTmp->pinNum);
			}
			else{ 
				deviceTmp = findDeviceByName(voiceHandler->comand,pdeviceHead);
				if(deviceTmp == NULL){
					printf("findDeviceByName error\n");
				}
				else{
					printf("findDevice = %s\n",deviceTmp->deviceName);
					deviceTmp->deviceInit(deviceTmp->pinNum);
					deviceTmp->open(deviceTmp->pinNum);
				}
			}			
			
		}
		
pthread_mutex_unlock(&mutex);    //解锁
 
	}
}
 
 
 
size_t readData1( void *ptr, size_t size, size_t nmemb, void *stream)    //cameraContrlPostUrl函数里的回调函数
{
	memset(buf,'\0',1024);
	strncpy(buf,ptr,1024);
 
}
 
char *getBase641(char *picture)    //获取照片64流
{
	int fd;
	int len;
	char cmd[256] = {'\0'};	
 
	sprintf(cmd,"base64 %s > pictureBase64File",picture);    //将照片的64流导入到一个文件中
	system(cmd);
 
	fd = open("./pictureBase64File",O_RDWR);    //打开有照片64流的文件
	len = lseek(fd,0,SEEK_END);                 //计算文件的大小(巧用lseek光标的移动)
	lseek(fd,0,SEEK_SET);                       //光标回到首位置 
 
	char *readBuf = (char *)malloc(len);	//如果不是用动态,当我们将这个readBuf指针返回去,则会段错误
	read(fd,readBuf,len);			//因为,readBuf是局部变量,静态内存,程序一运行完毕,里面的内容被释放(C语言基础)
	close(fd);
	system("rm ./pictureBase64File");
 
	return readBuf;
}
 
bool cameraContrlPostUrl()    //通过libcurl跨平台网络协议访问翔云人工智能平台人脸识别放案
{
	CURL *curl;
	CURLcode res;
	char *postString;
	struct Devices *deviceTmp = NULL;
 
    //翔云人工智能平台人脸识别方案所需要的信息参数
	char *img1;
	char *img2;
	char *key = "9ST43UmYcwtn8ZgWtHaXag";
	char *secret = "f87c4f5c3bb94c25b728848b54c637a9";
	int typeId = 21;
	char *format = "xml";
 
	
	chdir("/home/pi/mjpg-streamer/mjpg-streamer-experimental");    //改变当前文件路径
 	system("./start.sh");    //运行摄像头
	chdir("/home/pi/yu/smartHome5_camera+face");
	system("wget  http://172.20.10.2:8080/?action=snapshot -O ./visitor.jpg"); 	//截图实时视频的照片
 
	img1 = getBase641("./visitor.jpg");    //获取照片64流
	img2 = getBase641("./tx.jpg");//与之前已经上传好的照片比对
 
	int len = strlen(key)+strlen(secret)+strlen(img1)+strlen(img2)+124;
	postString = (char *)malloc(strlen(key)+strlen(secret)+strlen(img1)+strlen(img2)+124);
	memset(postString,'\0',len);
 
	sprintf(postString,"&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s",  //指定post内容,用$符号拼接,下面函数要发送到翔云去对比识别
			img1,img2,key,secret,typeId,format);
 
	system("rm visitor.jpg");
 
	curl = curl_easy_init();
 
	if (curl)
	{
		curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件
		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);    // ָ指定post内容,用$符号拼接
		cu
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值