在智能硬件产品中,一般有三类数据需要存储并管理:
系统设置数据是指产品自身正常工作所依赖的一些参数。
这类数据的特点:只能在生产过程中修改,出厂后用户无权限修改。
比如:产品 SN、产品密钥/token/license、传感器校准值。
用户设置数据是指在用户使用过程中,由用户根据自身喜好所设置的一些参数。
这类数据的特点:出厂时恢复默认,出厂后由用户动态修改。
比如:检测类产品的告警阈值、模块化功能是否打开。
用户使用数据是指在用户使用过程中,产品工作产生的一系列用户数据。
这类数据的特点:数据量较大,且用户需要查看历史数据。
比如:过去一小时的语音数据、过去一天的监测数据、过去一周的视频数据等等。
一些常见的参数存储方案如下,每种存储方案在不同的智能硬件产品中都有其独特的应用场景,选择合适的存储方案需要根据具体的需求、成本和技术限制来决定。
EEPROM 是一种容量较小的存储器,在产品中需要外挂一片 EEPROM,适用于存储少量的数据。
优点:
缺点:
比如:智能家居产品中用户设定的温度阈值设定可以存储在 EEPROM 中。
Flash 是一种容量较大的存储器,比如 Spi Flash/Nand Flash/Emmc,适用于存储大量参数数据。
优点:
缺点:
比如:智能手表中的用户使用数据(如步数、心率记录)可以存储在 Flash 中。
FRAM 是一种结合了 RAM 速度和 EEPROM 数据保存能力的存储器,适用于需要高频读写和数据保持的应用场景。
优点:
缺点:
比如:医疗产品中的病人数据记录器,可以使用 FRAM 来存储重要的参数和数据。
SD 卡和硬盘适用于需要大容量存储的应用场景。
优点:
缺点:
比如:智能监控摄像头会使用 SD 卡来存储视频录制文件。
云存储是一种通过互联网将数据存储在远程服务器上的方法,适用于需要大容量和易于共享的场景。
优点:
缺点:
比如:智能家居系统中将过去一天的监测数据上传到云端进行存储和分析。
参数能通过硬件进行存储后,还需要进行软件的管理,比如参数读取、参数写入、参数备份、参数重置、参数更新等软件功能。
一些常见的软件管理方案如下,这些方案都有其适用的场景,根据存储介质的特点和具体应用场景进行权衡,在使用过程中需要灵活使用,甚至可以配合使用。
一般来说:
适用场景: 大容量存储,如 Flash 或 SD 卡。
方法:
示例代码(使用 SPIFFS):
#include <SPIFFS.h>
void setup() {
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS Mount Failed");
return;
}
File file = SPIFFS.open("/config.txt", FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
file.println("parameter1=value1");
file.println("parameter2=value2");
file.close();
}
void loop() {
// 读取和处理参数
File file = SPIFFS.open("/config.txt");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
while (file.available()) {
Serial.println(file.readStringUntil('\n'));
}
file.close();
}
适用场景: 需要管理大量复杂参数或历史记录。
方法:
示例代码(使用 SQLite):
#include <sqlite3.h>
void setup() {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("/mydb.db", &db);
if (rc) {
Serial.printf("Can't open database: %s\n", sqlite3_errmsg(db));
return;
}
const char* sql = "CREATE TABLE IF NOT EXISTS CONFIG ("
"ID INTEGER PRIMARY KEY AUTOINCREMENT,"
"KEY TEXT NOT NULL,"
"VALUE TEXT NOT NULL);";
rc = sqlite3_exec(db, sql, 0, 0, &zErrMsg);
if (rc != SQLITE_OK) {
Serial.printf("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
sqlite3_close(db);
}
void loop() {
// 数据库操作示例
}
适用场景: 少量配置参数,频繁读写,如 EEPROM、FRAM。
方法:
示例代码(使用 FlashDB):
#include <flashdb.h>
#ifdef FDB_USING_KVDB
#define FDB_LOG_TAG "[sample][kvdb][basic]"
void kvdb_basic_sample(fdb_kvdb_t kvdb)
{
struct fdb_blob blob;
int boot_count = 0;
FDB_INFO("==================== kvdb_basic_sample ====================\n");
{ /* GET the KV value */
/* get the "boot_count" KV value */
fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
/* the blob.saved.len is more than 0 when get the value successful */
if (blob.saved.len > 0) {
FDB_INFO("get the 'boot_count' value is %d\n", boot_count);
} else {
FDB_INFO("get the 'boot_count' failed\n");
}
}
{ /* CHANGE the KV value */
/* increase the boot count */
boot_count ++;
/* change the "boot_count" KV's value */
fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
FDB_INFO("set the 'boot_count' value to %d\n", boot_count);
}
FDB_INFO("===========================================================\n");
}
#endif /* FDB_USING_KVDB */
适用场景: 需要远程访问和备份,数据量大。
方法:
示例代码(使用 HTTP POST 到云服务器):
#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid = "your-SSID";
const char* password = "your-PASSWORD";
const char* serverName = "http://your-server.com/upload";
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(serverName);
http.addHeader("Content-Type", "application/json");
String postData = "{\"param1\": 42, \"param2\": 3.14}";
int httpResponseCode = http.POST(postData);
if (httpResponseCode > 0) {
String response = http.getString();
Serial.println(httpResponseCode);
Serial.println(response);
} else {
Serial.print("Error on sending POST: ");
Serial.println(httpResponseCode);
}
http.end();
}
}
void loop() {
// 云端管理和处理参数
}
如果还有未被整理到的方案,欢迎留言区讨论补充!