您现在的位置是:首页 >技术交流 >智能家居项目网站首页技术交流

智能家居项目

dz小伟 2023-07-10 04:00:03
简介智能家居项目

一、功能描述

  1. 硬件

    1. 树莓派3b
    2. 语音模块LD3320(3.3v)
    3. 安卓手机
    4. 火焰传感器(3.3v,低电平有效)
    5. 摄像头
    6. 4路继电器组(5v,低电平有效)
    7. 继电器(3.3v,低电平有效)
    8. 蜂鸣器(3.3或5v,低电平有效)
    9. 锁(外接电源)
    10. stm32
    11. oled屏(5v)
  2. 实现功能

    1. 整体:通过手机或语音输入,对外设的控制
      1. 对灯开关控制:支持回家模式,离家模式
      2. 人脸识别开锁
      3. 火灾报警,蜂鸣器响
      4. 本地oled屏,显示语音播报信息
      5. app端
        1. 显示监控画面+火灾报警+cpu温度+灯和锁状态
        2. 除视频监控外,可实现远程操作(内网穿透)
        3. 可控制摄像头功能(录像或监控画面显示)
      6. 守护进程,保证不退出
  3. 待实现功能
    1. 温度45开风扇降温,50强制杀死进程等待1分钟

二、整体框架结构及编译

2.1、整体框架

  1. 树莓派作为主控,整合处理输入输出工厂,实现对应功能

在这里插入图片描述

2.2、编译Makefile

  1. APPlication

    1. 主Makefile

      .PHONY:all clean
      
      ##生成:SmarHome(主进程),camera(监控进程),recording(录像进程),daemonsmarthome(守护进程),quit(退出)
      PWD_DIR    = $(shell pwd)
      OBJ_DIR    = $(PWD_DIR)/obj
      INPUT_DIR  = $(PWD_DIR)/input_factory
      OUTPUT_DIR = $(PWD_DIR)/output_factory
      MAIN_DIR   = $(PWD_DIR)/main
      THREAD_DIR = $(PWD_DIR)/thread
      CAEX_DIR   = $(PWD_DIR)/camera_and_exit
      INC_DIR    = $(PWD_DIR)/include
      INC_WIR	   = /home/xw/pi
      INC_CURL   = /home/xw/curl-7.71.1/_install/include
      SO_WIR	   = /home/xw/pi
      SO_CURL    = /home/xw/curl-7.71.1/_install/lib
      
      ##
      CC = arm-linux-gnueabihf-gcc
      CFLAG = -I $(INC_DIR) -I $(INC_WIR) -I $(INC_CURL) -L $(SO_WIR) -L $(SO_CURL) -lwiringPi -lcurl -pthread
      
      ##
      export	PWD_DIR OBJ_DIR INPUT_DIR OUTPUT_DIR MAIN_DIR THREAD_DIR INC_DIR INC_WIR INC_CURL SO_WIR SO_CURL CC CFLAG
      
      ##
      all:
      	make -C $(MAIN_DIR)
      	make -C $(INPUT_DIR)
      	make -C $(OUTPUT_DIR)
      	make -C $(THREAD_DIR)
      	make -C $(OBJ_DIR)
      	make -C $(OBJ_DIR)
      	make -C $(CAEX_DIR)
      ##
      clean:
      	$(RM) -rf $(OBJ_DIR)/*.o
      	$(RM) SmartHome
      	$(RM) quit
      	$(RM) camera
      	$(RM) recording
      	$(RM) daemonsmarthome
      
    2. 分文件夹Makefile

      .PHONY:all
      ##编译.o文件,放入obj文件夹
      OBJS += $(OBJ_DIR)/bathroomLight.o
      OBJS += $(OBJ_DIR)/livingroomLight.o
      OBJS += $(OBJ_DIR)/restaurantLight.o
      OBJS += $(OBJ_DIR)/swimmingLight.o
      
      OBJS += $(OBJ_DIR)/buzzer.o
      OBJS += $(OBJ_DIR)/cameraFaceRecognition.o
      OBJS += $(OBJ_DIR)/lock.o
      OBJS += $(OBJ_DIR)/oledUart.o
      OBJS += $(OBJ_DIR)/socketCpuFire.o
      
      all:$(OBJS)
      
      $(OBJ_DIR)/%.o:%.c
      	$(CC) -c $^ -o $@ $(CFLAG)
      
    3. obj文件下Makefile

      # $^    	代表所有的依赖文件
      # $@  		代表目标文件  
      # $<   		代表第一个依赖文件
      # = 		引用此变量时是最后一次赋值
      # :=		引用变量时使用当前变量值
      # ?=		引用变量时,如果变量已被初始化,则不重新赋值,否则重新赋值
      # .PHONY	
       
      TARGET := SmartHome
      
      OBJ = $(wildcard *.o)
       
      $(PWD_DIR)/SmartHome:$(OBJ)
      	$(CC) -o $@ $^ $(CFLAG)
      
  2. drive

    # 开发板的linux内核的源码树目录
    KERN_DIR = /home/xw/xiaowei/linux-rpi-4.14.y
    
    obj-m	+= buzzer_lock.o
    obj-m	+= light.o
    obj-m	+= fire.o
    
    all:
    	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 O=output/ -C $(KERN_DIR) M=`pwd` modules 
    .PHONY: clean	
    clean:
    
  3. 树状图

├── APPlication
│   ├── camera_and_exit
│   │   ├── daemonSmartHome.c
│   │   ├── mainMjpg.c
│   │   ├── mainRecording.c
│   │   ├── Makefile
│   │   └── quit.c
│   ├── include
│   │   ├── contrlDevices.h
│   │   ├── inputCommand.h
│   │   ├── smartHomeInit.h
│   │   └── smartHomeTread.h
│   ├── input_factory
│   │   ├── fireSensor.c
│   │   ├── Makefile
│   │   ├── socketContrl.c
│   │   └── voiceContrl.c
│   ├── main
│   │   ├── mainPro.c
│   │   ├── Makefile
│   │   └── smartHomeInit.c
│   ├── Makefile
│   ├── obj
│   │   └── Makefile
│   ├── output_factory
│   │   ├── bathroomLight.c
│   │   ├── buzzer.c
│   │   ├── cameraFaceRecognition.c
│   │   ├── livingroomLight.c
│   │   ├── lock.c
│   │   ├── Makefile
│   │   ├── oledUart.c
│   │   ├── restaurantLight.c
│   │   ├── socketCpuFire.c
│   │   └── swimmingLight.c
│   └── thread
│       ├── appControlThread.c
│       ├── appDisplayThread.c
│       ├── cameraMonitorThread.c
│       ├── cmdFaceHandleThread.c
│       ├── cmdLightHandleThread.c
│       ├── cmdOledHandleThread.c
│       ├── fireThread.c
│       ├── Makefile
│       └── voiceControlThread.c
└── drive
    ├── buzzer_lock.c
    ├── fire.c
    ├── light.c
    └── Makefile

三、工厂模式

设备输出,控制输入分离(好处是加入新设备方便)
在这里插入图片描述

  1. 设备输出工厂
      1. 客厅灯,livingroomLight
      2. 泳池灯,swimmingLight
      3. 浴室灯,bathroomLight
      4. 卧室灯,restaurantLight
    1. 蜂鸣器
      buzzer
    2. 串口oled(stm32)
      oledUart
    3. 人脸识别
      cameraFaceRecognition

      1. lock
    4. 手机APP(温度,火灾检测报警,灯锁状态)
      socketCpuFire
  2. 控制输入工厂
    1. 语音(串口)
      voice
    2. 手机APP(命令输入)
      socketServer
    3. 火灾检测
      fireSensor(着火0,正常1)

四、守护进程udev

守护智能家居进程(意外退出重启进程)

  1. 生存周期长[非必须],一般操作系统启动的时候就启动,关闭的时候关闭。

  2. 守护进程和终端无关联,也就是他们没有控制终端,所以当控制终端退出,也不会导致守护进程退出

  3. 守护进程是在后台运行,不会占着终端,终端可以执行其他命令

  4. 一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程

  5. 代码演示

    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <time.h>
    #include <stdio.h>
    #include <stdbool.h>
    static bool flag = true;
    void handler(int sig)
    {
    	printf("I got a signal %d
    I'm quitting.
    ", sig);
    	flag = false;
    }
    int judMent()
    {
    	FILE *file;
    	char buffer[128] = {''};
    	char *cmd = "ps -elf |grep SmartHome|grep -v grep";
    	file = popen(cmd, "r");
    	fgets(buffer, 128, file);
    	if(strstr(buffer, "SmartHome") != NULL){
    		return 0;
    	}else{
    		return -1;
    	}
    	printf("BUFFER:%s
    ",buffer);
    }
    int main()
    {
    	time_t t;
    	int fd;
    	//创建守护进程
    	if(-1 == daemon(0, 0)){
    		printf("daemon error
    ");
    		exit(1);
    	}
    	//设置信号处理函数
    	struct sigaction act;
    	act.sa_handler = handler;
    	sigemptyset(&act.sa_mask);
    	act.sa_flags = 0;
    	if(sigaction(SIGQUIT, &act, NULL)){
    		printf("sigaction error.
    ");
    		exit(0);
    	}
    	sleep(30);
    	//进程工作内容
    	while(flag){
    		if( judMent() == -1){
    			system("/home/pi/SmartHome &");
    		}
    		sleep(5);
    	}
    	return 0;
    }
    

五、监控视频储存

  1. 树莓派摄像头可以使用的2个库
    1. mjpg-streamer(网页显示监视画面)
    2. motion(提供网络摄像头的功能。当拍摄过程中画面发生变动时,Motion可以保存动作发生时的图片和视频)
  2. mjpg-streamer
    1. 开启网页显示监视画面
      1. 运行脚本:MjpgStreamer/mjpg-streamer/mjpg-streamer-experimental/start.sh
    2. 拍照
      1. raspistill -o image.jpg -t 1000
        1. -w, –width : 设置图像宽度 <尺寸>
          -h, –height : 设置图像高度 <尺寸>
          -q, –quality : 设置jpeg品质 <0到100>
          -t, –timeout : 拍照和关闭时的延时指定,未指定时默认是5s,毫秒为单位,1000便表示1秒
          -o, –output : 输出文件名 <文件名>,如果要写到stdout,使用-o -,如果不特别指定,图像文件不会被保存
          -rot:图像旋转角度,只支持 0、90、180、270 度
      2. wget http://192.168.43.207:8080/?action=snapshot -O ./a.jpg
    3. 视频储存
      1. raspivid -o b.h264 -t 10000 -w 1280 -h 720
        1. 录制一段十秒钟的名为b.h264的视频,且分辨率为1280x720
    4. 转mp4格式
      1. 安装:sudo apt-get install gpac
      2. MP4Box -fps 30 -add b.h264 b.mp4(每秒30帧)

六、遇到的问题和未解决的问题

  1. 驱动部分

    1. 改一个IO,另一个IO也变了(5可以单独改。改4, 5引脚也会变)
      1. 解决:加一个中间变量,直接赋值
      2. 因为寄存器值不一定,所以前面可能是乱的
        1. 例如寄存器的值是10101011101
        2. 要给01重新写入11,但是用|=的话,就会把本来其他值(乱值)也赋进去,造成乱了
  2. 应用层

    1. 给APP传cpu温度和火灾检测,传过去值乱了

      1. 解决:将错就错,增加把数16777216当1用
      2. 因为存在一个字节序问题,树莓派传过去的值大小端就反过来了
        1. app端,字符串可以成功解析出来,int类型解析不出来
        2. 解决方法,要么树莓派端把数反过来在发送,要么APP端将错就错
    2. bind: Address already in use(端口占用

      1. 服务器先退出,下一次启用服务器会端口占用

      2. 解决:

        if(setsockopt(s_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int)) < 0){	//解决bind,端口占用问题
            perror("setsockopt");
            goto Sfd;
        }
        
      3. SO_REUSEADDR可以让当前的端口立即重用

        1. 在服务端终止之后,会有一个TIME_WAIT的状态,再次打开会出现:bind的

          但是,服务器端可以尽可能的使用REUSEADDR(在绑定之前尽可能调用setsockopt来设置REUSEADDR)套接字选项,这样就可以使得不必等待TIME_WAIT状态就可以重启服务器了,也就是说:TIME_WAIT状态还是存在的,但是不影响我们重新启动服务器
          
        2. CTRL+C结束时,出现了TIME_WAIT,2-4分钟后释放

        3. CTRL+Z结束时,出现的是ESTABLISHED,永久,除非杀死进程

      4. 原文链接:https://blog.csdn.net/msdnwolaile/article/details/50743254

    3. 摄像头切换功能,需要杀死相应进程

      1. ps -ef | grep 进程名| grep -v grep | grep -v mca |awk ‘{print $2}’ | xargs kill -9

      2. ps -ef 表示查看全格式的全部进程。此处用-A也行:显示所有程序

        grep 是过滤

        grep -v 是反过滤,就是不要把它顾虑出来

        print 是打印

        $2 是第二列,也就是进程的ID

        awk 是对于文件中一行行的独处来执行操作 。

        awk ‘{print $2}’ 是把每一行的第二列(进程ID)打印出来

        | xargs 是将"|" 前面获取到的进程号通过空格分割,传递给kill -9,将这些进程全部杀死
        原文链接:https://blog.csdn.net/u011736532/article/details/109532185

  3. 安卓APP

    1. 内网穿透,粘包
      1. 服务器(树莓派)发送的数据会被当成一个数据发送(粘包)
      2. 客户端(安卓APP)接收数据出错
        1. 解决方法:因为是固定数据大小,所以直接拿到后按字节拆包
  4. 未解决

    1. 在人脸识别的时候,继续输入人脸识别命令会阻塞(在人脸识别完成之前app或uart端无法继续输入)
      1. 解决方法:fork一个进程去等待上一个命令处理完成(无法做到
      2. fork后,互斥锁被复制了2份,不在是同一个锁,导致线程间同步无法完成
    2. 外网访问,手机APP控制会有延迟(控制和灯锁显示都有,控制延迟小点)

七、代码流程图

7.1、树莓派(8线程 ,2进程)

各个资源关系图及流程图(橘黄色:输入输出设备,绿色:线程,蓝色:共享变量)

在这里插入图片描述

流程图

在这里插入图片描述

  1. 互斥锁(对应5个共享变量)
    1. fire火
      1. fire线程(写)+appDisplag线程(读)
    2. light灯
      1. voiceControlThread(写)+appControlThread(写)+cmdLightHandleThread(读)
    3. face人脸识别
      1. voiceControlThread(写)+appControlThread(写)+cmdLightHandleThread(读)
    4. oled
      1. voiceControlThread(写)+cmdFaceHandleThread(写)+cmdOledHandleThread(读)
    5. lightAPP(app显示灯锁状态)
      1. cmdLightHandleThread(写)+cmdFaceHandleThread(写)+appDisplayThread(读)
    6. monitor 摄像头切换
      1. appControlThread(写)+ cameraMonitorHandleThread(读)
  2. 条件(同步)
    1. light条件
      1. cmdLightHandleThread(等待)
      2. appControlThread + voiceControlThread(通知)
    2. 人脸识别条件
      1. cmdFaceHandleThread(等待)
      2. appControlThread + voiceControlThread(通知)
    3. oled条件
      1. cmdOledHandleThread(等待)
      2. cmdFaceHandleThread + voiceControlThread(通知)
    4. monitor 摄像头切换
      1. cameraMonitorHandleThread(等待)
      2. appControlThread(通知)
  3. 线程
    1. 语音
      1. 获取输入命令,解析命令,改变灯或人脸识别或oled共享变量,通知处理(灯或人脸识别或oled)
    2. app按钮线程
      1. 获取输入命令,解析命令,改变灯或人脸识别或摄像头切换共享变量,通知处理(灯或人脸识别或摄像头切换)
    3. 火灾线程
      1. 获取火灾信息,并改变改变共享变量值+驱动蜂鸣器
    4. app显示线程
      1. 获取温度+获取火灾及灯锁共享变量,网络发送显示在app中
    5. 灯命令处理
      1. 条件等待,处理命令,改变灯共享变量
    6. 人脸识别
      1. 条件等待,处理命令,改变oled或锁共享变量
    7. oled屏
      1. 条件等待,处理命令,获取oled共享变量
    8. 摄像头切换
      1. 条件等待,处理命令,获取切换共享变量
  4. 进程
    1. 视频监控
    2. 视频录像
    3. 守护进程

7.2、手机APP

  1. 3个画面
    1. 起始画面(3s倒计时)
    2. 宿舍灯画面
      1. 开关灯+锁
      2. 跳转按钮(跳到智能家居画面)
    3. 智能家居画面
      1. 显示监控画面+灯锁+火灾+cpu温度
      2. 切换监控或录像模式
      3. 回家+离家模式(wholeLight)
      4. 人脸识别按钮(成功开锁)
      5. 卧室+客厅+泳池+浴室灯控制按钮
  2. 3个端口
    1. 8080,监控视频流端口
    2. 9878,按钮控制
    3. 9879,cpu温度+火灾检测显示+灯锁

八、内网穿透(实现远程访问)

  1. 下载

    wget “https://down.oray.com/hsk/linux/phddns_5.2.0_amd64.deb” -O phddns_5.2.0_amd64.deb

  2. 安装

    dpkg -i phddns_5.2.0_amd64.deb

  3. 安装完会给,SN和密码admin,还有配置连接

    http://b.oray.com

  4. 登录上去使用SN登录,然后用已注册花生壳账号扫码激活

  5. 然后映射:TCP,外网域名,内网ip和端口号

在这里插入图片描述

  1. 点击诊断获取外网ip

  2. 可以使用外网ip访问服务器了

九、代码部分展示

9.1、智能家居

主函数

/*********************************************2023.4.27----2023.5.3******************************************************
  智能家居
  环境:树莓派(WiringPi,mjpg-streamer,openssl,libcurl)
  功能:
	1. 对灯开关控制
	2. 人脸识别开锁
	3. 火灾报警,蜂鸣器响
	4. 本地oled屏,显示语音播报信息
	5. app端显示监控画面+火灾报警+cpu温度+灯和锁状态
	6. app端除视频监控外,可实现远程操作(内网穿透)
	7. app端可控制摄像头功能(录像或监控画面显示)
	8. 守护进程,保证不退出
  使用:
	1.编译生成:SmarHome(主进程),camera(监控进程),recording(录像进程),daemonsmarthome(守护进程),quit(退出)
			3个驱动文件
	2.全部放在/home/pi文件夹下和jpg文件(别的文件夹可更改头文件xxxxx和守护进程文件),更改开机自启动
	3.结束服务器:先退出守护进程,在退智能家居进程
 ****************************************************************************************************************************/
#include "smartHomeInit.h"
#include "smartHomeTread.h"
#include <signal.h>
struct Devices *pdeviceHand = NULL;
struct InputCommander *pCommandHand = NULL;
pthread_cond_t lightCond;				//条件同步,灯+人脸识别+oledUart+摄像头切换
pthread_cond_t faceCond;
pthread_cond_t oledCond;
pthread_cond_t monitorCond;
int flag = 1;						//退出标志位


/******************************************************
  信号处理函数
  功能:退出主服务器进程
 *******************************************************/
void handler(int signum)
{
	switch(signum){
		case 2:
			flag = 0;					//退出标志位(影响各线程退出)
			commandExit(pCommandHand);		//输入,输出工厂退出		
			devicesExit(pdeviceHand);
			unloadDrives();					//卸载驱动,退出摄像头相关进程
			pthread_cond_signal(&lightCond);		//通知命令线程,防止他们阻塞无法正常退出
			pthread_cond_signal(&faceCond);		
			pthread_cond_signal(&oledCond);		
			pthread_cond_signal(&monitorCond);		
			break;
		default:break;
	}
	printf("main: signum quit
");
	//exit(0);
}

int main()
{
	if(wiringPiSetup() == -1){
		printf("硬件初始化失败
");
		goto Exit;
	}

	int pid;				//运行摄像头进程pid

	pthread_t fire;			//线程
	pthread_t appDisplay;
	pthread_t appControl;
	pthread_t voiceControl;
	pthread_t cmdLightHandle;
	pthread_t cmdFaceHandle;
	pthread_t cmdOledHandle;
	pthread_t cameraMonitorHandle;
	
	pthread_mutex_t fireNutex;	//互斥锁
	pthread_mutex_t lightNutex;			
	pthread_mutex_t faceNutex;		
	pthread_mutex_t oledNutex;		
	pthread_mutex_t lightAppNutex;	
	pthread_mutex_t monitorNutex;	

	struct Param arg = {					//线程传参用
		.pdeviceHand = &pdeviceHand,			//输入输出头节点
		.pCommandHand = &pCommandHand,
		.fireNutex = &fireNutex,				//互斥锁
		.lightNutex = &lightNutex,
		.faceNutex = &faceNutex,
		.oledNutex = &oledNutex,
		.lightAppNutex = &lightAppNutex,
		.monitorNutex = &monitorNutex,
		.lightCond = &lightCond,				//条件同步
		.faceCond = &faceCond,
		.oledCond = &oledCond,
		.monitorCond = &monitorCond,
		.fireData = 1,						//共享变量
		.lightData = 0,
		.faceData = 0,
		.oledData = 0,
		.lightApp = 69905,					//默认关闭(灯灭锁关),app显示灯和锁信息
		.monitorData = 0
	};	

	signal(SIGINT,handler);	//信号初始化

	installDrives();			//驱动安装

	//设备工厂链表建立
	pdeviceHand = addLivingroomLightToDevicdLink(pdeviceHand);			//客厅灯
	pdeviceHand = addSwimmingLightToDevicdLink(pdeviceHand);			//泳池灯
	pdeviceHand = addBathroomLightToDevicdLink(pdeviceHand);			//浴室灯
	pdeviceHand = addRestaurantLightToDevicdLink(pdeviceHand);			//卧室灯
	pdeviceHand = addLockDevicdLink(pdeviceHand);					//锁
	pdeviceHand = addBuzzerDevicdLink(pdeviceHand);					//蜂鸣器
	pdeviceHand = addOledUartDevicdLink(pdeviceHand);				//uart_oled+stm32
	pdeviceHand = addcameraFaceRecognitionDevicdLink(pdeviceHand); 		//人脸识别
	pdeviceHand = addSocketCpuFireDevicdLink(pdeviceHand);			//手机APP火灾+cpu温度显示

	//控制工厂链表建立
	pCommandHand = addSocketContrlToInputCommandLink(pCommandHand);	//socket服务器(命令输入)
	pCommandHand = addfireSensorToInputCommandLink(pCommandHand);   	//火灾检测
	pCommandHand = addVoiceContrlToInputCommandLink(pCommandHand);	//语音+串口(命令输入)

	//初始化设备和控制工厂
	devicesInit(pdeviceHand);		
	commandInit(pCommandHand);

	
	//开启摄像头进程
	pid = fork();	
	if(pid < 0){
		perror("fork error!");
		goto Pid;
	}else if(pid == 0){	//运行摄像头
		system(SMP_CAMERA);
		printf("camera quit
");
		exit(0);
	}else{							//父进程继续执行
		//条件初始化
		pthread_cond_init(&lightCond, NULL);						//灯条件
		pthread_cond_init(&faceCond, NULL);					//人脸识别条件
		pthread_cond_init(&oledCond, NULL);					//oled条件
		pthread_cond_init(&monitorCond, NULL);					//摄像头切换
		//互斥锁初始化
		pthread_mutex_init(&fireNutex, NULL);					//fire互斥锁
		pthread_mutex_init(&lightNutex, NULL);					//灯互斥锁
		pthread_mutex_init(&faceNutex, NULL);					//人脸识别互斥锁
		pthread_mutex_init(&oledNutex, NULL);					//oled互斥锁
		pthread_mutex_init(&lightAppNutex, NULL);					//app显示灯锁信息互斥锁
		pthread_mutex_init(&monitorNutex, NULL);					//摄像头切换

		//控制线程初始化
		pthread_create(&fire, NULL, fireThread, (void*)(&arg));				//火灾线程
		pthread_create(&appControl, NULL, appControlThread, (void*)(&arg));		//app按钮命令线程
		pthread_create(&voiceControl, NULL, voiceControlThread, (void*)(&arg));		//语音命令线程
		//外设线程初始化
		pthread_create(&appDisplay, NULL, appDisplayThread, (void*)(&arg));				//app显示线程
		pthread_create(&cmdLightHandle, NULL, cmdLightHandleThread, (void*)(&arg));			//灯命令处理线程
		pthread_create(&cmdFaceHandle, NULL, cmdFaceHandleThread, (void*)(&arg));			//人脸识别命令处理线程
		pthread_create(&cmdOledHandle, NULL, cmdOledHandleThread, (void*)(&arg));			//oled处理线程
		pthread_create(&cameraMonitorHandle, NULL, cameraMonitorHandleThread, (void*)(&arg));	//摄像头切换


		pthread_join(fire, NULL);
		pthread_join(appControl, NULL);
		pthread_join(voiceControl, NULL);
		pthread_join(appDisplay, NULL);
		pthread_join(cmdLightHandle, NULL);
		pthread_join(cmdFaceHandle, NULL);
		pthread_join(cmdOledHandle, NULL);
		pthread_join(cameraMonitorHandle, NULL);

		pthread_mutex_destroy(&fireNutex);
		pthread_mutex_destroy(&lightNutex);
		pthread_mutex_destroy(&faceNutex);
		pthread_mutex_destroy(&oledNutex);
		pthread_mutex_destroy(&lightAppNutex);
		pthread_mutex_destroy(&monitorNutex);

		pthread_cond_destroy(&lightCond);
		pthread_cond_destroy(&faceCond);
		pthread_cond_destroy(&oledCond);
		pthread_cond_destroy(&monitorCond);
		printf("main: quit
");
	}	
	return 0;
Pid:
	commandExit(pCommandHand);			
	devicesExit(pdeviceHand);	
Exit:
	return -1;
}

线程(app显示)

#include "smartHomeTread.h"

/***************************************************************
获取cpu温度
参数:
	char型指针,执行完毕指针里放入温度值
返回值:
	成功返回0
****************************************************************/
int cpuRead(char *data)
{
	char a[1024];
	char *p, *q;	

	//获取温度
	//printf("%s
",a);//temp=37.6'C
	memset(a,0,sizeof(a));
	FILE *f = popen("vcgencmd measure_temp","r");
	fread(a,1024,1,f);
	pclose(f);

	//分割字符串
	p = strtok(a,"=");
	p = strtok(NULL,"=");
	q = strtok(p,"'");

	memset(data,'',sizeof(data));
	strcpy(data, q);
	return 0;
}

void *appDisplayThread(void *arg)
{
	struct Param *a = (struct Param*)arg;
	struct Devices *dev = NULL;
	dev = findDeviceByName("socketCpuFire", *(a->pdeviceHand));		//获取app显示链表

	while(flag){
		cpuRead(dev->dataes);		//获取温度
		//printf("cpu = %s
",dev->dataes);	
		pthread_mutex_lock(a->lightAppNutex);		//获取灯+锁信息	
		dev->pinNum = a->lightApp;
		pthread_mutex_unlock(a->lightAppNutex);

		pthread_mutex_lock(a->fireNutex);		//互斥锁				
		dev->write(a->fireData, dev);		//app显示温度,火灾,灯锁情况
		pthread_mutex_unlock(a->fireNutex);
	}
}

输入工厂(app输入)

#include "inputCommand.h"     
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/******************************************************
  获取网络输入
  等待连接,输入退出
  返回值:
  	1.正常返回读取到的字节数
  	2.错误返回-1
 *******************************************************/
int socketGetCommand(struct InputCommander *socketMes)
{
	int c_fd;
	int n_read = 0;
	int jieshou;

	struct sockaddr_in c_addr;
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	jieshou = sizeof(struct sockaddr_in);
	c_fd = accept(socketMes->fd,(struct sockaddr *)&c_addr,&jieshou);	//等待连接
	if(c_fd < 0)
	{
		perror("accept");
		goto Exit;
	}

	memset(socketMes->command,'',sizeof(socketMes->command));
	n_read = read(c_fd,socketMes->command,sizeof(socketMes->command));	//读取数据
	if(n_read == -1){
		perror("read");
		goto Cfd; 
	}else if(n_read > 0){
		printf("
get:%d
",n_read);	//打印读取字节数
	}else{
		printf("client quit
");
	}
	close(c_fd); //关闭连接
	return n_read;

Cfd:
	close(c_fd);
Exit:
	return -1;
}

/******************************************************
  初始化
  	socket创建,添加信息,监听
  返回值:
  	1.正常返回网络描述符
  	2.错误返回-1
 *******************************************************/
int socketInit(struct InputCommander *socketMes)
{
	int s_fd;
	int opt=1;
	struct sockaddr_in addr;
	memset(&addr,0,sizeof(struct sockaddr_in));

	//添加端口+IP
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(socketMes->port));
	inet_aton(socketMes->ipAddress,&addr.sin_addr);

	s_fd = socket(AF_INET,SOCK_STREAM,0);	//创建套接字
	if(s_fd < 0)
	{
		perror("socket");
		goto Exit;
	}

	if(setsockopt(s_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int)) < 0){	//解决bind,端口占用问题
		perror("setsockopt");
		goto Sfd;
	}

	if(bind(s_fd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in)) < 0)	//添加信息
	{	
		perror("bind");
		goto Sfd;
	}
	if(listen(s_fd,10) < 0)		//监听网络
	{
		perror("listen");
		goto Sfd;
	}

	printf("socket Server listening ......
");
	socketMes->fd = s_fd;

	return s_fd;
Sfd:
	close(s_fd);
Exit:
	return -1;
}

/******************************************************
  退出
  关闭网络描述符
 *******************************************************/
int socketExit(struct InputCommander *socketMes)
{
	close(socketMes->fd);
	printf("socket quit exit !
");
	return 0;
}

struct InputCommander socketContrl = {
	.commandName = "socketServer",
	.port = "9878",
	.ipAddress = "192.168.43.207",
	.command = {''},
	.commandesInit = socketInit,
	.commandesExit = socketExit,
	.getCommand = socketGetCommand,
	.fd = 0,
	.next = NULL
};

/******************************************************
  加入链表
  头插法
 *******************************************************/
struct InputCommander* addSocketContrlToInputCommandLink(struct InputCommander *phead)
{
	if(phead == NULL){
		return &socketContrl;
	}else{
		socketContrl.next = phead;
		phead = &socketContrl;
	}
}

输出工厂(灯)

#include "contrlDevices.h"

/******************************************************
更改输出
 *******************************************************/
int livingroomLightWrite(int cmd, struct Devices *dev)
{
	char devName[128] = {'0'};
	if(cmd){
		sprintf(devName, "echo 1 > %s", dev->devName);
	}else{
		sprintf(devName, "echo 0 > %s", dev->devName);

	}
	system(devName);
}

int livingroomLightInit(struct Devices *dev)
{
	char cmd[128] = {'0'};
	sprintf(cmd, "sudo chmod 777 %s", dev->devName);
	system(cmd);
}

int livingroomLightExit(struct Devices *dev)
{
	char cmd[128] = {'0'};
	sprintf(cmd, "sudo chmod 000 %s", dev->devName);
	system(cmd);
	printf("livingroomLight quit exit !
");
	return 0;
}

struct Devices livingroomLight = {
	.deviceName = "livingroomLight",
	.devName = "/sys/class/leds/myliving/brightness",
	.pinNum = 22,
	.write = livingroomLightWrite,
	.deviceInit = livingroomLightInit,
	.deviceExit = livingroomLightExit,
	.next = NULL
};

struct Devices* addLivingroomLightToDevicdLink(struct Devices *phead)
{
	if(phead == NULL){
		return &livingroomLight;
	}else{
		livingroomLight.next = phead;
		phead = &livingroomLight;
	}
}

9.2、驱动

fire

#include <linux/input.h> 
#include <linux/module.h> 
#include <linux/init.h>
#include <asm/irq.h> 
#include <asm/io.h>

#include <linux/gpio.h>
#include <linux/platform_device.h>

#define GPIO_FIRE	5  //io口号,可以使用命令gpio read
static struct input_dev *input;	//指针,后面动态分配内存
static struct timer_list timer;	//定时器结构体
static int history;		//记录上次io值

//定时器处理函数
static void fire_timer_handler(unsigned long data) 
{ 
	int flag;
	flag = gpio_get_value(GPIO_FIRE);
	if(flag != history){	//和上次值比较
		if(flag){
			input_report_key(input, KEY_OK, 1);	//上报应用层
		}else{
			input_report_key(input, KEY_OK, 0);
		}
		history = flag;
	}
	input_sync(input);	//同步包

	mod_timer(&timer, jiffies + HZ/100);	//更新定时器
}

//匹配成功注册设备
static int fire_button_probe(struct platform_device *pdev)
{
	
	int ret;
	
	ret = gpio_request(GPIO_FIRE, "GPIO_0_FIRE");	//申请io
	if(ret){
		printk("gpio GPIO_FIRE fail");
		ret = -1;
		goto err_gpio;
	}	
	gpio_direction_input(GPIO_FIRE);	//输入模式
	history = gpio_get_value(GPIO_FIRE);
		
	input = input_allocate_device();	//输入设备结构体,实例化
	if (!input) 
	{ 
		printk(KERN_ERR "fire.c: Not enough memory
");
		ret = -ENOMEM;
		goto err_gpio_request;
	}

	//填充结构体
	set_bit(EV_KEY, input->evbit);	//按键类型
	set_bit(KEY_OK, input->keybit); //哪一个按键
	//注册
	ret = input_register_device(input);
	if (ret) 
	{ 
		printk(KERN_ERR "fire.c: Failed to register device
");
		goto err_input_allocate;
	}
	
	//定时器
	init_timer(&timer);
	timer.function = fire_timer_handler;	//处理函数
	timer.expires = jiffies + (HZ/100);	//定时时间
	add_timer(&timer);		//启动
	return 0;
//倒影式处理错误
err_input_allocate:
	input_free_device(input);
err_gpio_request:
	gpio_free(GPIO_FIRE);
err_gpio:
	return ret;
}

//注销
static int fire_button_remove(struct platform_device *dev)
{
	del_timer(&timer);
	input_unregister_device(input); 
	input_free_device(input);
	gpio_free(GPIO_FIRE);	
	return 0;
}

//设备
static struct platform_device fire_input_device = {
	.name		= "fire",
	.id	= -1,
};

//驱动
static struct platform_driver fire_input_driver = {
	.probe = fire_button_probe,
	.remove		= fire_button_remove,
	.driver		= {
		.name		= "fire",
		.owner		= THIS_MODULE,
	},
};

//平台总线
static int __init button_init(void) 
{
	platform_device_register(&fire_input_device);
	return platform_driver_register(&fire_input_driver);
}
static void __exit button_exit(void) 
{ 
	platform_driver_unregister(&fire_input_driver);
	platform_device_unregister(&fire_input_device);
}

module_init(button_init); 
module_exit(button_exit); 

MODULE_LICENSE("GPL v2");	// 描述模块的许可证
MODULE_AUTHOR("ZangXiaowei");     // 描述模块的作者
MODULE_DESCRIPTION("fire");       // 描述模块的介绍信息
MODULE_ALIAS("fire gpio5");                    // 描述模块的别名信息

light

#include <linux/module.h>       // module_init  module_exit
#include <linux/init.h>         // __init   __exit
#include <linux/fs.h>
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/gpio.h>

#define GPIO6_LED_LIVI          6
#define GPIO13_LED_REST      13
#define GPIO19_LED_SWIM     19
#define GPIO26_LED_BATH      26


#define SMART_HOME_LED_LOW    0           
#define SMART_HOME_LED_HIGH      1           

//申请gpio数组,io号,输出并设置为1
static struct gpio led_gpios[] = {
    { GPIO6_LED_LIVI, GPIOF_OUT_INIT_HIGH, "living room" },
    { GPIO13_LED_REST, GPIOF_OUT_INIT_HIGH, "rest room" },
    { GPIO19_LED_SWIM, GPIOF_OUT_INIT_HIGH, "swimming room" },
    { GPIO26_LED_BATH, GPIOF_OUT_INIT_HIGH, "bath room" },
};

static struct led_classdev myliving;          // 定义结构体变量
static struct led_classdev myrest;          // 定义结构体变量
static struct led_classdev myswim;          // 定义结构体变量
static struct led_classdev mybath;          // 定义结构体变量

// 这个函数就是要去完成具体的硬件读写任务的
static void smart_home_myliving_set(struct led_classdev *led_cdev, enum led_brightness value)
{
    printk(KERN_INFO "smart_home_myliving_set
");
    if (value == 1){
        gpio_set_value(GPIO6_LED_LIVI, SMART_HOME_LED_HIGH);
    }else{
        gpio_set_value(GPIO6_LED_LIVI, SMART_HOME_LED_LOW);
    }
}
static void smart_home_myrest_set(struct led_classdev *led_cdev, enum led_brightness value)
{
    printk(KERN_INFO "smart_home_myrest_set
");
    if (value == 1){
        gpio_set_value(GPIO13_LED_REST, SMART_HOME_LED_HIGH);
    }else{
        gpio_set_value(GPIO13_LED_REST, SMART_HOME_LED_LOW);
    }
}
static void smart_home_myswim_set(struct led_classdev *led_cdev, enum led_brightness value)
{
    printk(KERN_INFO "smart_home_myswim_set
");
    if (value == 1){
        gpio_set_value(GPIO19_LED_SWIM, SMART_HOME_LED_HIGH);
    }else{
        gpio_set_value(GPIO19_LED_SWIM, SMART_HOME_LED_LOW);
    }
}
static void smart_home_mybath_set(struct led_classdev *led_cdev, enum led_brightness value)
{
    printk(KERN_INFO "smart_home_mybath_set
");
    if (value == 1){
        gpio_set_value(GPIO26_LED_BATH, SMART_HOME_LED_HIGH);
    }else{
        gpio_set_value(GPIO26_LED_BATH, SMART_HOME_LED_LOW);
    }
}

static int __init smart_home_led_init(void)
{
    int ret;
    // 用户insmod安装驱动模块时会调用该函数
    // 该函数的主要任务就是去使用led驱动框架提供的设备注册函数来注册一个设备

    // 在这里去申请驱动用到的各种资源,当前驱动中就是GPIO资源
    ret = gpio_request_array(led_gpios, ARRAY_SIZE(led_gpios));
    if (ret)
        goto err_gpio;

    //注册驱动,属性文件/sys/class/leds
    myliving.name = "myliving";
    myliving.brightness = 0;
    myliving.brightness_set = smart_home_myliving_set;
    ret = led_classdev_register(NULL, &myliving);
    if (ret < 0) {
        printk(KERN_ERR "led_classdev_register myliving failed
");
        goto err_gpio_request;
    }

    myrest.name = "myrest";
    myrest.brightness = 0;
    myrest.brightness_set = smart_home_myrest_set;
    ret = led_classdev_register(NULL, &myrest);
    if (ret < 0) {
        printk(KERN_ERR "led_classdev_register myrest failed
");
        goto err_register_myliving;
    }

    myswim.name = "myswim";
    myswim.brightness = 0;
    myswim.brightness_set = smart_home_myswim_set;
    ret = led_classdev_register(NULL, &myswim);
    if (ret < 0) {
        printk(KERN_ERR "led_classdev_register myswim failed
");
        goto err_register_myrest;
    }

    mybath.name = "mybath";
    mybath.brightness = 0;
    mybath.brightness_set = smart_home_mybath_set;
    ret = led_classdev_register(NULL, &mybath);
    if (ret < 0) {
        printk(KERN_ERR "led_classdev_register mybath failed
");
        goto err_register_myswim;
    }

    printk("insmod driver light success
");
    return 0;

err_register_myswim:
    led_classdev_unregister(&myswim);
err_register_myrest:
    led_classdev_unregister(&myrest);   
err_register_myliving:
    led_classdev_unregister(&myliving);
err_gpio_request:
    gpio_free_array(led_gpios, ARRAY_SIZE(led_gpios));
err_gpio:
    return -1;
}

static void __exit smart_home_led_exit(void)
{
    led_classdev_unregister(&mybath);
    led_classdev_unregister(&myswim);
    led_classdev_unregister(&myrest);   
    led_classdev_unregister(&myliving);

    gpio_free_array(led_gpios, ARRAY_SIZE(led_gpios));
    printk("insmod driver liget exit
");
}


module_init(smart_home_led_init);
module_exit(smart_home_led_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL v2");   // 描述模块的许可证
MODULE_AUTHOR("ZangXiaowei");     // 描述模块的作者
MODULE_DESCRIPTION("Smart Home led driver");       // 描述模块的介绍信息
MODULE_ALIAS("Smart Home led");                    // 描述模块的别名信息

buzzer_lock

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <asm/io.h>
#include <linux/cdev.h>

static struct cdev *buzzer;//注册设备
static struct cdev *lock;
static struct class *mybuzzer_lock_class;//类
static struct device *buzzer_class_dev;//设备
static struct device *lock_class_dev;

static dev_t devno;
static int major = 231;
static int minor = 0;

#define FSEL 0x3f200000	//设置模式
#define SET0 0x3f20001C	//1
#define CLR0 0x3f200028	//0

typedef struct GPFSEL{
	volatile unsigned int GPFSEL0;	//0-9
	volatile unsigned int GPFSEL1;	//10-19
	volatile unsigned int GPFSEL2;	//20-29
}gpfsel;
gpfsel *pgpfsel = NULL;


volatile unsigned int* GPSET0  = NULL;
volatile unsigned int* GPCLR0  = NULL;

static int buzzer_open(struct inode * inode, struct file * filp)	//27	16
{
	printk("buzzer_open
");//内核的打印函数
	return 0;
}

static int lock_open(struct inode * inode, struct file * filp)	//26	12
{
	printk("lock_open
");//内核的打印函数
	return 0;
}
static ssize_t buzzer_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{	
	int userCmd;
	unsigned int t = 0;
	//获取上层write值
	if (copy_from_user(&userCmd,buf,sizeof(int)))
		return -EFAULT;
	printk("get buzzer
");
	//根据值操作io口
	if(userCmd == 1){
		t |= 1<<16;
		*GPSET0 = t;
		printk("set 1
");
	}else if(userCmd == 0){
		t |= 1<<16;
		*GPCLR0 = t;
		printk("set 0
");
	}else{
		printk("undo
");
	}
	return 0;
}

static ssize_t lock_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{	
	int userCmd;
	unsigned int t = 0;
	//获取上层write值
	if (copy_from_user(&userCmd,buf,sizeof(int)))
		return -EFAULT;
	printk("get lock
");
	//根据值操作io口
	if(userCmd == 1){
		t |= 1<<12;
		*GPSET0 = t;
		printk("set 1
");
	}else if(userCmd == 0){
		t |= 1<<12;
		*GPCLR0 = t;
		printk("set 0
");
	}else{
		printk("undo
");
	}
	return 0;
}

//在内核源码查找struct file_operations看结构体成员,添加用到的函数
static const struct file_operations buzzer_fops = {
    .owner = THIS_MODULE,
    .write = buzzer_write,//函数指针
    .open  = buzzer_open
};

static const struct file_operations lock_fops = {
    .owner = THIS_MODULE,
    .write = lock_write,//函数指针
    .open  = lock_open
};

static int __init buzzer_lock_init(void)//驱动入口
{
	int ret;

	devno = MKDEV(major,minor);//制作合并主、次设备号

	ret = alloc_chrdev_region(&devno, 0, 2,"buzzer_lock");	//分配主次设备号
	if (ret)
		return -ENOMEM;

	buzzer = cdev_alloc();		//实例化火灾结构体变量
	if (!buzzer) {
		ret = -ENOMEM;
		goto err_buzzer_region;
	}
	cdev_init(buzzer,&buzzer_fops);	//初始化cdev结构体
	ret = cdev_add(buzzer, MKDEV(major,0), 2); 	//注册设备驱动
	if (ret)
		goto err_buzzer_cdev;

	lock = cdev_alloc();		//实例化锁结构体变量
	if (!lock) {
		ret = -ENOMEM;
		goto err_lock_region;
	}
	cdev_init(lock, &lock_fops);		//初始化cdev结构体
	ret = cdev_add(lock, MKDEV(major,1), 2);//注册设备驱动
	if (ret)
		goto err_lock_cdev;

	mybuzzer_lock_class = class_create(THIS_MODULE,"mybuzzer_lock");//创建类
	if (IS_ERR(mybuzzer_lock_class)) {
		goto err_lock_cdev;
	}
	buzzer_class_dev = device_create(mybuzzer_lock_class,NULL,MKDEV(major,0),NULL, "buzzer");//创建设备文件
	if (IS_ERR(buzzer_class_dev)) {
		ret = PTR_ERR(buzzer_class_dev);
		goto err_class;
	}
	lock_class_dev = device_create(mybuzzer_lock_class,NULL,MKDEV(major,1),NULL, "clock");//创建设备文件
	if (IS_ERR(lock_class_dev)) {
		ret = PTR_ERR(lock_class_dev);
		goto err_buzzer_class;
	}


	pgpfsel = ioremap(FSEL, sizeof(gpfsel));	//虚拟地址映射
	if (pgpfsel == NULL) {
		ret = -ENOMEM;
		goto err_clock_class;
	}
	GPSET0  = (volatile unsigned int *)ioremap(SET0,4);
	if (GPSET0 == NULL) {
		ret = -ENOMEM;
		goto err_pgpfsel_ioremap;
	}
	GPCLR0  = (volatile unsigned int *)ioremap(CLR0,4);
	if (GPSET0 == NULL) {
		ret = -ENOMEM;
		goto err_GPSET0_ioremap;
	}

	//设置io模式
	pgpfsel->GPFSEL1 &= ~(6<<18);
	pgpfsel->GPFSEL1 |= 1<<18;
	pgpfsel->GPFSEL1 &= ~(6<<6);
	pgpfsel->GPFSEL1 |= 1<<6;
	printk("insmod driver buzzer_lock success
");

	return 0;

err_GPSET0_ioremap:
	iounmap(GPSET0);
err_pgpfsel_ioremap:
	iounmap(pgpfsel);
err_clock_class:
	device_destroy(mybuzzer_lock_class,MKDEV(major,1));//销毁设备
err_buzzer_class:
	device_destroy(mybuzzer_lock_class,MKDEV(major,0));//销毁设备
err_class:
	class_destroy(mybuzzer_lock_class);//销毁类
err_lock_cdev:
	cdev_del(lock);
err_lock_region:
err_buzzer_cdev:
	cdev_del(buzzer);
err_buzzer_region:
	unregister_chrdev_region(devno,2);
	return ret;
}

static void __exit  buzzer_lock_exit(void)
{
	iounmap(GPCLR0);
	iounmap(GPSET0);
	iounmap(pgpfsel);

	device_destroy(mybuzzer_lock_class,MKDEV(major,0));//销毁设备
	device_destroy(mybuzzer_lock_class,MKDEV(major,1));//销毁设备
	class_destroy(mybuzzer_lock_class);//销毁类

	cdev_del(lock);	//销毁设备驱动
	cdev_del(buzzer);	
	unregister_chrdev_region(devno,2);	//销毁主次设备号
	printk("insmod driver buzzer_lock exit
");
}

module_init(buzzer_lock_init);//入口,是个宏
module_exit(buzzer_lock_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL v2");	// 描述模块的许可证
MODULE_AUTHOR("ZhangXiaowei");              // 描述模块的作者
MODULE_DESCRIPTION("buzzer_lock output");  // 描述模块的介绍信息
MODULE_ALIAS("alias buzzer_lock");          // 描述模块的别名信息

9.3、守护进程

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
static bool flag = true;
void handler(int sig)
{
	printf("I got a signal %d
I'm quitting.
", sig);
	flag = false;
}
int judMent()
{
	FILE *file;
	char buffer[128] = {''};
	char *cmd = "ps -elf |grep SmartHome|grep -v grep";
	file = popen(cmd, "r");
	fgets(buffer, 128, file);
	if(strstr(buffer, "SmartHome") != NULL){
		return 0;
	}else{
		return -1;
	}
	printf("BUFFER:%s
",buffer);
}
int main()
{
	time_t t;
	int fd;
	//创建守护进程
	if(-1 == daemon(0, 0)){
		printf("daemon error
");
		exit(1);
	}
	//设置信号处理函数
	struct sigaction act;
	act.sa_handler = handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if(sigaction(SIGQUIT, &act, NULL)){
		printf("sigaction error.
");
		exit(0);
	}
	sleep(30);
	//进程工作内容
	while(flag){
		if( judMent() == -1){
			system("/home/pi/SmartHome &");
		}
		sleep(5);
	}
	return 0;
}

9.4、安卓APP

package com.example.xwd;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.Window;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.net.xwd.Netutills;

public class MainActivity extends Activity {

	public TextView textView;//温度显示控件
	public TextView textView2;//火灾显示控件
	public ImageView ImageButton1; //卧室灯显示控件
	public ImageView ImageButton2; //客厅灯显示控件
	public ImageView ImageButton3; //泳池灯显示控件
	public ImageView ImageButton4; //浴室灯显示控件
	public ImageView ImageButton5; //锁显示控件
	public Handler h;		//改变画面用的类
	boolean qh = false;		//切换按键标志位
    @Override
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        
        textView = (TextView) findViewById(R.id.cpu);	//与画面的控件连接
        textView2 = (TextView) findViewById(R.id.huo);
        ImageButton1 = (ImageView) findViewById(R.id.tp4301);
        ImageButton2 = (ImageView) findViewById(R.id.tp4302);
        ImageButton3 = (ImageView) findViewById(R.id.tp4303);
        ImageButton4 = (ImageView) findViewById(R.id.tp4304);
        ImageButton5 = (ImageView) findViewById(R.id.ssssss);
        
        		
        WebView wb = (WebView) findViewById(R.id.web1);	//监控画面控件
        wb.loadUrl("http://192.168.43.207:8080/?action=stream");
        wb.setWebViewClient(new WebViewClient());
        shujv();	//每10ms获取树莓派信息
        
        //更新画面信息
        h = new Handler(){	//UI主线程的电话,接到电话,去处理其他线程无法处理的事件
        	@Override
        	public void handleMessage(Message msg) {//区分事件的类型
        		// TODO Auto-generated method stub
        		super.handleMessage(msg);
        		Bundle b = msg.getData();
        		String string = b.getString("msg");	//取出msg里的字符串,温度
        		int a = b.getInt("data");			//火灾信息
        		int a1 = b.getInt("data1");			//灯锁信息

        		System.out.println("1");
        		textView.setText(string + "℃");//Ui线程改变控件
        		System.out.println("2");
        		//ImageButton1.setImageResource(R.drawable.qwqw);
        		
        		if(a == 0){
        			textView2.setText("着火");
        			textView2.setTextColor(Color.RED);
        		}
        		if(a == 16777216){
        			textView2.setText("正常");
        			textView2.setTextColor(Color.WHITE);
        		}
        		if((a1 & 0x01000000) == 0x01000000){// 01 00 00 00,卧室灯
        			ImageButton1.setImageResource(R.drawable.wqwq);
        			System.out.println("111");
        		}
        		if((a1 & 0x01000000) == 0){
        			ImageButton1.setImageResource(R.drawable.qwqw);
        			System.out.println("222");
        		}
        		if((a1 & 0x10000000) == 0x10000000){//10 00 00 00,客厅
        			ImageButton2.setImageResource(R.drawable.wqwq);
        		}
        		if((a1 & 0x10000000) == 0){
        			ImageButton2.setImageResource(R.drawable.qwqw);
        		}
        		if((a1 & 0x00010000) == 0x00010000){//00 01 00 00,泳池
        			ImageButton3.setImageResource(R.drawable.wqwq);
        		}
        		if((a1 & 0x00010000) == 0){
        			ImageButton3.setImageResource(R.drawable.qwqw);
        		}
        		if((a1 & 0x00100000) == 0x00100000){//00 10 00 00,浴室
        			ImageButton4.setImageResource(R.drawable.wqwq);
        		}
        		if((a1 & 0x00100000) == 0){
        			ImageButton4.setImageResource(R.drawable.qwqw);
        		}
        		if((a1 & 0x00000100) == 0x00000100){	//00 00 01 00,锁
        			ImageButton5.setImageResource(R.drawable.qqww);
        		}
        		if((a1 & 0x00000100) == 0){
        			ImageButton5.setImageResource(R.drawable.wwqq);
        		}
        		
        	}
        };
    }
    
  //每10ms获取树莓派温度+火灾信息
    public void shujv(){
		new Thread(new Runnable() {
			public void run() {
				// TODO Auto-generated method stub
				byte[] data = new byte[128];//字符串,温度信息
				InputStream in = null;	//输入流
				Socket client = null;	//客户端类
				Message msg;	//传输到画面的类,里面放3个信息(温度+火灾+灯锁)
				int len;	
				String str;	//温度
				Bundle b;
				int number;	//火灾
				int number1;	//灯锁

				while(true){
					try {
						Thread.sleep(10);
					} catch (InterruptedException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					try {
						try {
							client = new Socket("115.236.153.174", 12065);//连接服务器
							//client = new Socket("192.168.43.18", 8989);
							//client = new Socket("192.168.43.207", 9879);
							in = client.getInputStream();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						if(client != null){	//没连接上服务器
							b = new Bundle();
							len = in.read(data);	//读取字节流,字节数组
							System.out.println("3");
							if(len == 12){	//读取字节数不对,错误不执行,防止抛出异常
					
									str = new String(data,0,len-8);//取前4字节,转str温度
									System.out.println("4");
									//转int,火灾和灯锁
									number = (data[7]&0xff) | ((data[6]&0xff)<<8) | ((data[5]&0xff)<<16) | ((data[4]&0xff)<<24);
									System.out.println("5");
									number1 = (data[11]&0xff) | ((data[10]&0xff)<<8) | ((data[9]&0xff)<<16) | ((data[8]&0xff)<<24);
									
									//可以放很多类型,string int 等
									b.putString("msg", str); 		//温度
									b.putInt("data", number);		//火灾
									b.putInt("data1", number1);		//灯锁

									msg = new Message();
									msg.setData(b);	//只能放Bundle
									h.sendMessage(msg); //发送到Handler
									System.out.println("6");
			
				
							}
							in.close();
							client.close();
						}
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				
				}
			}
		}).start();
    }

    //按钮
    public void bottonBeCliecked(View v){

    	switch(v.getId()){
        	case R.id.bu1:
        		Netutills.sendMessageHandler("wholeLight on");
        		break;
        	case R.id.bu2:
        		Netutills.sendMessageHandler("wholeLight off");
        		break;
        	case R.id.bu8:
        		Netutills.sendMessageHandler("cameraFaceRecognition");
        		break;
        	case R.id.bu3:
        		Netutills.sendMessageHandler("restaurantLight on");
        		break;
        	case R.id.bu4:
        		Netutills.sendMessageHandler("livingroomLight on");
        		break;
        	case R.id.bu5:
        		Netutills.sendMessageHandler("swimmingLight on");
        		break;
        	case R.id.bu6:
        		Netutills.sendMessageHandler("bathroomLight on");
        		break;
        	case R.id.bu9:
        		Netutills.sendMessageHandler("restaurantLight off");
        		break;
        	case R.id.bu10:
        		Netutills.sendMessageHandler("livingroomLight off");
        		break;
        	case R.id.bu11:
        		Netutills.sendMessageHandler("swimmingLight off");
        		break;
        	case R.id.bu12:
        		Netutills.sendMessageHandler("bathroomLight off");
        		break;
        	case R.id.buqh:
        		if(qh == false)
        			qh = true;
        		else{
        			qh = false;
        		}
        		if(qh == true){
        			Netutills.sendMessageHandler("monitorRecording");
        		}else{
        			Netutills.sendMessageHandler("monitorCamera");
        		}
        		break;

    	}
    }
}

十、项目演示

  1. 开机自启动
  2. 内网访问
    1. 灯+人脸识别开锁+火灾报警展示
    2. 摄像头切换展示
  3. 外网访问
    1. 灯+人脸识别开锁+火灾报警展示
    2. 灯锁显示展示
  4. 退出服务器展示
    1. 关守护进程
    2. 关智能家居进程

智能家居

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。