我做过很多和“温度采集 + 网络上报”相关的项目,大部分都是设备已经装在现场,需要把多点温度汇总到一个控制面板或者上位机上。这类需求单独看都不难,真正麻烦的是很多人把“本地温度”和“远程温度”当成一回事,结果要么在本地追求过高的实时性,增加成本;要么在远程传输上想得太简单,把稳定性全赌在网络上。
这次想分享一套比较典型的组合:用 PJ85718DM 作为温度采集前端,用 PIC18F87J60 作为主控和联网芯片,把本地传感器数据和远传数据分开处理。适合正在做嵌入式、HVAC 或楼宇自控项目的朋友参考,尤其是那种既要在本机面板上显示实时温度,又要把数据传到监控中心的场景。下面从整个系统的功能拆分开始讲,再逐步落到硬件接线、代码实现和现场踩坑。
1. 这个方案要解决什么问题:本地测温与远程测温根本不是一回事
很多项目的需求文档里只会写一句话:实时监测温度并上传。但“实时”这个词在不同位置有不同的含义。面板上看的温度需要秒级刷新,出差错时操作员能立刻看到;而监控中心看的温度可能允许十几秒甚至一分钟的延迟,但数据不能丢、不能乱、不能漂。这两种场景如果混在一起设计,往往两头都不讨好。
1.1 本地温度:实时性优先
现场本地的温度监测对象通常是机柜内部温度、送风回风温度、管道表面温度这几个位置。这些温度有一个共同特点,就是它们的变化通常不会特别剧烈,布点位置却可能比较分散,线拉得比较长。
本地读数首先要保证的是刷新快、抗干扰。PJ85718DM 这类模块的好处是它把传感器、信号调理和数字化输出做在了一起,主控只需要通过数字接口去读取,不需要在前端额外做运放、滤波、线性化这些电路。软件采样周期可以做到几百毫秒一次,面板上的显示就能接近“跟随温度实时走动”的效果。
本地路径要关注的不是网络,而是采样稳定性和电源干净程度。尤其 HVAC 现场里压缩机启动、变频器调速瞬间会产生很大的干扰,如果传感器模块使用模拟电压输出,ADC 很容易被拉偏。这也是我为什么更倾向于选数字接口版本。
1.2 远程温度:传输可靠性优先
远程监测走的是网络通道,这里要处理的问题从“怎么读准”变成了“怎么传稳”。温度数据本身量不大,一个点也就两到四个字节,真正影响体验的是连接管理、帧格式、超时重试和断线缓存。
PIC18F87J60 在这条路径上的价值很明显:芯片内部集成了以太网 MAC 和 PHY,主控只需要外接网络变压器和 RJ45 座子,就能在物理层上把所有数据包收发处理好。对于 8 位单片机来说,这样的集成度意味着整体成本可以压得很低,BOM 里少了很多外围芯片。
远程温度数据不需要和本地完全同步,更常见的是周期性上报、变化超过阈值时主动上报。这一点需要在设计一开始就定好,因为控制器的工作模式完全不同。周期上报实现简单,适合对实时性要求不高的场景;变化上报省流量、省带宽,适合节点数量多的系统,但需要在协议里约定好心跳和超时判定。
1.3 把两者放一块,系统的分工就清晰了
一旦把本地和远程拆开看待,整个系统的结构就非常清晰。
PJ85718DM 负责把物理温度变成数字量,这是数据源头。PIC18F87J60 负责两件事:一是以固定间隔读取这些数字量,做滤波和显示;二是把处理完的数据通过以太网封装成帧,发送给上位机或服务器。本地的实时逻辑不放网络上,远程的网络逻辑也不阻塞本地采样。
这样做还有个额外好处:后期如果传感器换成别的型号,只需要改主控里对应的驱动部分;如果上位机协议要改,也只是改动网络封装层的代码。硬件板卡可以保持不变,维护成本低很多。
2. 传感器选型与接线:PJ85718DM 是起点
温度数据的质量决定了整个系统的质量。哪怕 PIC18F87J60 的处理和网络传输做得再稳,传感器端读出来的值是错的,后面全是白搭。所以这一部分值得多说几句。
2.1 模块的定位与信号接口
PJ85718DM 从命名习惯看,是一个典型的一体化温度测量模块,工作电压通常在直流低压范围,输出端已经是调理过的数字信号,和主控之间通过 I2C 或单总线这类协议通信。具体到某一批货的接口定义可能会略有差异,但大多数这类模块都会把电源、地、数据线这几个引脚直接引出,丝印上标注得很清楚。
焊接和接线之前一定要先看丝印和厂商给的原理图确认引脚顺序,尤其是电源正负搞反的问题。这类模块一般有防反接,但也不是所有批次都有保护二极管,接到损坏就麻烦了。
模块内部的测温核心通常是半导体测温元件或热敏电阻,经过 ADC 采样后再由内部算法换算成实际温度。用户拿到手的是已经在模块内部做过滤波和补偿的数字结果,精度通常在正负 0.5 摄氏度级别,已经完全满足 HVAC 监测的需求。如果你想追求正负 0.1 摄氏度的高精度,那就需要选配更高等级的探头和校准流程,但绝大多数场景真没必要。
2.2 接线和电源
接线遵循的基本原则是:传感器电源单独走线,别和继电器、电机驱动线捆在一起。HVAC 现场经常有 220V 动力线,如果传感器线缆长时间和动力线平行走,即使传感器模块本身做了滤波,干扰也可能通过电荷耦合进信号线,导致读数跳动。
我常采用的做法是传感器线用双绞屏蔽线,屏蔽层在控制板一端单点接地,另一端悬空。单点接地可以避免形成地环路,屏蔽层本身又能把空间耦合的电磁干扰导入大地,这是成本最低、效果最好的抗干扰手段。
电源方面需要注意压降。PJ85718DM 如果采用 5V 直流供电,较长的现场布线会有明显的线路压降。模块的供电电压如果再往下掉,内部 ADC 的参考电压可能不稳定,会造成读数整体偏移。如果发现传感器离控制器超过二十米,建议评估是否在传感器端配置独立的稳压电源,或者考虑提升供电电压后再在模块端做二次稳压。
2.3 现场读数的稳定措施
传感器装好后,测试阶段就要在白天的正常工况和晚上的低负荷工况分别读几组数据,不要只在环境温度稳定的情况下测一次就结束。
实测中经常遇到的问题是空调启动瞬间,控制面板上的温度显示突然跳高或跳低几度。用示波器看信号线,通常会看到明显的毛刺。简单有效的处理方法是两层:一层是硬件上在靠近主控的数据引脚对地加一个 100nF 左右的小电容,把高频噪声滤掉;另一层是软件上做滑动平均或中值滤波。
滑动平均适合缓慢变化的温度信号,窗口大小我一般取五到十个采样点。中值滤波更适合抵抗脉冲干扰,比如读取到异常跳变时直接丢弃。两者也可以结合:先取中值去掉野值,再做滑动平均进一步平滑。代价是响应变慢,但 HVAC 的温度变化本身是分钟级别,几秒钟的响应延迟完全能接受。
3. PIC18F87J60 的初始化与温度采集
PIC 系列单片机在工业控制领域用得非常广,PIC18F87J60 这个型号最大的特点是内部直接集成了 10/100M 以太网控制器,物理层和链路层都在芯片内部,这是选型时的核心优势。
3.1 内置以太网控制器,省掉一整块外部芯片
常见的单片机联网方案一般是 MCU 外接一块 ENC28J60 或 W5100 这类独立的以太网控制芯片。外接芯片当然可行,但会增加 PCB 面积、焊接成本和调试工作量。PIC18F87J60 把以太网 MAC 和 PHY 集成在 MCU 内部,MCU 和网络之间只隔一个隔离变压器和 RJ45 座,硬件设计一下子简单很多。
我实际测量过这类集成方案的稳定性。在正规设计的板卡上,配合良好的网络变压器,长时间运行 ping 的丢包率可以做到很低,完全能支撑温度采集上传这类小包长连接业务。要注意的是,网络变压器的选型不要贪便宜,劣质变压器在高低温变化时电气参数会漂,出现低温下网络偶尔掉线的怪问题。
芯片初始化时有几个关键步骤:配置时钟、配置以太网模块的 MAC 地址、设置 IP 参数、打开接收和发送缓冲区。MAC 地址可以用芯片的序列号拼接生成,也可以直接用一组静态地址,但要注意不要在同一个局域网里出现两块板卡 MAC 一样的情况,否则交换机的 MAC 表会乱掉。
3.2 读取 PJ85718DM 的流程
温度模块的读取流程不复杂,但有一个容易忽略的环节:模块上电之后需要一小段稳定时间,此时内部基准源和传感器都在建立工作状态,立刻读取得到的数据可能偏大。所以初始化时建议延时几十到几百毫秒后再启动采样。
以基本的 I2C 接口为例,采样逻辑可以按这样的思路写:
// 伪代码,接口地址和寄存器以模块手册为准 uint8_t temp_raw[2]; float temperature; void read_temperature(void) { if (i2c_start(DEV_ADDR_W) == 0) { i2c_write(REG_TEMP_MSB); i2c_stop(); i2c_start(DEV_ADDR_R); temp_raw[0] = i2c_read(1); // ACK temp_raw[1] = i2c_read(0); // NACK i2c_stop(); temperature = (temp_raw[0] << 8 | temp_raw[1]) * RESOLUTION; } else { // I2C 无应答,标记传感器异常 sensor_error = 1; } }这段代码的关键在于每次读取前要做一次总线起始信号,读取结束之后要发停止信号。I2C 总线上如果挂了多个设备,地址冲突会导致全部设备无法正常访问,现场调试时可以用扫描程序把所有应答地址打印出来确认一下。
3.3 采集频率与软件滤波
温度采集频率我通常设置为一秒一次,这个频率对 HVAC 场景足够实用了。如果采样频率太高,传感器模块和主控都处于连续工作状态,反而没有必要;如果太低,面板显示的数值会有明显的台阶感。
软件滤波直接决定显示效果。我试过三种方式:取最近 N 次的平均值、去掉最大最小值后的均值、带权重的递推平均。对温度这种惯性大的信号来说,普通均值就够了。温度如果出现突变,比如从 20 摄氏度一下跳到 40 摄氏度,先不要急着相信,最好连续多读几次确认不是干扰造成的。真正的大型制冷机组跳停时温度也不会一秒钟跳二十度。
滤波之后的数值可以同时用于本地显示和远程上报,这样至少保证两边看到的数据是一致的,不会出现面板和后台数值对不上的尴尬情况。
4. 把温度送到远端:HTTP 服务与自定义帧的两条路线
远程数据通道是整个系统的最后一段。这里有两种常见的实现方式:HTTP 适合人通过浏览器查看,自定义帧适合系统与系统之间对接。两种方式可以共存,但各自要处理的问题不太一样。
4.1 HTTP JSON 适合人看的 Web 页面
基于 PIC18F87J60 做一个简单的 HTTP 服务器,占用资源不算大。芯片本身有内置以太网缓冲区,再加上 TCP/IP 协议栈的裁剪,完全可以支撑起一个轻量 Web 页面。
最简单的做法是让设备监听 80 端口,当浏览器访问时返回一段包含温度数据的 HTML 或 JSON。JSON 格式模型里比较干净,上位机解析起来也方便,代码逻辑上就是一个字符串拼接的过程:
void build_json_response(char *buf, float local_temp, float remote_temp) { sprintf(buf, "HTTP/1.1 200 OK\r\n" "Content-Type: application/json\r\n" "Connection: close\r\n" "\r\n" "{\"local\":%.2f,\"remote\":%.2f}\r\n", local_temp, remote_temp); }HTTP 的好处是“所见即所得”,用浏览器直接访问就能看到结果,非常适合调试和验证网络链路通不通。但 HTTP 也有个问题:如果上位机需要主动控制设备,比如设置温度阈值,基于 HTTP 的方式必须实现 POST 请求解析,代码复杂度会上升一个台阶。纯监测场景用 HTTP 够用,双向控制场景建议走自定义 TCP 帧。
4.2 自定义 TCP 帧适合系统对接
自定义帧没有标准协议的约束,但正因如此,怎么保证帧结构的完整性和可扩展性就成了设计重点。我常用的帧格式是:帧头、长度、设备地址、功能码、数据区、校验字、帧尾。温度数据放在数据区固定偏移位置。
// 帧结构示意 // 0xAA 0x55 | LEN | DEV_ID | FUNC | TEMP_H | TEMP_L | CRC16 | 0x0D 0x0A uint8_t frame[16];校验字我推荐用 CRC16,虽然比校验和多两个字节,但能检出更多错误类型,现场环境干扰偶尔会在长线缆上制造很难发现的数据位翻转。上位机解析时一定要保留“长度异常就丢弃”的逻辑,因为网络层重组出来的数据可能是半包或粘包,不处理会直接把后续数据解错位置。
链路层连接可以做成上位机主动连接设备的模式,也可以做成设备主动上位机模式。我更倾向后者中的一种特殊形态:设备维持短连接,每隔一段时间上报一次数据后关闭,下一次上报再从新建立连接。这种短连接方式对单片机内存的压力小,也方便后期加多台设备。
4.3 断线缓存与看门狗
远程温度监控最怕的不是网络慢,而是断线之后数据丢失。温度历史曲线中间缺一段,对事后分析非常不利。
在实际项目中,我建议给 PIC18F87J60 加一块小的非易失存储区域,用来缓存断网期间的采样数据。缓存区容量不用很大,只保留最近几个小时的温度记录即可,按一分钟一条算,几 KB 存储够用了。网络恢复后,设备把缓存的温度数据按时间顺序重新上报,并在上报完成后清除对应记录。
注意这里要处理好“当前时间”的同步问题。没有实时时钟芯片的话,远程服务器收到的是设备本地累计时间,做数据归档时会有偏差。最简单的方案是设备每次连接服务器成功后,以服务器的校时报文更新时间戳基准。这样至少能保证缓存数据的相对顺序是准确的。
主控本身必须有独立看门狗。以太网协议栈运行在 8 位单片机上,如果某个 RAW 数据帧触发了协议栈里的异常分支,可能存在死循环风险。看门狗能在程序卡死时让系统自动复位,保证设备不会因为一个网络数据包就永久停工。
5. HVAC 场景下的控制联动:别只把读数传上去
温度监测做到能看、能传,只完成了数据采集的部分。HVAC 现场真正需要的是基于温度数据做出控制决策,或者至少是提前预警。这里有一些和暖通强相关的策略,值得单独梳理。
5.1 本地控温和远程控温的分工
本地控制逻辑要放在 PIC18F87J60 里,不依赖上位机下发指令。这样即使远程链路断开,现场设备也能按照预设策略运行,不至于停摆。我在项目里通常把本地策略分成两档:普通模式按本地设定值控温,联网模式接收远程设定值覆盖本地值。远程断开后自动回退到本地设定值。
这套逻辑用代码表达其实就是一个简单的优先级判断:
float target_temp; if (remote_link_ok && remote_target != 0) { target_temp = remote_target; } else { target_temp = local_target; }远程设置的目标温度只保存在 RAM 里,本地默认温度则存在 EEPROM 中。每次冷启动时先读 EEPROM,再用当前的远程值覆盖,这样现场断电重启后也能快速回到可用状态。
5.2 滞回区间的设置
HVAC 控制不能设计成“温度一到设定值就立即动作”的硬开关逻辑,否则压缩机和电加热器会被频繁启停,设备寿命大幅缩短。标准的做法是设置滞回区间。
比如要加热,可以设定目标温度为 20 摄氏度,允许上限为 20 摄氏度,回差 2 摄氏度。实际逻辑是:温度低于 20 摄氏度时开启加热,加热到 22 摄氏度以上才停止,中间区域不操作。这样压缩机或电加热器每次启停之间的间隔明显拉长,整体使用寿命会好很多。
滞回区间的大小需要根据被控对象的惯性和现场容忍范围来调整。惯性小的电加热可以取 1 摄氏度,惯性大的风管系统取 3 到 5 摄氏度都正常。这个参数不建议写死在代码的宏定义里,最好做成可以通过远程下发的寄存器项,现场调试时有很大的便利。
5.3 传感器失效时的保护策略
温度传感器也是电子器件,也会坏。HVAC 现场常见故障是传感器断线、短路、信号线进水导致读数异常。代码里要针对每一种异常做处理。
传感器断线通常会让模块返回一个异常标志位,或者返回一个远超过正常范围的数值。比如真实温度不可能低于 -40 摄氏度也不可能高于 150 摄氏度,超出去就可以判定异常。此时控制系统不能继续按错误温度去控制阀门或加热器,必须进入保护模式:关闭执行器或输出报警。
在多传感器系统中,我还会做交叉判定。送风温度和回风温度不可能相差太离谱,如果同一台设备上两个传感器读数相差过大,说明其中一个已经漂移了,需要上报“温度数据可疑”而不是直接上报数值。这个细节在现场维护时非常有价值,能大大缩短排查时间。
6. 我踩过的坑:校准、网口和电磁干扰
最后一部分直接写我在类似项目里遇到过的真问题。这些问题不一定每次都会出现,但一旦出现就很隐蔽,提前知道能省很多调试时间。
6.1 温度不准先查这几件事
温度显示不准时,不要第一时间怀疑传感器模块坏掉。按照优先级排查:先看模块供电电压是否稳定,再看信号线是否和动力线走在一起,最后才检查传感器探头安装位置。
我遇到过很典型的一次:某设备的温度读数始终比实际水温高两度左右,检查半天模块和线缆都没问题,后来发现探头安装在管道外壁保温层里,贴着的其实是高温侧管壁,而不是真正的水体温度。这类问题属于安装位置的系统性偏差,单纯修传感器是永远修不好的。
还有一个常见问题是电源纹波太大。某批板卡采样数值在小数点后一位不停跳动,用示波器量模块电源端看到毛刺很明显,加了一个低通滤波器后读数立刻稳定了。HVAC 现场电机的启停瞬态很容易把纹波引入电源轨,传感器模块的电源入口处加电容和磁珠是非常有必要的。
6.2 网口偶尔连不上的排查链路
PIC18F87J60 的以太网在硬件设计上虽然简化了很多,但调试时还是会遇到“偶尔能连上,过一会又断”的情况。我的排查链路是固定的:先看链路指示灯是否正常,再断开网线直接测量变压器中心抽头的偏置电压,然后换一根质量可靠的网线交叉测试。
有一次现场反馈设备总是凌晨掉线,白天正常。后来分析了设备间的距离、施工中的网络端子接线方式等因素,最终锁定是布线经过了一段温度变化剧烈的区域,接头处热胀冷缩导致接触不良。这是个很典型的现场问题,排查时如果只看设备端会走很多弯路。
如果确认硬件没问题,再怀疑软件。检查网络协议栈的缓冲区分配是否够用,尤其是发送缓冲区和接收缓冲区是否被大量广播包占满。控制网络规模,或者在交换机上开启 VLAN 隔离,这类问题都能规避。
6.3 一些好用的调试工具
调试阶段我习惯用一个串口打印调试信息,把温度原始值、滤波值、帧收发状态全部打印出来。这样在出问题时能快速定位是传感器端的问题还是网络端的问题。字符串打印对时间敏感的代码有影响,但调试阶段完全值得。
网络侧我常用 tcpdump 或类似的抓包工具抓取设备与上位机之间的交互报文,查看帧结构是否完整、重传是否过多。对于数据上报这种低速率业务,只要能看到周期性报文正常发出,链路稳定性基本就有保障。
最后的经验是:这类系统不需要追求多功能,稳定才是第一位。把本地采样和远程上报分成两个独立的软件模块来开发,分别测试、分别打包,整个项目的调试效率会高很多。真出了问题时,也能很快确定是哪一半的责任。