11 KiB
11 KiB
离线数据存储和补传功能说明
功能概述
本设备支持离线数据存储功能,当网络断开时,数据会自动存储到Flash文件系统中;当网络恢复后,数据会自动补传到MQTT服务器。
系统架构
┌─────────────────────────────────────────────────┐
│ 应用层 │
├─────────────────────────────────────────────────┤
│ MODBUS数据 设备状态 MQTT客户端 │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 离线数据管理模块 │ │
│ │ (OFFLINE_STORAGE 组件) │ │
│ └─────────────────────────────────────┘ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────────────────────────────┐ │
│ │ SPIFFS文件系统 │ │
│ │ (FLASH_SPIFS 组件) │ │
│ └─────────────────────────────────────┘ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────────────────────────────┐ │
│ │ 16MB Flash存储区 │ │
│ │ (8MB可用) │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
分区表配置
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 2MB,
storage, data, spiffs, 0x210000,8MB,
- storage分区:8MB,用于离线数据存储
- 挂载点:
/flash - 文件系统:SPIFFS
存储容量估算
| 参数 | 数值 |
|---|---|
| Flash总容量 | 16MB |
| 存储分区大小 | 8MB |
| MODBUS数据量 | 200字节/条(1秒/次) |
| 每小时数据量 | 720KB |
| 可存储时长 | 约11小时 |
注意:SPIFFS最小文件大小为4KB,200字节的文件实际占用4KB空间,因此实际可存储的时长会更短。建议将多条数据合并到一个文件中存储以提高空间利用率。
文件组织结构
/flash/
├── data/
│ ├── modbus/ # MODBUS采集数据
│ │ ├── 1738452345.json # 时间戳命名
│ │ ├── 1738452346.json
│ │ └── ...
│ └── status/ # 设备状态数据
│ ├── 1738452345.json
│ └── ...
└── index.json # 索引文件
工作流程
正常模式(网络在线)
数据产生
↓
检测网络状态(在线)
↓
直接发布到MQTT
↓
不存储
离线模式(网络断开)
数据产生
↓
检测网络状态(离线)
↓
存储到Flash SPIFFS
↓
写入成功
↓
等待网络恢复
补传模式(网络恢复)
检测网络状态(在线)
↓
检查是否有离线数据
↓
读取最旧的数据
↓
发布到MQTT
↓
发布成功?
├─ 是 → 删除该数据 → 继续下一条
└─ 否 → 等待重试
API接口
FLASH_SPIFS 组件
初始化文件系统
esp_err_t flash_spiffs_init(void);
检查是否挂载
bool flash_spiffs_is_mounted(void);
获取存储使用情况
uint32_t flash_spiffs_get_total_size(void);
uint32_t flash_spiffs_get_used_size(void);
格式化文件系统
esp_err_t flash_spiffs_format(void);
OFFLINE_STORAGE 组件
初始化存储模块
esp_err_t offline_storage_init(void);
存储离线数据
esp_err_t offline_storage_store(const char *data, size_t length, offline_data_type_t data_type);
参数:
data:数据内容(JSON字符串)length:数据长度data_type:数据类型OFFLINE_DATA_TYPE_MODBUS:MODBUS采集数据OFFLINE_DATA_TYPE_DEVICE_STATUS:设备状态数据
读取最旧的数据
esp_err_t offline_storage_read_oldest(char *buffer, size_t max_len, offline_data_type_t *out_data_type);
删除最旧的数据
esp_err_t offline_storage_delete_oldest(void);
获取数据数量
uint32_t offline_storage_get_count(void);
bool offline_storage_has_data(void);
清空所有数据
esp_err_t offline_storage_clear_all(void);
MQTT_ESP 扩展接口
存储离线数据(内部使用)
esp_err_t mqtt_store_offline(const char *data, size_t length, offline_data_type_t data_type);
启动/停止补传任务
BaseType_t mqtt_start_offline_upload_task(void);
void mqtt_stop_offline_upload_task(void);
网络状态检测
系统通过MQTT连接状态判断网络是否在线:
| MQTT事件 | 网络状态 | 处理 |
|---|---|---|
MQTT_EVENT_CONNECTED |
在线 | 启动补传任务 |
MQTT_EVENT_DISCONNECTED |
离线 | 停止补传任务 |
存储策略
自动循环覆盖
- 最大文件数限制:10,000个文件
- 触发条件:当文件数超过限制时,自动删除最旧的数据
- 优先级:按时间戳排序,删除最旧的数据
数据完整性保护
- 索引文件:
/flash/index.json记录所有文件信息 - 原子写入:使用临时文件+重命名机制
- 断电保护:SPIFFS提供基本断电保护
存储空间管理
获取空间使用情况
↓
检查可用空间
↓
空间不足?
├─ 否 → 正常存储
└─ 是 → 删除最旧数据 → 重试
SPIFFS 特性说明
优点
- ESP-IDF原生支持,无需额外组件
- 代码成熟稳定
- 内存占用小(约2KB)
- 挂载速度快
缺点
- 最小文件大小为4KB,小文件浪费空间
- 无磨损均衡,Flash寿命相对较短
- 碎片严重,长期使用后性能下降
- 不支持目录(所有文件在根目录)
优化建议
- 合并多条数据到单个文件:减少文件数量,提高空间利用率
- 定期格式化:消除碎片,恢复性能
- 限制文件数量:避免碎片积累
日志输出
初始化日志
I (xxxx) main: Initializing SPIFFS file system...
I (xxxx) FLASH_SPIFS: 正在初始化SPIFFS文件系统...
I (xxxx) FLASH_SPIFS: SPIFFS文件系统挂载成功
I (xxxx) FLASH_SPIFS: 总空间: 8388608 字节
I (xxxx) FLASH_SPIFS: 已用空间: 4096 字节
I (xxxx) FLASH_SPIFS: 可用空间: 8384512 字节
I (xxxx) FLASH_SPIFS: 挂载点: /flash
I (xxxx) main: Initializing offline storage module...
I (xxxx) main: 离线存储模块初始化完成
存储日志
I (xxxx) mqtt_esp: Storing offline data (type=1, size=256)
I (xxxx) OFFLINE_STORAGE: 存储数据: /flash/data/modbus/1738452345.json, 大小: 256 字节
I (xxxx) OFFLINE_STORAGE: Storage usage: 4096 / 8388608 bytes (0.0%)
W (xxxx) OFFLINE_STORAGE: 注意:SPIFFS最小文件大小为4KB,小文件会占用更多空间
补传日志
I (xxxx) mqtt_esp: Network is online, starting offline data upload
I (xxxx) mqtt_esp: Found 10 offline data files, uploading...
I (xxxx) mqtt_esp: Publishing offline data (type=1, size=256)
I (xxxx) mqtt_esp: Offline data published successfully, msg_id=12345
故障处理
Flash空间不足
现象:
E (xxxx) OFFLINE_STORAGE: 无法创建文件
解决:
- 自动删除最旧的数据
- 格式化存储分区
文件系统损坏
现象:
E (xxxx) FLASH_SPIFS: 挂载失败
解决:
- 系统会尝试自动修复
- 如修复失败,格式化文件系统
- 检查Flash硬件
数据补传失败
现象:
E (xxxx) mqtt_esp: Failed to publish offline data
解决:
- 检查网络连接
- 检查MQTT服务器状态
- 数据会保留在Flash中,下次重试
性能优化建议
1. 合并多条数据
将多条数据合并到一个文件中存储:
// 示例:每100条数据存储为一个文件
// 空间利用率:100 * 200字节 / 4096字节 ≈ 4.9%
// 单文件存储:100 * 200字节 / 4096字节 = 100%
2. 降低存储频率
网络离线时降低存储频率:
// 在线:1秒/次
// 离线:10秒/次
// 可存储时长:110小时
3. 选择性存储
只存储关键数据:
// 只存储异常数据、状态变化
// 正常数据丢弃
4. 定期格式化
建议每隔1-3个月格式化一次文件系统,消除碎片:
flash_spiffs_format();
监控和调试
查看存储使用情况
size_t used = 0, total = 0;
offline_storage_get_usage(&used, &total);
ESP_LOGI(TAG, "Storage: %zu / %zu bytes", used, total);
ESP_LOGI(TAG, "Usage: %.1f%%", (used * 100.0) / total);
查看离线数据数量
uint32_t count = offline_storage_get_count();
ESP_LOGI(TAG, "Offline data files: %u", count);
格式化存储
flash_spiffs_format();
注意事项
- 首次启动:会自动格式化文件系统(如果需要)
- 断电保护:SPIFFS提供基本断电保护,但建议避免频繁断电
- Flash寿命:SPIFFS无磨损均衡,建议定期格式化以延长寿命
- 容量限制:由于SPIFFS最小文件4KB,实际可存储时长会少于理论值
- 补传速率:每秒处理一条数据,避免阻塞新数据上传
- 碎片问题:长期使用后性能可能下降,建议定期格式化
后续扩展建议
- 数据压缩:集成zlib压缩,延长存储时长
- 批量存储:将多条数据合并到一个文件中
- 优先级队列:重要数据优先补传
- 数据导出:通过Web界面导出离线数据
- 统计信息:记录丢失/补传的数据统计
- 智能采样:根据存储空间动态调整采样率
- 定期维护:实现自动碎片整理和格式化
与LittleFS对比
| 特性 | SPIFFS | LittleFS |
|---|---|---|
| 最小文件大小 | 4KB | 0字节 |
| 内存占用 | ~2KB | ~4KB |
| 磨损均衡 | ❌ 无 | ✅ 自带 |
| 断电保护 | ⚠️ 一般 | ✅ 优秀 |
| 支持目录 | ❌ 不支持 | ✅ 支持 |
| 碎片问题 | ❌ 严重 | ✅ 轻微 |
| ESP-IDF支持 | ✅ 原生支持 | ❌ 需第三方组件 |
选择建议:
- 如果需要ESP-IDF原生支持且数据量不大 → SPIFFS
- 如果需要更好的性能和寿命 → LittleFS(需手动添加组件)