基于OrangePi Zero 2实现垃圾分类智能垃圾桶项目(11)将指令来源和次数保存到数据库中(SQLite),指令来源和发出时间以及垃圾类型保存在文件中

SQLite(嵌入式数据库)

概念:

一种轻量级的关系型数据库管理系统,可以在应用程序中作为一个单独的组件运行,因此也被称为嵌入式数据库。与传统的客户端-服务器架构不同,SQLite 数据库存储在单个文件中,并直接与应用程序交互。它不需要专门的服务器进程或守护进程,也不需要网络通信,因此非常适合用于移动设备、桌面应用程序或其他小型嵌入式环境。
SQLite 使用 C API 进行访问,可以轻松地集成到任何支持 C 语言的环境中。它还提供了多种语言绑定,包括 Python、Java、PHP、Ruby 等,使得在这些语言中使用 SQLite 更加容易。 是一种高效、易用的嵌入式数据库解决方案,特别适合在资源有限的环境中使用。

选择SQLite原因:

SQLite 和 MySQL 都是非常流行的关系型数据库管理系统,但它们之间存在一些重要的差异:

1. 架构差异:MySQL 是一种典型的客户-服务器架构,由服务器进程来管理和处理所有数据库请求;而 SQLite 则没有独立的服务器进程,所有的操作都在客户端完成,可以直接与应用程序交互。
 2. 大小和性能差异:MySQL 提供了许多高级特性,例如表分区、索引优化等,而且能够处理大规模的数据集;而 SQLite 则更加简单和紧凑,更适合小规模的应用程序和嵌入式环境。
 3. 安全性和可靠性:MySQL 支持复杂的权限控制和安全机制;而 SQLite 主要依赖于操作系统和文件系统的安全措施。

总体来说,MySQL 更适合高性能、高并发的大规模应用,而 SQLite 则更适合资源受限的小型应用或嵌入式系统。

SQLite数据库安装:

1、执行 sqlite 指令

找不到相关内容说明没有安装过

2、执行安装指令

一般sqlite3的使用场景更多,运行“sudo apt-get -y install sqlite3

sudo apt-get -y install sqlite3

运行“sudo apt-get -y install sqlite” ,此时的版本是2.8.17,大家直接装sqlite3

运行上面指令时报错E:dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem. 

“dpkg 已经被打断,您必须手动运行 'sudo dpkg --configure -a' 来纠正这个问题。” 这通常意味着你的 Ubuntu 系统正在尝试安装软件包的过程中遇到了某种错误,导致 dpkg (Debian 包管理器)无法正常工作。

按提示输入'sudo dpkg --configure -a'

再次输入安装指令

3、检查是否安装成功

完成后输入 sqlite3 查看

输入 .quit 可退出

 SQLite数据库基本操作:

在工作目录“/home/orangepi/”下创建一个文件夹,将数据库文件存放在这里:

1、数据库的创建/打开

法1:

在刚刚创建的文件夹里面

  • 输入“sqlite3” 进入数据库
  • 输入“.open test.db
  • 输入“.quit” 退出

执行“.open A.db”时,如果一个数据库A.db不存在则会创建并打开;若该数据库已经存在就会打开;且 “.open A.db”不是SQL命令,对应的C接口是sqlite3_open( )函数 

法2:

在刚刚创建的文件夹里面

  • 输入“sqlite3 test.db” 在命令运行当前窗口创建数据库test.db
  • 输入“.databases” 列出当前打开的数据库
  • 输入“.quit” 退出

 

2、表格的创建/打开

数据库表格的创建和结构体很类似,在实际中也经常将两者进行转换:

create table sf(age Integer,name char,height Integer); //在这里“int”应该写成“Integer”,且写在变量名的后方

打开刚刚创建的test.db数据库,并在其中添加一张名为sf的表格,表格有三个字段:整型的age和height,char型的name


 执行“create table AA(....)”时,如果表AA不存在则会创建并打开;若表格已经存在则会报错;且 “create table AA(....)”是SQL命令,对应的C接口是sqlite3_exec( )函数

3、插入数据

insert into sf values(18,'cxk',180); 
 
//也可以此一次只插入部分的字段内容,比如只插入name和score,不插入id
insert into sf(name,height) values('jg',190); //插入部分字段内容

4、查看数据库的记录

select * from sf; //查询所有字段的结果
select name,score from sf; //查询数据库中部分字段的内容

5、删除一条记录

delete from sf where name = cxk;

6、更改一条记录

update stu set name = 'majia' where score = 77;

7、增加一列

alter table stu add column sex char;

8、删除一张表

drop table stu;

9、输入错误指令处理方式

当输入了无法识别的指令,数据库会进入“...>”的状态:

输入“CTRL + Z” 就可以强制退出:


SQLite的编程操作

在学习了SQlite的基本操作之后,虽然有能力进行数据基本的增删改查,但是都需要输入“sqlite3”在数据库命令行来执行,我们希望实现的是更自动化的运行,比如程序在获取数据后可以自动写入数据库,这就需要学习如何打开/创建数据库的C接口:

打开/创建数据库的C接口

    sqlite3_open(const char *filename, sqlite3 **ppDb)

该指令打开一个指向 SQLite 数据库文件的连接,返回一个用于其他 SQLite 程序的数据库连接对象。

    sqlite3_close(sqlite3*)

该指令关闭之前调用 sqlite3_open() 打开的数据库连接。
所有与连接相关的语句都应在连接关闭之前完成。 如果还有查询没有完成,sqlite3_close() 将返回 SQLITE_BUSY 禁止关闭的错误消息。

    const char *sqlite3_errmsg(sqlite3*);

该指令通常用来获取最近调用的API接口返回的错误代码
 错误代码:

 test1.c

#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
 
int main(int argc, char** argv)
{
    sqlite3 *db;
    char *zErrMsg = 0;
    int ret;
 
    if(argc < 2){
        printf("Usage: %s xxx.db\n",argv[0]);
        return -1;
    }
 
    ret = sqlite3_open(argv[1], &db);
 
    if(ret != SQLITE_OK){
        printf("Can't open database: %s\n", sqlite3_errmsg(db));
        exit(0);
    }else{
        printf("Opened database successfully\n");
    }
    sqlite3_close(db);
    printf("close database\n");
}

 编译运行,gcc编译需要链接sqlite3的库,执行a.out时给程序指定数据库test.db

如果test.db本来就存在,那么“./a.out test.db” 也会成功运行,只不过从“创建test.db并关闭”变成了“打开test.db并关闭” 


sqlite3_exec函数的引入 & SELECT操作

`sqlite3_exec()` 是 SQLite 的一个内置函数,用于在指定的数据库连接上执行一个或多个 SQL 语句,并返回结果。它可以用于执行任何合法的 SQL 语句,包括 SELECT、INSERT、UPDATE 和 DELETE 等等。
使用 `sqlite3_exec()` 可以简化 SQLite 应用程序中的代码编写和维护工作,因为开发者只需要调用这个函数来执行 SQL 语句,而无需手动解析和处理查询结果。
此外,由于 `sqlite3_exec()` 支持回调函数,因此它还可以用于处理复杂的查询结果集。例如,当执行一个 SELECT 语句时,可以通过提供一个回调函数来处理每一行数据,以便实现更加灵活的数据处理方式。

sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)

其中:

  • `db`: 是一个已打开的数据库连接。
  • ` strSql`: 是要执行的 SQL 语句或多个 SQL 语句。
  • `callback`: 是一个回调函数,用于处理查询结果。如果不需要处理查询结果,则可以将其设置为 `NULL`。
  • `void*`: 是传递给回调函数的第一个参数。
  • `errmsg`: 是一个指针,用于接收错误消息。

该函数返回 SQLITE_OK(表示成功)、SQLITE_ERROR、SQLITE_INTERNAL、SQLITE_PERM、SQLITE_ABORT 等等,具体取决于操作的结果。

int callback(void *arg, int column_size, char *column_value[], char *column_name[])

用于处理 SQLite 查询结果的回调函数。当使用 sqlite3_exec() 执行包含 SELECT 语句或其他需要获取查询结果的操作时,可以将此回调函数作为参数传递给该函数。 

  •  第一个参数void *arg 是sqlite3_exec函数的第四个参数data
  • column_size:数据库的字段 数
  • column_value[ ]:列的值
  • column_name:字段名字

实现查看数据库记录指令 sqlite3_exec(db, "select * from sf;", callback, 0, &zErrMsg);

#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
 
 
int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
	int i;
	printf("arg=%s\n",(char *)arg);
 
	for(i=0;i<column_size;i++){
		printf("%s = %s\n", column_name[i], column_value[i]);
	}
	printf("=======================\n");
	return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
}
 
 
int main(int argc, char** argv)
{
	sqlite3 *db;
	char *zErrMsg = 0;
	int ret;
 
	if(argc < 2){
		printf("Usage: %s xxx.db\n",argv[0]);
		return -1;
	}
 
	/*open database*/
	ret = sqlite3_open(argv[1], &db);
 
	if(ret != SQLITE_OK){ 
		printf("Can't open database: %s\n", sqlite3_errmsg(db));
		exit(0);
	}else{
		printf("Opened database successfully\n");
	}
 
	/* execute sql statement */
	ret = sqlite3_exec(db, "select * from sf;", callback, 0, &zErrMsg);
 
	/* close database */
	sqlite3_close(db);
	printf("close database\n");
	return 0;
}

 编译运行,gcc编译需要链接sqlite3的库,执行a.out时给程序指定查看的数据库test.db

 


创建表 & 插入数据

#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
 
 
int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
	int i;
	printf("arg=%s\n",(char *)arg);
 
	for(i=0;i<column_size;i++){
		printf("%s = %s\n", column_name[i], column_value[i]);
	}
	printf("=======================\n");
	return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
}
 
 
int main(int argc, char** argv)
{
	sqlite3 *db;
	char *zErrMsg = 0;
	int ret;
	char *sql;
 
	if(argc < 2){
		printf("Usage: %s xxx.db\n",argv[0]);
		return -1;
	}
 
	/*open database*/
	ret = sqlite3_open(argv[1], &db);
 
	if(ret != SQLITE_OK){ 
		printf("Can't open database: %s\n", sqlite3_errmsg(db));
		exit(0);
	}else{
		printf("Opened database successfully\n");
	}
 
	/* execute sql statement -- create table */ 
	sql = "CREATE TABLE COMPANY(" \
           "ID INT PRIMARY KEY NOT NULL," \
           "NAME CHAR(30) NOT NULL," \
           "AGE INT NOT NULL," \
           "ADDRESS CHAR(50)," \
           "SALARY REAL );";
 
	ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
	if(ret != SQLITE_OK){
		printf("Can't create table: %s\n", sqlite3_errmsg(db));
		exit(0);
	}else{
		printf("Table create successfully\n");
	}
 
	/* execute sql statement -- insert data */
	sql = "insert into company values(1813,'company.1',3,'aaa street No.293',37732);";
 
    ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't insert data: %s\n", sqlite3_errmsg(db));
        exit(0);
    }else{
        printf("Inset data successfully\n");
    }
 
	/* execute sql statement -- show content */
    ret = sqlite3_exec(db, "select * from company;", callback, 0, &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't show content: %s\n", sqlite3_errmsg(db));
        exit(0);
    }
 
 
	/* close database */
	sqlite3_close(db);
	printf("close database\n");
	return 0;
}


UPDATE操作 & DELETE操作 

对于上一步创建的company表格,更新它的地址,然后删除这条记录

#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
 
 
int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
	int i;
	printf("arg=%s\n",(char *)arg);
 
	for(i=0;i<column_size;i++){
		printf("%s = %s\n", column_name[i], column_value[i]);
	}
	printf("=======================\n");
	return 0;//必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
}
 
 
int main(int argc, char** argv)
{
	sqlite3 *db;
	char *zErrMsg = 0;
	int ret;
	char *sql;
 
	if(argc < 2){
		printf("Usage: %s xxx.db\n",argv[0]);
		return -1;
	}
 
	/*open database*/
	ret = sqlite3_open(argv[1], &db);
 
	if(ret != SQLITE_OK){ 
		printf("Can't open database: %s\n", sqlite3_errmsg(db));
		exit(0);
	}else{
		printf("Opened database successfully\n");
	}
 
	/* execute sql statement -- create table */ 
	sql = "CREATE TABLE COMPANY(" \
           "ID INT PRIMARY KEY NOT NULL," \
           "NAME CHAR(30) NOT NULL," \
           "AGE INT NOT NULL," \
           "ADDRESS CHAR(50)," \
           "SALARY REAL );";
 
	ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
	if(ret != SQLITE_OK){
		printf("Can't create table: %s\n", sqlite3_errmsg(db));
		//exit(0); 这里将exit注释掉的原因是如果表格已经存在,不需要退出,而是继续执行即可
	}else{
		printf("Table create successfully\n");
	}
 
	/* execute sql statement -- show content */
    ret = sqlite3_exec(db, "select * from company;", callback, "before update", &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't show content1: %s\n", sqlite3_errmsg(db));
        exit(0);
    }
 
	/* execute sql statement -- update data */
    sql = "update company set address = 'ccc street No.666' where id = 1813;";
 
    ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't update data: %s\n", sqlite3_errmsg(db));
        exit(0);
    }else{
        printf("Update data successfully\n");
    }
 
	/* execute sql statement -- show content */
    ret = sqlite3_exec(db, "select * from company;", callback, "after update", &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't show content2: %s\n", sqlite3_errmsg(db));
        exit(0);
    }
 
	/* execute sql statement -- delete data */
    sql = "delete from company where id = 1813;";
 
    ret = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't delete data: %s\n", sqlite3_errmsg(db));
        exit(0);
    }else{
        printf("Delete data successfully\n");
    }
 
	/* execute sql statement -- show content */
    ret = sqlite3_exec(db, "select * from company;", callback, "after delete", &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't show content3: %s\n", sqlite3_errmsg(db));
        exit(0);
    }
 
 
	/* close database */
	sqlite3_close(db);
	printf("close database\n");
	return 0;
}

由于表格已经存在,所以创建表格的命令出错了,不过由于我的修改,代码会继续运行而不是直接exit;更新数据后,地址的确改变了;而删除数据后,由于本来就只有一条记录,所以此时company表没有数据,所以此时哪怕调用了“select * from company;” sqlite3_exec函数也不会调用callback来打印信息!
 




!!回归正题!!

代码实现:

项目源码:

https://pan.baidu.com/s/1KDznkE5dkpjBGxTbj9HnzQ
提取码:sail
 

1、使用数据库需包含头文件

2、定义变量

3、数据库和表创建函数

void create_db(){//创建/打开数据库并创建一个名为history的表格
    int ret;
    char *create_table_sql;
    //打开数据库
	ret = sqlite3_open("history.db", &db);
 
	if(ret != SQLITE_OK){
		printf("Can't open database: %s\n", sqlite3_errmsg(db));
		exit(0);
	}else{
		printf("Open database successfully\n");
	}
 
	//创建表格
	//create_table_sql = "create table history(cause char,count Integer);";
	create_table_sql = "CREATE TABLE HISTORY(" \
					   "CAUSE CHAR(30) PRIMARY KEY NOT NULL," \
					   "COUNT INT NOT NULL );" ;
 
	ret = sqlite3_exec(db, create_table_sql, callback, 0, &zErrMsg);
	if(ret != SQLITE_OK){
		printf("Can't create table: %s\n", sqlite3_errmsg(db));
		//exit(0);
	}else{
		printf("Table create successfully\n");
	}
}

4、表初始化函数(插入初始化数据)

void init_table() //用于初始化数据库中表格的值
{
	char *init_table_sql;
    int ret;
	init_table_sql = "insert into history values('socket',0);";
	ret = sqlite3_exec(db, init_table_sql, callback, 0, &zErrMsg);
	if(ret != SQLITE_OK){
		printf("Can't init table value: %s\n", sqlite3_errmsg(db));
	}
	init_table_sql = "insert into history values('sound',0);";
	ret = sqlite3_exec(db, init_table_sql, callback, 0, &zErrMsg);
	if(ret != SQLITE_OK){
		printf("Can't init table value: %s\n", sqlite3_errmsg(db));
	}
}

 

5、写入数据

在上一节将数据存储到文件中我们创建了一个线程record,这一节我们将指令来源和次数保存到数据库中(SQLite)、指令来源和发出时间以及垃圾类型保存在文件中这两个步骤都放到record里面运行

分别写两个函数功能是将数据写入文件和数据库

void data_file() //将历史记录写入文件的函数
{
	int hist_fd; // file description
    int ret;
	hist_fd = open("./history.txt",O_RDWR|O_CREAT|O_APPEND, 0666); //可读可写可打开的打开历史记录的文件,不存在就创建,且每次都追加写入
	if(hist_fd < 0){
		printf("fail to open history file!\n");
		fflush(stdout);
	}
	ret = write(hist_fd, &hist_whole, strlen(hist_whole));
	if(ret == -1){
		printf("fail to write history write to file!\n");
		fflush(stdout);
	}else{
		printf("write history to file successfully!\n");
        //文件内数据和数据库里面的数据一起显示有点乱我就先注释掉了
		/*printf("write the following history to file:\n");
		  printf("------------------------\n");
		  printf("%s",hist_whole);
		  printf("------------------------\n");*/
		fflush(stdout);
	}
	close(hist_fd);
	memset(hist_whole,'\0',sizeof(hist_whole)); //清空hist_whole!
 
}
 
void data_sql() //将历史记录写入数据库的函数
{
	char update_sql[128] = {'\0'};
    int ret;
   // printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
	sprintf(update_sql,"update history set count = %d where cause = 'socket';",sock_cmd);//客户端指令
    //printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
    ret = sqlite3_exec(db, (const char *)update_sql, callback, 0, &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't update date: %s\n", sqlite3_errmsg(db));
    }

	sprintf(update_sql,"update history set count = %d where cause = 'sound';",sond_cmd);//语音指令
    ret = sqlite3_exec(db, (const char *)update_sql, callback, 0, &zErrMsg);
    if(ret != SQLITE_OK){
        printf("Can't update date: %s\n", sqlite3_errmsg(db));
    }
 
	ret = sqlite3_exec(db, "select * from history;", callback, 0, &zErrMsg); //将数据库数据打印到屏幕
	if(ret != SQLITE_OK){
		printf("Can't show date: %s\n", sqlite3_errmsg(db));
	}

    //清零
    int sock_cmd = 0;//客户端指令
    int sond_cmd = 0;//语音指令
}

 将这两个函数在线程record里面调用

6、发生数据记录

我们统计指令来源(客户端、语音模块)在对应触发语句将各自变量+1

 



 

项目效果:

 

  • 16
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值