做暖通空调(HVAC)系统的温度监测,不少人第一反应就是铂电阻加变送器,或者 NTC 加 ADC 调理电路。这套方案当然成熟,但在我最近做的一个 HVAC 项目里,测点分布在风管、回风口和室外补偿点等多处,线缆长度从几米到二十米不等,用模拟量方案光是布线和校准就够折腾一阵。最终采用的方案是 PJ85718DM 数字温度传感器配合 STM32F767ZI 主控,传感器走 1-Wire 总线逐路读取,主控在本地完成滤波、报警和显示,同时通过以太网把数据打包上传到远程平台,实现真正意义上的本地与远程双通道监测。
这篇文章主要想聊透三层内容:为什么在 HVAC 场景里选这一对器件而不是更常见的方案;PJ85718DM 的 1-Wire 驱动在 F767 上怎么做到稳定可靠,包括我在总线上同时挂 8 个传感器时踩到的坑;以及远程监测链路用 MQTT 上报后,数据延迟和丢包要如何定位。给准备做嵌入式温度采集、又需要联网上报的同行一个可复用的参考。
1. 为什么是 STM32F767ZI 搭配 PJ85718DM:这套方案的取舍逻辑
1.1 HVAC 现场对测温节点提出的真实要求
暖通系统里的温度测点往往分布在一个比较大的空间,测点之间不是集中在同一块 PCB 上。以一个常见的空气处理机组为例,送风温度、回风温度、混风温度、表冷器出风温度这几个测点可能横跨整个机房,还要考虑室外温湿度补偿,现场布线从几米到二十米都有。这时候如果用模拟量传感器,每一路都要单独布屏蔽线,还要做增益校准和线阻补偿,调试工程量呈倍数增长。
数字温度传感器把模数转换做在了探头端,一根总线就能传回多路数据,每个器件带独立序列号,主控按地址点名读取即可。对 HVAC 这种测点分散、数量适中、一致性要求不低的应用来说,这个形态非常合适。另外 HVAC 系统常年 7x24 小时运行,测温节点不能频繁校准,也不能一有干扰就掉线,所以方案一开始就要考虑冗余:传感器支持多路级联,通信协议要有错误重试机制,数据链路要有断线重连策略。这些需求直接决定硬件和软件架构。
1.2 PJ85718DM 在传感器侧的定位与优势
PJ85718DM 是一款采用 1-Wire 总线的数字温度传感器,典型测温范围 -55°C 到 +125°C,在 -10°C 到 +85°C 的常用区间,精度指标为 ±0.5°C,这个档位做 HVAC 温度监测完全够用。和很多同类 1-Wire 器件一样,它在出厂时内部固化了 64 位唯一序列号,所以同一根总线上挂多个传感器时,主控不需要区分物理位置,只需要把序列号和安装位置做好映射。
我选它还有一个现实原因:封装选型灵活,TO-92 适合通过端子排安装,SOT-23 贴片版本可以直接设计到 PCB 上。项目里我把管道内的测点用带护套的探头接入,板上的环境温度补偿点直接用贴片封装,两套封装工作起来一致性没问题。不过要提醒一句:这类器件虽然协议兼容性很好,但不同批次、不同厂商的时序参数会出现细微差异,尤其是在拉低总线时隙的宽度和上拉强度上。所以驱动代码不能按“标准值”写死,要留出时序余量,下文驱动部分我会专门说明。
1.3 F767 这颗主控在方案里的不可替代性
STM32F767ZI 属于 Cortex-M7 内核,主频最高 216MHz,带 2MB Flash 和 512KB RAM。单纯读几个温度传感器,其实用不上这么强的算力;真正让我选择它的原因有两个。第一个是内置了 10/100M 以太网 MAC,只要外接一颗 PHY 芯片就能把数据送出去,不需要再挂一个串口转以太网模块。第二个是它有多路定时器和 DMA,可以在读取 1-Wire 的同时,继续跑显示刷新、报警判断和协议栈任务,不容易出现“读总线时其他任务被卡死”的窘境。
实际工作中我也对比过用 STM32F103 搭配 WiFi 模块的做法,成本确实更低,但稳定性和调试便利性差一些。F767 的以太网路径是芯片原厂参考方案,配合 LwIP 协议栈,出问题时有大量资料可查,不用靠猜。对产品化项目来说,这种“可预期性”比省几块钱更有价值。
2. 硬件电路搭建:从传感器到主控的链路设计
2.1 1-Wire 总线的外围电路:一个上拉电阻解决不了所有事
1-Wire 总线本质是开漏结构,所有器件通过一根数据线通信,平时靠上拉电阻把总线拉到高电平,器件通过拉低总线来表达逻辑 0。最常见的电路就是一颗 4.7kΩ 上拉电阻,但我在 HVAC 项目里发现,总线物理长度一旦超过 3 米,4.7kΩ 可能不够用。
原因在于线缆存在分布电容,上拉电阻越大,总线上升沿越缓,而 1-Wire 协议对读时隙的采样窗口很敏感。总线拉不上去,主控就会把高电平误判成低电平,出现 CRC 校验错误。我做了几组对比测试,结果如下表:
| 总线长度 | 上拉电阻 4.7kΩ | 上拉电阻 2.2kΩ | 上拉电阻 1kΩ |
|---|---|---|---|
| 小于 1 米 | 通信正常 | 正常 | 正常 |
| 3 米左右 | 偶发 CRC 错误 | 正常 | 正常 |
| 10 米以上 | 频繁超时 | 偶发错误 | 正常,但低电平偏移明显 |
从数据看,距离拉长后适当减小上拉电阻能提高驱动能力,但也不能一味减小,否则传感器拉低总线时,压降分配会出问题,表现为上拉电流偏大,部分器件在寄生供电模式下甚至可能无法复位。我最终在 8 路传感器、总线长度约 20 米的情况下,选了 1.5kΩ 上拉电阻,并配合屏蔽双绞线,既保证上升沿,又留了电磁干扰余量。
2.2 多点测温的接线拓扑与供电方式选择
多路传感器挂在同一根总线上时,接线拓扑尽量做成“主干线 + 短支线”的结构,支线长度控制在 30 厘米以内。我之前图省事,让每路传感器直接从 PCB 引线过去,结果靠近主控的几路没问题,远端几路总有间歇性通信失败,后来把线缆改成了菊花链式的主干接入,远端传感器通信马上稳定了。
供电方式上,1-Wire 传感器支持外部供电和寄生供电两种模式。寄生供电只要两根线,接线最少,但总线拉低时间受限制,而且长线下压降明显,多点应用容易唤醒失败。我建议 HVAC 项目里老老实实用外部供电,VDD 和 GND 各拉一路,数据线单独走,三线制总共就比寄生式多一根线,换来的是可靠性和排查便利性,非常值得。
2.3 我会在 PCB 上特别注意的几个细节
PCB 设计里有几个点容易在后期变成隐性坑。第一,数据线上要预留一个 0402 或 0603 的串联电阻位置,通常取 100Ω 到 220Ω,用来抑制线缆反射;如果样板调试发现波形振铃,可以直接换上。第二,靠近连接器的地方加一个 TVS 管,防止 HVAC 现场电机启停时产生的浪涌打坏传感器引脚。第三,1-Wire 数据线不要和 220V 交流布线平行走,间距尽量拉开到 5mm 以上,同时避开继电器、开关电源等强干扰源。
另外,多路传感器供电时,每一路最好都加一个独立的滤波电容,容量 0.1μF 就够,放在端子附近。我在第一版 PCB 上为了省事,只做了一个总滤波电容,结果其中一路传感器在压缩机启动瞬间出现了掉线,排查后发现是供电线上感应到的脉冲干扰所致。加上分路电容后,这个问题再也没复现过。
3. 驱动层的正确打开方式:用 HAL 库实现 1-Wire 时序协议
3.1 1-Wire 时序的本质及常见实现坑
1-Wire 的通信基础是精确的时序窗口,它没有单独的时钟线,所有位同步都靠时间片完成。写 1 时主控拉低总线 1μs 到 15μs,然后释放;写 0 时拉低 60μs 到 120μs;读时隙则是主控拉低后,在约 15μs 时采样电平。这些窗口看似宽裕,但叠加了 GPIO 翻转延迟、中断调度延迟之后,如果代码里直接塞 delay 函数,很容易在系统负载升高时错过采样点。
我的做法是用一个低速定时器作为微秒时基,并通过“先关中断,再操作 GPIO,再开中断”的方式保证关键时序连续性。读取温度这个动作本身占用时间很短,关闭全局中断毫秒级不会影响系统稳定性,反而能避免 FreeRTOS 任务切换把时隙切碎。
3.2 初始化与 ROM 操作命令实现
驱动启动时,首先要进行复位脉冲:主控拉低总线 480μs 以上,然后释放,等待传感器拉低响应脉冲。这个响应脉冲表示总线上有器件存在。接下来是 ROM 命令层,常用的命令包括 Search ROM、Read ROM、Skip ROM 和 Match ROM,单点采集用 Skip ROM 可以直接跳读温度,多点采集则必须用 Search ROM 逐一遍历序列号,建立设备列表。
下面是一段基于 STM32 HAL 库的复位函数实现,我写驱动时也是从这段逻辑开始的:
uint8_t ow_reset(void) { uint8_t presence = 0; GPIO_InitTypeDef gpio = {0}; // 先配置为输出并拉低 gpio.Pin = OW_PIN; gpio.Mode = GPIO_MODE_OUTPUT_OD; gpio.Pull = GPIO_NOPULL; HAL_GPIO_Init(OW_PORT, &gpio); HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_RESET); delay_us(500); HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_SET); delay_us(70); // 切换为输入,检测传感器拉低 gpio.Mode = GPIO_MODE_INPUT; gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(OW_PORT, &gpio); if (HAL_GPIO_ReadPin(OW_PORT, OW_PIN) == GPIO_PIN_RESET) presence = 1; delay_us(410); return presence; }这里有三个细节值得注意:开漏输出模式比推挽模式更接近 1-Wire 总线协议要求,避免主控强驱动高电平干扰到传感器;复位后的释放时间要留够,很多初学者只延时 60μs 就开始等响应,实际传感器从释放到拉低响应脉冲会有一定延迟,需要把采样窗口拉宽到 70μs 到 90μs 之间;切换输入模式时使能内部上拉,可以避免引脚悬空时电平漂移。
3.3 温度转换与读取 Scratchpad 的完整流程
一次完整测温的流程是:复位、发 Skip ROM、发 Convert T 命令,然后等待转换完成;再复位、发 Skip ROM、发 Read Scratchpad 命令,连续读出 9 个字节。前两个字节是温度值的低字节和高字节,第 9 字节是 CRC 校验值。HVAC 场景要求的刷新周期一般是 1 到 5 秒,而 12 位分辨率下转换时间最长 750ms,所以我不需要每次读完后立刻轮询,而是把转换和读取错开,让传感器在后台转换,主控先去处理其他任务。
转换时间的等待我采用轮询读总线的方式:发完 Convert T 后,总线会被传感器一直拉低,直到转换结束才释放。我只需要周期性读取总线状态即可,不会白白浪费等待时间。不过要注意,如果总线上挂了多个传感器,在转换期间是不能去读某个特定从设备的,因为总线状态已被占用;如果既要轮询又要并行转换,最好的办法是让所有传感器同时启动 Convert T,然后统一等待全部完成,再逐个执行 Match ROM 加 Read Scratchpad。
3.4 器件离线检测与超时容错
温度传感器在 HVAC 现场掉线是常态,可能是接线松动、器件损坏,也可能是附近大功率设备启动时造成的瞬时干扰。驱动层必须把“器件不存在”和“读数异常”明确区分开。我的做法是给每次复位和每次读取都加超时计数,连续 3 次复位失败才把该序列号标记为离线;读回的温度值则做范围检查,超过 -55°C 到 +125°C 的直接丢弃,并记录日志。
这里特别要提到一个容易忽略的点:1-Wire 总线通信失败时,不能简单地重发命令,而要先保证总线回到空闲状态。我调试时发现,如果上一次读取超时后总线还处于低电平,下一次复位时传感器会误判,造成更严重的乱码。所以超时处理里第一件事是主动把总线释放并至少等待 10ms,再进入下一次复位,这个看似多余的等待实际能消除大部分“连环错”。
4. 本地监测策略:HVAC 场景里的数据处理与报警逻辑
4.1 多点温度的平均、滤波与故障隔离
HVAC 控制逻辑经常需要的是一个区域的平均温度,而不是某一个点的瞬时值。我的设备在本地读取 8 路温度后,先做简单的滑动平均滤波,窗口取 3 次读数;然后剔除掉波动超过设定阈值的异常点,再计算有效点的平均值。这个剔除逻辑不能只靠传感器是否在线来判断,因为传感器即使在线,也可能因为探头贴附不良读到异常值。
具体做法是:每一路维护一个最近 10 次读数的数组,实时计算标准差,当当前读数偏离平均值超过 3 倍标准差时,标记为可疑数据,连续 5 次都偏离才报警并剔除;仅仅一两次跳变则直接忽略。这个策略在我实测中很有效,既过滤了瞬时干扰,又保留了真实温度变化的响应速度。
4.2 温度变化率的计算与预警
HVAC 里除了关注温度绝对值,还非常关注温度变化率,比如表冷器出风温度在短时间内骤升,可能意味着前端水阀故障或者换热面结垢。所以我在本地逻辑中加入了变化率计算,最简单的方法是用当前值与 30 秒前采样值做差值,再除以时间间隔。注意不能用单次差值的瞬时结果做判断,否则风机启停造成的波动也会引发误报。我的阈值按 0.5°C/30 秒设定,连续 3 个周期超过阈值才触发预警。
我把不同测点的变化率放在一张本地环形缓冲区里,每 10 秒更新一次,这样上位机或远程平台即使断线了,重新连接时也能拉取最近一段时间的趋势数据,不用从头再积累。这个环形缓冲在调试报警阈值时也很有用,可以直接回放异常发生前的过程。
4.3 本地显示与存储:LCD 界面和日志
本地端我用了一块 4.3 寸 RGB LCD,通过 F767 的 LTDC 接口驱动;主界面显示 8 路温度的实时值、平均温度和变化率趋势,底部滚动显示最近报警信息。这种本地展示在调试阶段尤其重要——当远程平台数据出现异常时,我直接在设备面板上就能判断是采集链路问题还是网络链路问题,省去了来回跑的麻烦。
存储方面,温度数据以 CSV 结构写入 TF 卡,每分钟写一行。写入操作放到独立的任务里,避免和 1-Wire 读取任务争抢总线与 DMA 通道。日志做了滚动覆盖,只保留最近 7 天数据,防止 TF 卡写满后影响系统稳定。这些看起来不起眼,但真正在项目现场运行几天之后,你就会发现没有日志的测温系统几乎无法排查问题。
5. 远程监测链路:基于以太网的 MQTT 数据上报
5.1 远程方案选型:为什么最终选了 MQTT over TCP
远程监测的难点在于设备要稳定地把温度数据送出去,而不是仅仅连上网。对比下来,我最终选择了 MQTT 协议,核心原因有三个:一是它基于发布订阅模型,多个客户端可以同时订阅同一个主题,现场调试时我用一个通用 MQTT 工具就能直接看数据,不需要专门开发上位机;二是 MQTT 的 QoS 机制可以在断线重连后补齐数据,适合 HVAC 这种低带宽、偶尔断线的场景;三是服务器端可以直接接入现成的规则引擎,后续做短信报警或者历史数据入库都很方便。
如果项目里完全没有公网服务器,也可以退而求其次,在局域网上跑一个 MQTT Broker 或者用 Modbus TCP 轮询。但考虑到大多数远程监测需求最终都要跨越机房和办公网络,我建议直接从 MQTT 入手,省得后面再换架构。
5.2 LwIP + FreeRTOS 的移植要点
STM32F767ZI 的以太网 MAC 需要配合一颗外部 PHY 芯片使用,我这里用的是常见的 RMII 接口 PHY,在 CubeMX 里把以太网外设、LwIP 中间件和 FreeRTOS 一起勾上后,还需要注意几个移植要点。首先是 RMII 时钟,PHY 的 50MHz 参考时钟必须稳定,否则网络会间歇性不通;我用的是主控 MCO 输出的 50MHz,实测没有发现与主频产生明显干扰。
其次是 LwIP 的 RAM 开销。虽然 F767 有 512KB RAM,但我还是把 LwIP 的 PBUF 池和内存堆调整成相对保守的大小,避免 FreeRTOS 堆被挤占过度。TCP/IP 任务栈我给到了 1024 字节,实测在数据上报任务和高频读取任务同时运行时,没有出现栈溢出。如果调试中发现硬错误或者任务卡死,优先怀疑 LwIP 的内存分配失败,可以打开 LWIP_STATS 查看丢包情况,而不是一开始就怀疑传感器驱动。
5.3 JSON 数据格式与云端数据结构设计
远程上报的数据格式,我建议直接用 JSON,虽然比二进制多消耗一些带宽,但可读性和调试效率高很多。每条上报消息包含设备 ID、时间戳、8 路温度数组、平均温度、当前设备运行状态和最近一次错误码。字段命名要稳定,不要频繁变更,否则云端解析逻辑和本地代码都容易出错。
一个典型的 MQTT payload 如下:
{ "deviceId": "HVAC-UNIT-001", "ts": 1732521600, "temps": [21.3, 21.8, 20.9, 22.1, 21.5, 21.2, 20.8, 21.0], "avgTemp": 21.2, "online": true, "lastError": 0 }上报周期我控制在 10 秒一次。这个频率对 HVAC 足够了,也不至于让服务器存储压力太大。如果后续要接入可视化大屏,直接消费这个主题并写入时序数据库即可,设备端基本不用再改。如果需要远程控制,也可以在同一个 Broker 里定义下行主题;不过温度监测项目初期往往先做只读通道,等跑稳定了再加控制逻辑会更好。
6. 联调实测中的踩坑记录与数据表现
6.1 总线过长导致通信失败的排查过程
第一次联调时,我在现场把 8 个传感器按 20 米总线的形式接好,结果最远端两路的温度读数每隔几分钟就出现一次错误。我用示波器测量总线波形,发现远端节点的上升沿明显变缓,高电平只能到 2.8V 左右,已经低于传感器识别高电平的门限。排查下来有三个叠加因素:总线电容过大、上拉电阻偏大、远端节点支线过长。
我依次做了三件事:把上拉电阻从 4.7kΩ 换到 1.5kΩ;把远端分支线从 50 厘米缩短到 20 厘米以内;在末端并联了一个 100Ω 的阻性端接后再次试验,通信恢复稳定。这个案例最值得记住的点是:1-Wire 看似简单,但它在长线场景下更像一条射频线路,波形质量和拓扑的影响比协议本身更大。
6.2 远程上报不及时:解包阻塞 CPU 的真凶
远程链路调试时出现过温度数据在本地正常、云端却延迟超过 1 分钟的情况。排查时我先看了 MQTT 包的发送间隔,发现设备 MQTT 线程被阻塞在以太网发送函数的等待上。继续追查,发现我把 8 路温度的读取和 JSON 序列化放在了同一个高优先级任务里,而 JSON snprintf 在数据数组较大时会产生毛刺,加上 LwIP 缓冲区不足,重传逻辑把主循环拖慢了。
修正方式比较直接:把 JSON 序列化放到独立任务,使用静态缓冲区复用;同时把 MQTT 消息发送改为非阻塞调用,失败时把消息缓存到本地环形队列,等网络恢复后再补发。改完之后,10 秒周期的上报实际延迟稳定在 100ms 以内,云端收到的数据也完整了。
6.3 温度数据的实测误差与校准结论
在恒温箱里做对比测试,读数在 0°C 到 60°C 范围内与铂电阻参考值的最大偏差约 0.3°C,符合传感器规格;在 20°C 到 30°C 的常用区间,偏差通常在 0.1°C 左右。考虑到传感器本身的一致性,我没有做逐点软件校准,而是让主控端支持一个全局偏移系数,便于安装现场统一微调。
我特别建议多测几个批次的传感器再决定是否需要批量校准;如果同一批器件的偏差方向一致,甚至可以只用一次校验就为整批设备设置同一个补偿值。不同批次之间的差异会相对明显,这时做逐件校验更稳妥。这个偏差分析思路也同样适用于任何 1-Wire 兼容器件,不必局限于某一种型号。
7. 现场维护里最容易被忽略的经验:序列号映射与批次校验
等系统稳定运行后,我回头整理整个项目时,发现真正提高维护效率的不是某个高性能外设,而是一张传感器映射表。所有 PJ85718DM 都有唯一的 64 位序列号,但序列号本身不会告诉你在 HVAC 现场它对应哪个风管、哪个房间。我在设备上预置了一段映射表,把每个安装位置的物理编号和传感器序列号绑定,上电初始化时自动比对,发现数量变化或序列号不匹配就在本地日志和远程消息里同时报出来。这个功能在我现场更换过几次探头后非常有用,否则你永远不知道当前数据对应的究竟是哪个安装位置。
批次校验这块也值得提一句:不同批次的 PJ85718DM 在出厂校准点附近的表现会有微小差异。我建议在采购时确认供货方提供的批次信息,并在入库时做一次简单的恒温水浴抽查,偏离超过 0.3°C 的器件直接换掉,不要等到设备挂上风管后再返工。对 HVAC 这种常年运行的系统,把问题挡在装配之前,远比事后挨个排查要省心。
最后分享一个实操中的心得:在整个系统跑了一个多月之后,我最大的感受是,HVAC 测温这个场景真正考验人的往往不是“能不能读到温度”,而是“长周期稳不稳定、出问题能不能快速定位”。PJ85718DM 这类 1-Wire 传感器在协议上很成熟,但在真实布线和供电条件下,上拉电阻、拓扑、滤波这些外围细节才决定成败。STM32F767ZI 的性能冗余带来的不只是“快”,更重要的是能同时扛住驱动、显示、网络和调试工具,让问题更容易暴露,也让整套方案具备继续扩展的底气。