一、数据库基础认知与选型逻辑
1. 数据库核心定义与结构
数据库是结构化数据的存储仓库 ,核心价值在于高效管理海量数据,支持数据的增删改查(CRUD)、统计分析、事务处理等操作。其核心层级结构可精准概括为:
数据库(DB)→ 数据表(Table)→ 数据记录(Row,行)→ 数据字段(Column,列)
- 数据库:数据的容器,一个数据库可包含多张数据表;
- 数据表:存储同一类数据的二维结构,由行和列组成;
- 数据记录:数据表中的一行,对应一个完整的对象信息;
- 数据字段:数据表中的一列,对应对象的一个属性(如用户的 "姓名""年龄")。
2. 数据库分类与嵌入式场景选型
数据库按规模和应用场景可分为三大类,不同类型的数据库适用于不同的业务需求,具体对比如下:
| 分类 | 代表产品 | 特点 | 适用场景 |
|---|---|---|---|
| 大型数据库 | ORACLE、DB2 | 功能强大、支持海量数据、高并发,需独立服务器部署 | 企业级核心业务系统、银行金融系统 |
| 中型数据库 | MySQL、MSSQL | 开源免费(MySQL)、性能稳定,需独立进程运行 | 网站后台、中小型业务系统 |
| 小型数据库 | SQLite、powdb | 轻量级、无独立进程、文件型存储 | 嵌入式设备、移动端应用、本地缓存 |
嵌入式设备为什么首选 SQLite? 嵌入式 Linux 设备通常存在内存小、存储有限、无后台服务的特点,而 SQLite 完美适配这些限制,其核心优势如下:
- 开源免费:遵循 GNU 协议,基于 C 语言开发,可直接嵌入到应用程序中;
- 极致轻量:核心代码量仅 1 万行左右,编译后体积小于 10M,运行时占用内存极低;
- 免安装部署:无需独立的数据库服务进程,与应用程序融为一体,即嵌即用;
- 文件型存储:整个数据库对应一个磁盘文件,可直接拷贝、移动,备份与迁移极其方便;
- 大容量支持:单数据库文件最大支持 2TB 数据存储,完全满足嵌入式设备的存储需求;
- 跨平台兼容:支持 Windows、Linux、嵌入式系统等几乎所有操作系统,代码可移植性强。
3. 数据库核心名词解释
在学习数据库的过程中,以下核心名词必须掌握,这是理解数据库操作的基础:
- DB(Database) :数据库,存储数据的容器。例如我们创建的
123.db就是一个数据库文件,所有的用户数据都存储在这个文件中。 - DBMS(Database Management System):数据库管理系统,用于管理数据库的软件。SQLite 本身就是一个 DBMS,我们通过它提供的指令和 API 操作数据库。
- MIS(Management Information System):管理信息系统,基于数据库开发的业务系统,用于实现数据的管理和业务流程自动化。例如嵌入式设备的传感器数据管理系统。
- OA(Office Automation):办公自动化系统,数据库的典型应用场景,如考勤管理、文件审批系统等。
二、SQLite3 环境搭建与终端指令实操
1. SQLite3 环境安装(Linux 平台)
对于嵌入式 Linux 开发,我们通常在 PC 端的 Linux 系统(如 Ubuntu)上完成代码开发和测试,再交叉编译到目标嵌入式设备。安装步骤如下:
# 安装 SQLite3 终端客户端,用于直接操作数据库
sudo apt-get install sqlite3
# 安装 SQLite3 开发库,用于C语言程序编译(必须安装)
sudo apt-get install libsqlite3-dev
# 验证安装是否成功
sqlite3 --version # 打印版本信息则说明安装成功
2. C 语言程序编译指令
在编写完 SQLite3 的 C 语言程序后,编译时必须链接sqlite3库,否则会出现函数未定义的错误。编译指令格式如下:
# 基础编译指令:gcc 源文件.c -o 可执行文件 -lsqlite3
gcc sqlite_test.c -o sqlite_test -lsqlite3
# 嵌入式交叉编译(以arm架构为例)
arm-linux-gcc sqlite_test.c -o sqlite_test_arm -lsqlite3
3. SQLite3 终端常用指令详解
安装完成后,通过sqlite3 数据库文件名指令即可进入终端交互模式,常用指令如下表所示:
| 指令 | 功能说明 | 示例 |
|---|---|---|
.database |
查看当前数据库关联的磁盘文件路径 | .database |
.table |
查看当前数据库中的所有数据表 | .table |
.schema [表名] |
查看指定表的建表语句,无表名则查看所有表 | .schema user |
.header on/off |
开启 / 关闭查询结果的表头显示(默认关闭) | .header on |
.mode column |
设置查询结果的显示模式为列对齐,更易读 | .mode column |
.quit/.exit/.q |
退出 SQLite3 终端交互模式 | .quit |
终端指令实操示例
# 1. 创建并进入名为 user.db 的数据库(不存在则创建,存在则打开)
sqlite3 user.db
# 2. 开启表头显示和列对齐模式(推荐开启,方便查看数据)
sqlite> .header on
sqlite> .mode column
# 3. 创建 user 表,包含 id(整型)、name(字符串)、age(整型)字段
sqlite> create table user(id int, name char, age int);
# 4. 查看表结构,验证创建是否成功
sqlite> .schema user
# 输出结果:CREATE TABLE user(id int, name char, age int);
# 5. 插入3条测试数据
sqlite> insert into user values(1, "zhangsan", 20);
sqlite> insert into user values(2, "lisi", 25);
sqlite> insert into user values(3, "wangwu", 30);
# 6. 查询所有数据
sqlite> select * from user;
# 7. 条件查询:查询年龄大于22岁的用户
sqlite> select id, name from user where age > 22;
# 8. 通配符查询:查询名字以"zhang"开头的用户
sqlite> select * from user where name like 'zhang%';
# 9. 修改数据:将 id=1 的用户年龄改为22
sqlite> update user set age=22 where id=1;
# 10. 删除数据:删除 id=3 的用户
sqlite> delete from user where id=3;
# 11. 退出终端
sqlite> .quit
4. SQL 核心语句深度解析
SQL(结构化查询语言)是操作所有关系型数据库的通用语言,SQLite 完全支持标准 SQL 语法,以下是嵌入式开发中最常用的 SQL 语句详解。
(1)建表语句:CREATE TABLE
语法格式:
CREATE TABLE [表名](字段名1 数据类型1, 字段名2 数据类型2, ...);
嵌入式场景示例:创建传感器数据表,存储温度、电压、电流数据和采集时间:
CREATE TABLE sensor_data(
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 自增主键,唯一标识每条记录
temperature FLOAT, -- 温度值,浮点型
voltage FLOAT, -- 电压值,浮点型
current FLOAT, -- 电流值,浮点型
collect_time TIMESTAMP -- 采集时间,时间戳类型
);
关键说明:
INTEGER PRIMARY KEY AUTOINCREMENT:设置自增主键,确保每条记录的唯一性;FLOAT:浮点型,适用于传感器的小数数据;TIMESTAMP:时间戳类型,记录数据采集的具体时间。
(2)插入语句:INSERT INTO
语法格式:
INSERT INTO [表名] VALUES(值1, 值2, ...); -- 插入所有字段
INSERT INTO [表名](字段名1, 字段名2) VALUES(值1, 值2); -- 插入指定字段
嵌入式场景示例:插入一条传感器采集数据:
INSERT INTO sensor_data(temperature, voltage, current, collect_time)
VALUES(25.5, 3.3, 0.8, datetime('now'));
关键说明 :datetime('now') 是 SQLite 内置函数,用于获取当前系统时间。
(3)查询语句:SELECT
语法格式:
SELECT 字段名1,字段名2 FROM 表名 WHERE 条件 ORDER BY 字段名 ASC/DESC;
核心关键词:
*:代表查询所有字段;WHERE:设置查询条件,支持>、<、=、OR、AND等运算符;ORDER BY:对查询结果排序,ASC升序(默认),DESC降序;LIKE:模糊查询,配合通配符使用:_:匹配一个任意字符;%:匹配零个或多个任意字符。
嵌入式场景示例:
-- 查询所有传感器数据,按采集时间降序排列(最新数据在前)
SELECT * FROM sensor_data ORDER BY collect_time DESC;
-- 查询温度大于25度且电压小于3.5V的数据
SELECT temperature, current, collect_time FROM sensor_data WHERE temperature>25 AND voltage<3.5;
(4)修改语句:UPDATE
语法格式:
UPDATE 表名 SET 字段名1=值1, 字段名2=值2 WHERE 条件;
关键注意 :务必加上WHERE条件,否则会修改表中所有记录!
(5)删除语句:DELETE
语法格式:
DELETE FROM 表名 WHERE 条件;
关键注意 :务必加上WHERE条件,否则会删除表中所有记录!
三、C 语言操作 SQLite3 核心 API 与回调函数深度实战
嵌入式开发中,我们需要通过 C 语言代码实现对 SQLite3 的自动化操作,核心是掌握 3 个 API:sqlite3_open、sqlite3_exec、sqlite3_close,其中回调函数是处理查询结果的核心机制,也是嵌入式开发中最易出错的关键点。
1. 核心 API 详解
| API 函数 | 函数原型 | 功能说明 | 参数解析 |
|---|---|---|---|
| sqlite3_open | int sqlite3_open(const char *filename, sqlite3 **ppDb); |
打开或创建数据库 | - filename:数据库文件名- ppDb:输出参数,返回数据库句柄- 返回值:SQLITE_OK(0)表示成功 |
| sqlite3_exec | int sqlite3_exec(sqlite3 *db, const char *sql, int (*callback)(void*,int,char**,char**), void *arg, char **errmsg); |
执行任意 SQL 语句 | - db:数据库句柄- sql:要执行的 SQL 语句- callback:查询语句的回调函数(非查询传 NULL)- arg:传递给回调函数的自定义参数- errmsg:输出参数,返回错误信息- 返回值:SQLITE_OK表示成功 |
| sqlite3_close | int sqlite3_close(sqlite3 *db); |
关闭数据库句柄 | - db:要关闭的数据库句柄- 返回值:SQLITE_OK表示成功 |
2. 回调函数核心规则(必记!)
回调函数是sqlite3_exec处理查询结果的核心,其调用逻辑和返回值直接影响结果处理,必须严格遵守以下规则:
- 调用次数 :回调函数会被逐行调用,查询结果有多少条记录,就调用多少次;
- 返回值要求 :回调函数必须返回
0,否则会立即终止后续调用(仅处理第一条记录); - 参数含义 :
void *arg:自定义参数,通过sqlite3_exec的第四个参数传递;int col:当前查询结果的字段数量;char **result:每行数据的字段值数组(result[i]为第 i 个字段的值);char **title:字段名数组(title[i]为第 i 个字段的名称);
- 空值处理 :
result[i]可能为NULL(字段值为空),必须判断后使用,避免程序崩溃。
3. 实战案例 1:基础用户表查询(回调函数入门)
本案例实现查询user表的所有数据,通过回调函数格式化输出结果,覆盖回调函数的核心使用场景。
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
/**
* @brief 用户表查询回调函数(核心)
* @param arg 自定义参数(此处传递查询条件提示)
* @param col 字段数量
* @param result 每行数据的字段值数组
* @param title 字段名数组
* @return 0:继续处理下一行;非0:终止处理
*/
int user_callback(void *arg, int col, char **result, char **title) {
// 静态变量:标记是否是第一次调用(仅打印一次表头)
static int is_first_call = 1;
// 打印自定义参数(验证arg的传递)
if (is_first_call && arg != NULL) {
printf("【查询条件】:%s\n", (char*)arg);
}
// 第一次调用时打印表头(字段名)
if (is_first_call) {
printf("--------------------------------------------------\n");
for (int i = 0; i < col; i++) {
// 格式化输出:左对齐,占10个字符宽度
printf("%-10s", title[i]);
}
printf("\n--------------------------------------------------\n");
is_first_call = 0; // 重置标记,避免重复打印表头
}
// 逐行打印数据(处理NULL值)
for (int i = 0; i < col; i++) {
// 如果字段值为NULL,显示"空",否则显示实际值
printf("%-10s", result[i] ? result[i] : "空");
}
printf("\n");
// 必须返回0!否则仅处理第一条记录
return 0;
}
/**
* @brief 初始化用户表并插入测试数据
* @param db 数据库句柄
* @return 0成功,非0失败
*/
int init_user_table(sqlite3 *db) {
char *errmsg = NULL;
int ret;
// 1. 创建user表(不存在则创建)
char *create_sql = "CREATE TABLE IF NOT EXISTS user("
"id INT, "
"name CHAR, "
"age INT);";
ret = sqlite3_exec(db, create_sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK) {
fprintf(stderr, "创建user表失败:%s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
// 2. 清空表中原有数据(避免重复)
char *clear_sql = "DELETE FROM user;";
ret = sqlite3_exec(db, clear_sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK) {
fprintf(stderr, "清空user表失败:%s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
// 3. 插入测试数据
char *insert_sql = "INSERT INTO user VALUES "
"(1, 'zhangsan', 20), "
"(2, 'lisi', 25), "
"(3, 'wangwu', 30), "
"(4, 'zhaoliu', 35);";
ret = sqlite3_exec(db, insert_sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK) {
fprintf(stderr, "插入测试数据失败:%s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
printf("user表初始化完成!\n");
return 0;
}
/**
* @brief 查询用户表数据(核心:调用回调函数)
* @param db 数据库句柄
* @return 0成功,非0失败
*/
int query_user_data(sqlite3 *db) {
char *errmsg = NULL;
int ret;
// 自定义参数:传递给回调函数的查询条件提示
char *arg = "查询所有用户数据";
// 执行查询SQL,指定回调函数处理结果
char *query_sql = "SELECT id, name, age FROM user WHERE age >= 20;";
ret = sqlite3_exec(db, query_sql, user_callback, (void*)arg, &errmsg);
if (ret != SQLITE_OK) {
fprintf(stderr, "查询user表失败:%s\n", errmsg);
sqlite3_free(errmsg); // 必须释放错误信息,避免内存泄漏
return -1;
}
return 0;
}
int main() {
sqlite3 *db = NULL;
int ret;
// 1. 打开数据库(不存在则创建)
ret = sqlite3_open("user.db", &db);
if (ret != SQLITE_OK) {
fprintf(stderr, "打开数据库失败:%s\n", sqlite3_errmsg(db));
sqlite3_close(db); // 即使打开失败,也要尝试关闭句柄
return -1;
}
// 2. 初始化用户表
if (init_user_table(db) != 0) {
sqlite3_close(db);
return -1;
}
// 3. 查询用户数据(核心:回调函数处理结果)
printf("\n开始查询用户数据:\n");
if (query_user_data(db) != 0) {
sqlite3_close(db);
return -1;
}
// 4. 关闭数据库
sqlite3_close(db);
printf("\n程序执行完成!\n");
return 0;
}
代码编译与运行结果
# 编译代码
gcc user_query.c -o user_query -lsqlite3
# 运行程序
./user_query
预期输出:
user表初始化完成!
开始查询用户数据:
【查询条件】:查询所有用户数据
--------------------------------------------------
id name age
--------------------------------------------------
1 zhangsan 20
2 lisi 25
3 wangwu 30
4 zhaoliu 35
程序执行完成!
4. 实战案例 2:传感器数据查询(回调函数进阶)
本案例实现传感器数据的查询与统计,通过回调函数的arg参数传递自定义统计数据,覆盖嵌入式场景中 "数据查询 + 实时统计" 的核心需求。
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
// 自定义统计结构体:通过回调函数arg传递
typedef struct {
int total_count; // 总记录数
float avg_temperature; // 平均温度
float sum_temperature; // 温度总和
float max_voltage; // 最大电压
} SensorStat;
/**
* @brief 传感器数据回调函数(带统计功能)
* @param arg 自定义统计结构体指针
* @param col 字段数量
* @param result 每行数据的字段值数组
* @param title 字段名数组
* @return 0:继续处理;非0:终止
*/
int sensor_callback(void *arg, int col, char **result, char **title) {
static int is_first_call = 1;
SensorStat *stat = (SensorStat*)arg;
// 打印表头
if (is_first_call) {
printf("------------------------传感器数据------------------------\n");
for (int i = 0; i < col; i++) {
printf("%-15s", title[i]);
}
printf("\n--------------------------------------------------------\n");
is_first_call = 0;
}
// 打印当前行数据
for (int i = 0; i < col; i++) {
printf("%-15s", result[i] ? result[i] : "空");
}
printf("\n");
// 统计数据(解析字段值并计算)
stat->total_count++; // 记录数+1
// 解析温度值(转换为浮点型)
float temp = atof(result[1] ? result[1] : "0");
stat->sum_temperature += temp;
// 解析电压值并更新最大值
float volt = atof(result[2] ? result[2] : "0");
if (volt > stat->max_voltage) {
stat->max_voltage = volt;
}
return 0;
}
/**
* @brief 初始化传感器表并插入测试数据
* @param db 数据库句柄
* @return 0成功,非0失败
*/
int init_sensor_table(sqlite3 *db) {
char *errmsg = NULL;
int ret;
// 创建传感器表
char *create_sql = "CREATE TABLE IF NOT EXISTS sensor_data("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"temperature FLOAT, "
"voltage FLOAT, "
"current FLOAT, "
"collect_time TIMESTAMP);";
ret = sqlite3_exec(db, create_sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK) {
fprintf(stderr, "创建传感器表失败:%s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
// 清空原有数据
char *clear_sql = "DELETE FROM sensor_data;";
sqlite3_exec(db, clear_sql, NULL, NULL, &errmsg);
// 插入测试数据
char *insert_sql = "INSERT INTO sensor_data(temperature, voltage, current, collect_time) VALUES "
"(25.5, 3.3, 0.8, datetime('now')), "
"(26.2, 3.4, 0.75, datetime('now')), "
"(24.8, 3.2, 0.9, datetime('now')), "
"(25.9, 3.5, 0.85, datetime('now'));";
ret = sqlite3_exec(db, insert_sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK) {
fprintf(stderr, "插入传感器数据失败:%s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
return 0;
}
/**
* @brief 查询并统计传感器数据
* @param db 数据库句柄
* @return 0成功,非0失败
*/
int query_sensor_data(sqlite3 *db) {
char *errmsg = NULL;
int ret;
// 初始化统计结构体
SensorStat stat = {0, 0.0, 0.0, 0.0};
// 执行查询SQL,传递统计结构体指针给回调函数
char *query_sql = "SELECT id, temperature, voltage, current, collect_time FROM sensor_data;";
ret = sqlite3_exec(db, query_sql, sensor_callback, (void*)&stat, &errmsg);
if (ret != SQLITE_OK) {
fprintf(stderr, "查询传感器数据失败:%s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
// 计算平均温度
if (stat.total_count > 0) {
stat.avg_temperature = stat.sum_temperature / stat.total_count;
}
// 打印统计结果
printf("\n------------------------统计结果------------------------\n");
printf("总记录数:%d\n", stat.total_count);
printf("平均温度:%.2f ℃\n", stat.avg_temperature);
printf("最大电压:%.2f V\n", stat.max_voltage);
printf("--------------------------------------------------------\n");
return 0;
}
int main() {
sqlite3 *db = NULL;
int ret;
// 打开数据库
ret = sqlite3_open("sensor.db", &db);
if (ret != SQLITE_OK) {
fprintf(stderr, "打开数据库失败:%s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return -1;
}
// 初始化传感器表
if (init_sensor_table(db) != 0) {
sqlite3_close(db);
return -1;
}
// 查询并统计传感器数据
query_sensor_data(db);
// 关闭数据库
sqlite3_close(db);
return 0;
}
运行结果
------------------------传感器数据------------------------
id temperature voltage current collect_time
--------------------------------------------------------
1 25.5 3.3 0.8 2026-01-14 10:00:00
2 26.2 3.4 0.75 2026-01-14 10:00:00
3 24.8 3.2 0.9 2026-01-14 10:00:00
4 25.9 3.5 0.85 2026-01-14 10:00:00
------------------------统计结果------------------------
总记录数:4
平均温度:25.60 ℃
最大电压:3.50 V
--------------------------------------------------------
四、Web 前端基础:HTML 实现嵌入式交互界面
嵌入式设备的管理界面无需复杂的前端框架,纯 HTML 即可满足基本需求。通过 HTML 表单,我们可以实现前端参数输入、数据提交,配合嵌入式设备的 CGI 程序,就能完成 "前端界面 - 后端数据库" 的交互。
1. HTML 基本结构与语法规范
HTML(超文本标记语言)是构建网页的基础,其核心是标签化语法,所有内容都包裹在标签中。一个完整的 HTML 页面结构如下:
<!DOCTYPE html> <!-- 声明文档类型为HTML5 -->
<html lang="zh-CN"> <!-- 根标签,lang指定页面语言 -->
<head> <!-- 头部标签,用于设置页面元信息 -->
<meta charset="UTF-8"> <!-- 设置字符编码为UTF-8,支持中文 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 适配移动端 -->
<title>嵌入式传感器数据管理系统</title> <!-- 页面标题,显示在浏览器标签栏 -->
</head>
<body> <!-- 主体标签,页面所有可见内容都在这里 -->
<h1>嵌入式传感器数据管理界面</h1> <!-- 一级标题 -->
<p>这是一个基于HTML和SQLite3的嵌入式数据管理页面</p> <!-- 段落标签 -->
</body>
</html>
2. 超链接与图片:实现页面跳转与资源展示
超链接(<a>标签)是网页跳转的核心,图片(<img>标签)用于展示嵌入式设备的状态图标、传感器波形图等资源。
(1)超链接标签<a>
语法格式:
<a href="目标地址" target="打开方式">链接文字/图片</a>
属性说明:
href:必填,目标地址,可以是外部网址、本地 HTML 文件路径;target:可选,_self(当前窗口打开,默认)、_blank(新窗口打开)。
嵌入式场景示例:
<!-- 1. 链接到外部网站(如传感器数据云平台) -->
<a href="https://www.xxx.com/cloud" target="_blank">查看云端数据</a>
<br> <!-- 换行标签 -->
<!-- 2. 链接到本地页面(如数据查询页面) -->
<a href="query_data.html">查询本地传感器数据</a>
<br>
<!-- 3. 图片链接(点击图片跳转到数据添加页面) -->
<a href="add_data.html">
<img src="add_icon.png" alt="添加数据" width="50" height="50">
</a>
(2)图片标签<img>
语法格式:
<img src="图片路径" alt="替代文本" width="宽度" height="高度">
属性说明:
src:必填,图片路径,支持相对路径和绝对路径;alt:必填,图片加载失败时显示的文本;width/height:可选,设置图片尺寸,支持像素(px)和百分比(%)。
嵌入式场景示例:
<!-- 显示传感器实时波形图 -->
<img src="waveform.png" alt="传感器波形图" width="80%" height="300">
3. 表单标签<form>:实现前端数据提交
表单是前端与嵌入式后端交互的核心,通过表单可以收集用户输入的参数(如查询时间范围、传感器阈值),并提交给嵌入式设备的 CGI 程序,最终实现对数据库的操作。
(1)表单核心属性
| 属性 | 说明 | 嵌入式场景常用值 |
|---|---|---|
action |
表单提交的目标地址 | 嵌入式 CGI 程序路径,如/cgi-bin/query_sensor.cgi |
method |
数据提交方式 | get(参数附在 URL 后,简单数据)、post(数据打包发送,敏感数据) |
(2)表单核心元素<input>
<input>是表单中最常用的元素,通过type属性可以实现不同的输入控件,嵌入式开发中常用的类型如下:
type值 |
控件类型 | 嵌入式场景用途 |
|---|---|---|
text |
单行文本框 | 输入查询时间、传感器 ID 等 |
number |
数字输入框 | 输入传感器阈值、采集间隔等 |
password |
密码框 | 输入设备管理密码 |
submit |
提交按钮 | 提交表单数据 |
reset |
重置按钮 | 清空表单所有输入内容 |
hidden |
隐藏字段 | 传递无需用户输入的参数(如操作类型) |
(3)嵌入式场景实战:传感器数据查询表单
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>传感器数据查询</title>
</head>
<body>
<h2>传感器数据查询界面</h2>
<!-- 表单提交到嵌入式设备的CGI程序 -->
<form action="/cgi-bin/query_sensor.cgi" method="post">
<!-- 隐藏字段:传递操作类型 -->
<input type="hidden" name="op_type" value="query">
<!-- 温度范围查询 -->
<label>温度最小值:</label>
<input type="number" name="temp_min" step="0.1" required>
<br><br>
<label>温度最大值:</label>
<input type="number" name="temp_max" step="0.1" required>
<br><br>
<!-- 提交和重置按钮 -->
<input type="submit" value="查询数据">
<input type="reset" value="清空输入">
</form>
</body>
</html>
五、嵌入式场景整合方案
在嵌入式 Linux 设备中,SQLite3 和 HTML 的整合可以通过 **CGI(通用网关接口)** 实现,完整的业务流程如下:
- 数据采集层:嵌入式程序通过传感器驱动采集温度、电压、电流数据;
- 数据存储层:程序调用 SQLite3 API,将采集到的数据存入本地数据库;
- 前端交互层:用户通过浏览器访问嵌入式设备的 HTML 页面,提交查询 / 操作请求;
- 后端处理层:嵌入式 HTTP 服务(如 boa、lighttpd)将请求转发给 CGI 程序,CGI 程序调用 SQLite3 API(含回调函数)操作数据库;
- 结果展示层:CGI 程序将数据库操作结果(通过回调函数处理后)格式化为 HTML 页面,返回给浏览器展示。
核心优势
- 断网可用:数据存储在本地数据库,断网时仍可采集和查看数据;
- 轻量高效:SQLite3 和 HTML 均无需额外依赖,占用资源少;
- 操作便捷:通过 Web 界面即可管理数据,无需复杂的终端指令。
总结
- SQLite3 回调函数是处理查询结果的核心,必须返回 0,否则仅处理第一条记录;
- 回调函数的
arg参数可传递自定义数据(如统计结构体),实现 "查询 + 统计" 一体化; - 嵌入式场景中,SQLite3(C 语言 + 回调函数)+ HTML 表单 + CGI 是实现 "本地数据管理 + 前端交互" 的最优组合。