分类:
2012-05-30 15:11:13
原文地址:基于Goahead和SQLite的数据采集与查询 作者:xuyuanchao
摘 要:在远程抄表、智能家居等各种嵌入式系统中,都需要实现数据采集和远程查询。提出了一种基于goahead和sqlite的实现方案,并给出了实现细节,包括移植编译安装以及交互式程序的编写和其中遇到的问题。本方案在模块化和扩展性方面要好于其他方案,容易借鉴,已得到成功应用。
关键词:goahead;sqlite;嵌入式web服务器;嵌入式数据库
Data Gathering and Querying based on Goahead and SQLite
Abstract: Many embedded systems, like remote meter reading system and intelligent home system,, need data gathering and remote querying. This paper proposed a solution based on goahead and sqlite, including cross compiling, installing, interaction programming and some problems. This solution has better modulization and expansibility than other solutions, which has already used in intelligent home remote control system.
Key words: goahead;sqlite;embedded web server;embedded database
1 引 言
在远程抄表系统、智能家庭网关[1]等大量的嵌入式系统中,都涉及到数据的存储和查询。对于数据的存储,考虑到数据的一致性管理以及程序编写的便捷,普遍采用嵌入式关系数据库。对于数据的查询,分本地查询和远程查询。诸如无线点菜系统,需要编写界面友好的GUI应用程序查询本地和远程数据,此时需要配备液晶和触摸屏等外围设备,成本因此增加,所以大量的嵌入式设备都只需要进行远程的数据管理和查询,普遍采用的技术是嵌入式web服务器和编写动态web页面程序访问数据库。
目前,嵌入式web服务器和嵌入式数据库种类繁多,选择什么样的嵌入式数据库和web服务器成为问题的焦点。在工业界,广泛采用的嵌入式数据库为SQLite,广泛采用的嵌入式web服务器主要有两种:BOA和goahead。前者已有大量的文献记录,后者相对要少得多。
笔者在智能家居远程控制系统的研发中,使用zigbee无线模块接收部署在各个点的温度、湿度、烟雾传感器数据,这些数据都统一存储在基于S3C2410的中央处理模块中,该模块集成了SQLite和goahead,前者存储数据,后者提供远程数据管理。经过比较分析,笔者认为Goahead在功能、移植、使用、扩展等方面更甚一筹。实际上,这两种软件的独立使用并不困难,难点在于如何在Goahead中访问SQLite数据库以及如何在页面上显示出来,诸多文献[2]都只是简单提及各自的API函数,没有给出具体实现,实际上,在编译和集成应用中有很多需要解决的问题,这些问题成为工程应用的一个个障碍,本文将逐一探讨。
2 软件架构
笔者所用的实验平台是北京博创科技的S3C2410-S嵌入式教学平台,操作系统为嵌入式Linux 2.6.18。由于该平台没有zigbee无线模块,因此单独设计了一款采用CC2430的zigbee模块用于数据和控制信息的无线传输,zigbee模块与嵌入式平台使用串口通信,为了使系统各功能之间模块化更强,编写了独立的串口通信程序专门用于接收和存储来自zigbee模块的数据,而远程数据的查询只针对已经存储到SQLite中的数据,通过嵌入式web服务器以及编写相应的动态页面程序来实现。
Goahead是一款嵌入式web服务器,它基本上属于一个HTTP1.0标准的WEB服务器,对一些HTTP1.1的特性如(持久连接)也提供了支持,支持asp、cgi、embedded JavaScrip脚本语言,能够运行在Windows CE,VxWorks,Linux等主流平台上。
SQLite诞生于2000年5月,是实现了SQL 92标准的一个大子集的嵌入式数据库,SQLite的内存组织非常高效,只需在很小的内存中维护其很小的尺寸,非常适合嵌入式应用。
实际开发中发现,除在Goahead中如何编写程序访问数据库并在页面中显示出来之外,Goahead与SQLite的移植与编译也非常重要,编译如果通不过,就无法往下进行。
3 Goahead移植与编译
笔者移植的版本是2.1.8,可到文献[3]下载。之前的版本在移植时可能与本文所叙述的过程差异较大,不建议使用。Goahead的编译相对复杂,笔者认为,编译的不容易成功是影响Goahead普及的重要原因。
用命令tar zxvf webs218.tar.gz解压,可以看到源码目录下有VXWORKS、CE、LINUX等子目录,进入LINUX子目录,修改Makefile文件[4]。在该文件开头“all: compile”之前添加如下的黑斜体部分的文字。
CROSS_COMPILE = armv4l-unknown-linux-
CC = $(CROSS_COMPILE)gcc
......(部分省略)
CROSS_COMPILE为交叉编译器的名字,armv4l-unknown-linux-gcc为笔者使用的交叉编译器,要提前安装好交叉编译器,并且将路径加入到PATH环境变量中。在该文件末尾将cc -c -o $@ $(DEBUG) $(CFLAGS) $(IFLAGS) $<修改为$(CC) -c -o $@ $(DEBUG) $(CFLAGS) $(IFLAGS) $<,然后在该目录下直接输入make进行编译。
当编译出现main.c:325: warning: the use of `tempnam' is dangerous, better use `mkstemp'
警告时,需要修改main.c (LINUX子目录) ,在函数websGetCgiCommName(..)函数中,将pname1 = tempnam(NULL, T("cgi"));修改为pname1 = (char_t *)mkstemp(T("cgi"));之后警告消除,编译成功。
接着就是将编译生成的可执行文件和库文件等下载到开发板中,然后运行webs,设置好IP地址,交叉线连接开发版与PC机,发现无法连接。解决办法是直接在main.c的initwebs(...) 函数中写入IP地址。
/* if ((hp = gethostbyname(host)) == NULL) {
......(省略)
memcpy((char *) &intaddr, (char *) hp->h_addr_list[0], (size_t) hp->h_length); */
intaddr.s_addr = inet_addr("10.2.2.14"); //10.2.2.14为开发板的IP
重新编译后连接成功。笔者也曾在MIPS环境下用两个不同版本的交叉编译器进行编译,均出现/lib/libc.so.6: version `GLIBC_2.3' not found (required by webs)问题无法运行,网上咨询该问题的人很多,但很少有人清楚回答。经查发现,两次所使用的GLIBC的版本都是libc-2.2.5.so,低于2.3,很多高版本软件不仅对GCC编译器有高版本要求,而且对GLIBC库也要求高于2.3 版本,实际上只要高于所指定的2.3版本即可,并不要求必须等于该版本。
解决办法是重新找一个高版本GLIBC库(ftp://ftp.gnu.org/)重新编译,或重新找一个已经编译好的工具链,或使用crosstool制作交叉编译器。第一种方法不太容易成功。在拷贝覆盖glibc库时,注意符号连接信息不能丢失,用cp –raf方式拷贝,再次运行,此时会提示更莫名其妙的错误segmentation fault,通过ldd webs发现webs不仅依赖libc库,而且还依赖ld,也是版本问题,于是将ld相关库也从交叉编译安装目录的lib下拷贝到嵌入式开发板根文件系统的lib下,重新运行就可以了。
4 SQLite移植与编译
同样,在编译SQLite前需要提前安装和配置好交叉编译环境,下载SQLite源码[5]。用命令tar zxvf sqlite-3.5.9.tar.gz解压,然后运行配置文件./configure --host=arm-linux --prefix=/dist-sqlite3 --disable-tcl,在开发板上如果不需要使用tcl,使用--disable-tcl参数,--prefix是安装路径,可以随意指定。注意等号右边不能留有空格,如果一切顺利,会根据系统环境生成Makefile。下一步就是编译和安装make && make install。如果编译通过,会在/dist-sqlite3路径中生成三个目录:bin 有可执行文件sqlite3 ,可以放在开发板上运行;include 内有sqlite3.h与sqlite3ext.h两个头文件;lib 内有libsqlite3.a静态库文件和libsqlite3.so.0.8.6动态库文件。本文提到的SQLite版本相对以前的某些低版本而言编译要简单很多,所以要注意选择,剩下的是程序的编写。
SQLite的C语言API以下面三个核心函数为基础:
sqlite* sqlite_open(const char* dbname , int mode , char** errmsg);
void sqlite_close(sqlite* db);
int sqlite_exec (sqlite * db, char * sql , int (* Callback) (void* , int , char* * , char* * ), void* parg, char* * errmsg);
其中, 前两个函数用于打开与关闭数据库, sqlite_exec函数用来处理SQL查询, 它含有五个参数:(1)调用sqlite_open 函数获得的数据库结构的指针。(2)容纳了一个或更多SQL语句的字符串。(3)指向Callback函数的指针,查询结果的每一条记录都调用该函数。(4)成为Callback函数第一个参数的指针。(5)指向错误串的指针。其中,Callback函数由用户编写,用来接收查询结果, 查询结果的每一条记录都会调用Callback 函数一次,其原型为:int Callback(void* pArg, int argc , char* * argv, char* *columnNames);其中, 第一个参数接收客户代码的任意信息; 第二个参数是字段数; 第三个参数是一个字符串数组, 每一个串是记录的一个字段值; 第四个参数是字段名。Callback函数是用户根据应用编写的, 正常应返回0。如果Callback函数非0, 则查询失败。文献[6]有详细的参考范例。
5 Goahead中集成SQLite
无论是数据查询还是远程管理,都需要web服务器支持动态页面程序的编写和运行。Goahead支持ASP、CGI等多种方式,经过比较,推荐使用嵌入式Javascript方式。
嵌入式Javascript采用表单提交的方式,嵌入已经实现的C函数至页面文件中,在提交时,由已经在系统定义并注册的函数来接收处理,通过代码写web页面来实现输出。其过程主要涉及以下几个方面:
1、页面设计。嵌入式Javascript文件为ASP文件,其内容可按照标准的页面来设计,要达到交互使用,则必须响应用户提交,可以通过在页面中加入来实现,其中formTest即为响应时系统调用的注册后的函数。其函数格式为:static void formTest(webs_t wp, char_t *path, char_t *query);
2、注册Form提交函数。在main.c中定义websFormDefine(T("formTest"), formTest);
3、formTest函数实现。
在浏览器中输入将能看到表单提交和页面回显的简单示例,下面的代码实现了从SQLite数据库中读取数据,并在页面显示的基本功能。
static int callback(void *wp, int argc, char **argv, char **azColName)
{ int i;
for (i = 0; i < argc; i++) {
websWrite((webs_t)wp, T("%s : %s
\n"), azColName[i], argv[i] ? argv[i] : "NULL");
}
return 0;
}
static void formTest(webs_t wp, char_t *path, char_t *query)
{
char_t *dbname, *tblname;
sqlite3 *db;
char *zErrMsg = 0;
int rc;
dbname = websGetVar(wp, T("dbname"), T("ex")); //dbname是数据库名称,ex为缺省名称。
tablename = websGetVar(wp, T("tblname"), T("select * from temp;")); //tblname是表名称,可以为SQL语句。
websHeader(wp);
rc = sqlite3_open(dbname, &db);
rc = sqlite3_exec(db, tablename, callback, (void *)wp, &zErrMsg);
sqlite3_close(db);
websFooter(wp);
websDone(wp, 200);
}
由于用到了sqlite的库函数,需要加上sqlite3.h头文件以及在编译时加上sqlite3库函数,具体为修改Makefile文件,将其中的IFLAGS= -I..改为IFLAGS=-I.. -I/dist-sqlite3/include -L/dist-sqlite3/lib -lsqlite3。
6 结束语
Goahead和sqlite在移植过程中出现的各种问题给移植和应用带来了障碍,两者之间的融合也是一个技术难点。笔者尝试了Goahead交互式编程的多种方法,发现其中提到的标准CGI方法在程序运行时出现segmentation fault错误,还没有查到解决办法。不过,本文提到的方法已经完全可以满足数据管理的需求,功能扩展和代码修改都要比CGI简单清晰。该方法已经得到成功应用,实际上有类似需求的系统广泛存在,本文给出的流程以及结合参考文献可以轻松地应用在其他系统中。
参考文献:
[1]陈一明. 嵌入式数据库的智能家居网关设计[J]. 微计算机信息,2009,(11).
[2]石为人,张杰等.无线传感器网络嵌入式网关的设计与实现[J].计算机应用,2006(11): 2525-2527.
[3]GoAheadSoftware Inc.[EB/OL].
[4]goahead的移植.http://blog.chinaunix.net/u2/86537/showart_1667519.html[EB/OL].
[5]Sqlite download website. [EB/OL].
[6]sqlite的移植.http://blog.chinaunix.net/u2/86537/showart_1676823.html[EB/OL].