一、有哪些参数需要管理?
在智能硬件产品中,一般有三类数据需要存储并管理:
1. 系统设置数据
系统设置数据是指产品自身正常工作所依赖的一些参数。
这类数据的特点:只能在生产过程中修改,出厂后用户无权限修改。
比如:产品 SN、产品密钥/token/license、传感器校准值。
2. 用户设置数据
用户设置数据是指在用户使用过程中,由用户根据自身喜好所设置的一些参数。
这类数据的特点:出厂时恢复默认,出厂后由用户动态修改。
比如:检测类产品的告警阈值、模块化功能是否打开。
3. 用户使用数据
用户使用数据是指在用户使用过程中,产品工作产生的一系列用户数据。
这类数据的特点:数据量较大,且用户需要查看历史数据。
比如:过去一小时的语音数据、过去一天的监测数据、过去一周的视频数据等等。
二、参数的硬件存储方案
一些常见的参数存储方案如下,每种存储方案在不同的智能硬件产品中都有其独特的应用场景,选择合适的存储方案需要根据具体的需求、成本和技术限制来决定。
1. EEPROM
EEPROM 是一种容量较小的存储器,在产品中需要外挂一片 EEPROM,适用于存储少量的数据。
优点:
- 可以进行字节级别的擦写操作
- 寿命较长,擦写次数通常在 10 万次左右
- 价格低
缺点:
- 容量较小,一般为几 KB 到几百 KB
比如:智能家居产品中用户设定的温度阈值设定可以存储在 EEPROM 中。
2. Flash
Flash 是一种容量较大的存储器,比如 Spi Flash/Nand Flash/Emmc,适用于存储大量参数数据。
优点:
- 容量较大,从几 MB 到几 GB 不等。
- 擦写寿命较长,通常可达数十万次。
缺点:
- 擦写操作需要按块(通常是几 KB 到几 MB)进行
- 擦写时间较长
比如:智能手表中的用户使用数据(如步数、心率记录)可以存储在 Flash 中。
3. FRAM(Ferroelectric RAM)
FRAM 是一种结合了 RAM 速度和 EEPROM 数据保存能力的存储器,适用于需要高频读写和数据保持的应用场景。
优点:
- 擦写速度快,接近于 RAM。
- 擦写寿命极长,通常在 10 亿次以上。
- 非易失性,断电数据不丢失。
缺点:
- 容量相对较小,通常为几 KB 到几 MB。
比如:医疗产品中的病人数据记录器,可以使用 FRAM 来存储重要的参数和数据。
4. SD 卡或硬盘
SD 卡和硬盘适用于需要大容量存储的应用场景。
优点:
- 容量大,从几 GB 到几 TB 不等。
- 便于更换和升级。
缺点:
- 可靠性相对较低,易受物理损坏。
- 速度较慢,特别是与内部存储器相比。
比如:智能监控摄像头会使用 SD 卡来存储视频录制文件。
5. 云存储
云存储是一种通过互联网将数据存储在远程服务器上的方法,适用于需要大容量和易于共享的场景。
优点:
- 理论上容量无限
- 易于访问和共享
- 数据安全和备份有保障
缺点:
- 依赖网络连接。
- 存在隐私和安全风险。
比如:智能家居系统中将过去一天的监测数据上传到云端进行存储和分析。
三、参数的软件管理方案
参数能通过硬件进行存储后,还需要进行软件的管理,比如参数读取、参数写入、参数备份、参数重置、参数更新等软件功能。
一些常见的软件管理方案如下,这些方案都有其适用的场景,根据存储介质的特点和具体应用场景进行权衡,在使用过程中需要灵活使用,甚至可以配合使用。
一般来说:
- 简单的参数存储:选 KV 存储或配置文件
- 大量复杂数据或历史记录:嵌入式数据库
- 远程访问和备份数据:云存储
1. 文件系统
适用场景: 大容量存储,如 Flash 或 SD 卡。
方法:
- 使用嵌入式文件系统,如 FATFS、LittleFS 或 SPIFFS,将参数存储为文件,可以方便地进行读取和修改。
- 文件内容格式可以是 ini、json、xml 等。
示例代码(使用 SPIFFS):
代码语言:javascript复制#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();
}
2. 数据库
适用场景: 需要管理大量复杂参数或历史记录。
方法:
- 使用嵌入式数据库,如 SQLite,数据库可以提供强大的查询和管理功能。
示例代码(使用 SQLite):
代码语言:javascript复制#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: %sn", 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: %sn", zErrMsg);
sqlite3_free(zErrMsg);
}
sqlite3_close(db);
}
void loop() {
// 数据库操作示例
}
3. 键值存储(KV-存储)
适用场景: 少量配置参数,频繁读写,如 EEPROM、FRAM。
方法:
- 使用键值对存储结构,常见库如 FlashDB、EEPROM 库、Preferences 库。
示例代码(使用 FlashDB):
代码语言:javascript复制#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 %dn", boot_count);
} else {
FDB_INFO("get the 'boot_count' failedn");
}
}
{ /* 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 %dn", boot_count);
}
FDB_INFO("===========================================================n");
}
#endif /* FDB_USING_KVDB */
4. 云存储
适用场景: 需要远程访问和备份,数据量大。
方法:
- 使用云存储服务,如各个大厂的物联网平台,或者自建物联网平台。
- 通过网络接口(如 HTTP、MQTT)进行数据传输和管理。
示例代码(使用 HTTP POST 到云服务器):
代码语言:javascript复制#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() {
// 云端管理和处理参数
}
如果还有未被整理到的方案,欢迎留言区讨论补充!