智能家居项目

news2024/11/18 21:37:52

文章目录

    • 一、功能描述
    • 二、整体框架结构及编译
      • 2.1、整体框架
      • 2.2、编译Makefile
    • 三、工厂模式
    • 四、守护进程udev
    • 五、监控视频储存
    • 六、遇到的问题和未解决的问题
    • 七、代码流程图
      • 7.1、树莓派(8线程 ,2进程)
      • 7.2、手机APP
    • 八、内网穿透(实现远程访问)
    • 九、代码部分展示
      • 9.1、智能家居
        • 主函数
        • 线程(app显示)
        • 输入工厂(app输入)
        • 输出工厂(灯)
      • 9.2、驱动
        • fire
        • light
        • buzzer_lock
      • 9.3、守护进程
      • 9.4、安卓APP
    • 十、项目演示

一、功能描述

  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\nI'm quitting.\n", sig);
    	flag = false;
    }
    int judMent()
    {
    	FILE *file;
    	char buffer[128] = {'\0'};
    	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\n",buffer);
    }
    int main()
    {
    	time_t t;
    	int fd;
    	//创建守护进程
    	if(-1 == daemon(0, 0)){
    		printf("daemon error\n");
    		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.\n");
    		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\n");
	//exit(0);
}

int main()
{
	if(wiringPiSetup() == -1){
		printf("硬件初始化失败\n");
		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\n");
		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\n");
	}	
	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\n",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,'\0',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\n",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,'\0',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("\nget:%d\n",n_read);	//打印读取字节数
	}else{
		printf("client quit\n");
	}
	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 ......\n");
	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 !\n");
	return 0;
}

struct InputCommander socketContrl = {
	.commandName = "socketServer",
	.port = "9878",
	.ipAddress = "192.168.43.207",
	.command = {'\0'},
	.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 !\n");
	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\n");
		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\n");
		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\n");
    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\n");
    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\n");
    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\n");
    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\n");
        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\n");
        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\n");
        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\n");
        goto err_register_myswim;
    }

    printk("insmod driver light success\n");
    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\n");
}


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\n");//内核的打印函数
	return 0;
}

static int lock_open(struct inode * inode, struct file * filp)	//26	12
{
	printk("lock_open\n");//内核的打印函数
	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\n");
	//根据值操作io口
	if(userCmd == 1){
		t |= 1<<16;
		*GPSET0 = t;
		printk("set 1\n");
	}else if(userCmd == 0){
		t |= 1<<16;
		*GPCLR0 = t;
		printk("set 0\n");
	}else{
		printk("undo\n");
	}
	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\n");
	//根据值操作io口
	if(userCmd == 1){
		t |= 1<<12;
		*GPSET0 = t;
		printk("set 1\n");
	}else if(userCmd == 0){
		t |= 1<<12;
		*GPCLR0 = t;
		printk("set 0\n");
	}else{
		printk("undo\n");
	}
	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\n");

	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\n");
}

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\nI'm quitting.\n", sig);
	flag = false;
}
int judMent()
{
	FILE *file;
	char buffer[128] = {'\0'};
	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\n",buffer);
}
int main()
{
	time_t t;
	int fd;
	//创建守护进程
	if(-1 == daemon(0, 0)){
		printf("daemon error\n");
		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.\n");
		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. 关智能家居进程

智能家居

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/485289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习(五):基于KNN模型对高炉发电量进行回归预测分析

文章目录 专栏导读1、KNN简介2、KNN回归模型介绍3、KNN模型应用-高炉发电量预测3.1数据集信息:3.2属性信息3.3数据准备3.4数据标准化和划分数据集3.5寻找最佳K值3.6建立KNN模型预测4、完整代码专栏导读 ✍ 作者简介:i阿极,CSDN Python领域新星创作者,专注于分享python领域知…

二叉树的最近公共祖先

1题目 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它…

Redis做消息队列实现异步读写看这篇够了!

一、消息队列的简介 在企业的应用中&#xff0c;发送消息方和接收消息方&#xff0c;可以采取同步通信或异步通信。同步通信在实际的应用中效率不高。本文主要介绍异步通信&#xff0c;其中异步通信分为&#xff1a;第一&#xff0c;基于内存的jvm阻塞队列实现异步通信。这种方…

Android Studio Electric Eel 2022.1.1 Patch 2 导入opencv 4.5,并实现图片灰度变换和图片叠加

软件版本&#xff1a; Android Studio Electric Eel 2022.1.1 Patch 2 https://sourceforge.net/projects/opencvlibrary/files/4.5.0/opencv-4.5.0-android-sdk.zip/download 创建工程 with API23: 导入opencv sdk: File->New->Import Module 添加工程依赖&…

MySQL的概念、编译安装

一.数据库的基本概念 1、数据&#xff08;Data&#xff09; • 描述事物的符号记录 • 包括数字&#xff0c;文字&#xff0c;图形&#xff0c;图像&#xff0c;声音&#xff0c;档案记录等 • 以“记录”形式按统一的格式进行存储 2、表 • 将不同的记录组织在一起 • …

( 数组和矩阵) 287. 寻找重复数 ——【Leetcode每日一题】

❓287. 寻找重复数 难度&#xff1a;中等 给定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 &#xff0c;返回 这个重复的数 。 你…

基于樽海鞘算法的极限学习机(ELM)回归预测-附代码

基于樽海鞘算法的极限学习机(ELM)回归预测 文章目录 基于樽海鞘算法的极限学习机(ELM)回归预测1.极限学习机原理概述2.ELM学习算法3.回归问题数据处理4.基于樽海鞘算法优化的ELM5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;本文利用樽海鞘算法对极限学习机进行优化&…

TouchGFX界面开发 | 使用STM32CubeMX移植TouchGFX

本文基于STM32F429IGT6 RGB (800 * 480)硬件平台&#xff0c;详细记录了如何利用STM32CubeMX将TouchGFX移植到STM32F429IGT6&#xff0c;并驱动RGB屏幕。相关软件的安装&#xff0c;可参考TouchGFX软件安装一文 TouchGFX的应用框架如下图所示&#xff1a; 一、STM32CubeMX配…

JoJo‘s Incredible Adventures

题目&#xff1a; 题意解析&#xff1a; 这个题目是要求找出输入的字符串&#xff0c;&#xff0c;字符串的循环移位s由k右边是字符串Sn−k1...Sn&#xff0c;S1&#xff0c;S2...Sn−k。直到所有的字符&#xff0c;都循坏出现在字符串的开头&#xff0c;然后输入1形成的长方形…

MySQL之Adaptive Hash Index详解

前言 本文已收录在MySQL性能优化原理实战专栏&#xff0c;点击此处浏览更多优质内容。 目录 一、MySQL InnoDB是否支持哈希索引&#xff1f;1.1 InnoDB不支持Hash Index1.2 InnoDB支持Hash Index 二、Adaptive Hash Index的概念三、涉及Adaptive Hash Index的参数3.1 innodb_ad…

接口优化的策略

1.批处理 批量思想&#xff1a;批量操作数据库&#xff0c;这个很好理解&#xff0c;我们在循环插入场景的接口中&#xff0c;可以在批处理执行完成后一次性插入或更新数据库&#xff0c;避免多次IO。 //批量入库 batchInsert();List的安全操作有以下几种方式&#xff1a; 使…

DAY 50 LVS负载均衡器 NAT模式

群集概述 群集的含义 Cluster&#xff0c;集群、群集由多台主机构成&#xff0c;但对外只表现为一一个整体&#xff0c;只提供一-个访问入口(域名或IP地址)&#xff0c; 相当于一台大型计算机。 为什么使用群集 互联网应用中&#xff0c;随着站点对硬件性能、响应速度、服务…

Python学习日记(2)

有关数字类型&#xff0c;字符串&#xff0c;函数 目录 有关数字类型&#xff0c;字符串&#xff0c;函数 数字 字符串 索引操作 切片操作 单个字符编码 运算符 还有一些常用的内置函数 Python输入函数 输出函数print()语法 python的函数也能给默认值 Python是个脚…

C++系列一: C++简介

C入门简介 1. C语言的特点2. C编译器3. 第一个 C 程序4. 总结&#xff08;手稿版&#xff09; C 是一种高级编程语言&#xff0c;是C语言的扩展和改进版本&#xff0c;由Bjarne Stroustrup于1983年在贝尔实验室为了支持C语言中的面向对象编程而创建。C 既能够进行底层的系统编程…

全注解下的SpringIoc 续4-条件装配bean

Spring Boot默认启动时会加载bean&#xff0c;如果加载失败&#xff0c;则应用就会启动失败。但是部分场景下&#xff0c;我们希望某个bean只有满足一定的条件下&#xff0c;才允许Spring Boot加载&#xff0c;所以&#xff0c;这里就需要使用Conditional注解来协助我们达到这样…

二叉搜索树(BST)详解

文章目录 性质二叉搜索树的遍历遍历伪代码实现 二叉搜索树的查找伪代码实现 二叉搜索树最大元素伪代码实现 二叉搜索树最小元素伪代码实现 二叉搜索树的插入伪代码实现 二叉搜索树的删除删除叶子节点&#xff08;对应上面第一种情况&#xff09;&#xff1a;删除度为1的节点&am…

机械硬盘(HDD)与固态硬盘(SSD)

目录 机械硬盘&#xff08;HDD&#xff09; 最小组成单元是扇区 硬盘结构 硬盘工作原理 硬盘上的数据组织 硬盘指标 影响性能的因素 固态硬盘&#xff08;SSD&#xff09; 最小存储单元是Cell SSD的特点 SSD架构 NAND Flash 闪存介质 地址映射管理 FTL闪存转换层 机械硬盘&…

Python之模块和包(九)

1、模块 1、模块概述 模块是一个包含了定义的函数和变量等的文件。模块可以被程序引入&#xff0c;以使用该模块中的函数等功能。通俗讲&#xff1a;模块就好比是工具包&#xff0c;要想使用这个工具包中的工具(就好比函数)&#xff0c;就需要导入这个模块。 2、import 在P…

Redis分布式锁原理之实现秒杀抢优惠卷业务

Redis分布式锁原理之实现秒杀抢优惠卷业务 1. 实现秒杀下单2. 库存超卖问题分析2.1 乐观锁解决超卖问题 3. 优惠券秒杀-一人一单3.1 集群环境下的并发问题 4、分布式锁4.1 基本原理和实现方式对比4.2 Redis分布式锁的实现核心思路4.3 实现分布式锁版本一4.4 Redis分布式锁误删情…

【Java入门合集】第三章面向对象编程(上)

【Java入门合集】第三章面向对象编程&#xff08;上&#xff09; 博主&#xff1a;命运之光 专栏&#xff1a;JAVA入门 理解面向对象三大主要特征&#xff1b; 掌握类与对象的区别与使用&#xff1b; 掌握类中构造方法以及构造方法重载的概念及使用&#xff1b; 掌握包的定义、…