
如果你做过 HVAC 或者机房环境监控大概率会碰到这样的需求本地要实时显示回风温度和盘管温度同时还得把几十米外的几个独立区域温度汇总到一个面板上统一查看。我做过一个类似的冷站监控项目最早用的是 NTC 加独立变送器方案后来又尝试过用主控直接采模拟信号折腾几次之后稳定跑下来的组合是PJ85718DM 数字温度传感器配合 PIC18F87K22 这颗 8 位 MCU。这篇文章就把这套测温系统的完整做法拆开讲一讲覆盖器件选型、硬件连接、固件逻辑和远程上报适合正在做嵌入式环境监控、HVAC 控制盒或者设备状态监测的工程师参考。标题里“本地与远程”这两个词很多同事一开始理解得比较浅以为只是传感器放在近处和远处的区别。真正落地之后你会发现这背后其实是两套完全不同的链路本地传感器要解决的是多点采集、时序可靠的问题远程上报要解决的是远距离传输、抗干扰和协议解析的问题。下面就从需求开始一层层往下拆。1. 需求拆解嵌入式与 HVAC 场景里的“本地/远程温度”到底指什么1.1 两种“远程”的温度物理布点 vs 系统架构先厘清概念。在 HVAC 项目里本地温度通常指控制器所在位置的测量点比如空调箱的回风口、冷冻水的供回水管、电控柜内部的空气温度。这些点离主板近一般一两米以内直接走 I²C 或 SPI 都行。远程温度则是指分布在几十米甚至上百米外的测量点比如不同楼层的房间温控器、室外冷凝器附近的空气温度、车间远端的热源区域。但嵌入式系统里还存在第二种“远程”从 MCU 角度来说传感器无论接在哪条总线上都只是物理拓扑上的远真正的“远程监控”是把采集到的温度数据通过 RS-485、CAN 或者以太网上传给上位机让不在现场的人也能看到趋势。所以这份需求实际上包含两层一是传感器层面的本地与远程布局二是系统层面的本地控制与远程监控。1.2 从典型 HVAC 现场提炼出的硬性指标我那个项目是给一个工厂车间的空调系统做集中控制现场条件不算极端但很典型。拆解下来硬性指标大概是这几条指标项要求说明测温范围-20℃ ~ 85℃夏季车间温度可升到 45℃ 以上冬季冷库附近会到零下精度±0.5℃ 以内用于回风温度 PID 调节差太多会振荡采集点数本地 6 点 远程 4 点10 路通道实时性全通道刷新周期 ≤ 2 秒温度本身变化慢2 秒足够通信距离远程最远 120 米采用 RS-485 链路通信协议Modbus RTU方便对接已有的上位机供电24V DC/3.3V 稳压HVAC 控制柜常见的供电条件异常处理断线检测、超温报警、复位恢复无人值守场景这里要注意的是“精度 ±0.5℃”这个指标。如果是用 NTC 加 10 位 ADC在全温度范围内做到这个精度很吃力需要逐点校准而数字温度传感器本身已经把 ADC、基准和线性校准做在芯片内部压力小得多。这也是我后来换掉 NTC 方案的根本原因。2. 器件选型背后的取舍PJ85718DM 与 PIC18F87K22 的组合逻辑2.1 PJ85718DM一颗 I²C 数字温度传感器能解决什么PJ85718DM 是一颗 I²C 接口的数字温度传感器封装是典型的 8 引脚 SOIC工作电压 2.7V 到 5.5V测温范围覆盖 -55℃ 到 125℃12 位分辨率下最小刻度 0.0625℃典型精度能做到 ±0.5℃。它的 I²C 从机地址由 A0、A1、A2 三个引脚的电平决定默认地址是 0x48最多一条总线上可以挂 8 个芯片。真正让它在 HVAC 场景里好用的是 ALERT 输出引脚。芯片可以配置成窗口比较器模式当温度超出上下限时不需要 MCU 轮询ALERT 引脚自己拉低可以直接接 MCU 的外部中断或者 PLC 的数字输入。对于需要在温度越限瞬间触发联锁控制的场景这个特性比纯软件轮询可靠得多。它内部还有一个 12 位 Σ-Δ ADC转换时间长一些大概几十毫秒但换来的是极低的噪声。用手持万用表的温度档和它对比稳定下来后末位数字基本不动。相比之下如果自己用 MCU 内部 ADC 采 NTC1℃ 以内的波动很常见。2.2 PIC18F87K228 位 MCU 的底气来自哪里PIC18F87K22 是 8 位 MCU 里相当能打的一颗。80MHz 主频意味着单周期指令执行速度快温度采集这种任务对它来说很轻松128KB Flash 和 3.9KB RAM 足够放下协议栈、滤波缓冲区和掉电保护数据更重要的是它外设很全两个 MSSP 模块可以独立跑 I²C/SPI两个 EUSART 可以跑 RS-485还有一个 12 位 ADC 和 ECAN 模块后续想扩展 CAN 总线出口也不用换主芯片。选它还有个很现实的原因功耗和发热。HVAC 控制柜常年通电控制板就装在电柜里如果 MCU 本身功耗大、发热高反而会影响附近温度传感器的读数。这颗芯片正常工作电流在十几毫安级别发热可以忽略对测温系统来说误差源又少了一个。2.3 为什么不用 NTC 或 PT100我不是说 NTC 和 PT100 不能用但在“多点、统一监测、远程上传”这个需求下它们的综合成本不占优。NTC 方案省钱但每个通道都要做分压电阻、滤波电容、ADC 校准6 个点就要校准 6 次批量生产时校准工时非常痛PT100 精度高但需要恒流源或高精度电桥配套变送器本身就要几十块远距离传输还得 4-20mA 变送器。数字温度传感器把校准做好了直接用 I²C 读出来就是工程单位省掉了一大堆前端调理电路。这条链路在系统可靠性上的收益比省那几块钱的 BOM 成本更有价值。3. 硬件搭建I²C 传感器巡检链路的连接、供电与抗干扰设计3.1 地址规划一条 I²C 总线上挂 8 路传感器PJ85718DM 的地址引脚 A0、A1、A2 可以接 GND 或 VCC组合出 8 个地址0x48 到 0x4F。我做的是 6 路本地温度分别配了地址 0x48~0x4D远程节点板上的 4 路用另一颗从机 MCU 采集后打包上传所以本地总线没有挂满。设计上要注意地址引脚不能悬空。悬空的话电平不稳定上电后传感器可能随机地址导致初始化时找不到设备。我在第一版原理图里偷懒把 A0~A2 直接没接结果一批板子有 30% 出现“设备丢失”查了半天才发现是地址漂移。后来老老实实用 10kΩ 电阻把每个地址引脚拉低或拉高问题彻底消失。I²C 总线上拉电阻也要选对。PJ85718DM 在 100kHz 模式下建议上拉电阻 4.7kΩ 到 10kΩ如果线比较长、电容大阻值取小一点但不能小于 2.2kΩ否则灌电流过大低电平可能被拉不到阈值。我这边本地线缆总长约 3 米用 4.7kΩ 比较稳。3.2 传感器供电与去耦最容易翻车的环节HVAC 控制柜里的 24V DC 是从开关电源来的纹波不小。我最初直接用一个 AMS1117-3.3 线性稳压给传感器供电结果发现温度读数会出现周期性跳动尤其是湿度大的时候更明显。查了一圈问题是传感器电源引脚上的纹波叠加到了内部基准上导致转换结果抖动。解决办法很直接传感器供电单独走一组 LC 滤波10μH 电感加 100μF 电容再在每个传感器电源引脚旁边放一个 0.1μF 陶瓷电容。这样改完以后读数的稳定性肉眼可见地提升。另外I²C 的 SDA/SCL 走线尽量短且远离 220V 交流线和继电器输出线。继电器闭合瞬间的拉弧在示波器上能看到明显的尖峰耦合到 I²C 线上。场景线缆长度上拉电阻去耦电容注意事项板内短距离 30cm10kΩ0.1μF基本无压力板间走线1 ~ 3m4.7kΩ0.1μF 10μF 电解SDA/SCL 远离功率线柜内长距离5m 以上2.2kΩLC 滤波建议改用 RS-485 子板3.3 工业现场的隔离策略本地 I²C、远程 RS-485 的混合拓扑这就是我在 1.1 里说的“两层远程”在硬件上的落地。本地 6 路传感器在控制板上直接 I²C 读取简单高效远程 4 个点在 120 米外如果直接拉 I²C 线信号完整性基本没戏所以我做了一块远程采集从机板面板上用 PJ85718DM 采 4 路温度然后主从机之间用 RS-485 通信。有人会问为什么不直接每个远程点挂一个传感器到 RS-485 上也可以但每颗 PJ85718DM 本身没有 RS-485 接口得给每个点配一颗 MCU 或者专门的协议转换芯片成本反而比集中一块从机板更高。而且现场条件恶劣多一个设备点就多一个故障点。集中到一块从机板放在远程区域的接线盒里供电、防雷、接地都方便处理。主控板这边RS-485 收发器选带失效保护的型号A/B 线之间并联 120Ω 终端电阻。通信线采用双绞屏蔽线屏蔽层单端接地。如果你在电柜里布线务必走独立的线槽别跟变频器输出线绑在一起。4. 固件实现温度采集、滤波、报警与自诊断的完整逻辑4.1 MSSP 主模式初始化与单次温度读取PIC18F87K22 上电后先把 MSSP 模块配置成 I²C 主模式。在 XC8 环境下我习惯把这些初始化都放到一个函数里void i2c1_init_master(void) { // 100kHz 64MHz SSP1ADD 159; SSP1CON1 0x28; // SSPEN1, I2C Master mode SSP1CON2 0x00; SSP1STAT 0x80; // Slew rate control enabled }读传感器温度的基本流程是启动条件 - 发送从机地址写位 - 发送命令字节比如 0x00 表示读温度寄存器- 重新启动 - 发送从机地址读位 - 读高字节 - 读低字节 - 停止条件。MCU 连续读两个字节后把数据拼成一个 16 位值。uint16_t read_temp_reg(uint8_t slave_addr) { uint16_t raw; uint8_t msb, lsb; i2c_start(); i2c_write((slave_addr 1) | 0); // 写命令 i2c_write(0x00); // 温度寄存器地址 i2c_restart(); i2c_write((slave_addr 1) | 1); // 读模式 msb i2c_read(1); // 主设备发送 ACK lsb i2c_read(0); // 最后一个字节发 NACK i2c_stop(); raw (uint16_t)((msb 8) | lsb); return raw; }4.2 数据换算、负数处理与温度格式PJ85718DM 的 12 位温度数据是左对齐的高字节的高 8 位是整数部分低字节的高 4 位是小数部分。温度值的 LSB 是 0.0625℃也就是 16 分之一度。换算公式是温度 (msb 4) | (lsb 4)然后除以 16。负数的情况要小心。温度低于 0℃ 时12 位数据用二进制补码表示比如 -1℃ 对应 0xFFF0。如果直接把 16 位数当成无符号整数除以 16会得到 4095这就错了。统一做法是先转成有符号 16 位整数再右移 4 位除以 16。int16_t raw_signed (int16_t)raw; int16_t temp_x16 raw_signed 4; // 放大 16 倍 float temp_c (float)temp_x16 / 16.0f;我在实际代码里基本不用浮点直接保留整数部分和小数部分通过一个联合体拆开。比如 25.0625℃ 显示成 250625发给上位机时再按 0.01℃ 分辨率换算省掉浮点库占用。4.3 滑动滤波与带迟滞的报警逻辑温度信号本身变化慢但 HVAC 现场的风机、压缩机启停会引起局部气流波动导致读数上下跳 0.2℃ 左右。我加了一个 8 次滑动平均滤波队列里存最近 8 次采样值每次取平均。这个成本很低却让后续 PID 控制平滑了很多。报警逻辑要加迟滞不然会在阈值附近频繁触发。比如超温报警设为 60℃恢复值要设成 58℃这样温度在 59℃ 附近波动时不会一会儿报警一会儿恢复。PJ85718DM 的 ALERT 引脚在窗口比较模式下也有类似机制固件里再用软件迟滞做一层保护双重保障。#define TEMP_ALERT_HIGH 60.0f #define TEMP_ALERT_LOW 58.0f这个“高报低复”的策略在 HVAC 联锁控制里很关键否则继电器反复吸合触点寿命会快速消耗。4.4 传感器失联与总线超时处理多点测温系统最怕的不是温度不准而是传感器丢了之后系统不知道。PJ85718DM 如果从机不响应I²C 总线上表现为 NACK但如果传感器损坏导致 SDA 被拉死主控会一直等不到释放。解决办法是给 I²C 操作加超时保护。我在代码里用一个循环计数超过 N 次仍检测不到 ACK 就放弃本次通信执行 9 个时钟周期的总线恢复释放可能被从机锁住的总线再标记该通道为故障。这样任何一个传感器掉线系统都能继续读其他通道同时把故障状态通过 Modbus 上报给上位机而不是整条总线“一挂全挂”。实时性方面要保证全通道刷新周期在 2 秒内。温度传感器转换时间几十毫秒10 个通道顺序读取也就几百毫秒完全够用。不要为了省电把采样间隔拉太长否则 PID 控制的滞后会明显变差。5. 远程汇聚基于 RS-485/Modbus 的远程温度上报方案5.1 从机端聚合把 I²C 多点温度打包成一份数据表远程从机板的固件逻辑很清晰每 500ms 读一次板上的 4 路传感器做同样的滑动平均滤波把结果存到一份结构体里。然后等待主机通过 Modbus 命令来读。这里有个很关键的工程细节从机板上的 MCU 我选的也是 PIC18 系列但如果你用其他 MCU 更顺手完全没问题。重要的是从机固件要负责自己本地传感器的健康状态一个传感器失联不要把整包数据作废而是把那个通道的温度字段填成一个约定的异常值比如 0x7FFF让主机知道这个点失效了。5.2 Modbus RTU 寄存器映射与 CRC 校验Modbus RTU 是 HVAC 行业上位机最通用的协议基本上所有组态软件都支持。我规划的寄存器表如下寄存器地址内容数据类型范围0x0000本地通道 1 温度int160.01℃/bit0x0001本地通道 2 温度int160.01℃/bit............0x0006远程通道 1 温度int160.01℃/bit0x0010系统状态字uint16bit0~bit9 对应各通道故障CRC16 是 Modbus RTU 的标配标准多项式 0xA001查表法或者按位法都行。我直接贴按位计算的实现因为存储空间够省事又直观uint16_t modbus_crc(uint8_t *buf, uint16_t len) { uint16_t crc 0xFFFF; for (uint16_t i 0; i len; i) { crc ^ buf[i]; for (uint8_t j 0; j 8; j) { if (crc 1) crc (crc 1) ^ 0xA001; else crc 1; } } return crc; }收发时序上主机发送读保持寄存器命令从机在接收到完整帧且 CRC 校验通过后延迟几十微秒再回复。如果校验失败从机必须静默不能回错误帧否则主机会认为通信链路有冲突。5.3 主机轮询周期与故障重连策略主机轮询从机的时间间隔我设置为 1 秒一次每个从机帧长不到 20 字节115200bps 下传输时间几乎可以忽略。1 秒的轮询周期足够让上位机画出平滑的趋势曲线。RS-485 通信最容易出的问题是接线顺序和终端电阻。多台设备菊花链连接终端电阻只接在链路两端中间设备不能接。另外A/B 线极性不能接反很多现场的“通信不稳定”最后查出来就是有一台设备的 A/B 接反了。碰到连续通信超时固件里要做重连策略连续 3 次读超时标记从机离线再每隔 5 秒尝试重连一次不让上位机被错误数据刷屏也不让总线被无限重试占满。6. 实测数据与高频问题处理校准、电压跌落与总线锁死排查6.1 校准前的误差分布与软件补偿PJ85718DM 出厂精度 ±0.5℃但实际装到系统里误差还包含传感器自身发热、探头外壳热阻、线缆压降带来的参考电压偏差。我实测下来把传感器直接裸露在空气中和水银温度计对比25℃ 附近误差在 ±0.2℃ 内但套上防水探头之后由于探头外壳的热阻温度响应变慢稳定值也会偏低 0.3℃ 左右。针对这个我做了两点处理一是软件里给每个通道加一个可配置的偏置量通过 Modbus 寄存器在线校准二是温度响应慢的探头采样周期保持 1 秒以上连续读取别用单次读数做控制跳变。这个偏置参数在批量生产时每台设备可以单独标定效率比硬调电阻高得多。6.2 SDA 被拉死的总线锁死现象与恢复调试过程中印象最深的一个问题远程从机板运行一段时间后主控板上 I²C 总线完全不响应示波器看 SDA 一直是低电平像是被某个传感器拉死了。原因基本可以锁定为总线竞争传感器复位或者电源毛刺导致它误进入异常状态。后续在固件里加了总线恢复机制每次通信超时就对 SCL 连续翻转 9 个时钟周期让总线释放。这招在绝大多数 I²C 从机锁死场景下都有效可以理解为“喂一条走廊里的每个人一口水让全体恢复正常状态”。同时我在每个传感器的电源脚加了 0.1μF 电容减少复位时的电源毛刺后锁死概率降到了个位数级别。6.3 压缩机启停引发的电压跌落与复位HVAC 现场最恶劣的电噪声来自大型压缩机启动瞬间。有一次调试压缩机一启动控制器就重启看门狗误触发。测量 24V 电源发现启动瞬间电压跌落超过 5V维持几十毫秒。这直接导致控制板上的 3.3V 稳压输出跟着跌落MCU 电压低于复位阈值。解决组合拳电源输入端加大电容储能TVS 管吸收浪涌MCU 的欠压复位模块使能设定合适的跌落阈值看门狗超时时间调到比电源波动时间更长避免瞬间跌落后又恢复时看门狗提前复位。改完以后压缩机随便启停控制器再也没有无故重启过。6.4 报警误触发的最后排查传感器探头安装位置最后一个高频问题不是硬件故障而是安装位置。回风温度传感器如果装在空调回风口正下方风机抽风时形成一个局部低温区读到的温度比实际房间平均温度低 2℃ 还多。送风温度传感器如果太靠近加热盘管辐射热会让读数偏高。遇到误报警我现在的习惯是先问安装位置再查代码。把探头的安装位置调整到气流的代表区域避开直接辐射源并保证探头插入深度足够很多“报警异常”根本不需要改程序。这点对任何做 HVAC 温度采集的同行来说都非常重要——传感器测的是探头位置的温度不是空间平均值安装的人必须明白这一点。