[ 嵌入式安防监控项目 ] [ 2 ] 主框架搭建

简介

完整框架如下图所示:

image.png

由于客观原因限制,只做部分功能的实现

分层分析

web 页面显示部分

  1. 用户登录
  2. 摄像头采集图像
  3. 硬件控制
  4. 环境信息

A9 数据处理部分

  1. 完成 web 页面和物理元器件之间的数据传递和交互

数据采集部分

  1. LED
  2. 传感器
  3. 图片

数据流分析

数据上传

通过共享内存和信号量实现数据上传。

数据类型定义(参考)

typedef  unsigned char  uint8_t;
typedef  unsigned short uint16_t;
typedef  unsigned int   uint32_t;

//考虑到内存对齐的问题
struct makeru_zigbee_info{
    uint8_t head[3]; //标识位: 'm' 's' 'm'  makeru-security-monitor  
    uint8_t type;  //数据类型  'z'---zigbee  'a'---a9
    float temperature; //温度
    float humidity;  //湿度
    float tempMIN;//温度下限
    float tempMAX;//温度上限 
    float humidityMIN;   //湿度下限
    float humidityMAX;   //湿度上限
    uint32_t reserved[2]; //保留扩展位,默认填充0
};

struct makeru_a9_info{
  uint8_t head[3]; //标识位: 'm' 's' 'm'  makeru-security-monitor  
  uint8_t type;  //数据类型  'z'---zigbee  'a'---a9
  float adc;
  short gyrox;   //陀螺仪数据
  short gyroy;
  short gyroz;
  short  aacx;  //加速计数据
  short  aacy;
  short aacz;
  uint32_t reserved[2]; //保留扩展位,默认填充0
};

struct makeru_env_data{
  struct makeru_a9_info       a9_info;    
  struct makeru_zigbee_info   zigbee_info;
};

//所有监控区域的信息结构体
struct env_info_client_addr
{
  struct makeru_env_data  monitor_no[MONITOR_NUM];  //数组  老家---新家
};

数据下发 

采用消息队列将消息下发到下位机,用于控制硬件。

man msgsnd

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                    int msgflg);
消息队列用于通信的结构体: 包括数据类型和数据

将消息队列封装成函数,直接通过参数传递的方式来发送信息: 
int send_msg_queue(long type,unsigned char text)
{
  struct msg msgbuf;
  msgbuf.type = 1L;
  msgbuf.msgtype = type;   //具体的消息类型
  msgbuf.text[0] = text;   //控制命令字 
  if(msgsnd(msgid,&msgbuf,sizeof(msgbuf) - sizeof(long),0) == -1){
    perror("fail to msgsnd type2");
    exit(1);
  }
  return 0;
}

struct msgbuf {
         long mtype;       /* message type, must be > 0 */
         char mtext[1];    /* message data */
      };

//消息队列结构体
#define QUEUE_MSG_LEN 32                 
struct msg
{
    long type;   //从消息队列接收消息时用于判断的消息类型
    long msgtype;//具体的消息类型
    unsigned char text[QUEUE_MSG_LEN];//消息正文
};

long msgtype;//具体的消息类型
消息类型的分配:
  1L:     LED控制
  2L:     蜂鸣器控制
  3L:     四路LED灯模拟的数码管
  4L:     风扇
  5L:     温湿度最值设置
  6L-7L-8L-9L,用于个人的扩展
  10L:    3G通信模块-GPRS 
switch(msgbuf.msgtype){
  case 1L: ...  break;
  ....
  default ....  break;
}


控制命令的制定:

消息队列接收消息:
  msgrcv (msgid, &msgbuf, sizeof (msgbuf) - sizeof (long), 1L, 0);
解析buf中的数据:
  printf ("Get %ldL msg\n", msgbuf.msgtype);
  printf ("text[0] = %#x\n", msgbuf.text[0]);

外部线程

extern void *pthread_client_request (void *arg);        //接收 CGI 等的请求
extern void *pthread_refresh(void *arg);                //刷新共享内存数据线程
extern void *pthread_sqlite(void *arg);                 //数据库线程,保存数据库的数据
extern void *pthread_transfer(void *arg);               //接收ZigBee的数据并解析
extern void *pthread_sms(void *arg);                    //发送短信线程
extern void *pthread_buzzer(void *arg);                 //蜂鸣器控制线程
extern void *pthread_led(void *arg);                    //led灯控制线程


extern int send_msg_queue(long type,unsigned char text);

处理消息队列中请求的线程

void *pthread_client_request(void *arg)
{
  // convert a pathname and a project identifier to a System V IPC key
  if((key = ftok("/tmp",'g')) < 0){
    perror("ftok failed .\n");
    exit(-1);
  }

  // get a System V message queue identifier
  msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666);
  if(msgid == -1) {
    /*
      If  msgflg  specifies  both  IPC_CREAT and IPC_EXCL and a message queue
      already exists for key, then msgget() fails with errno set  to  EEXIST.
      (This  is  analogous  to the effect of the combination O_CREAT | O_EXCL for open(2).)
    */
    if(errno == EEXIST){
      msgid = msgget(key,0777);
    }else{
      perror("fail to msgget");
      exit(1);
    }
  }
  printf("pthread_client_request\n");
  
  while(1){
    /*
     The  bzero()  function  erases  the  data  in the n bytes of the memory
     starting at the location pointed to by s, by writing zeroes (bytes con‐
     taining '\0') to that area.
    */
    bzero(&msgbuf,sizeof(msgbuf));
    printf("wait form client request...\n");
    msgrcv (msgid, &msgbuf, sizeof (msgbuf) - sizeof (long), 1L, 0);
    printf ("Get %ldL msg\n", msgbuf.msgtype);
    printf ("text[0] = %#x\n", msgbuf.text[0]);
...
...
...

LED模块线程

#include "data_global.h"


void *pthread_led(void *arg)
{
  printf("pthread_led\n");
#if 0
  5.  open(dev_led,  )
  6.  pthread_cond_wait (cond_led,  );
  7.  获取dev_led_mask(控制标志)
  8.  通过 ioctl() 控制led
#endif 
}

刷新共享内存数据线程

extern struct env_info_client_addr  sm_all_env_info;

struct shm_addr
{
  char shm_status;   //shm_status可以等于home_id,用来区分共享内存数据
  struct env_info_client_addr  sm_all_env_info;
};
struct shm_addr *shm_buf;

int file_env_info_struct(struct env_info_client_addr  *rt_status,int home_id);

void *pthread_refresh(void *arg)
{
  ...

  //share memap
  if((shm_buf = (struct shm_addr *)shmat(shmid,NULL,0)) == (void *)-1)
  {
    perror("fail to shmat");
    exit(1);
  }

  printf("pthread_refresh ......>>>>>>>\n");
  
#if 0
  bzero (shm_buf, sizeof (struct shm_addr));
  while(1){
    sem_p(semid,0);
    shm_buf->shm_status = 1;
    file_env_info_struct(&shm_buf->sm_all_env_info,shm_buf->shm_status);
    sleep(1);
    sem_v(semid,0);
  }
#endif 
}


int file_env_info_struct(struct env_info_client_addr *rt_status,int home_id)
{
  int  env_info_size = sizeof(struct env_info_client_addr);
  printf("env_info_size = %d.\n",env_info_size);
...
...


总结

介绍安防系统整体框架,从两个维度进行分析:

1. 分层意识

用户层、处理层和感知层

2. 数据流

从上到下:配置下发(消息队列)

从下到上:数据采集(共享内存)

每类元器件都有一个对应线程,多个线程之间通过互斥锁和条件变量,实现同步互斥。

疑问

Q1. 感知层元器件怎么把采集到的数据写入共享内存?

A:通过驱动读取元器件中的寄存器