
以前做HVAC风柜控制项目经常遇到一种很朴素的甲方需求现场的柜面温度要能在本地控制面板上显示同时还要能被中控室远程看到。早期方案是每个温度点都拉一根4-20mA模拟信号线到控制柜再靠PLC模块采集布线麻烦还容易受干扰。后来换成数字温度传感器配合微控制器之后整个监测链路清爽了很多。这篇文章就完整记录我基于PJ85718DM这颗I2C数字温度传感器、STM32L152ZD微控制器做的一套嵌入式/HVAC本地与远程温度监测方案包括硬件接线、软件驱动、温度换算、Modbus远程上报、低功耗设置以及调试中踩过的一个真实坑。如果你正在做温控面板、环境监测终端、风机盘管控制器这类设备这套思路可以直接拿来参考。1. 先拆一遍需求本地温度和远程温度分别对硬件提了什么要求1.1 HVAC项目里的温度点到底长什么样HVAC现场的温度监测点通常分两类。第一类是设备回风/送风温度装在风管内或者风机盘管附近距离控制主板有一段线束短则几十厘米长则三五米第二类是房间或者机房的环境温度装在装饰面板后面和主控之间的走线更远。过去我习惯用NTC热敏电阻加运放的方式采集好处是探头很便宜坏处是每一路都得单独标定分压电阻温度一变线缆一长误差就被放大。尤其当楼层里其他动力线缆在旁边的时候采样值经常抖滤波怎么调都压不住那种随机漂移。到了需要本地显示远程上传双通道的时候NTC方案的短板变成了可复现性和派接难度。同一型号的电阻、同一批电路板装在A座和B座之后读数差出1℃都不算新闻。数字温度传感器天然有优势不用标定分压数据就是数字线缆抗干扰能力也好很多。PJ85718DM就是这类里很常见的一种I2C接口数字温度芯片我们把它挂到STM32L152ZD的I2C总线上读取后做本地显示、协议转换和远程上报。整套系统的核心只有一个把温度从传感器端可靠地搬到需要它出现的地方。1.2 本地和远程的物理边界我习惯把产品边界画成这个样子本地温度等于传感器探头所在位置的温度由MCU直接通过I2C读回来远程温度等于需要离开本机、上传到上位机或云端的温度。二者本质上是同一份数据只是通信方式不同。本地侧MCU和传感器之间的通信属于短距离板级通信选I2C、SPI还是单总线都行。PJ85718DM走I2C两根线搞定多颗芯片还能挂同一条总线通过地址引脚区分。我在项目里最多挂过8颗分别贴在风管送风口、回风口、混风段和新风入口MCU通过不同的从机地址轮询读数不需要为每个点单独布线。远程侧HVAC项目里最常见的通道是RS485串行总线加Modbus RTU协议因为楼宇里已有大量基于Modbus的网关、触摸屏、PLC。如果要上云就再从另一路UART引出WiFi模块把温度点变成MQTT主题下的JSON数据。这套双通道架构可以同时存在于一台设备里互不干扰。1.3 对MCU的隐形要求低功耗、可唤醒、外设够用为什么不选一颗常见的通用MCU而要选STM32L152ZD核心原因在于现场设备的运行方式。HVAC温度监测终端往往不常断电但又不像空调主机那样持续满负荷运行很多时间处于等下一个采集周期的状态。如果只用一颗普通MCU全速跑功耗积累下来发热和电费都不好看如果启用低功耗模式设备外壳不烫、供电压力小、甚至电池备用方案也能做进去。STM32L152ZD属于低功耗L1系列Cortex-M3内核写代码不那么憋屈外设也比较齐I2C、USART、RTC、LCD段码接口、看门狗都有。我们用512KB Flash装应用逻辑绰绰有余96KB RAM跑Modbus缓冲区、滑窗滤波、MQTT报文缓存都够。真正选它的原因不是主频有多高而是它能在低功耗等待状态下保持RTC和需要的唤醒源这一点对温度定时采集来说非常关键。2. 硬件搭法STM32L152ZD做主控PJ85718DM做传感器远程通信用什么2.1 最小系统的供电和时钟设计STM32L152ZD支持1.8V到3.6V供电我们板子统一用3.3V。时钟我直接用HSI加PLL不需要外部高速晶振就能把系统跑起来RTC则配一颗32.768kHz的外部晶振这样进入低功耗停止模式后每隔固定时间唤醒一次做采样。这看起来是常识但很容易踩坑RTC晶振的负载电容如果不按规格书配RTC时间会漂定时唤醒来得偏早偏晚温度数据的时间戳就会乱。我的做法是看晶振手册要求的CL值如果写在12.5pF就在芯片OSC两端放两颗近似6pF的电容走线尽量短。板上实测一个月的唤醒间隔误差能控制在几十秒级别对温度采集来说完全可以接受。2.2 PJ85718DM的接线和PCB注意点PJ85718DM按I2C接法SDA和SCL各接一个上拉电阻到3.3V电阻我选4.7k。芯片供电脚旁边放一个0.1uF去耦电容如果现场交流干扰厉害再加一个10uF的电解电容兜底。因为我希望低功耗时连传感器的电都能彻底断开所以给传感器的VDD不是直接连3.3V而是通过一个MOS管开关MCU用GPIO控制只有采集时才给它供电。这样整板待机电流可以压得很低。PCB布局上有一个容易被忽略的细节传感器不要放在板子发热源附近。我们项目里第一版PCB把PJ85718DM放到了LDO稳压器旁边实测温度比环境高了近2℃后面挪到板边并杀菌用地线夹了一下才恢复正常。数字温度芯片测得准不准很大程度取决于PCB热布局而不是芯片标称精度。传感器探头区域最好开窗或做成引线端子式让探头脱离主板的发热区域。2.3 远程通道RS485收发器与WiFi模块的取舍远程通道我做了两个出口方便项目扩展。第一个是RS485半双工总线STM32L152ZD的USART2接到某款常用3.3V RS485收发器比如MAX3485或SP3485A/B线走双绞线出去。USART2的TXD接收发器DIRXD接RO方向控制脚DE/RE并联后接一个GPIO。发送数据前拉高方向脚发送完再拉低转回接收状态。第二个出口是WiFi模块USART3通过AT指令与一个常见的低成本WiFi模块通信模块预先刷好标准AT固件我们只需要处理串口透传和AT指令状态机不自己处理WiFi协议栈。需要说明一下RS485在HVAC领域依然是主力原因在于稳定、简单、不带IP地址管理一条总线上挂几十台设备网关统一扫。WiFi加MQTT更适合需要跨地域远程看数的场景。我的建议是板子上把两种通道都预留通过跳线或配置位选择避免后续改板。3. 把本地温度真正读准寄存器、换算、滤波和标定3.1 传感器读取的基本流程PJ85718DM这类I2C温度传感器的寄存器结构比较常规一个配置寄存器选分辨率和工作模式一个温度数据寄存器存放最近一次转换结果。下面用HAL库给出一段典型的读取流程。具体寄存器地址和配置位请以你手里芯片型号的数据手册为准不同批次的细微差异会在联调时暴露出来。#define PJ85718_ADDR 0x48 // 根据A0/A1地址脚配置7位地址 #define PJ85718_REG_TEMP 0x00 #define PJ85718_REG_CFG 0x01 static float temp_value; void temp_read(void) { uint8_t cfg 0x60; // 12位分辨率连续转换模式位段定义参考数据手册 uint8_t regs[2]; temp_power_on(); // 打开传感器供电MOS控制GPIO HAL_Delay(10); // 等上电稳定 HAL_I2C_Mem_Write(hi2c1, PJ85718_ADDR, PJ85718_REG_CFG, I2C_MEMADD_SIZE_8BIT, cfg, 1, 100); HAL_I2C_Mem_Read(hi2c1, PJ85718_ADDR, PJ85718_REG_TEMP, I2C_MEMADD_SIZE_8BIT, regs, 2, 100); int16_t raw (int16_t)((regs[0] 8) | regs[1]); temp_value raw * 0.0625f; temp_power_off(); // 关闭传感器供电降低待机功耗 }核心坑在符号位。温度负值时寄存器内容以二进制补码表示直接当成无符号short读出来再乘0.0625会得到几百度的荒谬结果。必须先转成int16_t再参与换算。3.2 分辨率、转换时间和数值抖动如果把分辨率调到12位一个LSB对应0.0625℃。理论上精度够了实际上读数会在最后几位上下来回跳比如环境温度本来就在轻微波动读数一直在23.4375和23.625之间跳也正常。此时不要急着做很重的滤波先分清抖动来源变频器、继电器启停瞬间都可能在I2C长线上感应出干扰导致偶发跳变甚至通信失败这时候应该先想办法物理隔离干扰再做软件滤波。我常用的软件滤波是滑动平均保留最近5次采样去掉最大和最小值后取平均。温度变化缓慢5点窗口延迟很小抗单点毛刺能力不错。如果现场电磁环境特别糟再加一个变化量限制新值与上次有效值温差超过3℃就认为这次读取异常直接丢弃并重新发一次读请求。#define BUF_N 5 static float temp_buf[BUF_N]; static uint8_t temp_buf_idx; float temp_filter(void) { float sum 0, max_v -1000, min_v 1000; for (int i 0; i BUF_N; i) { float v temp_buf[i]; sum v; if (v max_v) max_v v; if (v min_v) min_v v; } return (sum - max_v - min_v) / (BUF_N - 2); }3.3 与参考温度计对照做单点偏移校准即便芯片标称精度±0.5℃实际装配后由于PCB热传导、外壳风道布置整体读数还是可能系统性偏离真实环境温度。我在项目里做的最有效的校准是单点偏移校准把设备放到恒温环境比如25℃的空调房里静置1小时旁边放一支校准过的参考温度计记录传感器读数和参考读数的差值把差值存到EEPROM或Flash的出厂校准区。后续每次上电读校准值最终输出值等于原始滤波温度减去偏移值。这个方法对HVAC应用足够了因为工作温区通常较窄传感器本身线性也好单点偏移能把误差收敛到0.2℃以内。如果要求全温区高精度才需要两点校准或多项式拟合那会增加校准成本和点位设计。提示校准偏移量不能只写一个固定值最好在出厂测试工位写两个数据参考温度、偏移值。回头排查设备时能看到校准时的环境条件避免把正常的出厂数据当成故障。4. 远程温度上报Modbus RTU从站优先MQTT作为对照组4.1 为什么首推Modbus RTUHVAC项目和楼宇自控对接的时候Modbus RTU几乎绕不开。楼宇网关、现场触摸屏、PLC的主站能力基本都是Modbus RTU/TCP你只要做从站把温度放到寄存器里主站就能轮询读取。相比自己定义私有帧格式Modbus RTU的好处是链路层健壮、CRC16校验成熟、调试工具多。不要一上来就设计自定义协议除非你的设备永远只跟自家网关通信——现实中这个前提通常不成立。初次接触Modbus RTU的读者可以把它想象成一张填好的快递单地址是收件人功能码是业务类型寄存器地址和数量是货位CRC是防拆封条。主站发查询帧从站经过校验后回数据帧一帧一答。4.2 从站寄存器布局与CRC16实现串口参数按行业惯例设置为9600,8,N,1。从站地址我在板子上用拨码开关设置比如设备地址0x01到0x0F可拨。温度值为了保留一位小数且能表示负温度我定义了一个有符号16位寄存器实际值等于温度数值乘10比如23.4℃存为234。寄存器地址类型内容0x0000保持寄存器当前温度×10有符号0x0001保持寄存器传感器状态0正常1通信失败2超上限3超下限0x0002保持寄存器最近一次错误码用于现场诊断主站用03功能码读保持寄存器如果需要也可以支持04读输入寄存器内容相同。CRC16计算不需要引入大型算法函数体就很短uint16_t crc16_modbus(uint8_t *data, uint16_t len) { uint16_t crc 0xFFFF; for (uint16_t i 0; i len; i) { crc ^ data[i]; for (int j 0; j 8; j) { if (crc 0x0001) crc (crc 1) ^ 0xA001; else crc 1; } } return crc; }发送响应时按低字节在前、高字节在后附加到帧尾。很多初学者在Modbus联调时出的问题一半以上不是业务逻辑而是CRC高低字节顺序搞反抓包一看就懂。4.3 从站状态机接收、校验、应答三件套Modbus RTU从站代码不要写成阻塞式轮询用串口接收中断配合一个简单状态机就够。收到一帧后先检查帧里的从站地址是不是自己CRC16校验是否通过功能码是否支持寄存器范围是否超界。都通过才组装应答。还有一个容易被忽视的点是帧间隔。Modbus RTU规定主机发送帧与帧之间需要一定静默时间如果上一个字节到下一个字节超过1.5个字符时间从站应认为这是一次新的请求。用9600波特率算1.5个字符时间约1.5ms定时器很容易实现超时判断。我在项目里直接用UART的IDLE中断来切帧省事很多HAL库里配置UART IDLE检测即可。4.4 如果要上云WiFi模块加MQTT怎么和Modbus共存现场偶尔会有中控室要看手机端也要看的需求这时我加了一条MQTT链路。STM32L152ZD通过USART3发送AT指令控制WiFi模块连接固定AP再通过TCP连接到MQTT Broker。温度数据每30秒向一个主题发一次JSON{device:1f03a2,t:23.4,alarm:0,ts:1700000000}两条链路不要互相抢数据。我用一个冷却时间信号量Modbus每次主站查询都能立马拿到最新温度MQTT则单独一个定时任务读出当前值后组包。WiFi模块在启动和连接AP时会比较耗流量和电流如果是在低功耗设备上做建议不要让WiFi模块一直在线改为定时唤醒发送后立即关闭模块电源这样电池方案才扛得住。我第一版就犯过让WiFi模块常挂着的错误整机电流多了小一百毫安后面加了MOS管按需供电才压下来。5. 让板子常年在现场跑低功耗、看门狗和传感器失效处理5.1 功耗分配算清楚每一毫安去哪了做温度监测设备功耗不是一锤子买卖而是明摆着的散热和运维成本。我们板子目标平均电流控制在1mA以内采集周期2秒一次。粗算一下MCU停止模式电流约2.5uARTC约1.5uARS485收发器空闲时先关掉LDO静态电流不高。真正的大头在传感器供电和I2C总线灌电所以把传感器供电做成GPIO加MOS开关之后整机停止状态电流能到7uA左右。每次采集周期动作时间很短上电、延时、读寄存器、滤波、刷新显示、应答Modbus请求一共不到30ms。30ms乘以平均10mA并且每2秒一次占比很低所以上面算的待机1mA以下是有余量的。大家如果不确定自己的功耗不要凭感觉直接把万用表串联进供电端用示波器电流探头看波形或使用带电流采集功能的分析工具实测才有说服力。5.2 RTC定时唤醒与采集节奏我用STM32L152ZD的RTC闹钟事件作为唤醒源。系统绝大多数时间停在Stop模式下RTC每隔2秒产生一次闹钟中断MCU被唤醒后做一轮温度采集和Modbus应答然后再次进入停止模式。进入停止模式前要处理好几件事串口引脚状态、I2C释放总线、关闭不需要的时钟和外设电源。这里的坑比较隐蔽如果只用USART2做RS485且一直开着接收从停止模式唤醒后串口接收状态可能会同步丢失上位机刚好在这两秒间隙发来查询帧从站可能没反应。ST的L1系列支持USART低功耗唤醒检测到起始位即可把MCU从停止模式拉醒这是最干净的方案。如果固件版本或HAL库不太好配退而求其次的做法是让MCU保持短周期唤醒唤醒后立即检查串口但这会牺牲一些功耗。我的建议是优先把UART低功耗唤醒调通。5.3 看门狗别在主循环里无脑喂温度监测设备无人值守看门狗必须有。我用的独立看门狗IWDG溢出时间约2秒。采集流程中的任何一步卡死I2C总线被拉死、WiFi模块AT指令无响应、Flash写保护异常都会导致IWDG超时复位。要注意喂狗位置不能放在主循环开头而要放在一整套采集加通信动作都顺利完成之后这样能保证故障真正被兜住。还有一个小细节不要在低功耗Stop模式期间把恢复完全寄托给看门狗。有些型号的看门狗在停止模式下继续计数如果你进去了又没喂理论上就到点复位但现场复位恢复时间不可控。最好是进入Stop前处理好喂狗策略比如在RTC唤醒中断里先喂狗再干活规则要提前在代码里固定别依赖今天刚好没触发。5.4 传感器失效判断和异常上报数字温度传感器不是说数字出数据就一定准。实际项目中我遇到过三类问题一是传感器芯片损坏I2C地址无应答二是接线松动读出来全是0xFF或0x00三是传感器在低温高湿环境下结露读数异常偏低。处理办法是在采集函数里加返回状态ACK缺失、寄存器全0、连续多次超量程都算故障。故障时本地显示更新为ErrModbus寄存器里的状态位置故障值MQTT的告警字段也带上。绝对不能把错误的温度值继续当正常值往上上传否则中控室看到的可能是错误的自动控制依据比没有数据更危险。6. 实测记录一次远程温度偶发跳变的完整排查6.1 现象Modbus读回来的温度偶尔会跳10℃以上项目装到现场后反馈说中控室监控软件里有一台设备的回风温度偶尔会从23.4℃瞬间跳到35.1℃持续几秒又跳回来。Modbus主站轮询是1秒一次跳变没有固定规律有时候一天两三次有时候半天一次。这种偶发问题最麻烦因为它不会稳步复现只能靠日志和现场环境排查。6.2 分步定位先区分传感器层、通信层还是软件层我的排查链路是层层缩小范围。第一步把上位机的Modbus记录拉下来看跳变发生时寄存器里的状态字有没有同步变化结果是状态字一直正常说明不是传感器报故障。第二步在现场把同一时刻的本地面板显示和Modbus读数对比面板也跳了说明问题不在远程传输而在本地数据源头或传感器。第三步把PJ85718DM的裸数据打印到调试串口发现跳变时I2C返回的不是连续突变值而是寄存器读成了全0xFF然后被当成了负数参与计算得出一个不合理的高温。到这里基本锁定了I2C通信在特定瞬间收到损坏字节。再查现场布线发现温度探头线和小功率变频器的输出线走在同一个线槽里变频器动作时产生共模干扰影响了I2C线。虽然线不长但I2C两根线直接走线槽没有屏蔽也没有双绞抗干扰能力不足。6.3 修复方案与验证结果修复做了三件事。第一物理层面把传感器探头线改成双绞屏蔽线屏蔽层在控制板端单点接地并让它与动力线槽保持距离。第二电气层面I2C频率从100kHz降到10kHz上拉电阻从4.7k换成2.2k增强抗容性负载能力。第三软件层面加入前面说的变化量限制单次突变超过3℃时丢弃并重读连续三次异常才报故障。改造后在同一台设备上连续观察了两周Modbus数据未再出现跳变。这个案例给我的经验是遇到偶发数据异常先想清楚数据从传感器到上位机经过几个环节然后逐层排除而不是一上来就怀疑传感器型号不对或协议写错。嵌入式现场问题多数是物理层的干扰化装成了软件层的Bug。我在做完这个项目后对远程温度监测这类需求的态度是本地的传感器读数是一切的基础先把I2C读稳、把PCB热布局处理好再去谈Modbus、MQTT、云平台这些看起来很花哨的部分。数据采集链路只要有一个环节不够踏实远程再怎么优化展示都只是把一个错误数据送到了更大的屏幕上。PJ85718DM和STM32L152ZD这套组合胜在架构干净、外设够用、低功耗可控特别适合HVAC温控面板、环境监测终端这类需要长期运行的嵌入式设备。如果后续项目需要多通道温度或风温湿一体化采集我会在这套基础上扩展传感器总线数量核心采集和上传逻辑基本不用推翻重写。最后补一个每次都要分享的小习惯把所有调试过程里出现过的问题记到项目维护笔记里包括I2C波形截图、Modbus抓包记录、校准偏移值。下次再去现场这些记录比任何说明书都管用。