• 20155110王一帆 《远程安防监控系统》课程设计个人报告


    20155110王一帆 《远程安防监控系统》课程设计个人报告

    一、个人所做的工作

    1. 编译linux内核,制作文件系统镜像
    2. 交叉编译项目代码
    3. 配合组长完成SD卡的烧写
    4. 调试运行开发板的各项功能

    二、遇到的问题的及解决方法

    问题1:fastboot的驱动安装

    首先,我们在设备管理器中找到Android设备

    2-0-1

    但是安装fastboot驱动时,却失败了。

    2-0-2

    问题1的解决方法

    在windows设置中找到“更新与安全”

    2-0-3

    找到恢复,在高级启动中找到“立即重启”

    2-0-4

    重启之后,选择“疑难解答”

    2-0-5

    接着选择“高级选项”

    2-0-6

    然后选择“启动设置”

    2-0-7

    重启以后,按下数字键“7”禁用驱动的强制签名

    2-0-8

    之后,我们就可以成功安装驱动了。

    2-0-9

    2-0-10

    我们这时再观察设备管理器,我们就可以发现Android设备从未知变成已知了。

    2-0-11

    问题2:open /dev/ttyUSB0 error: No such file or directory

    在进行串口通讯的时候,打开开发板后,一直会提示这样的消息。

    2-1-1

    原因是/dev目录下没有ttyUSB0这个设备文件。

    2-1-2

    问题2的解决方法

    事实上,我们依旧可以输入linux命令的,要想不看到这个信息,需要在/dev下建立一个软链接。

    输入ln -s /dev/null /dev/ttyUSB0,用“空设备”做个软链接。

    2-1-3

    这样就不会一直出现open /dev/ttyUSB0 error: No such file or directory这样的提示了

    而且这样做不会影响正常的操作。

    三、剖析CGI源码时的疑问与解答

    我自己有过Web编程的经验,也了解早期的Web编程是靠CGI来完成的,用的语言也是五花八门——C/C++、perl、bash……

    我选择研究该项目的CGI源码,也是因为自己对Web编程有一些了解,而对系统编程就完全不懂,也搞不清什么线程、锁、同步的概念。

    (一)登录表单处理——login.cgi

    我们进入文件系统rootfs的www目录下,这里就是存放web服务器html,css,js和cgi程序的地方。我们先看看index.html.

    我们直接看登录的表单部分

    <form name="form1" method="post"  action="cgi-bin/login.cgi">
      <table width="100%" border="0" cellspacing="9" cellpadding="0">
      <tbody>
      <tr>
        <td width="92">用户帐号:</td>
        <td width="130"><label>
        <input name="username" type="text" id="username" value="user"></label></td>
      </tr>
      <tr>
        <td>登录密码:</td>
        <td>
        <label>
          <input name="password" type="password" id="password" value="123456">
        </label>
        </td>
      </tr>
      <tr>
        <td height="25"></td>
        <td><input type="image" name="submit" style="97px;height:25px;" src="images/login/go.gif"></td>
      </tr>
      </tbody> 
      </table>
    </form>
    

    这个表单的数据会提交给login.cgi这个程序去处理。学过java servlet的同学会发现,CGI程序和servlet比较接近。

    我们来分析一下login.cgi的源码。

    //login.c
    
    cgiFormStringNoNewlines("username", name, N);
    cgiFormStringNoNewlines("password", pw, N);
    

    这两条语句将username和password分别放到char数组name和pw中,接下来肯定是要到数据库里面去查询了。这里的数据库用的是sqlite,非常轻量级的一个数据库。

    //login.c
    
    if(sqlite3_open("/user.db", &db) != SQLITE_OK)
    {
    	fprintf(cgiOut, "<BODY>");
    	fprintf(cgiOut, "<H1>%s</H1>", "Server is busy...");		
    	fprintf(cgiOut, "<meta http-equiv="refresh" content="1;url=../index.html">");
    	return -1;
    }
    
    sprintf(sql, "select * from usr where name='%s' and password='%s'", name, pw);
    
    if(sqlite3_get_table(db, sql, &result, &row, &column, NULL) != SQLITE_OK)
    {
    	fprintf(cgiOut, "<BODY>");
    	fprintf(cgiOut, "<H1>%s</H1>", "Server is busy...");		
    	fprintf(cgiOut, "<meta http-equiv="refresh" content="1;url=../index.html">");
    	sqlite3_close(db);
    	return -1;
    }
    

    非常容易理解,sqlite3_open()函数用来打开数据user.db,如果打开失败就会看到网页上有Server is busy...字样。

    char数组sql中存放了一条select语句,sqlite3_get_table()函数就是用来查询的,如果数据库出现问题,依然在网页上显示有Server is busy...

    如果在数据库里找不到对应的用户,变量row的值就是0,网页上显示Name or password error

    //login.c
    
    if(row == 0)
    {
    	fprintf(cgiOut, "<BODY>");
    	fprintf(cgiOut, "<H1>%s</H1>", "Name or password error");		
    	fprintf(cgiOut, "<meta http-equiv="refresh" content="1;url=../index.html">");
    	sqlite3_close(db);
    	return 0;
    }
    

    查看user.db数据库,只有一条记录

    c-1

    代码里的cgiOut是什么呢?,我们在cgic.h头文件的实现文件cgic.h中找到了答案。

    //cgic.c
    
    	cgiIn = stdin;
    	cgiOut = stdout;
    

    cgiOut就是标准输出。登录成功后,会跳转到主页面main.html。

    (二)环境信息获取——env_1_a9_info.cgi

    main.html页面是一个frameset框架,由left.html,top.html,right.html三个页面构成。

    其中只要left.html导航栏具有实际的功能选项。

    c-2

    我们先看看环境信息env1.html页面中的cgi程序

    <iframe  frameborder="0"  border=0 scrolling="no" src="cgi-bin/env_1_a9_info.cgi" width="100%" height="100%"></iframe>
    

    这个iframe告诉我们env_1_a9_info.cgi程序会处理当前的实时数据,并返回结果。

    分析一下env_1_a9_info.cgi的源码,来看看这些数据是怎么得到的。

    get_env()函数是用来获取环境信息的。

    //env_1_a9_info.c
    
    void get_env()
    {
    	sqlite3 *db;
    	char sql1[N] = {0}, sql2[N] = {0};
    	char **result1, **result2;
    	int row1, colunm1, row2, colunm2;
    	if	(sqlite3_open("/smartfarm.db", &db) != SQLITE_OK) {
    		fprintf(cgiOut, "<H2>sqlit smartfarm.db open err</H2>");	
    		errflag = 1;
    		return ;
    	}
    	sprintf(sql1, "select * from env where farm_no=1");
    	if (sqlite3_get_table(db, sql1, &result1, &row1, &colunm1, NULL) != SQLITE_OK) {
    		fprintf(cgiOut, "<H2>sqlite3_get_table  err</H2>");	
    		errflag = 1;
    		return ;
    	}
    	strncpy(temp_max, result1[colunm1 + 1], 9);
    	strncpy(temp_min, result1[colunm1 + 2], 9);
    	strncpy(hum_max, result1[colunm1 + 3], 9);
    	strncpy(hum_min, result1[colunm1 + 4], 9);
    	strncpy(light_max, result1[colunm1 +5], 9);
    	strncpy(light_min, result1[colunm1 + 6], 9);
    
    	.......
    
    	sqlite3_close(db);
    }
    

    不难发现,这些数据都是从数据库smartfarm.db中获取的。

    c-3

    最高/最低气温,湿度,光照,都在env表中。

    还有一张collect_env表,显示的是实时信息,但是好像没有用到。

    c-4

    //env_1_a9_info.c
    
    void get_env()
    {
    	........
    
    	sprintf(sql2, "select * from collect_env where farm_no=-1");
    	if (sqlite3_get_table(db, sql2, &result2, &row2, &colunm2, NULL) != SQLITE_OK) {
    		errflag = 1;
    		return ;
    	}
    #if 0
    	strncpy(temp, result2[colunm2 + 1], 9);
    	strncpy(hum, result2[colunm2 + 2], 9);
    	strncpy(light, result2[colunm2 + 3], 9);
    #endif
    
    }
    

    很明显,temp,hum,light分别存放当前的温度、湿度和光照,但是被注释掉了

    (三)实时监控的图像拍照处理——take_photo.cgi

    实现监控的CGI程序是take_photo.cgi,我们还是直接分析它的源代码。

    抛开cgi程序的fprintf不看,我们直接看核心处理的部分

    //take_photo.c
    
    	key_t key; 
    	int msgid; 
    	char buf[2] = {0};
    
    	if((key = ftok("/lib", 'a')) < 0)
    	{
    		perror("ftok");
    		exit(1);
    	}
    	if ((msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666)) < 0)
    	{
    		if (errno == EEXIST) {
    			msgid = msgget(key, 0666);
    		}else{
    			perror("msgget msgid");
    			return 0;
    		}
    	}
    
    	struct message msgbuf;
    	msgbuf.type = 1L;
    	msgbuf.msg_type = 2L;
    
    	cgiFormString("mode", buf, 2);
    	if(buf[0] <= '0' || buf[0] > '9')
    		goto err;
    	
    	msgbuf.text.camera = buf[0] - '0';
    	msgsnd (msgid, &msgbuf, sizeof (msgbuf) - sizeof (long), 0);
    
    

    这是一段linux的进程间通信,我们需要关注以下三个系统调用:

    • key_t ftok(const char *pathname, int proj_id)
    • int msgget(key_t key, int msgflg)
    • int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg)

    这些调用的细节不去管它,只要知道该进程需要收发其他进程的消息就可以了。

    我们关注这段代码,看看进程发送的消息msgbuf是什么。

    //take_photo.c
    
    	struct message msgbuf;
    	msgbuf.type = 1L;
    	msgbuf.msg_type = 2L;
    
    	cgiFormString("mode", buf, 2);
    	if(buf[0] <= '0' || buf[0] > '9')
    		goto err;
    	
    	msgbuf.text.camera = buf[0] - '0';
    

    注意到这里的cgiFormStirng函数,我们的buf存的是表单中收集到的数字,下面是表单的内容。

    <form id="take_photo" name="take_photo" method="post" action="cgi-bin/take_photo.cgi">
      <td width="15%"><input type="radio" name="mode" id="mode_1" value="1" />1</td>
      <td width="15%"><input type="radio" name="mode" id="mode_2" value="3" />3</td>
      <td width="15%"><input type="radio" name="mode" id="mode_3" value="5" />5</td>
      <td width="15%"><input type="radio" name="mode" id="mode_4" value="7" />7</td>
      <td width="15%"><input type="radio" name="mode" id="mode_5" value="9" />9</td>
      <td width="25%"><input type="submit" name="take_photo" id="take_photo" value="图像抓拍" /></td>
    </form>
    

    结构体message在struct.h头文件中有定义

    //struct.h
    
    union text {
    	unsigned char led;
    	unsigned char buzzer;
    	unsigned char camera;
    	unsigned char fan;
    	unsigned char relay;
    	unsigned char uart;
    	char phone[24];
    	struct control_parameter parameter;
    };
    
    struct message {
    	long type;
    	long msg_type;
    	union text text;
    };
    

    那么谁会接收msgbuf这个消息呢?我们可以在项目源码中找到答案。

    看一下,项目源码里面pthread_client_request.c中的一个代码片段

    //pthread_client_request.c
    
    	switch (msgbuf.msg_type) {
    
    		........
    
    		case CAMERA:
    			printf("received camera request
    ");
    			pthread_mutex_lock (&mutex_camera);
    			dev_camera_mask = msgbuf.text.camera;
    			pthread_cond_signal (&cond_camera);
    			pthread_mutex_unlock (&mutex_camera);
    			break;
    		
    		........
    		
    		default:	
    #if DEBUG
    			printf("pthread client request default break
    ");
    #endif
    			break;
    	
    	}
    

    也就是说会 有专门的线程用来处理摄像头有关的消息

    我们还记得之前take_photo.c有这样一段代码

    	struct message msgbuf;
    	msgbuf.type = 1L;
    	msgbuf.msg_type = 2L;
    

    1L、2L这样的魔数是什么意思呢

    答案在项目源码的global_variable.h头文件中

    //global_variable.h
    
    /*message queue type */
    #define REQUEST 1L        //msgbuf.type=1L
    
    /*message queue msg_type*/
    #define BUZZER 1L
    #define CAMERA 2L		//msgbuf.msg_type = 2L
    #define FAN 3L
    #define LED 4L
    #define RELAY 5L
    #define SET_PARAMETER 6L
    #define SMS 7L
    //#define SQLITE 7L
    #define UART 8L
    #define MODIFY_PHONE_NUM 9L
    #define BEEP 10L
    
    

    linux下一切都是文件,打开摄像头也不例外

    //pthread_camera.c
    
    	if ((dev_camera_fd = open (DEV_CAMERA, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
    	{
    		printf ("Cann't open file /tmp/webcam
    ");
    		exit (-1);
    	}
    

    其中DEV_CAMERA这个常量的定义在头文件global_variable.h中也能找到

    //global_variable.h
    
    #define DEV_GPRS 			"/dev/ttyUSB1"
    #define DEV_GPRS_2 			"/dev/ttyUSB2"
    #define DEV_ZIGBEE 			"/dev/ttyUSB0"
    #define DEV_LED				"/dev/led"
    #define DEV_BUZZER			"/dev/buzzer"
    #define DEV_CAMERA			"/tmp/webcam"
    #define SQLITE_OPEN			"./smartfarm.db"
    

    这个常量就是/tmp/webcam,这应该指的就是摄像头设备

    (四)照片展示——show_photo.cgi

    在“历史照片”这一功能中,调用的CGI程序是show_photo.cgi

    我们同样还是忽略掉CGI源码中的fprintf部分。

    #define DIRNAME		"../pice"
    #define PHOTO_NUM_MAX	100 // 最多100张,0-99
    
    int cgiMain()
    {
    	.........
    
    	if((dir = opendir(DIRNAME)) == NULL)    //打开图片存放的目录
    	{
    		perror("fail to opendir");
    		exit(1);
    	}
    
    	while((dirp = readdir(dir)) != NULL)  //读文件夹里文件的名字
    	{
    		if(dirp->d_name[0] > '9' || dirp->d_name[0] < '0')   //名字的第一个字母
    			continue;
    		sprintf(photoname[syncmsg1.photo_num++], "%s", dirp->d_name);
    		if(syncmsg1.photo_num >= PHOTO_NUM_MAX)         //判断是否超过最大值
    		{
    			syncmsg1.photo_num = PHOTO_NUM_MAX - 1;
    			break;
    		}
    	}
    
    	.........
    
    }
    

    我们从中可以知道,历史照片都是保存在/www/pice目录下的,而且最多只保存100张。

    四、调试过程中无法解决的问题

    难题1:WIFI模块无法使用

    原本这个系统是通过WIFI来访问并进行控制的,但是我们的WIFI模块出现了问题,现在只能用网线直连的方式控制系统。

    我们将手机热点配置为my_accent,密码设为012345678,在rootfs/etc中添加配置文件wpa-spk-tkip.conf

    # WPA-PSK/TKIP
    
    ctrl_interface=/var/run/wpa_supplicant
    
    network={
    	ssid="my_accent"
    	key_mgmt=WPA-PSK
    	proto=WPA
    	pairwise=CCMP
    	group=CCMP
    	psk="012345678"
    }
    

    更新文件系统以后重新烧写镜像,配置IP和网关后,执行命令wpa_supplicant -B -i wlan0 -c /etc/wpa-psk-tkip.conf

    3-1

    我们可以看到,wlan网卡依然没有连接到手机热点上。我们暂时没有解决这个问题。

    难题2:视频模块无法使用

    我们无论直接使用含有项目内容的文件系统,还是自己重新编译mjpeg-streamer都出现了同样的问题。

    我们启动开发板后,对mjpeg-streamer进行测试。

    运行mjpg_streamer -i "/mjpg/input_uvc.so -y -d /dev/video0" -o "/mjpg/output_http.so -w 192.168.9.111:8080"

    3-2

    系统显示由于设备被占用了,初始化失败。我们没有找到解决的方法。

    这些问题目前依然没有找到解决方案

    五、心得体会

    我们小组成员之前都没有嵌入式开发的经验,这次课程设计——“远程安防监控系统”对我们来说是一次挑战吧。虽然已经有现成的源码和成熟的文档,我们真正操作的时候还是遇到了许多问题,才真正体会到什么是“纸上得来终觉浅”。

    我们在这次课程设计中收获很多,我们惊讶地发现,之前的linux操作经验对我们进行嵌入式开发有很大的帮助,串口通讯之后,文件的操作与查看,网络状态的查看,进程的查看与kill,都如出一辙,不得不感叹linux的伟大,在linux知识上的投资收益真棒。

    C语言的可移植性还是不错的,很多源码进行较差编译的时候,只需要把原来的编译器"CC=gcc"改为"CC=arm-none-linux-gnueabi-gcc"就可以,有时甚至不用改变C语言代码就能正常make。

    嵌入式开发还是很有意思的,我们通过这次课程设计慢慢摸到了点门道。

    六、参考资料

    1. Win10怎么禁用驱动程序强制签名
    2. /dev/tty /dev/ttyS0 /dev/tty0区别
    3. 《远程安防监控系统项目文档》华清远见教育集团研发中心
  • 相关阅读:
    LOJ 6192 城市网络(树上倍增)
    SDOI2010代码拍卖会 (计数类DP)
    失控的未来交通工具 (LOJ 508,带权并查集,数论)
    线段树维护区间前k小
    POJ 1966 Cable TV Network (最大流最小割)
    网络流学习笔记
    最大子矩阵和
    POJ 1723 Soldiers (中位数)
    最大子矩阵求法详解
    CH0805 防线 (二分值域,前缀和,特殊性质)
  • 原文地址:https://www.cnblogs.com/wyf12138/p/9125423.html
Copyright © 2020-2023  润新知