今天开始学习制作智能家居网络系统,这个项目相对简单的多,但对于新手还是有难度的,所以今天我写出来,尽我最大努力写详细一点,单本人能力实在是有限,文章中肯定会出现很多错误,望大家指出来,一定改正。
讲解过程中所涉及全部代码下载地址:智能家居网络系统.rar
写制作过程之前我先讲解一下基本的框架和思路。(主要是框架,自己掌握知识了自己就可以添加其他的功能,比如显示温度湿度,气体浓度,光照强度,摄像头模块,这些都只需要添加相应的驱动就可以了,其他的基本一样)。
首先在开发板上搭建好服务器,然后自己写一个HTML表单,HTML主要是用来作为人际界面来交互信息,比如账户登陆,数据显示,传递数据,仅仅是一个界面,这些数据是要用一个叫CGI脚本文件来处理的,脚本文件怎么源程序里面是有的,CGI脚本文件是可以嵌套的,这些都是放在服务器端,只要服务器运行boa服务器就可以在局域网任意一台电脑访问这个服务器的IP地址来访问这个网页,在网页上输入账户,密码在进入到控制界面,这些数据处理全是放在脚本文件,里面有一些配置文件,下面我们来具体分析下这些代码。
!!!在做这个项目的前提是你的开发板能够正常跑起来,不管你是什么开发板,这里对开发板没有任何要求。
首先是搭建boa服务器:
下载boa源码,boa-0.94.13
# tar -xzvf boa-0.94.13.tar.tar
# cd boa-0.94.13/src/
# ./configure
# vi Makefile // : = gcc 和 CPP = gcc -E, 后 改 成 : = arm-Linux-gcc 和CPP =arm-linux-gcc -E
#vi src/compat.h /*把 120 行改为如下内容: #define TIMEZONE_OFFSET(foo) foo->tm_gmtoff*/
# vi boa.c
/*
if (setuid(0) != -1) {
DIE(”icky Linux kernel bug!”);
}
即修改为:
#if 0
if (setuid(0) != -1) {
DIE(”icky Linux kernel bug!”);
}
#endif
*/
/*下面红色这部分有些系统需要修改,有些不需要修改,你可以先不加,如果出错了在修改,错误打印在日志文件中*/
if (passwdbuf == NULL) {
DIE("getpwuid");
}
if (initgroups(passwdbuf->pw_name, passwdbuf->pw_gid) == -1) {
DIE("initgroups");
}
#endif
#if 0
if (passwdbuf == NULL) {
DIE("getpwuid");
}
if (initgroups(passwdbuf->pw_name, passwdbuf->pw_gid) == -1) {
DIE("initgroups");
}
#endif
#endif
#make
#arm-linux-strip boa /* 这里的优化就是去除 boa 中的调试信息: */把boa复制到根文件系统usr/bin目录下,这是一个应用程序。
配置boa.conf文件:
1 去掉boa.conf ServerName www.your.org.here 前的注释#
2 修改访问权限:修改User nobody 为 User 0 ; 修改Group nogroup 为 Group 0
3,修改DocumentRoot为DocumentRoot /etc/boa/www /* 存放网页的地方 */
DirectoryIndex index.html /* 网页名字,和这里要保持一致 */
ScriptAlias /cgi-bin//etc/boa/cgi-bin/ /* 存放cgi脚本的位置 */
在var文件下建立/log/boa文件下(这两个下面放ErrorLog /var/log/boa/error_log AccessLog /var/log/boa/access_log
)
拷贝PC上/etc/mime.types文件到开发板/etc目录下
现在boa服务器就搭建成功了,你可以拷贝 一个网页命名为index.html到开发板的/etc/boa/www下,记得把这些文件加可执行权限,在pc机上访问我们开发板,输入210.41.141.155(我的开发板ip是这么多,你只需要改成你的就可以了,但是移动要开发板和Pc机在同一个网段)就可以显示你写的网页了。
下面的代码是我一年前写的,现在回想起来确实出现了很多错误,内容显得累赘。当时自己对web前端不是很了解,所以很多简单的用法不是很会,正如有句话说的好,你今天在满意的节作到了明天再来回首,你仍然会觉得很多地方不足,需要改进。上面代码是可以使用的,这里我也就不更新代码了,在这个项目当中能够使用就行。如果需要精简代码我相信也难不倒在做的各位IT精英们。
下一编文章中我们将介绍怎么写html网页编写,主要用来人机界面来控制开发板的外设。
这一篇文章我将讲解html网页编程的基本规范,其实在我的html网页基础里面也讲解的很明白,这里我大概讲一下基本用法。下面写把代码写上来然后在一步一步讲解,后面的注释一定不要添加进去!!!
<html> /* 网页固定格式 */
<head> /* 网页头部 */
<title>智能网络家居系统--网络人VS灰鸽子制作</title>/* 网页上的标题栏 */
</head>
<body background="./image/back.jpg">/* 网页背景图片./image/back.jpg是存放的路径,*/
<br>
<br>
<h1 align="center"><font color="red"> /* 网页上的字体居中显示红色 */
/* 为了方便布局加的空格 */
智能网络家居系统</font></h1>
<p><center><b><font size="5"> /* 网页上的字体居中 */
请登录</font></b></center></p><BR><BR>
<form action="../cgi-bin/login.cgi" method="post"><center> /* 创建form表单设置第一条脚本路径和名字,post方式 */
用户名:
<input type="text" name="name"><br> /* 输入你的登录账户,字符串存放在name */
密 码:
<input type="password" name="password"><br><br>/* 输入你的登录密码 ,字符串存放在password*/
<input type="submit" value=" 登 录 "> /* 登录提交按钮,这是html基本格式,这里不详解了 */
<input type="reset" value="重新输入"> /* 重新输入按钮 */
</center></form>
<br><br><br><br><br><br><br><br><br><br> /*下面都是一些字体的显示,和前面基本一样的*/
<p><center><font color="brown">
智能网络家居系统项目 基于FL2440制作</center><p>
<p><center>
2015-08-11 by 网络人VS灰鸽子</center></p>
<p><center>
个人博客:http://blog.csdn.NET/qq_21792169</center></p>
</body>
</html>
在这里网页就写完了,其实网页也挺简单的,我们的主要工作是放在cgi脚本的处理,这里才是重点,我们可以测试下,./image/back.jpg,你可以在开发板上/etc/boa/www下创建一个image文件夹,该文件加专门用来存放图片,这里要可开始背景图片路径对应,不然网页找不到图片的位置。
然后开PC机上访问开发板的IP地址就OK了。
下文在是这个项目的核心内容,对C库函数,Java脚本,放心我都会总体讲解,主要还是框架,不懂发邮箱给我。
我还是按照这个程序的顺序来讲解,方便大家理解 一点,在这里得说明下boa服务器只是一个平台,只要搭建好了就可以不用去管它,我们只需要写cgi-bin和html文件,我们在上一篇文章中网页已经做好了,当我们点击提交按钮的时候,程序会去执行/etc/boa/cgi-bin目录下的login.cgi脚本文件,(为什么是这个文件,我们在上篇文章已经制定了这个脚本来处理数据),下面我们就来写这个脚本程序。
login.c文件:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *str_len=NULL;
int len=0;
char buf[100]="";
char user[20]="";
char passwd[20]="";
printf("%s\r\n\r\n","Content-Type:text/html"); /*固定格式,不用去管他,但是注意这条语句前后要空两行*/
printf("<html>\n<head>\n<title>CGI3:登录结果</title></head><br>\n"); /* 可以用printf打印网页,但是要按照 html格式打印,也可以网页保存在一个数组中来打印,后面文章中将会讲解,这里是打印新网页的标题*/
str_len = getenv("CONTENT_LENGTH");/* 这个很重要获取环境变量,是获取我们开始登陆信息 */
if( (str_len==NULL) || (sscanf(str_len, "%d", &len)!=1) || (len>80) ) /*这里注意下sscanf的用法*/
printf("sorry!error!");
fgets(buf, len+1, stdin);
sscanf(buf, "name=%[^&]&password=%s", user,passwd); /*把用户名保存在user中,把账户保存在passwd,观察这里的name,password。这里不是和网页里面设置的变量对应起来了么,这里只是提取出来吧了*/
if( (strncmp(user,"root",4)==0) && (strncmp(passwd, "111111", 6)==0) )/* 比较登陆用户是否正确 */
{
printf("<script language=\"JavaScript\">self.location='main.cgi';</script>");/* 调用一个新的main.cgi脚本 */
}
else
printf("<h1>Sorry! 用户名或密码错误!");
return 0;
}
编译:# arm-Linux-gcc login.c -o login.cgi (后面我会统一写进一个Makefile中)
在这一章可以这样测试,你在if( (strncmp(user,"root",4)==0) && (strncmp(passwd, "111111", 6)==0) )下面添加打印语句密码正确,和密码错误测试。注释掉打印脚本的那条语句,因为你都还没有写这个脚本,你怎么跳转呢。一定要把脚本文件复制到/etc/boa/cgi-bin目录下面,这是我们之前搭建boa服务器时候规定的文件加。
上一篇文章我们刚好可以登录用户了,如果登录成功就是调用main.cgi这个Java脚本在这一小节中我们就来学习main.c这个文件。
在这里我得强调一下,在网页中控制开发板上的开设,这个项目在所有开发板上基本通用,只是你的外设驱动程序不一样吧了,这里我写出我开发板上led驱动程序,如果你在网页上想显示温度,气体浓度,这需要加相应传感器和驱动就可以了,不同内核有可能用函数或者宏不一样,自己做相应的修改就好了。
led_drv.c驱动程序:(既然你都开始做项目了,我相信这个驱动程序应该难不倒你吧)
#include <Linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/gpio.h>
#define DEVICE_NAME "led1"
static struct class *led_1; /* 定义一个;类,用来自动创建设备节点 */
static unsigned long led_table [] = {
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB8,
S3C2410_GPB10,
};
static unsigned int led_cfg_table [] = {
S3C2410_GPB5_OUTP, //0x01<<10 defined in refg-gpio.h
S3C2410_GPB6_OUTP,
S3C2410_GPB8_OUTP,
S3C2410_GPB10_OUTP,
};
static int s3c2440_leds_ioctl( struct inode *inode, struct file *file,unsigned int cmd,unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4)
{
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[arg], !cmd);
return 0;
default:
return -EINVAL;
}
}
static struct file_operations s3c2440_leds_fops = {
.owner = THIS_MODULE,
.ioctl = s3c2440_leds_ioctl,
};
static int major;
static int __init s3c2440_leds_init(void)
{
int i;
major= register_chrdev(0, DEVICE_NAME, &s3c2440_leds_fops); /* 自动分配主设备号 */
led_1 = class_create(THIS_MODULE, DEVICE_NAME); /* 创建一个类 */
device_create(led_1, NULL, MKDEV(major, 0), NULL, "led");/* 在类下面创建一个设备节点,名字为led,应用程序就可以调用open("/dev/led", O_RDWR来访问驱动程序。 */
for (i = 0; i < 4 ; i++)
{
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);//设置输出输入
s3c2410_gpio_setpin(led_table[i], 1);
}
printk(DEVICE_NAME " initialized\n");
return 0;
}
static void __exit s3c2440_leds_exit(void)
{
unregister_chrdev(major, DEVICE_NAME); /* 取消注册 */
device_destroy(led_1,MKDEV(major, 0)); /* 删除类下面的设备节点 */
class_destroy(led_1);/* 删除类 */
}
module_init(s3c2440_leds_init);
module_exit(s3c2440_leds_exit);
MODULE_LICENSE("Dual BSD/GPL");
上面是我led驱动程序,在main.cgi中会用到。下面我们来看看main.c怎么编写的。
main.c文件:
#include<string.h>
#include <stdio.h>
#include <stdlib.h>
#include<fcntl.h>
void config(int *led_config,int led_num); /* led配置文件 */
void led_fun(int *led_config); /*调用led驱动函数*/
int main()
{
int led_num=0;
int led_config[4]={0,0,0,0};
FILE *fp_html;
char buf[512]="";
char *len;
printf("%s\r\n\r\n","Content-Type:text/html");
len = getenv("CONTENT_LENGTH");
if(len != NULL)
{
unsigned int content_len = 0;
char *data;
content_len = atoi(len);
data = malloc(content_len + 1);
read(0, data, content_len);
sscanf(data,"led%d",&led_num); /* 把那个led状态改变读出来 */
free(data);
}
else
{
led_num=0;
}
config(led_config,led_num);
led_fun(led_config);
if( (fp_html=fopen("main_html","r")) == NULL) /*这里注意,我们不能再写一个html网页,只能通过printf来打印一个网页,为了方便,我们把这个网页读取出来放在一个数组中,在用printf来打印,这个网页是我们用来人机控制界面,开始我们设置了一个用户登录界面*/
{
exit(1);
}
while(fgets(buf,512,fp_html))
{
if(strncmp(buf,"$FLAG4$",7)==0) /* 这里用到一个变量 */
{
int i=0;
for(i=0; i < 4; i++)
{
if(led_config[i]==0)
{
printf("<img src=\"../www/image/off.jpg\" width=30 height=30 hspace=45 >");/* 图片的切换 */
}
else
{
printf("<img src=\"../www/image/on.jpg\" width=30 height=30 hspace=45 >");
}
}
}
else
printf("%s",buf);
}
return 0;
}
void config(int *led_config,int led_num)
{
FILE *fp;
if((fp=fopen("config.ini","r+"))==NULL) /*这里很重要,开始我实现这个功能的时候,总是不成功,后来找到原因了,当我们点击网页上的按钮时候,这个脚本就会重新刷新一次,从开始重新执行,我们的led状态就会恢复初值,后来才想到一个办法,把led的状态保存在一个配置文件config.ini中,*/
{
printf("<p>fopen Error!<a href=\"/cgi-bin/main.cgi\">Return/a>");
exit(1);
}
fscanf(fp,"led1=%d,led2=%d,led3=%d,led4=%d",led_config,led_config+1,led_config+2,led_config+3);
/* fscanf是把配置文件的状态读取出来,来控制led */
if(led_num > 0)
{
if(led_config[led_num-1]==1)
{
led_config[led_num-1] = 0;
}
else
{
led_config[led_num-1] = 1;
}
fseek(fp,7*(led_num-1)+5,SEEK_SET);
fprintf(fp,"%d",led_config[led_num-1]); /* 把改变的状态重新存放在配置文件,注意看文件句柄fp */
}
fclose(fp);
}
void led_fun(int *led_config)
{
unsigned int led_num = 0;
int led_fd = open("/dev/led", O_RDWR); /* 打开驱动程序 */
for (led_num = 0; led_num < 4; led_num++) /*传入我们的led配置文件*/
{
ioctl(led_fd, led_config[led_num], led_num);
}
close(led_fd);
}
led配置文件:
config.ini:
led1=0,led2=1,led3=0,led4=1 /* 这个可以自己设置led初始状态,但是一定要按照这个格式写,因为我们
main.cgi中读取配置文件就是按照的这种格式 */
main_html文件编写如下:
<script>function AddDataPost(sUserId,sUserName)
{
var obj = new ActiveXObject("Microsoft.XMLHTTP");
sUserId = escape(sUserId);
sUserName = escape(sUserName);
var userInfo = "userid="+sUserId+"&username="+sUserName;
obj.open("POST","getData.asp",false);
obj.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
obj.send(userInfo);
return unescape(obj.responseText);
}
function show()
{
var date = new Date();
var now = "";
now = date.getFullYear()+"年";
now = now + (date.getMonth()+1)+"月";
now = now + date.getDate()+"日";
now = now + date.getHours()+"时";
now = now + date.getMinutes()+"分";
now = now + date.getSeconds()+"秒";
document.getElementById("nowDiv").innerHTML = now;
setTimeout("show()",1000);
}
</script>
<html>
<head>
<title>智能网络家电主页面</title>
</head>
<body bgcolor="Pink" >
<center>
<h2 align=center><font color=Blue><body οnlοad="show()">
<div id="nowDiv"></div></h2></font>
</body>
<br>
<h1 align="center"><font color="crimson">智能网络家居系统</h1>
<br><br><br>
<FORM METHOD="post">
<p><font color="red"><h2 align=center> 温 度
$FLAG1$</h2><b>
<p><font color="red"><h2 align=center> 湿 度
$FLAG2$</h2><b>
<p><font color="red"><h2 align=center> 粉尘浓度
$FLAG3$</h2><b>
<br><br><br>
<input type="submit" name="view" value=" 刷 新 ">
</FORM>
<br></b>
<h1 align="center"><font color="orangered">电灯控制</h1>
<br>
<FORM METHOD="post">
$FLAG4$
<br>
<input type="submit" name="led1" value=" 卧 室 ">
<input type="submit" name="led2" value=" 厨 房 ">
<input type="submit" name="led3" value=" 客 厅 ">
<input type="submit" name="led4" value=" 卫生间 ">
<br>
</FORM>
</center>
</body>
</html>
<a href="http://www.pzhu.cn"> < 返 回 > </a>
<p><font color="black" size="4"><center>攀枝花学院 2015-08-11 @四川</center></p>
Makefile的编写如下:
obj-m :=led_drv.o
KERNELDIR ?= /home/work/Linux/linux-2.6.28.7 /* 编译驱动的时候取药制定内核的路径 */
PWD := $(shell pwd)
default:
arm-linux-gcc login.c -o login.cgi /* 编译脚本文件 */
arm-linux-gcc main.c -o main.cgi /* 编译脚本文件 */
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules /* 编译驱动 */
clean:
rm -f *o *.mod.o *mod.c *.symvers *.order *.cgi
在这里说一下,我只给大家提供思路,基本框架是这样的,具体细节还是得靠大家去学习,眼睛痛的不行,需要休息下,所以就冲冲结束了,需要添加其他的功能也可以自己添加,比如摄像头,温度,湿度,GPS定位系统设计。这些我写出来的话估计得花一周,写这个只能家居网络系统设计主要给大家一个思路,如果发现错误,希望读者告知一声,小弟一定改动。补充一下这个用到的JavaScript可以用CSS代替,这个是以前做的智能家居网络系统,很多地方不是很成熟,很多地方写得不是很简洁,我也不想用我学的新知识来重新写这些代码,比较时间还是有限。如果代码有地方不是很清楚的可以看源码,源码的下载地址在第一篇文章已经给出来了,如果制作过程性遇到什么问题可以邮箱跟我,相互学习。