智能家居(下)

122 阅读12分钟

4月「掘金·日新计划」第30天

七、代码流程图

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

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

image.png

流程图

image.png

  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 "down.oray.com/hsk/linux/p…" -O phddns_5.2.0_amd64.deb

  2. 安装

    dpkg -i phddns_5.2.0_amd64.deb

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

    b.oray.com

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

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

  6. 点击诊断获取外网ip

  7. 可以使用外网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. 关智能家居进程