智能家居完结 -- 整体设计

智能家居 (1) ——智能家居整体功能框架 本项目实现的功能是通过手机app、生物识别等对门锁、灯光、空调、窗帘、电视、插座等设备进行控制。开发支持回家模式、睡觉模式等应用场景。 项目架构采用简单工厂模式来设计,将TCP服务器、语音识别、人脸识别设计成链表的每个节点,形成控制工厂,灯光、门锁、窗帘、空调等也设计成链表的每个节点,形成设备端工厂。基于这种架构添加新功能的时候,只要添加一个链表节点文件就可以了,稳定性、拓展性做得不错。 阅读详情

系统框图

前情提要:

智能家居1 -- 实现语音模块-优快云博客

智能家居2 -- 实现网络控制模块-优快云博客

智能家居3 - 实现烟雾报警模块-优快云博客

智能家居4 -- 添加接收消息的初步处理-优快云博客

智能家居5 - 实现处理线程-优快云博客

智能家居6 -- 配置 ini文件优化设备添加-优快云博客

实现主要程序: 

main.c 

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <wiringPi.h>

#include "control.h"
#include "mq_queue.h"
#include "voice_interface.h"
#include "socket_interface.h"
#include "smoke_interface.h"
#include "receive_interface.h"
#include "global.h"

// msg_queue_create



int main() {
    pthread_t thread_id;
    struct control *control_phead = NULL;
    struct control *pointer = NULL;
    ctrl_info_t *ctrl_info = NULL;
    ctrl_info = (ctrl_info_t *)malloc(sizeof(ctrl_info_t));
    ctrl_info->ctrl_phead = NULL;
    ctrl_info->mqd = -1;

    int node_num = 0; // 统计节点数
    if(-1 == wiringPiSetup())// 初始化 wiringPi 库
    {
        perror("wiringPi Init");
        return -1;
    }

    // 创建消息队列
    ctrl_info->mqd = msg_queue_create();
    if(-1 == ctrl_info->mqd)// 创建消息队列失败
    {
        printf("%s|%s|%d, mqd= %d\n",__FILE__,__func__,__LINE__,ctrl_info->mqd);
        return -1;
    }
    
    //  头插法插入 , so 头一直在变化
    ctrl_info->ctrl_phead = add_voice_to_ctrl_list(ctrl_info->ctrl_phead);
    ctrl_info->ctrl_phead = add_tcpsocket_to_ctrl_list(ctrl_info->ctrl_phead);
    ctrl_info->ctrl_phead = add_smoke_to_ctrl_list(ctrl_info->ctrl_phead);
    ctrl_info->ctrl_phead = add_receive_to_ctrl_list(ctrl_info->ctrl_phead);
  
  

    pointer = ctrl_info->ctrl_phead;

    while(NULL!=pointer) // 对所有控制结构体初始化,并且统计节点数
    {
        if(NULL != pointer->init)
        {
             printf("%s|%s|%d   control_name = %s\n",__FILE__,__func__,__LINE__,pointer->control_name);
             pointer->init();
        }
        pointer = pointer->next;
        node_num++; // 统计节点数
    }

    // 根据节点的总数 --> 创建对应数目的线程
    pthread_t *tid = (pthread_t *)malloc(sizeof(int) *node_num);
    pointer = ctrl_info->ctrl_phead;

    for(int i=0;i<node_num;++i)//遍历所有节点
    {
       if(NULL != pointer->get){
          printf("%s|%s|%d   control_name = %s\n",__FILE__,__func__,__LINE__,pointer->control_name);
          pthread_create(&tid[i],NULL,(void *)pointer->get,(void *)ctrl_info); // 传入这个结构体参数,方便同时调用多组线程里面的API
       }
        pointer = pointer->next;
    }
    
     for(int i=0;i<node_num;++i)
     {
     pthread_join(tid[i],NULL);
     }

     for(int i=0;i<node_num;++i)
     {
      if(NULL != pointer->final)
          pointer->final(); // 接打开的使用接口关闭
      pointer = pointer->next;
     }
     
     msq_queue_final(ctrl_info->mqd);

     if(NULL != ctrl_info)
          free(ctrl_info); // 这个是malloc 堆区申请的内存 -->  需要手动的释放

     if(NULL != tid)
          free(tid);


     return 0;
}

语言控制模块 - voice_interface.c


#if 0
struct control
{
char control_name[128]; //监听模块名称
int (*init)(void); //初始化函数
void (*final)(void);//结束释放函数
void *(*get)(void *arg);//监听函数,如语音监听
void *(*set)(void *arg); //设置函数,如语音播报
struct control *next;
};
#endif

#include <pthread.h>
#include <stdio.h>
#include "voice_interface.h"
#include "mq_queue.h"
#include "uartTool.h"
#include "global.h"


static int serial_fd = -1; // static 这个 变量只在当前文件有效

static int voice_init(void )
{
  serial_fd = myserialOpen(SERIAL_DEV,BAUD); // 初始化并且打开串口
  printf("%s|%s|%d   serial_fd = %d\n",__FILE__,__func__,__LINE__,serial_fd);

  return serial_fd;
}

static void voice_final(void)
{
  if(-1 != serial_fd) // 打开串口成功
  {
    close(serial_fd); // 关闭我们打开的串口
    serial_fd = -1; // 复位
  }
}
// 接收语言指令
static void* voice_get(void *arg)// mqd 通过arg 传参获得
{
    int len = 0;
    mqd_t mqd = -1;
    ctrl_info_t * ctrl_info = NULL; 
    if(NULL != arg)
        ctrl_info = (ctrl_info_t*)arg;

    unsigned char buffer[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 初始化 buffer
    if (-1 == serial_fd)
    {
        //打开串口
        serial_fd = voice_init();// 尝试打开串口
        if (-1 == serial_fd){ //还是打开失败
        printf("%s | %s | %d:open serial failed\n", __FILE__, __func__, __LINE__); // 三个宏的含义: 文件名 - main.c,函数名 - pget_voice ,行号 -  138
        pthread_exit(0);   
        }                                                        // 串口打开失败 -->退出
    }
 
     
    mqd = ctrl_info->mqd; 
    

    if((mqd_t)-1 == mqd)
    {
       pthread_exit(0);  
    }

    pthread_detach(pthread_self());// 与父线程分离
    printf("%s thread start\n",__func__);

    while (1)
    {
        len = serialGetstring(serial_fd, buffer); // 通过串口获得语言输入
        printf("%s|%s|%d,  0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",__FILE__,__func__,__LINE__,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]);
        printf("%s|%s|%d:len = %d\n",__FILE__,__func__,__LINE__,len);
        if (len > 0)         // 判断是否 接到识别指令
        {
          if(buffer[0] == 0xAA && buffer[1] == 0x55 
            &&buffer[4]==0x55 && buffer[5]==0xAA)
            {
               printf("%s|%s|%d, send: 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",__FILE__,__func__,__LINE__,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]);
        
              send_msg(mqd,buffer,len); // 注意获取len长度不能使用strlen() --> 0x00 会识别为截止位-->只能读取到三个字节(但不是我们实际的截止位(0x55 0xAA ))
            }
            memset(buffer,0,sizeof(buffer)); // 复位buffer
        }
    }

    pthread_exit(0);

}
// 语音播报


static void* voice_set(void *arg)
{
  pthread_detach(pthread_self());// 与父线程分离
  unsigned char *buffer = (unsigned char*)arg;
  
  if (-1 == serial_fd)
    {
        //打开串口
        serial_fd = voice_init();// 尝试打开串口
        if (-1 == serial_fd){ //还是打开失败
        printf("%s | %s | %d:open serial failed\n", __FILE__, __func__, __LINE__); // 三个宏的含义: 文件名 - main.c,函数名 - pget_voice ,行号 -  138
        pthread_exit(0);   
        }                                                        // 串口打开失败 -->退出
    }

    if(NULL != buffer){ // 接收到数据
      serialSendstring(serial_fd,buffer,6); // 向串口发送接收到的数据
     // 语言模块识别到串口发送的数据后就,进行相应的语言输出 
    }

 pthread_exit(0);
}

struct  control voice_control ={
    .control_name = "voice",
    .init = voice_init,
    .final = voice_final,
    .get = voice_get,
    .set = voice_set,
    .next = NULL
};


struct control *add_voice_to_ctrl_list(struct control *phead)
{
  //头插法实现 添加链表节点
   return add_interface_to_ctrl_list(phead,&voice_control);

};

网络控制模块 - socket_interface.c
 

#include <pthread.h>

#include "socket_interface.h"
#include "control.h"
#include "socket.h"
#include "mq_queue.h"
#include "global.h"
#include <netinet/tcp.h> // 设置 tcp 心跳 的参数

static int s_fd = -1;

static int  tcpsocket_init(void)
{
    s_fd = socket_init(IPADDR,IPPORT);
    //return s_fd;
    return -1;
}

static void tcpsocket_final(void) 
{
  close(s_fd);
  s_fd = -1;
}

static void* tcpsocket_get(void *arg)
{
  
    int c_fd = -1;
    unsigned char buffer[BUF_SIZE];
    int ret = -1;
    struct sockaddr_in c_addr;
    mqd_t mqd = -1;
    ctrl_info_t * ctrl_info = NULL; 
    int keepalive = 1;    // 开启TCP_KEEPALIVE选项
    int keepidle = 10;    // 设置探测时间间隔为10秒
    int keepinterval = 5; // 设置探测包发送间隔为5秒
    int keepcount = 3;    // 设置探测包发送次数为3次

   

    pthread_detach(pthread_self()); // 和主线程(他的父线程)分离

    printf("%s|%s|%d   s_fd = %d\n", __FILE__, __func__, __LINE__, s_fd);
   
    if(-1 == s_fd) // 判断是否初始化成功
    {
     s_fd = tcpsocket_init();
     if(-1 == s_fd)
     {
      printf("tcpsocket_init error\n");
      pthread_exit(0);
     }

    }

     if(NULL != arg)
        ctrl_info = (ctrl_info_t*)arg;
     if(NULL != ctrl_info)
         mqd = ctrl_info->mqd;      
    
    
    if((mqd_t)-1 == mqd)
    {
       pthread_exit(0);  
    }

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

    int clen = sizeof(struct sockaddr_in);

    printf("%s thread start\n", __func__);
    while (1) // 一直等待接收
    {
        c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen); // 获得新的客户端 描述符

       if (c_fd == -1)
        {
            continue;
        }

        ret = setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive,sizeof(keepalive));
        if(-1 == ret){
          perror("setsockopt");
          break;
        }
       
        ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&keepidle, sizeof(keepidle));
          if(-1 == ret){
          perror("setsockopt");
          break;
        }
             ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepinterval,
                         sizeof(keepinterval));
          if(-1 == ret){
          perror("setsockopt");
          break;
        }
         ret = setsockopt(c_fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcount,
                         sizeof(keepcount)); 
             if(-1 == ret){
          perror("setsockopt");
          break;
        }

        // 打印调试信息
        printf("%s | %s | %d: Access a connection from %s:%d\n", __FILE__, __func__, __LINE__, inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));

    

        while (1)
        {
            memset(buffer, 0, BUF_SIZE);
            ret = recv(c_fd, buffer, BUF_SIZE, 0); // 等待接收
            // 将接收到数据打印出来
               printf("%s|%s|%d,  0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",__FILE__,__func__,__LINE__,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]);
       
            if (ret > 0)
            {
            if(buffer[0] == 0xAA && buffer[1] == 0x55 
                 &&buffer[4]==0x55 && buffer[5]==0xAA)
            {
                  printf("%s|%s|%d,  send: 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",__FILE__,__func__,__LINE__,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]);
       
              send_msg(mqd,buffer,ret); 
            }
                
            }
            else if (0 == ret || -1 == ret) // 没读到,or 读到空
            {
                break;
            }
        }

      
    }

    pthread_exit(0);

}


struct  control tcpsocket_control ={
    .control_name = "tcpsocket",
    .init = tcpsocket_init,
    .final = tcpsocket_final,
    .get = tcpsocket_get,
    .set = NULL, //不需要实现 设置
    .next = NULL
};


struct control *add_tcpsocket_to_ctrl_list(struct control *phead)
{
  //头插法实现 添加链表节点

   return add_interface_to_ctrl_list(phead,&tcpsocket_control);

};












烟雾报警模块 - smoke_interfac.c


#include <pthread.h>
#include <wiringPi.h>
#include <stdio.h>

#include "smoke_interface.h"
#include "control.h"
#include "mq_queue.h"
#include "global.h"
#include <netinet/tcp.h> // 设置 tcp 心跳 的参数

#define SMOKE_PIN   6 // 烟雾报警模块接的引脚
#define SMOKE_MODE INPUT

static int s_fd = -1;

static int  smoke_init(void)
{
    printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
    pinMode(SMOKE_PIN, SMOKE_MODE); // 引脚 和 模式配置
    return 0;
}

static void smoke_final(void) 
{
  // do nothing
}

static void* smoke_get(void *arg)
{
  // AA 55 45 00  55 AA -->  45 00 -->触发警报
  int status = HIGH; //低电平有效 -- 默认设置为高电平
  int switch_status = 0;  // 报警开关 -- 默认设置为不开 -- 0
  ssize_t byte_send = -1;
  unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};
  mqd_t mqd = -1;
  ctrl_info_t * ctrl_info = NULL; 

   if(NULL != arg)
        ctrl_info = (ctrl_info_t*)arg;
   if(NULL != ctrl_info)
         mqd = ctrl_info->mqd;      
    
    
    if((mqd_t)-1 == mqd)
    {
       pthread_exit(0);  
    }

  pthread_detach(pthread_self());  // 父子线程分离
  printf("%s thread start.\n",__func__);
  
  while(1)
  {
    status = digitalRead(SMOKE_PIN); // 读取当前引脚状态
    if(LOW == status) //  探测到烟雾 -- 发生报警
    {
     switch_status = 1; // 打开报警器开关
     buffer[2] = 0x45;
     buffer[3] = 0x00;// 低电平触发警报 --
     //蜂鸣器是低电平触发 --> 我们这里把buffer 修改得与beep匹配,方便与他产生联系
     printf("%s|%s|%d,  0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",__FILE__,__func__,__LINE__,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]);
       
     byte_send = mq_send(mqd, buffer, 6,0); // 向消息队列里面发送数据 -- 接收到后语言模块会识别播报 - 火灾警报
      if (-1 == byte_send)
      {
        continue;
      }
    }
    else if(HIGH == status && 1 == switch_status) // 未探测到烟雾,并且报警器开关还没关闭 -- 关闭报警器开关
    {
      switch_status = 0; //  关闭报警器开关
      buffer[2] = 0x45;
      buffer[3] = 0x01;//警报结束
      printf("%s|%s|%d,  0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",__FILE__,__func__,__LINE__,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]);
       
      byte_send = mq_send(mqd, buffer, 6,0);
      if (-1 == byte_send)
      {
        continue;
      }
    }
    sleep(5);

  }


      
  pthread_exit(0); // 退出线程
}


struct  control smoke_control ={
    .control_name = "smoke",
    .init = smoke_init,
    .final = smoke_final,
    .get = smoke_get,
    .set = NULL, //不需要实现 设置
    .next = NULL
};


struct control *add_smoke_to_ctrl_list(struct control *phead)
{
 return add_interface_to_ctrl_list(phead,&smoke_control);

}

接收处理线程 - receive_interface.c

#include <pthread.h>
#include <mqueue.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <wiringPi.h>

#include "receive_interface.h"
#include "control.h"
#include "mq_queue.h"
#include "global.h"
#include "face.h"
#include "myoled.h"
//#include "lrled_gdevice.h"
#include "gdevice.h"
// #include "fan_gdevice.h"
// #include "bled_gdevice.h"
// #include "beep_gdevice.h"
// #include "lock_gdevice.h"

#include "ini.h"
#include "face.h"



#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0

/*
接收模块:
对接收到消息做出相应处理
包括 oled 人脸识别 语言播报 GPIO  引脚状态配置

*/

static int oled_fd = -1;
static struct gdevice *pdevhead = NULL;

typedef struct
{
  int msg_len;
  unsigned char *buffer;
  ctrl_info_t *ctrl_info;
} recv_msg_t;


static int handler_gdevice(void *user, const char *section, const char *name, const char *value)
{
    struct gdevice *pdev = NULL;
    if (NULL == pdevhead)
    {
        pdevhead = (struct gdevice *)malloc(sizeof(struct gdevice));
        memset(pdevhead, 0, sizeof(struct gdevice));
        pdevhead->next = NULL;
        strcpy(pdevhead->dev_name, section);
    }
    // printf("section = %s, name = %s, value = %s\n", section, name, value);

    else if (0 != strcmp(section, pdevhead->dev_name)) // 当section对不上的时候,表示到了下一个设备
    {
        // 把新节点(设备)使用头插法插入
        pdev = (struct gdevice *)malloc(sizeof(struct gdevice));
        memset(pdev, 0, sizeof(struct gdevice));
        strcpy(pdev->dev_name, section);
        pdev->next = pdevhead;
        pdevhead = pdev;
    }

    if (NULL != pdevhead)
    {
        if (MATCH(pdevhead->dev_name, "key"))
        {
            sscanf(value, "%x", &pdevhead->key); // 把value(string)的值 转为int类型 16进行格式 传递给  pdevhead->key)
            printf("%d  pdevhead->key = 0x%x\n", __LINE__, pdevhead->key);
        }

        else if (MATCH(pdevhead->dev_name, "gpio_pin"))
        {
            pdevhead->gpio_pin = atoi(value);
        }

        else if (MATCH(pdevhead->dev_name, "gpio_mode"))
        {

            if (strcmp(value, "OUTPUT") == 0)
            {
                pdevhead->gpio_mode = OUTPUT;
            }
            else if (strcmp(value, "INPUT") == 0)
            {
                pdevhead->gpio_mode = INPUT;
            }
            else
            {
                printf("gpio_mode error\n");
            }
        }

        else if (MATCH(pdevhead->dev_name, "gpio_status"))
        {

            if (strcmp(value, "LOW") == 0)
            {
                pdevhead->gpio_mode = LOW;
            }
            else if (strcmp(value, "HIGH") == 0)
            {
                pdevhead->gpio_mode = HIGH;
            }
            else
            {
                printf("gpio_status error\n");
            }
        }

        else if (MATCH(pdevhead->dev_name, "check_face_status"))
        {
            pdevhead->check_face_status = atoi(value);
        }

        else if (MATCH(pdevhead->dev_name, "voice_set_status"))
        {
            pdevhead->voice_set_status = atoi(value);
        }
    }

    return 1;
}



static int receive_init(void)
{
  // pdevhead = add_lrled_to_gdevice_list(pdevhead); // 头插法加入 客厅灯
  // pdevhead = add_bled_to_gdevice_list(pdevhead);  // 加入卧室灯
  // pdevhead = add_fan_to_gdevice_list(pdevhead);   // 加入风扇
  // pdevhead = add_beep_to_gdevice_list(pdevhead);  // 蜂鸣器
  // pdevhead = add_lock_to_gdevice_list(pdevhead);  // 开锁
     if (ini_parse("/etc/gdevice.ini", handler_gdevice, NULL) < 0) {
        printf("Can't load 'gdevice.ini'\n");
        return 1;
    }

    struct gdevice *pdev = NULL;
    pdev = pdevhead;
    while (pdev != NULL)
    {
        // printf("inside %d",__LINE__);

        printf("dev_name:%s\n", pdev->dev_name);
        printf("key:%x\n", pdev->key);
        printf("gpio_pin:%d\n", pdev->gpio_pin);
        printf("gpio_mode:%d\n", pdev->gpio_mode);
        printf("gpio_status:%d\n", pdev->gpio_status);
        printf("check_face_status:%d\n", pdev->check_face_status);
        printf("voice_set_status:%d\n", pdev->voice_set_status);

        pdev = pdev->next;
    }

  // 设备类链表添加
  oled_fd = myoled_init(); // 初始化oled
  face_init();             // 初始化人脸识别

  return oled_fd;
}

static void receive_final(void)
{
  face_final();
  if (-1 != oled_fd)
  {
    close(oled_fd); // 关闭oled 打开的文件
    oled_fd = -1;   // 复位
  }
}







//  处理设备 --  比如打开灯 和风扇等





static void *handler_device(void *arg)
{
  pthread_detach(pthread_self()); // 和主线程(他的父线程)分离

  recv_msg_t *recv_msg = NULL;
  struct gdevice *cur_gdev = NULL;
  char success_or_failed[20] = "success";
  pthread_t tid = -1;
  int smoke_status = 0;
  double face_result = 0.0; //存放人脸匹配度

  int ret = -1;

  if (NULL != arg) // 有参数
  {
    recv_msg = (recv_msg_t *)arg; // 获取参数
    printf("recv_len = %d\n", recv_msg->msg_len);
    printf("%s|%s|%d, handler: 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n", __FILE__, __func__, __LINE__,
           recv_msg->buffer[0], recv_msg->buffer[1], recv_msg->buffer[2], recv_msg->buffer[3], recv_msg->buffer[4], recv_msg->buffer[5]);
  }

  // need to do something
  if (NULL != recv_msg && NULL != recv_msg->buffer) // if 消息队列非空,并且buffer 里面接收到数据
  {
    
    // recv_msg->buffer[2] -->  第三位 用于存放设备类型
    cur_gdev = find_device_by_key(pdevhead, recv_msg->buffer[2]);
    printf("%s|%s|%d,find success   buffer[2] = 0x%x \n", __FILE__, __func__, __LINE__, recv_msg->buffer[2]);
  }
   
  if (NULL != cur_gdev) // if 能找到的这设备 --> 设备存在
  {
     printf("%s|%s|%d, cur_gdev \n", __FILE__, __func__, __LINE__);
    // BUFFER 的第四个参数  用于 存放开关状态 0 表示开, 1 表示关
    cur_gdev->gpio_status = recv_msg->buffer[3] == 0 ? LOW : HIGH; // 获取状态存入cur_gdev中
    //人脸识别
    if(1 == cur_gdev->check_face_status){
      face_result = face_status(); //得到人脸识别的匹配度
      if(face_result > 0.6){ //匹配成功
      ret = set_gpio_device_status(cur_gdev); // 设置电平 --> 开锁
      recv_msg->buffer[2] = 0x47;  //识别成功的语音播报
      }
      else{
      recv_msg->buffer[2] = 0x46;
      }
    }

    else if( 0 == cur_gdev->check_face_status){
    // printf("%s|%s|%d,Set  before set_gpio_device_status\n",__FILE__,__func__,__LINE__);
    ret = set_gpio_device_status(cur_gdev); // 将获取到的状态真正赋值给引脚
    // printf("%s|%s|%d, after set_gpio_device_status \n",__FILE__,__func__,__LINE__);
    }

 printf("%s|%s|%d, = %d\n", __FILE__, __func__, __LINE__,cur_gdev->voice_set_status);
   
   // 需要语言播报
  if (1 == cur_gdev->voice_set_status) 
  {
    printf("%s|%s|%d,2\n", __FILE__, __func__, __LINE__);
    if (NULL != recv_msg && NULL != recv_msg->ctrl_info && NULL != recv_msg->ctrl_info->ctrl_phead)
    {
     printf("%s|%s|%d,2\n", __FILE__, __func__, __LINE__);
      struct control *pcontrol = recv_msg->ctrl_info->ctrl_phead;
      while (NULL != pcontrol)
      {
        if (strstr(pcontrol->control_name, "voice")) //匹配到语言播报
        {

          if (0x45 == recv_msg->buffer[2] && 0 == recv_msg->buffer[3]) // 语音播报 打开
          {
            smoke_status = 1;
            
          }
          pthread_create(&tid, NULL, pcontrol->set, (void *)recv_msg->buffer); // 新开线程区进行语言播报
          break;
          
        }
        pcontrol = pcontrol->next;
      }
    }
  }

printf("%s|%s|%d,2\n", __FILE__, __func__, __LINE__);
  if (-1 == ret) // 设置失败
  {
    printf("%s|%s|%d,2\n", __FILE__, __func__, __LINE__);
    memset(success_or_failed, '\0', sizeof(success_or_failed));
    strncpy(success_or_failed, "failed", 6);
  }

  printf("%s|%s|%d,2\n", __FILE__, __func__, __LINE__);
  // 配置OLED
  char oled_msg[512];
  memset(oled_msg, 0, sizeof(oled_msg));
  char *change_status = cur_gdev->gpio_status == LOW ? "Open" : "Close";
  sprintf(oled_msg, "%s %s %s!\n", change_status, cur_gdev->dev_name, success_or_failed);
  if(smoke_status == 1)
  {
    memset(oled_msg, 0, sizeof(oled_msg));
   sprintf(oled_msg, "A risk of fire!\n");
  

  }  
  
  myoled_show(oled_msg);
 
  //让门打开5s自动关闭
  if(1 == cur_gdev->check_face_status && 0 == ret && face_result >0.6){
     sleep(5); //开门5s后关门
     cur_gdev->gpio_status = HIGH; //设置高电平(低电平有效)
     ret = set_gpio_device_status(cur_gdev); //关门
  }




  }

  pthread_exit(0);
}


static void *receive_get(void *arg) // 接收消息队列里面的 数据
{
  printf("enter receive_get\n");
  //  通过参数 初始化我们 定义的recv_msg_t 结构体
  recv_msg_t *recv_msg = NULL;
  unsigned char *buffer = NULL;
  struct mq_attr attr;
  pthread_t tid = -1;
  ssize_t read_len = -1;

  

  if (NULL != arg)
  {
    recv_msg = (recv_msg_t *)malloc(sizeof(recv_msg_t));
    recv_msg->ctrl_info = (ctrl_info_t *)arg; // 这里实际上就获取到了mqd 和 phead(我们需要操作的struct control 链表 的头节点)
    recv_msg->msg_len = 0;
    recv_msg->buffer = NULL;
  }
  else
    pthread_exit(0);

  if (mq_getattr(recv_msg->ctrl_info->mqd, &attr) == -1)
  { // 获取消息队列失败 -- 异常
    pthread_exit(0);
  }

  // 能获取到消息队列
  recv_msg->buffer = (unsigned char *)malloc(attr.mq_msgsize); // 分配内存
  buffer = (unsigned char *)malloc(attr.mq_msgsize);
  // mq_msgsize -- 每条消息的大小
  memset(recv_msg->buffer, 0, attr.mq_msgsize); // 初始化
  memset(buffer, 0, attr.mq_msgsize);           // 初始化

  pthread_detach(pthread_self()); // 和主线程(他的父线程)分离

  while (1)
  {
    read_len = mq_receive(recv_msg->ctrl_info->mqd, buffer, attr.mq_msgsize, NULL);

    printf("%s|%s|%d, recv: 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n", __FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
    printf("%s|%s|%d: read_len = %ld\n", __FILE__, __func__, __LINE__, read_len);
    if (-1 == read_len)
    { // 接收失败
      if (errno == EAGAIN)
      {
        printf("queue is empty\n");
      }
      else
      {
        break;
      }
    }
    // 以下是接收到正常数据的情况
    else if (buffer[0] == 0xAA && buffer[1] == 0x55 && buffer[4] == 0x55 && buffer[5] == 0xAA)
    {
      recv_msg->msg_len = read_len;
      memcpy(recv_msg->buffer, buffer, read_len);
      //  创建线程去 处理我们的接收到的信号
      pthread_create(&tid, NULL, handler_device, (void *)recv_msg);
    }
  }
  if (NULL != recv_msg)
    free(recv_msg);

  if (NULL != buffer)
    free(buffer);

  pthread_exit(0);
}

struct control receive_control = {
    .control_name = "receive",
    .init = receive_init,
    .final = receive_final,
    .get = receive_get,
    .set = NULL, // 不需要实现 设置
    .next = NULL};

struct control *add_receive_to_ctrl_list(struct control *phead)
{
  // 头插法实现 添加链表节点

  return add_interface_to_ctrl_list(phead, &receive_control);
};

编译运行

环境配置:

// 我们人脸识别开门模块调用了阿里云的sdk,请确保arm设上已经安装了对应SDK,和阿里云服务配置,请去这里安装指示配置(按照指示下载SDK,添加阿里云AccessKey)

人脸识别_身份验证识别_客流分析系统_人脸门禁闸机-阿里云 (aliyun.com)

编译

由于文件众多,我们采用Makefile,来编译

Makefile

CC := aarch64-linux-gnu-gcc

SRC := $(shell find src -name "*.c")

INC :=  ./inc \
		./3rd/usr/local/include \
		./3rd/usr/include \
		./3rd/usr/include/python3.10 \
		./3rd/usr/include/aarch64-linux-gnu/python3.10 \
		./3rd/usr/include/aarch64-linux-gnu

OBJ := $(subst src/,obj/,$(SRC:.c=.o))

TARGET=obj/smarthome

CFLAGS := $(foreach item, $(INC),-I$(item)) # -I./inc -I./3rd/usr/local/include

LIBS_PATH := ./3rd/usr/local/lib \
		 ./3rd/lib/aarch64-linux-gnu \
		 ./3rd/usr/lib/aarch64-linux-gnu \
		 ./3rd/usr/lib/python3.10 \
#L
LDFLAGS := $(foreach item, $(LIBS_PATH),-L$(item)) # -L./3rd/usr/local/libs

LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt

obj/%.o:src/%.c
	mkdir -p obj
	$(CC) -o $@ -c $< $(CFLAGS)

$(TARGET) :$(OBJ)
	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)
	scp obj/smarthome ini/gdevice.ini orangepi@192.168.1.18:/home/orangepi


compile : $(TARGET)

clean:
	rm $(TARGET) obj $(OBJ) -rf
debug:
	echo $(CC)
	echo $(SRC)
	echo $(INC)
	echo $(OBJ)
	echo $(TARGET)
	echo $(CFLAGS)
	echo $(LDFLAGS)
	echo $(LIBS)



.PHONY: clean compile debug

可以看到我的Makefile 里面添加了scp传送,请根据自己的派的ip进行修改

运行:

 sudo -E ./smarthome

sudo -- 因为我们调用了wiringpi库来调节电平,需要访问到系统文件

-E 保持环境,即我们可以使用root用户里面配置的AccessKey,来访问阿里云的接口

STM32智能家居系统设计(门禁、人体感应、GSM远程控制) STM32智能家居系统设计,其功能包括门禁功能、人体感应灯功能、GSM远程短信控制家电功能。整个系统的设计是建立在STM32F407开发板上的,并运用了开发板上的触摸屏进行系统显示。其中门禁功能包括了密码开门和指纹开门两个功能,可以修改系统中的密码和指纹。指纹部分采用的是AS608指纹识别模块。GSM模块采用sim800c模块,运用短信功能控制灯光的开关。...... 阅读详情

相关推荐

基于STM32的智能家居控制系统设计

今天这里简单举一个前几年做的一个很简单的小项目给大家,帮助大家了解一下单片机STM32的简单应用。后续也会给大家不断分享项目实践。 功能需求 随着社会的进步,经济的快速发展,人们对于日常生活科技化的追求越来越高,大量的智能家电进入了家家户户,对这些种类繁杂的智能家电有一个便捷的集中化控制系统成为了迫在眉睫的需求,本次设计要求综合运用传感器、单片机技术、电路分析、模拟电子技术等相关知识,结合学生的实际动手能力,通过现有的电话网络终端或者互联网网络终端实现远程控制的智能家居系统,给出了该系统的电路...

an520_的博客 1万+

OpenDuelyst卡牌系统深度剖析:6大派系与718种修饰器详解

OpenDuelyst作为一款融合了数字集换式卡牌与回合制策略的游戏,其核心魅力在于深度的卡牌系统设计。本文将全面解析游戏中的6大派系特色与718种修饰器机制,帮助新手玩家快速掌握卡组构建的精髓。 ## 游戏核心战场与卡牌系统概述 OpenDuelyst的战斗在六边形网格战场上展开,玩家通过召唤单位、释放法术和使用artifact来争夺胜利。游戏的卡牌系统主要由**派系**和**修饰器**两大

gitblog_00086的博客 638

《智能家居设计与未来的深入探讨》

随着科技的不断进步,智能家居已经成为现代家居生活的重要发展方向。本论文详细阐述了智能家居的设计理念、关键技术、现有产品及系统,分析了其优势与面临的挑战,并对未来发展趋势进行了深入探讨。通过研究智能家居的现状与未来,旨在为智能家居的进一步发展提供有益的参考和指导。

2301_78160658的博客 2424

基于树莓派的智能家居

实现一些基本的智能家居功能,希望能对大家有所帮助。

WLLTB 1万+

毕业设计 基于51单片机的智能家居安防系统的设计

序🔥 毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是:基于51单片机的智能家居安防系统的设计。

Nueve_Y的博客 2917

智能家居:从感知到交互,让生活更美好

作者:禅与计算机程序设计艺术 随着人们生活节奏的加快、生活环境的变化和新兴产业的出现,智能家居已经成为众多人的生活中不可缺少的一环。那么,什么是智能家居呢?如何将智能家居应用在个人、家庭和企业的生活中,并最终实现其“智能”功能,是本文将要探讨的重点。 什么是智能家居:“智能家居”这个词汇,已经被赋予了许多新的含义。早期,它可能代表的是智能

AI天才研究院 2194

基于物联网的智能家居系统设计(课设)

既互联网技术之后,物联网作为一个信息技术综合应用的代名词,掀起信息产业第三浪潮,大国纷纷将其纳为重点领域,显而易见,它将影响到政治,军事,经济,环境等方方面面,必在未来极大地改变人们的生活。物联网是新一代信息技术的重要组成部分。其英文名称是“The internet of things”。顾名思义,“物联网就是物物相连的互联网”。这里包括两层意思:第一,物联网的核心和基础仍然是互联网,它是在互联网基础上的延伸和扩展的网络;第二,其用户端延伸和扩展到了任何物体与物体之间,进行信息交换和通信。

晓风残月 8867

【雕爷学编程】Arduino智能家居之基于无线通信的全屋联动报警

1、温度和湿度控制:通过连接温度传感器和湿度传感器,Arduino可以实时监测室内环境的温度和湿度,并通过控制空调、加热器或加湿器等执行器,实现室内温湿度的自动调节。设计用户友好的界面和操作方式,提供简单直观的控制和反馈机制,以及考虑用户习惯和需求,能够提升系统的整体用户体验。这意味着用户可以根据自己的需求和创意,自定义和定制智能家居系统的功能和外观。5、能源管理:Arduino可以与电能监测模块和智能插座等设备结合使用,实时监测家庭能源的使用情况,并通过自动控制电器设备的开关,实现能源的有效管理和节约。

雕爷学编程 391

基于STM32的智能家居控制器设计与实现

本项目的目标是深入探索音视频编码与解码技术的原理与实践,通过实现一个音视频编码与解码系统,加深对该领域的理解,并掌握相关的软件开发和硬件设计技术。可以使用适当的视频输入设备进行视频数据的采集,并对采集到的视频数据进行预处理,例如图像增强、分辨率调整等。硬件平台的选择将直接影响系统的性能和资源消耗。在实验3中,我们使用了不同的视频编码算法对采集到的视频数据进行编码,并计算了编码后的码流大小和图像质量指标。在实验1中,我们使用了不同的音频编码算法对采集到的音频数据进行编码,并计算了编码后的码流大小和音质指标。

牛肉胡辣汤 6394

人工智能家居系统:智能家居的应用前景

1.背景介绍 随着人工智能技术的不断发展,智能家居已经成为了人们生活中不可或缺的一部分。智能家居系统可以帮助我们更高效地管理家庭生活,提高生活质量,降低家庭成本。在这篇文章中,我们将深入探讨人工智能家居系统的核心概念、算法原理、具体实现以及未来发展趋势。 1.1 智能家居的发展历程 智

AI天才研究院 582

基于树莓派4B设计的智能家居系统(华为云IOT)

本次设计主要以树莓派为主要的硬件开发平台,以Raspbian操作系统为软件开发平台。主板树莓派通过HTTP协议和MQTT协议分别与上位机和下位机通信,搭建的服务器用来储存数据和日志记录。搭配有线和无线组合通信方式设计出一套完整的智能家居系统。

2万+

智能家居设计原理c语言,基于STM32的小型智能家居系统设计

摘 要:随着科学技术的不断发展,智能家居系统作为物联网技术的一项重要应用也随着物联网的快速发展发生了明显的改善。现阶段,智能家居系统的应用还仅仅只是存在于高端群体中,其主要原因还是由于智能家居系统的性价比,为实现智能家居市场的有效扩展,开发和研制出一种针对低端用户及大众化的小型智能家居系统就显得尤为必要。关键词:物联网;智能家居;小型;大众化;STM32中图分类号:TP273 文献标识码:A智能家...

weixin_36158842的博客 5040

智能家居控制

嵌入式开发。21世纪是信息时代,各种电信新技术推动了人类文明的进步。自从1876年,贝尔发明电话以来,世界各国的电话网络发展非常迅速,近十年来,中国的固定电话业务呈现出举世瞩目的快速增长。正是因为通信技术、计算机技术、网络技术、控制技术的迅猛发展与提高,促使了家庭实现了生活现代化,居住环境舒适化、安全化。这些高科技已经影响到人们生活的方方面面,改变了人们生活习惯,提高了人们生活质量,家居智能化也正是在这种形势下应运而生的。智能家居控制系统的主要功能包括通信、设备自动控制、安全防范三个方面。 智能家居是以住宅为平台,兼备建筑、网络通讯、办公自动化,集系统、结构、管理为一体的高效、便利的居住环境。 智能家居是在家庭产品自动化,智能化的基础上,利用先进的计算机技术、网络通讯技术、无线技术、将与家居生活有关的各种子系统,有机地结合在一起。与普通家居相比,由原来的被动静止结构转变为具有能动智能的工具,提供全方位的信息交换功能,帮助家庭与外部保持信息交流畅通。智能家居强调人的主观能动性,要求重视人与居住环境的协调,能够随心所欲地控制室内居住环境。

简约直观的智能家居app设计,用起来更方便

在当今数字化时代,物联网技术的迅猛发展使得智能家居不再是遥不可及的概念,而是切实走进了人们的日常生活。从智能灯光、智能家电到智能安防系统,各种智能设备为人们带来了前所未有的便捷与舒适体验。而作为操控这些智能设备的核心入口,智能家居 app 的设计质量直接决定了用户对智能家居系统的使用感受。一款设计精良、简约直观的智能家居 app,能够极大地提升用户体验,让智能家居真正实现 “用起来更方便” 的目标。接下来,我们将深入探讨这类 app 的关键设计要点。

2401_82616673的博客 2365

智能家居案例.pdf

智能家居案例pdf,智能家居案例

MCGS触摸屏Modbus TCP通讯实战:从寄存器写入到字符串处理的完整代码解析

本文详细解析了MCGS触摸屏通过Modbus TCP协议实现寄存器写入和字符串处理的完整代码,涵盖单寄存器写入、多寄存器写入及字符串传输等核心技术。文章还提供了MCGS组态环境中的实战配置步骤和常见问题排查方法,帮助开发者快速掌握工业自动化通讯的关键技术。

study的专栏 755

智能家居语音控制系统的设计与实现

以树莓派作为控制终端,采用 Python 语言, 通过整合图灵机器人、和风天气和百度人工智能等平台,利用爬虫和嵌入式技术,设计并实现了一款基于语音控制的智能家居系统,用户只需通过唤醒词“依米”唤醒系统来发送语音指令进而执行所需功能。该系统包括了智能门禁、智能聊天、生活指南、待办事项提醒等多项日常常用功能。通过测试表明,本系统已具备与用户交互的稳定性、满足用户需求的多样性以及家居功能的实用性。

EmithFla的博客 1万+

AI算力瓶颈与系统优化实战指南

AI算力瓶颈已成为制约人工智能发展的关键因素,特别是在处理transformer类模型时,传统的冯·诺依曼架构面临严重的'内存墙'问题。通过硬件加速、系统优化和算法改进的三级策略,可以有效提升资源利用率。在嵌入式AI领域,结合量化、剪枝和专用指令集扩展等技术,能在资源受限设备上实现高效推理。这些优化方法在自动驾驶、工业视觉等场景中已展现出显著价值,推动AI应用从云端向边缘迁移。随着AI算力需求的持续增长,掌握系统优化技能将成为开发者的核心竞争力。

weixin_32597009的博客 340

智能家居监控系统设计

智能家居最初的发展主要以灯光遥控控制、电器远程控制和电动窗帘控制为主,随着行业的发展,智能控制的功能越来越多,控制的对象不断扩展,控制的联动场景要求更高,其不断延伸到家庭安防报警、背景音乐、可视对讲、门禁指纹控制等领域,可以说智能家居几乎可以涵盖所有传统的弱电行业,市场发展前景诱人,因此和其产业相关的各路品牌不约而同加大力度争夺智能家居业务,市场渐成春秋争霸之势。如图所示,环境温度为25度时,煤气浓度为48%,烟雾浓度为62%时,采集到的数据为温度25.0度,煤气浓度为47.8%,烟雾浓度为61.9%。

3254968421的博客 2625

遗传算法驱动吃豆人进化:从零构建AI游戏智能体

遗传算法是一种模拟自然选择过程的优化技术,其核心原理是通过选择、交叉和变异等操作,在解空间中迭代搜索最优解。该技术为解决复杂优化问题提供了强大工具,尤其在游戏AI、机器人控制和参数调优等场景中价值显著。在游戏AI领域,遗传算法常被用于训练智能体自主决策,例如在经典游戏《吃豆人》中,通过定义智能体的“基因”(如神经网络权重)和“适应度”(如游戏得分),可以引导其进化出躲避幽灵、高效吃豆等策略。本项目结合Pygame框架与神经网络,构建了一个完整的遗传算法训练沙盒,详细展示了如何通过适应度函数设计和参数调优,实

weixin_30467199的博客 273

避坑指南:在Ubuntu 20.04上从零部署IS-Fusion环境(含CUDA 11.1和PyTorch 1.10.1配置)

本文提供了在Ubuntu 20.04系统上从零部署IS-Fusion 3D目标检测环境的详细避坑指南。内容涵盖CUDA 11.1、PyTorch 1.10.1、spconv等核心组件的精确版本安装与兼容性配置,并指导如何准备nuScenes数据集及启动模型训练,旨在帮助研究者和开发者高效搭建稳定的多模态融合算法开发环境。

gitlab7runner的博客 552

智能家居系统模型设计2.0

智能家居系统模型设计引言实现的功能硬件选型主控模块无线通信模块数据采集模块温湿度监测空气质量监测光照监测数据展示模块家具模拟模块云平台选择代码编写主函数结果展示实物图与手机界面展示云平台数据可视化界面云平台控制界面展示总结 引言 作为电子专业本科毕业生既需要撰写毕业论文,又需要做出毕业设计实物。很开心,在前几日我的答辩正式结束,现在此对我所做的毕业设计做一个总结和分享,希望对本项目有兴趣的同学能有所收获。同时,如果有问题或者需要代做同学可以私信我哦,此项目可应用于环境监测和智能家居! 实现的功能 监测空气

heartbeat196的博客 8044

[XJTUSE DATABASE]——第三章 关系数据库标准语言SQL

新年了,来个大的,写了好几天,将近9000字,希望对大家有所帮助

weixin_47692652的博客 3120
上一篇: 智能家居6 -- 配置 ini文件优化设备添加
下一篇: 从零开始实现自己的串口调试助手(3) - 显示底部收发,优化串口打开/关闭
mx_jun
博客等级 码龄5年 1605粉丝 148原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值