尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

工业以太网温湿度采集:断线重连与断点续传的工程实践

发布时间:2026/9/26 2:13:08

资讯中心
01
ARTICLE

工业以太网温湿度采集:断线重连与断点续传的工程实践

工业以太网温湿度采集:断线重连与断点续传的工程实践
1. 从一次现场数据丢失说起为什么断线重连和断点续传必须一起做做过工业现场数据采集的人大概都遇到过这种场景一套以太网温湿度采集系统在实验室跑了一周都稳如老狗拉到现场第一天就出问题。现场环境里电磁干扰大、交换机端口偶尔抖动、供电不稳导致设备重启甚至清洁工打扫时不小心碰掉了网线。这些在实验室里几乎不会出现的状况在现场是家常便饭。问题的核心不在于会不会断而在于断了之后怎么办。很多采集系统的处理方式非常粗暴连接断了就重连重连上了就从当前时刻继续采集。听起来没什么毛病但这里有一个致命的隐患——断线期间的数据怎么办如果采集周期是1秒一次断了30秒那就意味着30条温湿度记录凭空消失了。对于冷链仓储、药品存储、精密制造这些场景30秒的数据缺失可能直接导致一整批产品报废因为无法证明这段时间的温湿度是否合规。所以真正可用的以太网温湿度采集系统必须同时解决两个层面的问题断线重连解决的是通道恢复的问题断点续传解决的是数据完整的问题。这两件事必须一起做缺了任何一个系统在工业现场都是不合格的。我见过太多项目只做了断线重连就交付了结果客户一查历史曲线发现每隔几天就有一段空白解释都解释不清楚。这篇文章就把这套机制的设计思路、协议选型、缓存策略、续传逻辑和实际踩过的坑完整地拆一遍。不管你是用ESP32加LAN8720做低成本方案还是用STM32F407配RMII接口的PHY芯片做中高端设备底层的设计逻辑是相通的。2. 多协议并存的现实Modbus TCP、MQTT与私有TCP各有各的断线脾气2.1 为什么一个采集器要同时支持多种协议先说一个很多人不理解的点为什么温湿度采集器要支持多种协议用一个不就行了吗现实情况是采集器面对的上游往往不止一个。车间本地的PLC和SCADA系统走的是Modbus TCP因为这是工控圈的事实标准组态软件原生支持云端平台走的是MQTT因为要穿透NAT、要支持海量设备、要有QoS保障而某些老旧的MES系统可能只认私有TCP协议因为十几年前就是这么开发的改不动。这就意味着采集器需要同时维护多条通信链路每条链路的断线特征和重连策略都不一样。Modbus TCP是基于请求-响应的短连接或长连接模式断线通常表现为请求超时MQTT是长连接加心跳保活断线表现为心跳超时或收到DISCONNECT私有TCP则完全取决于自定义的心跳机制设计。2.2 三种协议的断线检测机制对比协议类型断线检测方式典型检测延迟重连触发条件数据缓存策略Modbus TCP请求超时无响应1-3秒连续N次超时本地环形缓冲区MQTT心跳超时Keep Alive的1.5倍1.5倍心跳周期心跳无响应或Socket异常本地持久化队列私有TCP自定义心跳序列号确认取决于心跳周期心跳丢失或ACK超时双缓冲区Flash存储这张表里的每一个参数都不是拍脑袋定的。Modbus TCP的超时时间设1-3秒是因为工业现场的网络RTT通常在10-50ms级别超过1秒没响应基本可以判定异常。MQTT的心跳周期一般设30-60秒1.5倍就是45-90秒才能检测到断线这个延迟对于温湿度采集来说其实偏大所以实际项目中我会把心跳周期压到15-20秒代价是网络流量略微增加但换来更快的故障感知。私有TCP协议最灵活也最麻烦因为所有机制都要自己设计。我的做法是在应用层加一个序列号确认机制每条数据上报后等待ACK如果连续3条数据没有收到ACK就判定链路异常触发重连流程。同时保留一个轻量级心跳包每5秒发一次用于快速检测链路状态。2.3 多协议共存时的资源竞争问题当三种协议同时运行时会遇到一个很实际的问题网络资源竞争。ESP32这类单核或双核MCUTCP/IP协议栈的处理能力有限如果三个Socket同时活跃加上温湿度传感器的采样任务很容易出现任务调度不及时导致的心跳丢失。我的经验是给不同协议分配不同的优先级和CPU时间片。Modbus TCP因为是本地通信优先级最高响应时间要求最短MQTT次之因为云端平台通常对延迟容忍度较高私有TCP如果只是备用通道可以放到最低优先级。在FreeRTOS环境下可以通过调整任务优先级和栈大小来实现具体来说Modbus TCP任务优先级设为5MQTT设为4私有TCP设为3传感器采集任务设为6最高确保采样不被通信任务阻塞。注意任务优先级不是越高越好如果通信任务优先级过高可能导致传感器采样任务被饿死反而造成数据采集不均匀。建议传感器采集任务始终是最高优先级通信任务根据实时性要求依次降低。3. 断线重连的工程实现从检测到恢复的完整链路3.1 断线检测不能只靠Socket返回值很多初学者写重连逻辑时习惯性地依赖Socket的返回值来判断连接状态。比如send()返回-1就认为断线了然后触发重连。这种做法在实验室能用在现场会出大问题。原因在于TCP协议的半开连接问题。当网络中断时如果没有任何数据发送Socket可能长时间保持已连接状态因为TCP协议本身不会主动检测链路是否还活着。你以为连接还在实际上数据早就发不出去了。这就是为什么必须要有应用层的心跳机制。我的做法是三层检测第一层是Socket层的错误返回值这是最快的但最不可靠第二层是应用层心跳超时这是最可靠的但有一定延迟第三层是数据发送后的ACK确认超时这是针对关键数据的额外保障。三层检测的触发条件不同处理逻辑也不同但最终都会汇聚到同一个重连状态机。3.2 重连状态机的设计要点重连逻辑最忌讳的是断了就立刻重连重连失败就立刻再重连这种暴力方式。这样做的后果是如果对端设备重启需要30秒你的采集器会在30秒内发起几百次连接请求不仅浪费资源还可能被对端防火墙拉黑。我通常设计一个带退避的重连状态机状态转换如下IDLE正常通信状态心跳正常数据正常收发SUSPECT检测到一次心跳超时或发送失败进入怀疑状态立即发起一次探测DISCONNECTED确认断线关闭旧Socket清理资源进入等待重连RECONNECTING按照退避策略发起重连首次等待1秒之后2秒、4秒、8秒、16秒、30秒封顶RECOVERING连接建立成功但还需要验证链路质量发送测试数据确认双向通信正常RESYNC链路确认可用后进入数据续传阶段把缓存的数据补发上去这个状态机里最关键的是RECOVERING和RESYNC的区分。很多人重连成功后直接就开始发新数据结果缓存的老数据永远补不上去。正确的做法是重连成功后先进入恢复验证阶段确认链路稳定后再进入续传阶段把断线期间缓存的数据按时间顺序补发。3.3 退避策略的参数计算退避策略的参数不是随便定的。假设现场最坏情况是对端设备重启需要45秒那么退避序列必须保证在45秒内至少尝试3-4次连接同时总尝试次数不能太多以免造成网络风暴。我常用的退避序列是1s, 2s, 4s, 8s, 15s, 30s, 30s, 30s... 这样在45秒内会尝试6次1248153060秒实际第5次在30秒时尝试第6次在60秒时尝试。如果对端45秒恢复第5次尝试30秒时可能还连不上第6次60秒时肯定能连上。这个序列在ESP32上实测下来既不会造成网络拥堵又能保证较快的恢复速度。如果是对接云端MQTT服务器退避可以更激进一些因为云端服务通常恢复很快。但如果是现场PLC退避要更保守因为PLC重启可能涉及整个控制系统的初始化流程时间不可控。3.4 重连过程中的资源清理这是一个很容易被忽略的坑重连时如果没有正确关闭旧Socket会导致文件描述符泄漏。在Linux系统上每个Socket占用一个文件描述符默认上限是1024。如果每次重连都泄漏一个跑几天系统就崩溃了。正确的清理流程是先调用shutdown()关闭读写通道再调用close()释放文件描述符最后把Socket句柄置为-1。在ESP32的LwIP协议栈下还需要调用lwip_close()确保底层资源被回收。我见过一个项目因为没做这个清理设备运行72小时后必然死机排查了很久才定位到是Socket泄漏。4. 断点续传的核心数据缓存、序列号与幂等性设计4.1 缓存放在RAM还是Flash断点续传的前提是断线期间的数据不能丢所以必须有本地缓存。缓存介质的选择直接决定了系统的可靠性和成本。RAM缓存速度快、寿命无限但掉电就丢。如果设备断电重启缓存的数据就没了。Flash缓存掉电不丢但写入速度慢、有擦写寿命限制通常10万次。对于温湿度采集这种低频场景1秒-60秒一次Flash的寿命完全够用假设1秒采集一次10万次擦写可以用27小时但如果用环形缓冲区加磨损均衡算法实际寿命可以延长到几年。我的建议是双缓冲策略最近1小时的数据放RAM环形缓冲区保证高频读写性能同时异步写入Flash保证掉电不丢。Flash写入采用批量方式每积累10条记录写一次减少擦写次数。这样既保证了性能又保证了可靠性。4.2 序列号机制的设计细节断点续传要解决的核心问题是怎么知道哪些数据已经传了哪些还没传答案就是序列号。每条采集数据都带一个单调递增的序列号接收端记录最后收到的序列号。重连后发送端从接收端确认的最后一个序列号1开始补发。这个机制听起来简单但有几个细节必须处理好。第一序列号必须持久化。如果序列号只存在RAM里设备重启后序列号归零接收端会认为收到了重复数据或者乱序数据。我的做法是每1000条数据把当前序列号写入Flash一次重启后从Flash读取并加1000作为起始值避免序列号回绕。第二序列号要有回绕处理。如果用32位无符号整数每秒1条数据可以跑136年才回绕基本不用考虑。但如果用16位65536秒约18小时就回绕了必须处理回绕逻辑。我的建议是直接用32位省心。第三接收端要能处理重复数据。因为网络重传、ACK丢失等原因接收端可能收到重复的序列号。这时候需要幂等性设计接收端根据序列号判断如果已经处理过这个序列号直接丢弃并返回ACK不重复入库。4.3 缓存容量的计算与溢出处理缓存容量需要根据最坏断线时间来设计。假设现场最坏情况是断线2小时采集周期1秒那么需要缓存7200条数据。每条温湿度记录假设16字节时间戳4字节温度4字节湿度4字节序列号4字节总共需要115KB的存储空间。ESP32的Flash通常有4MB拿出256KB做数据缓存绰绰有余。STM32F407的Flash有1MB也可以分配128KB做缓存。但如果用外部SPI Flash成本增加不多容量可以做到几MB能缓存几天的数据。溢出处理策略有两种覆盖最老数据和停止采集。覆盖最老数据适合对实时性要求高、对历史数据完整性要求相对低的场景停止采集适合数据绝对不能丢的场景但代价是断线时间过长后系统会停止工作。我通常采用折中方案缓存写到80%时产生告警写到95%时开始覆盖最老数据同时记录覆盖事件方便事后追溯。4.4 续传时的流量控制断线2小时后重连缓存里有7200条数据要补发。如果一次性全发出去很可能把网络打爆导致刚恢复的链路再次拥塞断线。所以续传必须有流量控制。我的做法是分批续传每批50-100条批间间隔100-200ms。这样既能保证续传速度7200条大约需要15-30秒又不会造成网络拥塞。同时续传期间新采集的数据继续写入缓存等续传完成后再切换回实时发送模式。如果续传过程中再次断线已经补发的部分不需要重发因为接收端已经确认从未确认的序列号继续补发即可。这就是序列号机制的另一个好处支持断点续传的续传。5. 以太网硬件层的坑从LAN8720到RMII接口的实战经验5.1 ESP32配LAN8720最常见的三个问题虽然这篇文章重点在协议层但硬件层的坑不填平协议层做得再好也白搭。ESP32加LAN8720是低成本以太网采集方案里最常见的组合也是坑最多的组合。第一个坑是RMII时钟配置。LAN8720需要50MHz的时钟输入可以由ESP32的GPIO17输出也可以由LAN8720自己的晶振提供。如果配置错了表现为PHY能识别但无法建立链路。我的经验是优先用ESP32的GPIO17输出50MHz时钟因为这样少一个晶振成本更低但需要在代码里正确配置EMAC_CLK_EXT_IN和EMAC_CLK_OUT。第二个坑是PHY地址。LAN8720的PHY地址由PHYAD0引脚决定悬空时为0下拉时为1。如果代码里写的地址和硬件不一致表现为esp_eth_driver_install失败或者能安装但收不到包。用ethernet_init例程时它会自动扫描PHY地址但生产环境建议固定地址避免扫描带来的不确定性。第三个坑是电源和复位时序。LAN8720对电源纹波比较敏感如果和ESP32共用LDOWiFi发射时的电流波动可能导致PHY复位。我的做法是给LAN8720单独加一个LDO或者在PHY的电源脚加100uF以上的电容。复位引脚要确保上电时有一个至少10ms的低电平否则PHY可能不工作。5.2 STM32F407的RMII接口配置要点STM32F407配DP83848是另一个常见组合坑相对少一些但也不是没有。最关键的是RMII_REF_CLK的配置。STM32F407的RMII接口需要50MHz参考时钟可以来自外部晶振也可以来自MCO输出。如果时钟不对表现为HAL_ETH_Init返回错误或者能初始化但收发包异常。另一个容易忽略的是GPIO复用配置。RMII接口涉及多个GPIO每个都要正确配置为AF11以太网功能并且速度等级要设为GPIO_SPEED_FREQ_VERY_HIGH。我见过一个项目因为把TX_EN引脚的速度等级设成了LOW导致发送数据时偶尔丢包排查了很久才发现是GPIO速度问题。5.3 以太网帧间隔与实时性的关系以太网的帧间隔Inter-Frame Gap是12字节时间在100Mbps下是960纳秒。这个参数通常不需要修改但在某些对实时性要求极高的场景下如果发现连续发送时丢包可能需要检查PHY的配置是否支持背靠背发送。对于温湿度采集这种低频应用帧间隔基本不会成为瓶颈。但如果采集器同时承担多个TCP连接的数据转发帧间隔和PHY的缓冲能力就需要关注了。我的经验是选择支持存储转发模式的交换机避免使用直通模式的廉价交换机因为直通模式在端口速率不匹配时容易丢包。6. 实测中的意外情况与排查链路6.1 重连成功但数据补发失败这是我在一个冷链项目里实际遇到的问题。设备重连成功了心跳正常新数据也能发出去但缓存的老数据就是补发不上去。排查过程如下第一步确认缓存数据是否存在。通过调试串口打印缓存队列的长度发现缓存里有3000多条数据说明数据没丢。第二步确认续传逻辑是否被触发。在状态机里加日志发现重连后直接进入了IDLE状态没有进入RESYNC状态。原因是重连成功的回调函数里直接设置了状态为IDLE跳过了RESYNC。第三步修复状态机确保重连成功后先进入RECOVERING验证链路后再进入RESYNC。修复后数据正常补发。这个问题的根因是状态机设计时没有把连接建立和数据同步两个阶段分开。连接建立只是物理层和传输层的事数据同步是应用层的事两者不能混为一谈。6.2 序列号回绕导致的数据重复另一个项目里设备运行了18小时后开始出现数据重复。排查发现序列号用的是16位无符号整数65536秒后回绕到0接收端看到序列号变小了以为是新数据实际上是一小时前的老数据。修复方案很简单把序列号改成32位。但已经入库的重复数据需要清理写了一个脚本根据时间戳去重。这个坑的教训是序列号位宽的选择要考虑设备的最长运行时间工业设备通常要求连续运行几年16位绝对不够。6.3 网络抖动导致的频繁重连现场网络质量不好时偶尔丢一两个包是正常的。但如果重连逻辑过于敏感丢一个包就触发重连会导致系统频繁在重连和正常状态之间切换反而影响数据采集。我的做法是加一个抖动容忍机制连续3次心跳超时才判定断线单次超时只记录不动作。同时重连成功后有一个稳定期比如30秒内如果再次断线退避时间加倍避免在网络不稳定时反复重连。7. 一些让系统更稳的工程习惯7.1 日志要分级且可远程查看调试现场问题时日志是最重要的工具。但日志不能太多否则串口输出会阻塞主任务也不能太少否则出了问题无从查起。我的做法是分三级ERROR级只记录断线、重连、缓存溢出等关键事件WARN级记录心跳超时、ACK丢失等异常但可恢复的事件INFO级记录每次数据发送和接收的摘要信息。默认只输出ERROR和WARN需要详细排查时通过远程命令打开INFO。7.2 看门狗要喂但不要乱喂看门狗是保证系统不死机的最后一道防线。但很多人的做法是在主循环里无脑喂狗这样看门狗就失去了意义。正确的做法是在每个关键任务里分别喂狗比如传感器采集任务喂一次、通信任务喂一次、缓存管理任务喂一次任何一个任务卡死都会导致看门狗复位。7.3 参数要可配置且能持久化采集周期、心跳周期、重连退避序列、缓存容量这些参数不要硬编码在代码里。现场情况千差万别硬编码意味着每次调整都要重新烧录固件。我的做法是把这些参数放在Flash的一个配置区通过Modbus寄存器或MQTT下行命令可以修改修改后立即生效并持久化。7.4 定期做断线演练系统交付前一定要做断线演练拔网线、关交换机、重启对端设备、模拟网络拥塞各种情况都要试一遍。我见过太多项目在实验室跑得好好的一到现场就出问题就是因为没有做断线演练。演练时重点观察断线检测时间是否符合预期、重连是否成功、缓存数据是否完整、续传是否正常、有没有资源泄漏。这套机制我在多个项目里反复打磨过从最早的裸机版本到后来的FreeRTOS版本从ESP32到STM32核心逻辑没有变过。变的只是具体的API和资源限制。把断线重连和断点续传做扎实温湿度采集系统才能在现场真正站稳脚跟而不是三天两头出问题。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。