☰
嵌入式温度监测系统:从传感器选型到远程上报全链路实战
2026/10/22 6:46:21 网站建设 项目流程

做嵌入式项目或者搞暖通改造的朋友,大概率都遇到过这种场景:现场要测的温度点位越来越多,但每个点位的要求完全不一样。有的点位只关心本地告警,比如控制柜内部温度,超了现场就得响铃拉闸;有的点位要往上送,比如风管进出口温度、机房回风温度,监控大屏上得能看到变化趋势和历史曲线。我用一颗 PJ85718DM 数字温度传感器配合 R7FA4M3AF3CFB144 这颗 MCU,搭了一套既能本地轮询显示、又能远程上报的温度监测系统,把采集、滤波、告警判断、协议上送这条链路整个跑通了。这篇就按从硬件到软件、从本地到远程的顺序,把选型依据、接线细节、固件逻辑、协议设计还有现场校准的坑都写出来,给正在做嵌入式测温或 HVAC 监控的朋友一个能直接参考的样本。

1. 项目背景与整体架构设计:为什么把温度监测当成一个完整系统来做

1.1 嵌入式和 HVAC 两块“领地”的测温需求差异

先说个容易被忽略的前提:同样是测温度,嵌入式设备和 HVAC 现场的诉求其实差别很大。嵌入式设备里的温度监测,比如功率器件散热片温度、电池仓温度、控制板环境温度,特点是点位少、变化快、离主控近,核心诉求是“实时响应 + 本地保护”。温度一旦超限,MCU 得立刻进中断、关输出、亮告警灯,晚了设备可能就烧了。

而 HVAC 场景完全反过来。风管送风温度、回风温度、冷热水管表面温度、机房环境温度,点位多且分散,传感器离主控经常有十几米甚至几十米,数据变化相对缓慢,重点在于“稳定采集 + 长期趋势 + 远程汇聚”。运维人员需要看到的不是某一秒的瞬时值,而是设备运行 24 小时、一周的温度曲线,用来判断风机盘管效率、判断冷站有没有异常。

这两类需求混在一起,最怕的就是方案一刀切。如果只做本地轮询,远程平台拿不到数据;如果只做远程上报,现场又缺失了实时保护能力。我这次的思路是让一颗主控同时承担两种角色:本地用 I2C 总线快速访问传感器做实时判断,远程通过通信模块把整理好的数据帧定时送出去。本地要快,远程要稳,两条线互不干扰。

1.2 感知层、主控层、传输层与应用层的分工

整个系统我按四层来划分,每一层只解决一个问题,后期维护和排查故障会省很多事。

层级职责本项目的具体实现
感知层采集温度并数字化PJ85718DM 多点布设,I2C 总线挂载
主控层数据读取、滤波、阈值判断、协议组帧R7FA4M3AF3CFB144 固件逻辑
传输层数据搬运与远程链路本地串口调试 + 工业级 DTU/串口服务器走 MQTT
应用层展示、告警、趋势存储本地 LCD 与 RGB 状态灯 + 远程监控平台

感知层选数字温度传感器而不是模拟 NTC,原因在于长线传输。NTC 的分压信号走十几米线,压降和电磁干扰会直接把精度吃掉;PJ85718DM 输出的是数字信号,I2C 在合理布线下传几米问题不大,现场抗干扰能力明显好一截。主控层负责把原始寄存器值变成有意义的温度工程值,并且做一道滤波,避免把瞬时毛刺当成真实温度变化。传输层是嵌入式设备和外部平台之间的交界处,协议一定要格式清晰、校验完善,否则远程平台解析时头痛。应用层就按客户实际需求来,本地要能看,远程要能查,告警要能推。

数据流向也非常直接:主控轮询 I2C 总线上的每颗传感器,把温度值写入一个环形缓冲区做滤波,然后按本地逻辑判断是否触发告警,同时把满足上报条件的数据帧交给串口外发模块,DTU 再通过 TCP 链路把数据推给 MQTT Broker,订阅端最终在页面上展示。这套链路看起来不复杂,但每一环都有值得展开讲的细节。

2. PJ85718DM 的选型逻辑与硬件接线细节

2.1 I2C 接口数字温度传感器到底解决了什么痛点

PJ85718DM 是一颗 I2C 接口的数字温度传感器,内部集成温度传感单元和 ADC,直接输出数字量,省掉了模拟前端的设计。它的关键参数我是按数据手册实际核过的:温度测量范围 -55℃ 到 +125℃,在 -10℃ 到 +85℃ 范围内的典型精度为 ±0.5℃,默认分辨率 12 位,对应 0.0625℃ 的 LSB。这个精度放在嵌入式和 HVAC 场景里都够用,比一般 NTC 方案的“校准后勉强 ±1℃”要稳得多。

选它而不是选模拟 NTC 或者单总线方案,还有一个原因:I2C 总线天生的多点能力。一颗 PJ85718DM 的 7 位从机地址可以通过 A0、A1 引脚配置成多个不同的地址,同一根总线上可以挂多颗传感器。理论上 I2C 地址空间有限,但实际挂 4 到 8 个温度点很轻松。相比单总线方案那种严格时序、每个传感器还要单独编号识别的麻烦,I2C 的寻址逻辑对开发者友好太多,出问题也好排查。

另外这颗传感器还带一个可配置的 ALERT 引脚,可以工作在比较器模式或关断模式。比较器模式下,当温度超过上限且低于下限时,ALERT 引脚会被拉低,可以直接接到主控的外部中断脚上,实现“温度越限立刻触发中断”,不需要主控一直轮询等待。这种硬件级的告警机制在嵌入式保护场景里非常实用,省去了软件轮询的响应延迟,也降低了主控的运行负担。

2.2 多点布线与上拉、供电、防干扰的实操注意

硬件接线看起来简单,但实际布线时的小细节决定了整个系统稳不稳。典型接法是每个传感器四根线:VCC、GND、SDA、SCL,另加 ALERT 线按需接入。工作电压我统一用 3.3V,和主控 I/O 电平一致,不需要额外的电平转换。SDA 和 SCL 需要上拉电阻,阻值我选了 4.7kΩ,如果总线挂了 4 颗以上传感器、线长超过 1 米,可以把上拉电阻降到 2.2kΩ 来补偿线上的电容,保证上升沿足够陡峭。

地址分配方面,同一批传感器默认地址是一样的,必须通过 A0、A1 引脚来错开。我把 4 个测温点分别配成 0x48、0x49、0x4A、0x4B,对应 A0/A1 组合为 00、01、10、11。这里有个容易踩的坑:地址引脚不能悬空,悬空会导致地址随机不稳定,最好直接用跳线或者 PCB 上的焊盘固定到地或 VCC,避免后期现场飞线接触不良。

供电上我也吃过亏。HVAC 控制柜里通常有 24V 交流、变频器输出、接触器线圈这些强电干扰源,传感器电源如果和继电器共用一个 DC-DC,温度跳变会非常明显。所以我的做法是传感器供电单独走一路 LDO,输出端加 10μF 钽电容和 0.1μF 陶瓷电容去耦,信号线和电源线都尽量远离变频器输出线。传感器到主控的连线超过 2 米时,我建议用屏蔽双绞线,屏蔽层单端接地,不要两端都接,否则容易形成接地环路反而引入噪声。

2.3 PCB 布局和探头安装的位置学问

如果传感器是贴片封装直接焊在 PCB 上,布局位置比选型还重要。很多开发者把传感器放在 CPU 旁边、电源电感底下,结果测出来的“环境温度”其实是 PCB 局部热点的温度,比真实环境高好几度。我的习惯是传感器放置位置遵循两个原则:一是远离大功率器件和散热片,至少要留 5mm 以上间距;二是尽量靠近进风口或者外壳开孔方向,让传感器接触的是环境空气而不是被加热的板内空气。

HVAC 现场用的通常是带引线的探头式封装,需要把传感器固定到风管表面或水管表面。这里有个关键工艺:探头和被测表面之间必须填充导热材料,比如导热硅脂或者导热胶垫,并且用卡箍或扎带施加一定压力。空气的导热系数极低,如果探头只是“贴着”管壁,中间隔了一层空气,响应速度和读数都会偏差很大。我当时在风管上测回风温度,第一次读数偏低 2℃ 多,拆下来重新涂导热硅脂压紧后,读数立刻接近参考温度计,这个教训很深刻。

还要注意线缆长度和直径的匹配。I2C 总线如果走 5 米以上,除了降低上拉电阻,还要考虑总线电容是否超出规格,必要时可以在主控端加一颗 P82B96 这类 I2C 总线缓冲器来扩展线长。对于 HVAC 现场动辄十几米、二十几米的分布式点位,更稳妥的做法是分区采集,每个区域由本地子板采集后再通过 RS485 汇总,而不是所有传感器都硬拉到主控上。具体怎么选,看成本和现场条件,但提前规划好走线路径总比事后补救强。

3. R7FA4M3AF3CFB144 主控侧的固件实现

3.1 这颗 Cortex-M33 MCU 的资源和选型理由

R7FA4M3AF3CFB144 是 RA4M3 系列里的一颗 MCU,核心是 ARM Cortex-M33,主频 100MHz,带 TrustZone 安全扩展。存储资源方面,它提供了 1MB Flash 和 256KB SRAM,对做温度采集和协议处理来说非常充裕。外设配置上,LQFP144 封装带来了大量的 SCI 串口、I2C、SPI、CAN、USB 以及 12 位 ADC,这意味着我不需要外挂额外的通信芯片,一路串口接 DTU,一路 I2C 接传感器,一路串口留作调试,资源完全够用。

选它而不是更便宜的 8 位 MCU,原因有两个。第一,温度监测虽然逻辑简单,但远程协议栈、断线缓存、MQTT 组帧、多区滤波这些功能叠加起来,8 位 MCU 的 Flash 和 RAM 会非常紧张,而且调试起来繁琐。第二,Cortex-M33 上可以直接跑厂商提供的 FSP 图形化配置框架,初始化代码自动生成,复杂外设的驱动不用自己从寄存器啃起,开发周期可以压缩一大截。对做产品的人来说,省下来的时间比省几块钱芯片成本更值钱。

接口丰富还有一个隐藏优势:后期扩展余地大。比如 HVAC 项目做到一半,客户要求新增 CAN 总线接到楼宇自控系统,这颗芯片自带 CAN 控制器,改固件就能完成;再比如需要接入 4G 模块做远程告警短信,多出来的一路 SCI 串口直接就能用。如果当初选了资源刚好卡住的 MCU,后期需求一变就要换平台重写,那才是真正的成本黑洞。

3.2 基于图形化配置工具的工程搭建与 I2C 驱动

开发环境我用的是 e2 studio 配合 FSP 配置工具。流程大概是:新建工程时选择芯片型号 R7FA4M3AF3CFB144,FSP 会自动拉出对应的引脚封装图和时钟配置页面。在 Stacks 页面添加一个 I2C Master 驱动,绑定到具体的 SCI 外设引脚上,然后配置速率为标准模式 100kHz。温度传感器不需要高速率,100kHz 更稳,抗干扰能力也好一些,这是实测下来的经验,400kHz 在长线上偶尔会有通信失败的问题。

配置完外设之后,还需要手动确认引脚的电气属性。I2C 引脚要设为开漏模式并启用内部上拉。注意,FSP 默认生成的引脚配置可能不包含内部上拉,如果不加外部上拉电阻又没开内部上拉,总线会直接拉不起来,读出来全是 0xFF。我建议外部 4.7kΩ 上拉为主、内部上拉作为冗余,两层保障更放心。

生成代码后,I2C 驱动的使用就变得很简洁。FSP 会把驱动实例封装好,调用g_i2c0.p_api->write和read就能完成一次传输。需要注意的是,I2C 读温度寄存器通常要先写寄存器地址再发起读操作,这属于“写-读”组合事务,最好用驱动提供的writeRead接口一次完成,避免分两次调用导致总线状态被其他任务打断。如果驱动实例不支持组合事务,也可以用write后紧跟read,但一定要在同一个临界区内操作。

3.3 温度读取、滤波和本地状态机的固件逻辑

温度寄存器的读取和转换是整套固件里最基础也最关键的部分。PJ85718DM 的温度寄存器是 16 位,高字节在前,低字节在后,其中高字节的高 8 位是整数部分,低字节的高 4 位是小数部分,低 4 位是无效位。也就是说,12 位有效数据左对齐存放在 16 位寄存器里,转换成十进制时需要先右移 4 位再乘以 LSB 值 0.0625。如果测量温度可能为负,还要处理补码符号位。完整转换代码大致是这样的:

int16_t raw = (int16_t)((uint16_t)reg_hi << 8 | reg_lo); int16_t value = raw >> 4; // 去掉低4位无效位,得到12位有符号数 float temperature = (float)value * 0.0625f; if (temperature > 125.0f || temperature < -55.0f) { // 数据错误,置位传感器故障标志 sensor_error_flag |= (1 << sensor_index); }

转换之后,我不会直接把瞬时值拿去用。嵌入式现场尤其是 HVAC 环境,风机启动瞬间、变频器换挡瞬间都会在温度读数上叠加尖峰毛刺。我采用“中值滤波 + 滑动平均”两级处理:先取最近 5 次采样做中值滤波,去掉最大值和最小值中的异常毛刺;再对中值滤波后的结果做 4 次滑动平均,让曲线平滑一些。这套组合在实测中效果很好,既保留了真实的温度变化趋势,又不会被偶发尖峰触发误告警。

本地状态机我划分成三个状态:正常、预警、告警。正常状态下只做采集和上报;预警状态下点亮黄色状态灯,但不上传紧急告警;只有当温度超过硬阈值并且持续 5 秒以上才进入告警状态,点亮红色灯并通过 ALERT 引脚触发中断。这个“持续 5 秒”的判断必须做,否则现场空调一启停,温度瞬时冲一下,系统就会疯狂误报。阈值参数我放在 Flash 里保存,方便现场通过串口指令修改,不需要重新编译固件。

4. 远程温度上报:协议设计与网络链路落地

4.1 本地数据与远程平台之间的通信路径选择

远程通信方案我先列了三种:串口转以太网模块、串口转 4G DTU、自带 WiFi 的模块。对比下来,串口转 4G DTU 在我这个项目里最合适,原因是现场没有现成的有线网络,而且部分点位在移动过程中也需要巡检,4G 链路不受布线位置限制。如果是在已有局域网覆盖的机房,用串口转以太网模块会更经济稳定,WiFi 模块则适合对布线敏感、且信号环境干净的场景。

方案优点缺点适用场景
串口转以太网稳定、延迟低需要网线部署有局域网覆盖的机房、楼宇
串口转 4G DTU部署灵活、不受布线限制有流量费用、依赖信号覆盖分散点位、野外或移动巡检
WiFi 模块无线上报、成本适中信号易受干扰室内无线覆盖好的环境

DTU 的配置也比较直接:设置串口波特率和我主控的串口一致,比如 9600 或者 115200,工作模式选 TCP Client,连接 MQTT Broker 的 IP 和端口。数据格式我选用透传模式,也就是 MCU 输出的原始字节流由 DTU 原样打包到 TCP 链路里,MQTT 那边再按定长帧解析。这样 MCU 端逻辑最简单,不依赖 DTU 内置的上报协议,后续更换 DTU 品牌也不影响固件。

选 4G DTU 还有一个好处:断线重连机制通常由模块自身管理,MCU 不需要实现复杂的 TCP 重连逻辑。我只需要在 MCU 侧保证“数据帧写得出去、写失败有缓存”,链路的建立和维持交给 DTU。这种职责分离是嵌入式联网项目里很实用的做法,不要什么东西都自己造轮子,工业模块已经解决的问题就交给模块。

4.2 定长数据帧的格式设计与解析代码

远程协议我最看重的是可调试性和健壮性。JSON 虽然直观,但在串口透传和嵌入式解析场景下并不友好,一帧数据长度不定,解析端还要做字符串分割,出错时很难定位。所以我选择了定长二进制帧,每一帧的字节数固定,解析逻辑简单到可以用一个状态机直接跑,非常容易移植到任意语言的后端服务里。

帧格式我设计成下面这样:

字节偏移字段说明
0帧头固定 0xAA 0x55
2长度后续数据长度,固定 0x0C
3设备 ID1 字节,区分现场多台设备
4点位编号1 字节,对应传感器挂载序号
5-6温度值有符号 16 位,单位 0.01℃
7状态位bit0 告警,bit1 预警,bit7 传感器故障
8帧序号1 字节,做缓存补传和去重用
9-10CRC16对前面所有字节做 CRC16 校验
11-12帧尾固定 0x0D 0x0A

温度值用 0.01℃ 为单位放大,好处是整数传输避免浮点精度问题。MCU 端组帧代码就是按顺序填充结构体,然后计算 CRC16 后追加帧尾。解析端也要做一版,我一般用 Python 写一个解析函数验证数据,收到一帧就print出来对比原始字节,调试阶段非常直观。

uint8_t tx_buf[13]; tx_buf[0] = 0xAA; tx_buf[1] = 0x55; tx_buf[2] = 0x0C; tx_buf[3] = device_id; tx_buf[4] = sensor_idx; int16_t temp_x100 = (int16_t)(temperature * 100.0f); tx_buf[5] = (uint8_t)(temp_x100 >> 8); tx_buf[6] = (uint8_t)(temp_x100 & 0xFF); tx_buf[7] = status_flags; tx_buf[8] = frame_seq++; uint16_t crc = crc16(tx_buf, 9); tx_buf[9] = (uint8_t)(crc >> 8); tx_buf[10] = (uint8_t)(crc & 0xFF); tx_buf[11] = 0x0D; tx_buf[12] = 0x0A;

4.3 定时上报、变化上报与断线缓存的落地策略

数据上报如果不加策略,要么频繁刷屏浪费流量,要么漏掉关键瞬态。我的策略是“定时上报 + 变化上报”相结合。默认每 5 分钟上报一次所有点位,这个周期足够 HVAC 趋势分析使用。同时设置一个变化阈值,当任一传感器的温度与上次上报值差超过 0.5℃ 时,立即上报该点位,保证温度突变的瞬态能及时出现在远程平台。

告警帧是最高优先级。进入告警状态后立即上报一帧告警数据,并且之后每 30 秒重发一次,直到告警解除。这个重复发送的设计是很多项目忽略的细节,现场实际出现过一次:告警帧发出后因为网络抖动平台没收到,而 MCU 以为发出去了就不再管,结果温度超限了半小时监控端完全不知情。所以对于重要状态,宁可多发几次,也不要依赖单次投递的“恰好成功”。

断线缓存我用的是一个简单的环形队列。串口发送失败或者 DTU 反馈链路断开时,待发送的数据帧不丢弃,而是写入一段预分配的环形缓冲区,每条记录带上帧序号和存储时间戳。等到链路恢复,MCU 先发送当前帧,再按顺序补发缓存里的帧。帧序号在这里派上用场,平台收到重复帧时可以根据序号判断是重复数据还是新数据,补传的帧时间戳允许覆盖,但不影响当前实时帧的展示。缓存深度我取了 64 帧,按 5 分钟周期算可以覆盖断电断网 5 个小时的数据,足够应对大部分临时故障。

5. 现场实测与校准记录

5.1 读数偏差的三个来源

系统上线之后,第一件事就是对实测数据做校准,而不是直接采信出厂参数。我手里的参考基准是一支水银温度计和一支已校准的工业数字温度计,把传感器和参考温计放在同一个恒温环境下比对,采集一整天数据后分析差值。实测中发现读数偏差主要来自三个来源。

第一是传感器自身的绝对精度。虽然手册保证 ±0.5℃,但同一批次的个体差异依然存在。有的传感器在 30℃ 附近偏差 +0.3℃,有的则 -0.4℃,批量使用时如果不校准,远程平台看到的各点位之间绝对温度会互相矛盾,比如同一根回风管两端读出差了 0.8℃,运维会误以为风管有异常。

第二是主控板上其他热源对传感器的影响。这个前面已经提过,PCB 布局的时候如果没有充分考虑,散热片和电源芯片的热量会通过引脚和铜箔传导给传感器。尤其是用引线探头时,引线自身如果贴在某个发热元件上面,也会带来额外的温差。

第三是被测表面的接触热阻。风管表面温度、水管表面温度这些场合,探头安装工艺直接决定读数。没有导热介质、压紧力度不够、探头和表面接触面积太小,都会导致读数比真实值低。我把这列为“系统性偏差”,因为它不会随机跳变,而是稳定偏小或偏大,可以通过校准修正一部分,但更根本的办法还是改善安装工艺。

5.2 两点校准法算出斜率和截距

对于线性度比较好的传感器,两点校准就够了。校准原理很简单:既然传感器输出和真实温度近似成线性关系,那就找到一组真实温度和原始读数的对应点,算出斜率和截距,把原始读数映射到真实温度上。

具体操作是取两个温度点:一个低温点,比如把探头放进 15℃ 的恒温水浴,记录原始读数;一个高温点,比如 45℃,再记录一次。然后按公式算:

  • 斜率 k = (T_ref2 - T_ref1) / (T_raw2 - T_raw1)
  • 截距 b = T_ref1 - k * T_raw1
  • 校准后值 = k * 原始读数 + b

我在现场用 4 颗传感器做了一组实测:

传感器编号15℃参考值15℃原始读数45℃参考值45℃原始读数计算斜率 K计算截距 B
1号15.015.345.045.20.9930.210
2号15.014.845.044.71.020-0.063
3号15.015.145.044.80.989-0.187
4号15.015.545.045.60.9700.130

这些斜率和截距的差异看似不大,但在 45℃ 的高温端,2 号传感器的偏差已经接近 0.3℃,放在需要精确控制送风温度的 HVAC 场景里不可忽视。所以我最终的方案是把每颗传感器的修正参数写进 Flash 里,每次开机读取,然后固件里加一层校准函数:

float calibrated_temp = slope[sensor_idx] * raw_temperature + offset[sensor_idx];

批量生产中,这个校准环节可以做成半自动化流水线:工装里固定一支参考温度计,自动读取传感器原始值,然后算出 K、B 直接写入 Flash。一台设备校准只需要几十秒,却能换来整批产品的数据一致性,非常值得投入。

5.3 变频器干扰、末端压降和接地环路

现场踩坑的记录也要写一下。第一个案例是空调机组变频器启动瞬间,某一路传感器温度跳变 2℃ 后又恢复。排查下来不是传感器坏了,而是传感器供电线离变频器输出线太近,变频器启停时的高频噪声耦合进了电源和 I2C 信号线。处理办法是把传感器供电回路改成独立隔离 DC-DC,信号线换成屏蔽双绞线,并且把屏蔽层单端接在主控的地上,问题随即消失。

第二个案例是远端传感器偶尔读不到数据。主控在柜内,最远的传感器点位走了大概 8 米线,I2C 通信偶发失败。这类问题的典型原因是总线电容过大和末端电压跌落。我的处理是降低 I2C 速率到 50kHz,同时把上拉电阻从 4.7kΩ 换成 2.2kΩ,并把传感器供电电压从 3.3V 提到 3.3V 高精度 LDO 的输出端,保证远端传感器电源电压不低于 3.15V。如果之后再出现通信失败,就该考虑在远端加 I2C 总线缓冲器或者改成 RS485 传输方案了。

第三个案例更有代表性:两个传感器读数完全一致,但都明显偏低,参考温度计都显示正常。我拆开现场接线发现,探头的屏蔽层被两端接地了,现场其他设备的漏电流通过屏蔽层形成了一个接地环路,在电感效应下给传感器引入了一个直流偏置。把屏蔽层改成单端接地后,读数马上恢复正常。这类问题从代码上看完全无解,必须靠现场排查和走线规范来避免,所以我在项目文档里专门加了一条硬性规定:所有传感器屏蔽层只能单端接地。

最后再分享一个我个人的习惯:温度传感器硬件设计好了、固件跑通了,不要急着批量部署,先把样机带着探头放进恒温箱,从 -10℃ 到 70℃ 做一遍完整的温度循环,记录每一颗传感器的原始读数。这个过程能提前暴露虚焊、封装不良、导热失效这类“软件永远看不见”的问题。另外,Flash 里保存校准参数和阈值参数这件事,一定要留一份在线的串口修改命令,现场调试点位时不用反复烧录,能省下大量调试时间。这个项目的完整链路和校准流程已经稳定跑了几个月的现场,目前看下来,当初针对每个细节做的取舍,最后都为减少返工换来了实际回报。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询