文章目录
- 一、功能描述
- 二、整体框架结构及编译
- 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
- 十、项目演示
一、功能描述
-
硬件
- 树莓派3b
- 语音模块LD3320(3.3v)
- 安卓手机
- 火焰传感器(3.3v,低电平有效)
- 摄像头
- 4路继电器组(5v,低电平有效)
- 继电器(3.3v,低电平有效)
- 蜂鸣器(3.3或5v,低电平有效)
- 锁(外接电源)
- stm32
- oled屏(5v)
-
实现功能
- 整体:通过手机或语音输入,对外设的控制
- 对灯开关控制:支持回家模式,离家模式
- 人脸识别开锁
- 火灾报警,蜂鸣器响
- 本地oled屏,显示语音播报信息
- app端
- 显示监控画面+火灾报警+cpu温度+灯和锁状态
- 除视频监控外,可实现远程操作(内网穿透)
- 可控制摄像头功能(录像或监控画面显示)
- 守护进程,保证不退出
- 整体:通过手机或语音输入,对外设的控制
-
待实现功能
1. 温度45开风扇降温,50强制杀死进程等待1分钟
二、整体框架结构及编译
2.1、整体框架
- 树莓派作为主控,整合处理输入输出工厂,实现对应功能
2.2、编译Makefile
-
APPlication
-
主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
-
分文件夹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)
-
obj文件下Makefile
# $^ 代表所有的依赖文件 # $@ 代表目标文件 # $< 代表第一个依赖文件 # = 引用此变量时是最后一次赋值 # := 引用变量时使用当前变量值 # ?= 引用变量时,如果变量已被初始化,则不重新赋值,否则重新赋值 # .PHONY TARGET := SmartHome OBJ = $(wildcard *.o) $(PWD_DIR)/SmartHome:$(OBJ) $(CC) -o $@ $^ $(CFLAG)
-
-
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:
-
树状图
├── 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
三、工厂模式
设备输出,控制输入分离(好处是加入新设备方便)
- 设备输出工厂
- 灯
- 客厅灯,livingroomLight
- 泳池灯,swimmingLight
- 浴室灯,bathroomLight
- 卧室灯,restaurantLight
- 蜂鸣器
buzzer - 串口oled(stm32)
oledUart - 人脸识别
cameraFaceRecognition- 锁
lock
- 锁
- 手机APP(温度,火灾检测报警,灯锁状态)
socketCpuFire
- 灯
- 控制输入工厂
- 语音(串口)
voice - 手机APP(命令输入)
socketServer - 火灾检测
fireSensor(着火0,正常1)
- 语音(串口)
四、守护进程udev
守护智能家居进程(意外退出重启进程)
-
生存周期长[非必须],一般操作系统启动的时候就启动,关闭的时候关闭。
-
守护进程和终端无关联,也就是他们没有控制终端,所以当控制终端退出,也不会导致守护进程退出
-
守护进程是在后台运行,不会占着终端,终端可以执行其他命令
-
一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程
-
代码演示
#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; }
五、监控视频储存
- 树莓派摄像头可以使用的2个库
- mjpg-streamer(网页显示监视画面)
- motion(提供网络摄像头的功能。当拍摄过程中画面发生变动时,Motion可以保存动作发生时的图片和视频)
- mjpg-streamer
- 开启网页显示监视画面
- 运行脚本:MjpgStreamer/mjpg-streamer/mjpg-streamer-experimental/start.sh
- 拍照
- raspistill -o image.jpg -t 1000
- -w, –width : 设置图像宽度 <尺寸>
-h, –height : 设置图像高度 <尺寸>
-q, –quality : 设置jpeg品质 <0到100>
-t, –timeout : 拍照和关闭时的延时指定,未指定时默认是5s,毫秒为单位,1000便表示1秒
-o, –output : 输出文件名 <文件名>,如果要写到stdout,使用-o -,如果不特别指定,图像文件不会被保存
-rot:图像旋转角度,只支持 0、90、180、270 度
- -w, –width : 设置图像宽度 <尺寸>
- wget http://192.168.43.207:8080/?action=snapshot -O ./a.jpg
- raspistill -o image.jpg -t 1000
- 视频储存
- raspivid -o b.h264 -t 10000 -w 1280 -h 720
- 录制一段十秒钟的名为b.h264的视频,且分辨率为1280x720
- raspivid -o b.h264 -t 10000 -w 1280 -h 720
- 转mp4格式
- 安装:sudo apt-get install gpac
- MP4Box -fps 30 -add b.h264 b.mp4(每秒30帧)
- 开启网页显示监视画面
六、遇到的问题和未解决的问题
-
驱动部分
- 改一个IO,另一个IO也变了(5可以单独改。改4, 5引脚也会变)
- 解决:加一个中间变量,直接赋值
- 因为寄存器值不一定,所以前面可能是乱的
- 例如寄存器的值是10101011101
- 要给01重新写入11,但是用|=的话,就会把本来其他值(乱值)也赋进去,造成乱了
- 改一个IO,另一个IO也变了(5可以单独改。改4, 5引脚也会变)
-
应用层
-
给APP传cpu温度和火灾检测,传过去值乱了
- 解决:将错就错,增加把数16777216当1用
- 因为存在一个字节序问题,树莓派传过去的值大小端就反过来了
- app端,字符串可以成功解析出来,int类型解析不出来
- 解决方法,要么树莓派端把数反过来在发送,要么APP端将错就错
-
bind: Address already in use(端口占用)
-
服务器先退出,下一次启用服务器会端口占用
-
解决:
if(setsockopt(s_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int)) < 0){ //解决bind,端口占用问题 perror("setsockopt"); goto Sfd; }
-
SO_REUSEADDR可以让当前的端口立即重用
-
在服务端终止之后,会有一个TIME_WAIT的状态,再次打开会出现:bind的
但是,服务器端可以尽可能的使用REUSEADDR(在绑定之前尽可能调用setsockopt来设置REUSEADDR)套接字选项,这样就可以使得不必等待TIME_WAIT状态就可以重启服务器了,也就是说:TIME_WAIT状态还是存在的,但是不影响我们重新启动服务器
-
CTRL+C结束时,出现了TIME_WAIT,2-4分钟后释放
-
CTRL+Z结束时,出现的是ESTABLISHED,永久,除非杀死进程
-
-
原文链接:https://blog.csdn.net/msdnwolaile/article/details/50743254
-
-
摄像头切换功能,需要杀死相应进程
-
ps -ef | grep 进程名| grep -v grep | grep -v mca |awk ‘{print $2}’ | xargs kill -9
-
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
-
-
-
安卓APP
- 内网穿透,粘包
- 服务器(树莓派)发送的数据会被当成一个数据发送(粘包)
- 客户端(安卓APP)接收数据出错
- 解决方法:因为是固定数据大小,所以直接拿到后按字节拆包
- 内网穿透,粘包
-
未解决
- 在人脸识别的时候,继续输入人脸识别命令会阻塞(在人脸识别完成之前app或uart端无法继续输入)
- 解决方法:fork一个进程去等待上一个命令处理完成(无法做到)
- fork后,互斥锁被复制了2份,不在是同一个锁,导致线程间同步无法完成
- 外网访问,手机APP控制会有延迟(控制和灯锁显示都有,控制延迟小点)
- 在人脸识别的时候,继续输入人脸识别命令会阻塞(在人脸识别完成之前app或uart端无法继续输入)
七、代码流程图
7.1、树莓派(8线程 ,2进程)
各个资源关系图及流程图(橘黄色:输入输出设备,绿色:线程,蓝色:共享变量)
流程图
- 互斥锁(对应5个共享变量)
- fire火
- fire线程(写)+appDisplag线程(读)
- light灯
- voiceControlThread(写)+appControlThread(写)+cmdLightHandleThread(读)
- face人脸识别
- voiceControlThread(写)+appControlThread(写)+cmdLightHandleThread(读)
- oled
- voiceControlThread(写)+cmdFaceHandleThread(写)+cmdOledHandleThread(读)
- lightAPP(app显示灯锁状态)
- cmdLightHandleThread(写)+cmdFaceHandleThread(写)+appDisplayThread(读)
- monitor 摄像头切换
- appControlThread(写)+ cameraMonitorHandleThread(读)
- fire火
- 条件(同步)
- light条件
- cmdLightHandleThread(等待)
- appControlThread + voiceControlThread(通知)
- 人脸识别条件
- cmdFaceHandleThread(等待)
- appControlThread + voiceControlThread(通知)
- oled条件
- cmdOledHandleThread(等待)
- cmdFaceHandleThread + voiceControlThread(通知)
- monitor 摄像头切换
- cameraMonitorHandleThread(等待)
- appControlThread(通知)
- light条件
- 线程
- 语音
- 获取输入命令,解析命令,改变灯或人脸识别或oled共享变量,通知处理(灯或人脸识别或oled)
- app按钮线程
- 获取输入命令,解析命令,改变灯或人脸识别或摄像头切换共享变量,通知处理(灯或人脸识别或摄像头切换)
- 火灾线程
- 获取火灾信息,并改变改变共享变量值+驱动蜂鸣器
- app显示线程
- 获取温度+获取火灾及灯锁共享变量,网络发送显示在app中
- 灯命令处理
- 条件等待,处理命令,改变灯共享变量
- 人脸识别
- 条件等待,处理命令,改变oled或锁共享变量
- oled屏
- 条件等待,处理命令,获取oled共享变量
- 摄像头切换
- 条件等待,处理命令,获取切换共享变量
- 语音
- 进程
- 视频监控
- 视频录像
- 守护进程
7.2、手机APP
- 3个画面
- 起始画面(3s倒计时)
- 宿舍灯画面
- 开关灯+锁
- 跳转按钮(跳到智能家居画面)
- 智能家居画面
- 显示监控画面+灯锁+火灾+cpu温度
- 切换监控或录像模式
- 回家+离家模式(wholeLight)
- 人脸识别按钮(成功开锁)
- 卧室+客厅+泳池+浴室灯控制按钮
- 3个端口
- 8080,监控视频流端口
- 9878,按钮控制
- 9879,cpu温度+火灾检测显示+灯锁
八、内网穿透(实现远程访问)
-
下载
wget “https://down.oray.com/hsk/linux/phddns_5.2.0_amd64.deb” -O phddns_5.2.0_amd64.deb
-
安装
dpkg -i phddns_5.2.0_amd64.deb
-
安装完会给,SN和密码admin,还有配置连接
http://b.oray.com
-
登录上去使用SN登录,然后用已注册花生壳账号扫码激活
-
然后映射:TCP,外网域名,内网ip和端口号
-
点击诊断获取外网ip
-
可以使用外网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;
}
}
}
十、项目演示
- 开机自启动
- 内网访问
- 灯+人脸识别开锁+火灾报警展示
- 摄像头切换展示
- 外网访问
- 灯+人脸识别开锁+火灾报警展示
- 灯锁显示展示
- 退出服务器展示
- 关守护进程
- 关智能家居进程
智能家居