首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >服务器机柜内部监测:小型RJ45以太网温湿度变送器嵌入式开发笔记

服务器机柜内部监测:小型RJ45以太网温湿度变送器嵌入式开发笔记

原创
作者头像
盛世宏博科技
修改于 2026-09-28 11:29:16
修改于 2026-09-28 11:29:16
40
举报

服务器机柜内部监测:小型RJ45以太网温湿度变送器嵌入式开发笔记

近期在调试一套工业环境监控系统时,遇到了关于Modbus连接保持的问题。现场部署了一批网口温湿度变送器,PoE取电、Modbus TCP上云,同时利旧接入存量RS485探头,并开启了SNMP和UDP Trap服务。服务器机柜内部空间极其有限——1U PDU旁边、盲板缝隙、走线槽拐角,留给传感器的物理空间往往只有火柴盒大小。这篇从硬件选型、PCB布局、固件架构到PoE供电调优,完整记录小型RJ45温湿度变送器的嵌入式开发全过程。


一、硬件选型与尺寸约束

1.1 尺寸目标

维度

目标值

约束来源

外壳尺寸

≤ 40×40×18mm

服务器机柜盲板开孔/PDU侧面

PCB尺寸

≤ 35×35mm

外壳内腔

RJ45连接器

集成式(不可突出)

面板安装要求

传感器开孔

φ2mm

气流通道

1.2 核心器件选型

器件

型号

理由

MCU

STM32F103C8T6

64KB Flash/20KB RAM,性价比高,LWIP可跑

以太网PHY

LAN8720A

单芯片10/100M,RMII接口,QFN-24(4×4mm)

温湿度传感器

SHT30

I²C,±0.2℃/±2%RH,DFN-8(2.5×2.5mm)

PoE PD

Si3402

集成整流桥+DC-DC,支持802.3af,TSSOP-16

Flash

W25Q16

2MB SPI NOR,SOIC-8

晶振

25MHz ±10ppm

PHY RMII参考时钟

RJ45

HR911105A

集成变压器+LED,单口,底部焊盘接地

关键决策:放弃外部SDRAM,用STM32F103内部20KB RAM跑LWIP;放弃外部RTC,用STM32内部RTC+温补晶振(LSE 32.768kHz);放弃RS485扩展,只做单点监测。


二、PCB布局要点

2.1 层叠与分区

代码语言:javascript
复制
4层板叠构:
Layer 1: 信号(RJ45/PHY/MCU高速线)
Layer 2: 完整地平面(GND)
Layer 3: 电源平面(3.3V/1.2V/VDD_POE)
Layer 4: 信号(I²C/SPI/GPIO)

分区原则:

代码语言:javascript
复制
┌─────────────────────────────────────────────┐
│  RJ45 (左)  │  PHY (中左)  │  MCU (中右)   │
│  ─────────  │  ──────────  │  ───────────  │
│  变压器内置  │  RMII走线    │  GPIO/I²C     │
│  屏蔽壳接   │  <15mm       │  <10mm        │
│  Layer2地   │              │               │
├─────────────┴──────────────┴───────────────┤
│  PoE PD (下)                                │
│  Si3402 + 整流桥 + 滤波电感                 │
│  注意:高压(48V)与低压(3.3V)间距≥2mm    │
├─────────────────────────────────────────────┤
│  SHT30 (右上角)                              │
│  开孔对准外壳气流通道                        │
└─────────────────────────────────────────────┘

2.2 RMII走线规则

代码语言:javascript
复制
RMII信号(50MHz,严格等长):
  REF_CLK: MCU → PHY, 长度 ≤ 25mm
  TXD0/1:  MCU → PHY, 等长 ±2mm
  TX_EN:   MCU → PHY, 等长 ±2mm
  RXD0/1:  PHY → MCU, 等长 ±2mm
  CRS_DV:  PHY → MCU, 等长 ±2mm
  MDC/MDIO: 管理接口,长度不限但远离噪声源

关键:REF_CLK是50MHz源同步时钟,走线尽量短,避免过孔。所有RMII信号线宽0.15mm,间距0.15mm(3W规则)。

2.3 PoE电源布局

代码语言:javascript
复制
RJ45 Pin 1,2,3,6 (差分对)
       │
       ├───→ 中心抽头(内置变压器)
       │         │
       │    ┌────▼────┐
       │    │ Si3402  │  PoE PD + DC-DC
       │    │ TSSOP16 │
       │    └────┬────┘
       │         │ 3.3V/1.2V
       │    ┌────▼────┐
       │    │ 滤波     │  10μF + 0.1μF
       │    │ π型     │
       │    └────┬────┘
       │         │
       └─────────┼──→ PHY/MAC
                 │
            ┌────▼────┐
            │ 3.3V LDO│ → MCU/SHT30/Flash
            └─────────┘

注意:Si3402的GND_PAD必须焊接到Layer2地平面,散热过孔至少4个(直径0.3mm)。


三、固件架构

3.1 任务划分(FreeRTOS)

代码语言:javascript
复制
// 任务优先级(0最低,configMAX_PRIORITIES-1最高)
#define TASK_PRIO_SENSOR    3   // 传感器采集
#define TASK_PRIO_NET       4   // 网络协议栈
#define TASK_PRIO_MODBUS    3   // Modbus TCP服务
#define TASK_PRIO_UDP       2   // UDP推送
#define TASK_PRIO_SYNC      1   // 本地存储同步
#define TASK_PRIO_WATCHDOG  5   // 看门狗

// 任务栈大小
#define STACK_SENSOR       256
#define STACK_NET          512
#define STACK_MODBUS       384
#define STACK_UDP          256
#define STACK_SYNC         384
#define STACK_WATCHDOG     128

3.2 内存配置(LWIP)

代码语言:javascript
复制
// lwipopts.h - 针对20KB RAM的精简配置
#define MEM_SIZE                   8192    // 堆内存 8KB
#define MEMP_NUM_PBUF             32
#define MEMP_NUM_TCP_PCB          4        // 最多4个TCP连接
#define MEMP_NUM_TCP_PCB_LISTEN   2
#define MEMP_NUM_TCP_SEG          16
#define MEMP_NUM_REASSDATA        4
#define MEMP_NUM_FRAG_PBUF        4
#define PBUF_POOL_SIZE            16
#define PBUF_POOL_BUFSIZE         256
#define TCP_MSS                   1460
#define TCP_SND_BUF               2048
#define TCP_WND                   2048
#define TCP_QUEUE_OOSEQ           0        // 关闭乱序队列省内存
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK   1

3.3 主循环

代码语言:javascript
复制
// main.c
int main(void) {
    // 硬件初始化
    HAL_Init();
    SystemClock_Config();       // 72MHz
    MX_GPIO_Init();
    MX_USART1_UART_Init();      // 调试串口
    MX_I2C1_Init();             // SHT30
    MX_SPI1_Init();             // W25Q16
    MX_TIM2_Init();             // 系统tick
    MX_ETH_Init();              // RMII + LAN8720A
    
    // 外设初始化
    sht30_init();
    flash_init();
    ntp_init();
    
    // LWIP初始化
    lwip_init();
    netif_add(&g_netif, &ipaddr, &netmask, &gw, NULL, ethernetif_init, netif_input);
    netif_set_default(&g_netif);
    netif_set_up(&g_netif);
    
    // FreeRTOS任务创建
    xTaskCreate(sensor_task, "sensor", STACK_SENSOR, NULL, TASK_PRIO_SENSOR, NULL);
    xTaskCreate(net_task, "net", STACK_NET, NULL, TASK_PRIO_NET, NULL);
    xTaskCreate(modbus_task, "modbus", STACK_MODBUS, NULL, TASK_PRIO_MODBUS, NULL);
    xTaskCreate(udp_task, "udp", STACK_UDP, NULL, TASK_PRIO_UDP, NULL);
    xTaskCreate(sync_task, "sync", STACK_SYNC, NULL, TASK_PRIO_SYNC, NULL);
    xTaskCreate(watchdog_task, "wdg", STACK_WATCHDOG, NULL, TASK_PRIO_WATCHDOG, NULL);
    
    // 启动调度器
    vTaskStartScheduler();
    
    while (1) {}
}

四、传感器驱动

4.1 SHT30读取

代码语言:javascript
复制
// sht30.c
#include "sht30.h"
#include "i2c.h"

#define SHT30_ADDR     0x44 << 1
#define CMD_MEAS_HIGH  0x2400  // 高精度模式,15ms转换时间

static float g_temperature = 0.0f;
static float g_humidity = 0.0f;
static uint8_t g_sensor_ready = 0;

int sht30_init(void) {
    uint8_t cmd[2] = {0x30, 0xA2};  // 软复位
    HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR, cmd, 2, 100);
    HAL_Delay(1);
    
    // 读取状态寄存器
    cmd[0] = 0xF3; cmd[1] = 0x2D;
    HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR, cmd, 2, 100);
    
    g_sensor_ready = 1;
    return 0;
}

int sht30_read(float *temp, float *hum) {
    if (!g_sensor_ready) return -1;
    
    uint8_t cmd[2] = {CMD_MEAS_HIGH >> 8, CMD_MEAS_HIGH & 0xFF};
    if (HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR, cmd, 2, 100) != HAL_OK)
        return -1;
    
    HAL_Delay(15);  // 等待转换完成
    
    uint8_t rx[6];
    if (HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR, rx, 6, 100) != HAL_OK)
        return -1;
    
    // CRC校验
    if (!sht30_crc_check(rx, 2, rx[2])) return -2;
    if (!sht30_crc_check(rx + 3, 2, rx[5])) return -3;
    
    // 转换
    uint16_t temp_raw = (rx[0] << 8) | rx[1];
    uint16_t hum_raw = (rx[3] << 8) | rx[4];
    
    *temp = -45.0f + 175.0f * temp_raw / 65535.0f;
    *hum = 100.0f * hum_raw / 65535.0f;
    
    g_temperature = *temp;
    g_humidity = *hum;
    
    return 0;
}

static uint8_t sht30_crc_check(uint8_t *data, uint8_t len, uint8_t crc) {
    uint8_t crc_calc = 0xFF;
    for (int i = 0; i < len; i++) {
        crc_calc ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crc_calc & 0x80)
                crc_calc = (crc_calc << 1) ^ 0x31;
            else
                crc_calc <<= 1;
        }
    }
    return (crc_calc & 0xFF) == crc;
}

4.2 传感器任务

代码语言:javascript
复制
// sensor_task.c
void sensor_task(void *pvParameters) {
    TickType_t xLastWakeTime = xTaskGetTickCount();
    const TickType_t xPeriod = pdMS_TO_TICKS(5000);  // 5秒采样
    
    float temp, hum;
    env_record_t record;
    
    while (1) {
        vTaskDelayUntil(&xLastWakeTime, xPeriod);
        
        if (sht30_read(&temp, &hum) == 0) {
            record.collect_ts = get_corrected_timestamp();
            record.temperature = (int16_t)(temp * 100);
            record.humidity = (uint16_t)(hum * 10);
            record.voltage = (uint16_t)(read_poe_voltage() * 100);
            record.status = get_status_bits();
            record.reserved = 0;
            
            // 写入本地存储
            storage_write_record(&record);
            
            // 通知UDP任务推送
            xQueueSend(g_udp_queue, &record, 0);
            
            // 通知Modbus任务更新保持寄存器
            xQueueSend(g_modbus_queue, &record, 0);
        } else {
            // 传感器故障
            set_status_bit(STATUS_SENSOR_FAULT);
        }
    }
}

五、PoE供电调试

5.1 Si3402关键配置

代码语言:javascript
复制
Si3402引脚配置:
  Pin 1-4:  VNEG (整流输出负)
  Pin 5-8:  VPOS (整流输出正)
  Pin 9:    VDD (内部LDO 3.3V)
  Pin 10:   GND
  Pin 11:   CLASS (分类电流设置)
  Pin 12:   SLEW (浪涌限流)
  Pin 13:   nPWRON (电源就绪指示)
  Pin 14:   VDD_AUX (辅助供电输入)
  Pin 15:   VDD (3.3V输出)
  Pin 16:   GND_PAD (散热地)

分类电阻:802.3af Class 1(3.84W~6.49W),R_CLASS = 133Ω

浪涌限流:R_SLEW = 100kΩ,限制浪涌电流

5.2 功耗预算

代码语言:javascript
复制
STM32F103:     ~75mA × 3.3V = 0.25W
LAN8720A:      ~50mA × 3.3V = 0.17W
SHT30:         ~1mA × 3.3V  = 0.003W
W25Q16:        ~15mA × 3.3V = 0.05W
LED:           ~10mA × 3.3V = 0.03W
LWIP协议栈:    ~30mA × 3.3V = 0.10W
────────────────────────────────────
总计:          ~181mA × 3.3V = 0.60W

Class 1(最大6.49W)绰绰有余,即使考虑转换效率(约85%),输入功率约0.7W。

5.3 常见供电问题

现象

原因

对策

上电后反复重启

PoE交换机端口功率不足

确认交换机支持802.3af,端口未限流

运行中突然掉线

网线过长导致电压跌落

缩短距离或改用Cat6A

发热严重

Si3402散热不良

增加散热过孔,GND_PAD焊接良好

只有Link灯不亮

极性反接

检查RJ45线序,T568B标准


六、机柜内安装注意

6.1 安装位置选择

代码语言:javascript
复制
机柜气流方向:
  前进后出 / 下进上出

推荐安装位置:
  ✅ 机柜后部盲板(热通道侧,监测排风温度)
  ✅ PDU侧面(监测供电环境)
  ✅ 机柜顶部(热空气聚集区)
  ❌ 服务器风扇正前方(气流干扰测量)
  ❌ 机柜底部(冷通道,不能代表热点)

6.2 气流通道设计

代码语言:javascript
复制
外壳开孔:
  ┌──────────────────┐
  │  ░░░░░░░░░░░░░░  │  ← 顶部开孔阵列(φ1.5mm × 20个)
  │  ░░░░░░░░░░░░░░  │     确保气流通过SHT30传感器
  │  ┌──────────┐    │
  │  │ SHT30    │    │  ← 传感器位于开孔正下方
  │  └──────────┘    │
  │  RJ45            │  ← 底部出线
  └──────────────────┘

注意:开孔面积 ≥ 50mm²,避免形成"死腔"导致温度滞后。


七、调试经验

7.1 常见问题

现象

原因

排查方法

PHY不Link

RMII时钟不对

示波器测REF_CLK 50MHz

LWIP初始化失败

PHY地址错误

读PHY ID寄存器

温度偏高2℃

自身发热传导

增加隔热垫,降低采样频率

I²C读不到数据

上拉电阻未焊

万用表测SDA/SCL电压

Flash写入失败

写保护引脚

确认WP引脚拉高

运行中死机

堆栈溢出

FreeRTOS uxTaskGetStackHighWaterMark

7.2 温度校准

SHT30出厂精度±0.2℃,但PCB自身发热会影响测量。校准方法:

代码语言:javascript
复制
// 校准系数(通过恒温箱标定)
static float g_temp_offset = 0.0f;   // 从Flash读取

float calibrated_temperature(float raw) {
    return raw + g_temp_offset;
}

// 标定流程:
// 1. 放入恒温箱(25℃),稳定运行30分钟
// 2. 读取10次平均值
// 3. 计算偏差:offset = 25.0 - avg
// 4. 写入Flash校准区

八、工程交付检查表

  • [ ] PCB光板测试(飞针测试报告)
  • [ ] 焊接质量检查(AOI或目检)
  • [ ] 功能测试(Link、采集、Modbus、UDP)
  • [ ] 温箱校准(-10℃~+60℃,每10℃一个点)
  • [ ] PoE兼容性测试(不同品牌交换机)
  • [ ] 长期稳定性(72小时连续运行)
  • [ ] 外壳密封性(IP20防尘)
  • [ ] 标签打印(设备地址、IP、位置)
  • [ ] 安装支架/3M胶
  • [ ] 文档交付(原理图、BOM、固件烧录说明)

九、关键词

服务器机柜、内部监测、小型RJ45温湿度变送器、嵌入式开发、STM32F103、LAN8720A、SHT30、Si3402、PoE供电、RMII、LWIP、FreeRTOS、PCB布局、机柜安装


十、一句话总结

小型RJ45温湿度变送器的嵌入式开发核心在于"小体积下的资源平衡":用STM32F103+LAN8720A+SHT30+Si3402四颗核心芯片实现PoE供电、以太网通信、温湿度采集,LWIP在20KB RAM下精简配置跑通Modbus TCP和UDP双协议,PCB 4层板紧凑布局确保RMII信号完整性,机柜内安装位置选在热通道侧避免气流干扰——最终交付的是一个火柴盒大小、即插即用、可批量部署的机柜级环境监测节点。

物联网 #Modbus #TCP/IP #UDP #POE供电 #腾讯云 #Wireshark #Python #InfluxDB #以太网温湿度传感器 #网口温湿度变送器 #机房监控 #服务器机柜 #嵌入式开发 #STM32 #PoE #工业物联网

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 服务器机柜内部监测:小型RJ45以太网温湿度变送器嵌入式开发笔记
    • 一、硬件选型与尺寸约束
      • 1.1 尺寸目标
      • 1.2 核心器件选型
    • 二、PCB布局要点
      • 2.1 层叠与分区
      • 2.2 RMII走线规则
      • 2.3 PoE电源布局
    • 三、固件架构
      • 3.1 任务划分(FreeRTOS)
      • 3.2 内存配置(LWIP)
      • 3.3 主循环
    • 四、传感器驱动
      • 4.1 SHT30读取
      • 4.2 传感器任务
    • 五、PoE供电调试
      • 5.1 Si3402关键配置
      • 5.2 功耗预算
      • 5.3 常见供电问题
    • 六、机柜内安装注意
      • 6.1 安装位置选择
      • 6.2 气流通道设计
    • 七、调试经验
      • 7.1 常见问题
      • 7.2 温度校准
    • 八、工程交付检查表
    • 九、关键词
    • 十、一句话总结
  • 物联网 #Modbus #TCP/IP #UDP #POE供电 #腾讯云 #Wireshark #Python #InfluxDB #以太网温湿度传感器 #网口温湿度变送器 #机房监控 #服务器机柜 #嵌入式开发 #STM32 #PoE #工业物联网
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档