网关开发从入门到落地(07)断网不丢数据:本地缓存 + 断点续传实战代码(工业网关必备)

3 阅读3分钟

开篇

前面 6 篇,我们已经实现了:网关 上电 → 采集 Modbus 数据 → MQTT 上传云端

真实工业现场一定会遇到:

  • 4G 信号差、突然断网
  • 网线松动、基站故障
  • 云端升级、临时断开

如果没有缓存 + 断点续传,断网期间的数据直接丢失,项目直接验收不通过。

这一篇,我用最简单、最稳定、嵌入式网关最常用的方案,教你实现:断网自动存本地 → 联网自动补发 → 不丢一条数据。这是廉价网关没有、老工程师最值钱、私活必加的加分功能。


一、先讲清楚:断点续传到底是什么?

用大白话讲:

  1. 网络正常:采集 → 直接上传
  2. 网络断开:采集 → 存到网关 Flash(相当于本地小硬盘)
  3. 网络恢复:自动把断网期间存的历史数据,一条一条补发云端
  4. 存满自动循环覆盖,不会撑爆存储空间

这就是工业网关量产级必备功能


二、实现思路(超级简单,3 步搞定)

  1. 定义一条数据结构(时间、地址、电压、电流)

  2. 写两个函数:

    • SaveDataToFlash():断网时存本地
    • LoadAndSendHistory():联网时读出来补发
  3. 主流程加判断:

    • MQTT 在线 → 直接发
    • MQTT 不在线 → 先存起来

三、数据结构定义(复制即用)

我们要存的就是一条采集记录:

c

运行

// 历史数据结构体(断网缓存用)
typedef struct {
    unsigned int timestamp;    // 时间戳(什么时候采的)
    unsigned char dev_addr;    // 设备地址
    float voltage;            // 电压
    float current;            // 电流
} HistoryData;

// 最多缓存 1000 条(可根据 Flash 大小调整)
#define MAX_HISTORY 1000

四、核心函数 1:断网时保存数据到本地

c

运行

// 缓存数据到本地(断网时调用)
int SaveHistoryData(HistoryData data)
{
    // 1. 获取当前存储位置(循环覆盖,防止撑满)
    static int index = 0;

    // 2. 写入 Flash / 文件(嵌入式网关通用)
    SaveToFlash(index % MAX_HISTORY, &data);

    // 3. 下标自增
    index++;

    return 0;
}

作用:网一断,自动把数据一条条存进去,存满 1000 条从头覆盖,不会死机。


五、核心函数 2:联网后补发历史数据

c

运行

// 联网后,读取所有历史并上传
int SendAllHistoryData(void)
{
    int i;
    HistoryData data;

    for (i = 0; i < MAX_HISTORY; i++)
    {
        // 1. 从本地读取一条
        if (ReadFromFlash(i, &data) < 0)
            break;

        // 2. 组装JSON上传
        BuildHistoryJson(&data);
        MQTT_PublishData(PUB_TOPIC, json_buf);

        // 3. 延时一下,防止发太快被平台限流
        usleep(200000);
    }

    return 0;
}

六、最终完整网关主逻辑(最强闭环)

这就是真正工业量产网关的代码逻辑

c

运行

void Gateway_MainTask(void)
{
    HistoryData data;

    // 1. Modbus 采集(第5篇代码)
    Modbus_Collect(&data.dev_addr, &data.voltage, &data.current);

    // 2. 打上时间戳
    data.timestamp = GetSystemTimestamp();

    // 3. MQTT 在线:直接上传
    if (MQTT_IsConnected())
    {
        // 先补发历史数据
        SendAllHistoryData();

        // 再上传当前数据
        Build_JsonData(data.dev_addr, data.voltage, data.current);
        MQTT_PublishData(PUB_TOPIC, json_buf);
    }
    // 4. MQTT 不在线:保存本地
    else
    {
        SaveHistoryData(data);
    }

    // 轮询间隔
    sleep(1);
}

七、新手最容易踩的 3 个坑

1. 不做循环覆盖,Flash 直接写爆

一定要用 index % MAX_HISTORY,循环覆盖,不然设备跑半个月必挂。

2. 补发太快,平台拒收

补发时加 200ms 延时,不然几百条一起发,平台直接判定攻击。

3. 不存时间戳

工业项目必须带时间,否则补发的数据平台不认。


八、本篇总结(一句话)

Modbus 是基础,MQTT 是通路,断点续传才是网关能量产交付的灵魂。

很多年轻开发者只会调通功能,但缓存、断点续传、异常自愈,才是 40+ 老工程师最值钱的地方。


下篇预告

下一篇我们讲 **《网关稳定性保命设计:看门狗 + 内存泄漏防护 + 日志管理》**专门解决:设备跑半个月死机、重启、卡死、不采集、不上传等现场最头疼问题。