4月「掘金·日新计划」第30天
七、代码流程图
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(读)
-
-
条件(同步)
-
light条件
- cmdLightHandleThread(等待)
- appControlThread + voiceControlThread(通知)
-
人脸识别条件
- cmdFaceHandleThread(等待)
- appControlThread + voiceControlThread(通知)
-
oled条件
- cmdOledHandleThread(等待)
- cmdFaceHandleThread + voiceControlThread(通知)
-
monitor 摄像头切换
- cameraMonitorHandleThread(等待)
- appControlThread(通知)
-
-
线程
-
语音
- 获取输入命令,解析命令,改变灯或人脸识别或oled共享变量,通知处理(灯或人脸识别或oled)
-
app按钮线程
- 获取输入命令,解析命令,改变灯或人脸识别或摄像头切换共享变量,通知处理(灯或人脸识别或摄像头切换)
-
火灾线程
- 获取火灾信息,并改变改变共享变量值+驱动蜂鸣器
-
app显示线程
- 获取温度+获取火灾及灯锁共享变量,网络发送显示在app中
-
灯命令处理
- 条件等待,处理命令,改变灯共享变量
-
人脸识别
- 条件等待,处理命令,改变oled或锁共享变量
-
oled屏
- 条件等待,处理命令,获取oled共享变量
-
摄像头切换
- 条件等待,处理命令,获取切换共享变量
-
-
进程
- 视频监控
- 视频录像
- 守护进程
7.2、手机APP
-
3个画面
-
起始画面(3s倒计时)
-
宿舍灯画面
- 开关灯+锁
- 跳转按钮(跳到智能家居画面)
-
智能家居画面
- 显示监控画面+灯锁+火灾+cpu温度
- 切换监控或录像模式
- 回家+离家模式(wholeLight)
- 人脸识别按钮(成功开锁)
- 卧室+客厅+泳池+浴室灯控制按钮
-
-
3个端口
- 8080,监控视频流端口
- 9878,按钮控制
- 9879,cpu温度+火灾检测显示+灯锁
八、内网穿透(实现远程访问)
-
下载
wget "down.oray.com/hsk/linux/p…" -O phddns_5.2.0_amd64.deb
-
安装
dpkg -i phddns_5.2.0_amd64.deb
-
安装完会给,SN和密码admin,还有配置连接
-
登录上去使用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;
}
}
}
十、项目演示
-
开机自启动
-
内网访问
- 灯+人脸识别开锁+火灾报警展示
- 摄像头切换展示
-
外网访问
- 灯+人脸识别开锁+火灾报警展示
- 灯锁显示展示
-
退出服务器展示
- 关守护进程
- 关智能家居进程