SQLite3 数据库与网页html

一、数据库基础认知与选型逻辑

1. 数据库核心定义与结构

数据库是结构化数据的存储仓库 ,核心价值在于高效管理海量数据,支持数据的增删改查(CRUD)、统计分析、事务处理等操作。其核心层级结构可精准概括为:

复制代码
数据库(DB)→ 数据表(Table)→ 数据记录(Row,行)→ 数据字段(Column,列)
  • 数据库:数据的容器,一个数据库可包含多张数据表;
  • 数据表:存储同一类数据的二维结构,由行和列组成;
  • 数据记录:数据表中的一行,对应一个完整的对象信息;
  • 数据字段:数据表中的一列,对应对象的一个属性(如用户的 "姓名""年龄")。

2. 数据库分类与嵌入式场景选型

数据库按规模和应用场景可分为三大类,不同类型的数据库适用于不同的业务需求,具体对比如下:

分类 代表产品 特点 适用场景
大型数据库 ORACLE、DB2 功能强大、支持海量数据、高并发,需独立服务器部署 企业级核心业务系统、银行金融系统
中型数据库 MySQL、MSSQL 开源免费(MySQL)、性能稳定,需独立进程运行 网站后台、中小型业务系统
小型数据库 SQLite、powdb 轻量级、无独立进程、文件型存储 嵌入式设备、移动端应用、本地缓存

嵌入式设备为什么首选 SQLite? 嵌入式 Linux 设备通常存在内存小、存储有限、无后台服务的特点,而 SQLite 完美适配这些限制,其核心优势如下:

  1. 开源免费:遵循 GNU 协议,基于 C 语言开发,可直接嵌入到应用程序中;
  2. 极致轻量:核心代码量仅 1 万行左右,编译后体积小于 10M,运行时占用内存极低;
  3. 免安装部署:无需独立的数据库服务进程,与应用程序融为一体,即嵌即用;
  4. 文件型存储:整个数据库对应一个磁盘文件,可直接拷贝、移动,备份与迁移极其方便;
  5. 大容量支持:单数据库文件最大支持 2TB 数据存储,完全满足嵌入式设备的存储需求;
  6. 跨平台兼容:支持 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:设置查询条件,支持><=ORAND等运算符;
  • 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_opensqlite3_execsqlite3_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处理查询结果的核心,其调用逻辑和返回值直接影响结果处理,必须严格遵守以下规则:

  1. 调用次数 :回调函数会被逐行调用,查询结果有多少条记录,就调用多少次;
  2. 返回值要求 :回调函数必须返回0,否则会立即终止后续调用(仅处理第一条记录);
  3. 参数含义
    • void *arg:自定义参数,通过sqlite3_exec的第四个参数传递;
    • int col:当前查询结果的字段数量;
    • char **result:每行数据的字段值数组(result[i]为第 i 个字段的值);
    • char **title:字段名数组(title[i]为第 i 个字段的名称);
  4. 空值处理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(通用网关接口)** 实现,完整的业务流程如下:

  1. 数据采集层:嵌入式程序通过传感器驱动采集温度、电压、电流数据;
  2. 数据存储层:程序调用 SQLite3 API,将采集到的数据存入本地数据库;
  3. 前端交互层:用户通过浏览器访问嵌入式设备的 HTML 页面,提交查询 / 操作请求;
  4. 后端处理层:嵌入式 HTTP 服务(如 boa、lighttpd)将请求转发给 CGI 程序,CGI 程序调用 SQLite3 API(含回调函数)操作数据库;
  5. 结果展示层:CGI 程序将数据库操作结果(通过回调函数处理后)格式化为 HTML 页面,返回给浏览器展示。

核心优势

  1. 断网可用:数据存储在本地数据库,断网时仍可采集和查看数据;
  2. 轻量高效:SQLite3 和 HTML 均无需额外依赖,占用资源少;
  3. 操作便捷:通过 Web 界面即可管理数据,无需复杂的终端指令。

总结

  1. SQLite3 回调函数是处理查询结果的核心,必须返回 0,否则仅处理第一条记录;
  2. 回调函数的arg参数可传递自定义数据(如统计结构体),实现 "查询 + 统计" 一体化;
  3. 嵌入式场景中,SQLite3(C 语言 + 回调函数)+ HTML 表单 + CGI 是实现 "本地数据管理 + 前端交互" 的最优组合。
相关推荐
isNotNullX2 小时前
什么是云计算?一文讲清云计算的概念与作用
数据库·云计算·企业管理
北京地铁1号线2 小时前
1.4 RAG中的Schema
数据库·rag
冰暮流星2 小时前
c语言如何实现字符串复制替换
c语言·c++·算法
技术不打烊2 小时前
从 MySQL 到 PG,你需要跨越的几道语法“鸿沟”
数据库·mysql·postgresql
m0_748245922 小时前
HTML 文本格式化
前端·html
五阿哥永琪2 小时前
MySQL 索引原理与优化实战指南:从失效场景到联合索引设计
数据库·mysql
无限进步_2 小时前
【C语言&数据结构】二叉树链式结构完全指南:从基础到进阶
c语言·开发语言·数据结构·c++·git·算法·visual studio
desert_xu2 小时前
ORA-20079 错误栗子
数据库·oracle