首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >电力中心配电柜:SNMP、UDP双协议以太网温湿度变送器固件开发经验

电力中心配电柜:SNMP、UDP双协议以太网温湿度变送器固件开发经验

原创
作者头像
HONSOR盛世宏博
发布于 2026-09-28 11:20:08
发布于 2026-09-28 11:20:08
50
举报

电力中心配电柜:SNMP、UDP双协议以太网温湿度变送器固件开发经验

关键词:电力中心配电柜、以太网温湿度变送器、SNMP、UDP、双协议固件、嵌入式开发、LwIP、FreeRTOS、MIB设计、协议共存 标签:#物联网 #TCP/IP #UDP #POE供电 #以太网温湿度传感器 #网口温湿度变送器 #机房监控 #嵌入式开发

一、为什么要在固件层同时实现 SNMP 和 UDP

电力中心配电柜场景对温湿度数据的需求是双重的:

需求维度

运维侧(动环监控)

调度侧(电力监控)

使用方式

平台轮询,趋势分析

实时告警,联动跳闸

通信偏好

SNMP(标准化,易集成)

UDP 主动上报(低延迟)

容忍延迟

秒级

亚秒级

可靠性要求

可容忍短暂中断

丢数据需可感知

协议生态

NMS、Zabbix、PRTG

自研采集器、SCADA

单协议固件无法满足两类用户的部署习惯。双协议同时运行,UDP 负责"快",SNMP 负责"稳",网关侧按前文(第12篇)的融合策略消费即可。

固件层面的挑战在于:如何在资源受限的 MCU 上,让两个协议栈高效共存,互不阻塞,且对硬件资源(RAM、Flash、CPU)的消耗可控。


二、硬件平台与软件架构

2.1 典型硬件选型

器件

型号/规格

说明

MCU

STM32F407VGT6 (Cortex-M4, 168MHz, 192KB RAM, 1MB Flash)

主流工业级,性价比高

以太网 PHY

LAN8742A (RMII)

自带魔术包唤醒

POE PD

TPS2375x (802.3af/at)

集成 DC-DC 隔离

温湿度芯片

SHT45 (I2C, ±0.1℃/±1.5%RH)

高精度,长期稳定性好

外部 Flash

W25Q128 (16MB, SPI)

存储配置、日志、固件备份

看门狗

TPS3823 (外部硬件)

防死机

2.2 软件架构

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────┐
│                    应用层 (FreeRTOS Tasks)                   │
├────────────┬────────────┬────────────┬───────────────────────┤
│ 传感器任务  │ UDP发送任务 │ SNMP任务   │ 配置/管理任务         │
│ (100ms周期) │ (5s周期)   │ (事件驱动)  │ (Web/CLI/Flash)      │
├────────────┴────────────┴────────────┴───────────────────────┤
│                    协议栈层 (LwIP 2.1.2)                     │
├───────────────────────────┬─────────────────────────────────┤
│  UDP (raw API / netconn)  │  SNMP (net-snmp 移植 / 自研)    │
│  - 二进制帧封装            │  - MIB-II 标准组                │
│  - 序列号 + CRC16         │  - 私有 MIB (环境数据)           │
│  - 可配置目标IP/端口       │  - GET/GETNEXT/SET 支持         │
│  - QoS (DSCP) 标记        │  - Trap v1/v2c 主动上报         │
├───────────────────────────┴─────────────────────────────────┤
│                    网络接口层 (ethernetif)                    │
│  - RMII + DMA 描述符环                                      │
│  - 中断 + 零拷贝接收                                        │
├─────────────────────────────────────────────────────────────┤
│                    硬件抽象层 (HAL / LL)                      │
│  - GPIO/I2C/SPI/RCC/NVIC                                    │
└─────────────────────────────────────────────────────────────┘

2.3 任务划分与优先级

代码语言:javascript
复制
/* FreeRTOS 任务配置 */
#define TASK_SENSOR_PRIO     (tskIDLE_PRIORITY + 4)  /* 传感器采集,最高 */
#define TASK_UDP_TX_PRIO     (tskIDLE_PRIORITY + 3)  /* UDP发送 */
#define TASK_SNMP_PRIO       (tskIDLE_PRIORITY + 2)  /* SNMP(事件驱动,大部分时间阻塞) */
#define TASK_CONFIG_PRIO     (tskIDLE_PRIORITY + 1)  /* 配置管理(Web/CLI) */
#define TASK_IDLE_PRIO       (tskIDLE_PRIORITY + 0)  /* 空闲 */

/* 栈大小 */
#define STACK_SENSOR   512
#define STACK_UDP_TX   1024
#define STACK_SNMP     2048   /* SNMP需要较大栈空间处理OID解析 */
#define STACK_CONFIG   1536

三、LwIP 配置要点

3.1 lwipopts.h 关键配置

代码语言:javascript
复制
/* ===== lwipopts.h - 双协议场景关键配置 ===== */

/* 内存配置 */
#define MEM_SIZE                (32 * 1024)   /* 堆内存 32KB */
#define MEMP_NUM_PBUF           64
#define MEMP_NUM_TCP_PCB        8
#define MEMP_NUM_TCP_PCB_LISTEN 4
#define MEMP_NUM_UDP_PCB        6             /* UDP + SNMP 各需PCB */
#define MEMP_NUM_NETBUF         32
#define MEMP_NUM_NETCONN        16
#define MEMP_NUM_TCPIP_MSG_API  32
#define MEMP_NUM_TCPIP_MSG_INPKT 32

/* UDP 配置 */
#define LWIP_UDP                1
#define UDP_TTL                 128
#define LWIP_UDPLITE            0

/* SNMP 配置 */
#define LWIP_SNMP               1
#define LWIP_SNMP_V1            1
#define LWIP_SNMP_V2C           1
#define LWIP_SNMP_V3            0             /* 资源受限,暂不启用V3 */
#define LWIP_SNMP_MIB2          1
#define LWIP_SNMP_PRIVATE_MIB   1

/* TCP 配置(Web管理界面用) */
#define LWIP_TCP                1
#define TCP_MSS                 1460
#define TCP_SND_BUF             (4 * TCP_MSS)
#define TCP_WND                 (4 * TCP_MSS)

/* 网络接口 */
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK   1

/* 线程安全 */
#define SYS_LIGHTWEIGHT_PROT       1
#define NO_SYS                     0             /* 使用RTOS */

/* 校验和 */
#define CHECKSUM_GEN_IP            0             /* 硬件加速 */
#define CHECKSUM_GEN_UDP           0
#define CHECKSUM_GEN_TCP           0
#define CHECKSUM_CHECK_IP          0
#define CHECKSUM_CHECK_UDP         0
#define CHECKSUM_CHECK_TCP         0

3.2 LwIP 线程与中断

代码语言:javascript
复制
/* ethernetif.c - 中断到任务的零拷贝路径 */

/* 接收中断 */
void ETH_IRQHandler(void)
{
    HAL_ETH_IRQHandler(&heth);
}

/* 中断回调:将帧放入队列,唤醒 tcpip_thread */
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
    struct pbuf *p;
    /* 从DMA描述符获取帧 */
    p = low_level_input(netif);
    if (p != NULL) {
        /* 直接交给 tcpip_input(零拷贝) */
        if (netif->input(p, netif) != ERR_OK) {
            pbuf_free(p);
        }
    }
    /* 重新激活DMA接收 */
    HAL_ETH_BuildRxDescriptors(heth);
}

四、UDP 协议实现

4.1 UDP 发送任务

代码语言:javascript
复制
/* udp_tx_task.c */

#include "lwip/udp.h"
#include "lwip/ip_addr.h"

static struct udp_pcb *udp_pcb;
static ip_addr_t dest_ip;
static u16_t dest_port = 9000;

/* UDP帧格式:
 * 包头(2B, 0xAA55) | 设备ID(4B) | 温度(2B, ×0.1) | 湿度(2B, ×0.1) 
 * | 露点(2B, ×0.1) | 序列号(2B) | 状态码(1B) | CRC16(2B)
 * 总计:15字节
 */

typedef struct {
    u16_t header;        /* 0xAA55 */
    u32_t dev_id;        /* 设备唯一ID(从Flash读取) */
    s16_t temperature;   /* 温度 ×10 */
    s16_t humidity;      /* 湿度 ×10 */
    s16_t dewpoint;      /* 露点 ×10 */
    u16_t sequence;      /* 序列号,每包递增 */
    u8_t  status;        /* 状态码:bit0=传感器故障, bit1=POE欠压 */
    u16_t crc;           /* CRC-16/MODBUS */
} __attribute__((packed)) udp_frame_t;

static udp_frame_t tx_frame;
static u16_t seq_counter = 0;

void udp_tx_task(void *arg)
{
    /* 创建UDP PCB */
    udp_pcb = udp_new();
    if (udp_pcb == NULL) {
        vTaskDelete(NULL);
        return;
    }
    
    /* 配置目标地址(从Flash读取,默认广播) */
    ipaddr_aton(get_config()->udp_dest_ip, &dest_ip);
    dest_port = get_config()->udp_dest_port;
    
    /* 初始化帧头 */
    tx_frame.header = 0xAA55;
    tx_frame.dev_id = get_device_id();  /* 从唯一ID寄存器或Flash读取 */
    tx_frame.sequence = 0;
    tx_frame.status = 0;
    
    TickType_t last_wake = xTaskGetTickCount();
    const TickType_t period = pdMS_TO_TICKS(get_config()->udp_interval * 1000);
    
    for (;;) {
        vTaskDelayUntil(&last_wake, period);
        
        /* 填充传感器数据 */
        sensor_reading_t reading;
        sensor_get_latest(&reading);
        
        tx_frame.temperature = (s16_t)(reading.temperature * 10);
        tx_frame.humidity = (s16_t)(reading.humidity * 10);
        tx_frame.dewpoint = (s16_t)(reading.dewpoint * 10);
        tx_frame.sequence = seq_counter++;
        
        /* 状态码 */
        tx_frame.status = 0;
        if (!reading.valid) tx_frame.status |= 0x01;
        if (reading.poe_voltage < 37) tx_frame.status |= 0x02;
        
        /* 计算CRC */
        tx_frame.crc = crc16((u8_t *)&tx_frame, sizeof(udp_frame_t) - 2);
        
        /* 发送 */
        struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, sizeof(udp_frame_t), PBUF_RAM);
        if (p != NULL) {
            memcpy(p->payload, &tx_frame, sizeof(udp_frame_t));
            
            /* 设置DSCP(如果LwIP支持) */
            /* ip_set_option(pcb, IP_TOS, 0xB8);   DSCP 46 (EF) */
            
            err_t err = udp_sendto(udp_pcb, p, &dest_ip, dest_port);
            if (err != ERR_OK) {
                /* 发送失败,记录统计 */
                stats.udp_tx_err++;
            } else {
                stats.udp_tx_ok++;
            }
            pbuf_free(p);
        }
    }
}

4.2 UDP 接收(可选,用于接收配置命令)

代码语言:javascript
复制
/* UDP接收回调 - 处理来自网关的配置命令 */
void udp_recv_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p,
                       const ip_addr_t *addr, u16_t port)
{
    if (p == NULL) return;
    
    /* 简单命令协议:
     * CMD_SET_INTERVAL (1B) | 间隔秒数 (1B)
     * CMD_SET_DEST_IP (1B) | IP地址 (4B)
     * CMD_REBOOT (1B)
     */
    u8_t *data = (u8_t *)p->payload;
    u8_t cmd = data[0];
    
    switch (cmd) {
        case 0x01:  /* SET_INTERVAL */
            if (p->len >= 2) {
                u8_t interval = data[1];
                if (interval >= 1 && interval <= 60) {
                    config_set_udp_interval(interval);
                    save_config_to_flash();
                }
            }
            break;
        case 0x02:  /* SET_DEST_IP */
            if (p->len >= 5) {
                ip_addr_t new_ip;
                IP4_ADDR(&new_ip, data[1], data[2], data[3], data[4]);
                config_set_udp_dest_ip(&new_ip);
                save_config_to_flash();
            }
            break;
        case 0xFF:  /* REBOOT */
            NVIC_SystemReset();
            break;
    }
    
    pbuf_free(p);
}

五、SNMP 协议实现

5.1 MIB 设计

代码语言:javascript
复制
私有MIB树结构:

.iso.org.dod.internet.private.enterprises (1.3.6.1.4.1)
  └── <enterprise-id> (如 12345)
       ├── 1 (deviceInfo)
       │    ├── 1.0  deviceName      (OCTET STRING)  设备名称
       │    ├── 2.0  deviceId        (INTEGER)       设备ID
       │    ├── 3.0  firmwareVersion (OCTET STRING)  固件版本
       │    └── 4.0  uptime          (Timeticks)     运行时间
       ├── 2 (environment)
       │    ├── 1.0  temperature     (INTEGER)       温度 ×10 (0.1℃)
       │    ├── 2.0  humidity        (INTEGER)       湿度 ×10 (0.1%RH)
       │    ├── 3.0  dewpoint        (INTEGER)       露点 ×10
       │    ├── 4.0  tempStatus      (INTEGER)       温度状态 (0=正常,1=超限,2=故障)
       │    ├── 5.0  humStatus       (INTEGER)       湿度状态
       │    └── 6.0  alarmStatus     (INTEGER)       综合告警 (0=正常, 1=告警)
       ├── 3 (network)
       │    ├── 1.0  ipAddress       (IpAddress)     IP地址
       │    ├── 2.0  macAddress      (OCTET STRING)   MAC地址
       │    ├── 3.0  linkSpeed       (INTEGER)       链路速率 (Mbps)
       │    ├── 4.0  linkStatus      (INTEGER)       链路状态 (1=up, 2=down)
       │    └── 5.0  snmpReqCount    (Counter32)     SNMP请求计数
       └── 4 (power)
            ├── 1.0  poeVoltage      (INTEGER)        POE输入电压 ×10
            ├── 2.0  poeCurrent      (INTEGER)        POE输入电流 ×100
            └── 3.0  poePower        (INTEGER)        POE功率 ×10

5.2 SNMP 核心实现(基于 LwIP 内置 SNMP Agent)

代码语言:javascript
复制
/* snmp_private_mib.c */

#include "lwip/snmp.h"
#include "lwip/snmp_structs.h"

/* OID 定义 */
static const u32_t deviceInfo_oid[] = {1,3,6,1,4,1,12345,1};
static const u32_t environment_oid[] = {1,3,6,1,4,1,12345,2};
static const u32_t network_oid[] = {1,3,6,1,4,1,12345,3};
static const u32_t power_oid[] = {1,3,6,1,4,1,12345,4};

/* 读取回调函数 */
static u16_t deviceInfo_get_value(struct snmp_node_instance* instance, 
                                   void* buf, u16_t buflen)
{
    u8_t id = instance->node->oid[instance->node->oid_len - 1];
    
    switch (id) {
        case 1: /* deviceName */
            return snmp_set_varbind_value(buf, buf, buflen,
                   (u8_t*)DEVICE_NAME, strlen(DEVICE_NAME));
        case 2: /* deviceId */
            *(s32_t*)buf = get_device_id();
            return sizeof(s32_t);
        case 3: /* firmwareVersion */
            return snmp_set_varbind_value(buf, buf, buflen,
                   (u8_t*)FIRMWARE_VERSION, strlen(FIRMWARE_VERSION));
        case 4: /* uptime */
            *(u32_t*)buf = sys_now() / 10;  /* sysTick → 0.01s */
            return sizeof(u32_t);
    }
    return 0;
}

static u16_t environment_get_value(struct snmp_node_instance* instance,
                                    void* buf, u16_t buflen)
{
    u8_t id = instance->node->oid[instance->node->oid_len - 1];
    sensor_reading_t reading;
    sensor_get_latest(&reading);
    
    switch (id) {
        case 1: /* temperature */
            *(s32_t*)buf = (s32_t)(reading.temperature * 10);
            return sizeof(s32_t);
        case 2: /* humidity */
            *(s32_t*)buf = (s32_t)(reading.humidity * 10);
            return sizeof(s32_t);
        case 3: /* dewpoint */
            *(s32_t*)buf = (s32_t)(reading.dewpoint * 10);
            return sizeof(s32_t);
        case 4: /* tempStatus */
            *(s32_t*)buf = reading.temp_valid ? 0 : 2;
            return sizeof(s32_t);
        case 5: /* humStatus */
            *(s32_t*)buf = reading.hum_valid ? 0 : 2;
            return sizeof(s32_t);
        case 6: /* alarmStatus */
            *(s32_t*)buf = (reading.temperature > TEMP_HIGH_THRESH ||
                            reading.humidity > HUM_HIGH_THRESH) ? 1 : 0;
            return sizeof(s32_t);
    }
    return 0;
}

/* SNMP Agent 初始化 */
void snmp_agent_init(void)
{
    /* 设置系统信息(MIB-II sys组) */
    snmp_set_sysdesr((u8_t*)"Dual-Proto Temp&Hum Sensor v1.0", 32);
    snmp_set_syscontact((u8_t*)"admin@facility.local", 20);
    snmp_set_sysname((u8_t*)"Sensor-01", 9);
    snmp_set_syslocation((u8_t*)"Power Distribution Cabinet A", 25);
    
    /* 注册私有MIB */
    snmp_mib_add_node(deviceInfo_oid, 8, SNMP_NODE_SCALAR, 
                      deviceInfo_get_value, NULL, NULL);
    snmp_mib_add_node(environment_oid, 8, SNMP_NODE_SCALAR,
                      environment_get_value, NULL, NULL);
    
    /* 启动SNMP Agent(LwIP内部已启动UDP监听161端口) */
    snmp_trap_dst_enable(0, 1);
    snmp_trap_dst_ip_set(0, &trap_dest_ip);
}

5.3 SNMP Trap 主动上报

代码语言:javascript
复制
/* snmp_trap.c - 告警时主动发送Trap */

void send_alarm_trap(u8_t alarm_type, s16_t value, s16_t threshold)
{
    struct snmp_varbind *vb_head = NULL;
    u32_t now = sys_now() / 10;  /* 系统运行时间(0.01s) */
    
    /* 构造Trap PDU:
     * sysUpTime (1.3.6.1.2.1.1.3.0) + snmpTrapOID (1.3.6.1.6.3.1.1.4.1.0)
     * + 私有变量
     */
    
    /* sysUpTime */
    vb_head = snmp_varbind_alloc(&sysUpTime_oid, 9, SNMP_ASN1_TYPE_TIMETICKS, 4);
    if (vb_head) {
        *(u32_t*)vb_head->value = now;
    }
    
    /* snmpTrapOID - 使用私有Trap OID */
    /* 1.3.6.1.4.1.12345.0.1 = temperatureHigh */
    vb_head = snmp_varbind_append(vb_head, &trapOID_oid, 11, 
                                   SNMP_ASN1_TYPE_OBJ_ID, trap_oid, trap_oid_len * 4);
    
    /* 附加变量:告警类型、当前值、阈值 */
    vb_head = snmp_varbind_append(vb_head, &alarmType_oid, 10,
                                   SNMP_ASN1_TYPE_INTEGER, &alarm_type, 4);
    vb_head = snmp_varbind_append(vb_head, &currentValue_oid, 10,
                                   SNMP_ASN1_TYPE_INTEGER, &value, 4);
    vb_head = snmp_varbind_append(vb_head, &threshold_oid, 10,
                                   SNMP_ASN1_TYPE_INTEGER, &threshold, 4);
    
    /* 发送Trap v2c */
    snmp_send_trap(&trap_dest_ip, SNMP_TRAP_PORT, vb_head);
    
    /* 释放varbind链 */
    snmp_varbind_free(vb_head);
}

六、双协议共存的关键设计

6.1 资源隔离

代码语言:javascript
复制
/* 内存分区:为SNMP和UDP分配独立缓冲区 */

#define UDP_BUF_SIZE    16
#define SNMP_BUF_SIZE   256

/* UDP用较小的缓冲区(帧固定15字节) */
static u8_t udp_tx_buf[UDP_BUF_SIZE] __attribute__((aligned(4)));

/* SNMP用较大缓冲区(OID解析、Trap构造) */
static u8_t snmp_buf[SNMP_BUF_SIZE] __attribute__((aligned(4)));

/* 使用FreeRTOS的heap_4,避免碎片化 */

6.2 时序协调

代码语言:javascript
复制
关键原则:传感器采集不能被网络任务阻塞

时间线(5秒周期):
0ms     传感器任务采集(I2C, ~2ms)
2ms     数据就绪,通知UDP任务
        通知SNMP任务(更新MIB缓存)
3ms     UDP任务发送(~1ms)
        如果SNMP有GET请求,处理(~5ms)
5ms     空闲
...
5000ms  下一个周期
代码语言:javascript
复制
/* 使用队列传递传感器数据,解耦采集和网络 */
QueueHandle_t sensor_data_queue;

void sensor_task(void *arg)
{
    sensor_reading_t reading;
    for (;;) {
        sensor_read(&reading);
        xQueueSend(sensor_data_queue, &reading, 0);
        vTaskDelay(pdMS_TO_TICKS(100));  /* 100ms采样率 */
    }
}

/* UDP和SNMP任务从队列读取最新数据,不阻塞采集 */

6.3 看门狗策略

代码语言:javascript
复制
/* 独立看门狗(IWDG)配置 */
void iwdg_init(void)
{
    /* 超时时间:5秒 */
    hiwdg.Instance = IWDG;
    hiwdg.Init.Prescaler = IWDG_PRESCALER_256;
    hiwdg.Init.Reload = 0x0FFF;  /* ~5s */
    HAL_IWDG_Init(&hiwdg);
}

/* 每个任务定期喂狗 */
void task_feed_watchdog(void)
{
    static TickType_t last_feed = 0;
    TickType_t now = xTaskGetTickCount();
    if (now - last_feed > pdMS_TO_TICKS(1000)) {
        HAL_IWDG_Refresh(&hiwdg);
        last_feed = now;
    }
}

七、典型坑

  1. LwIP SNMP 内存不足:默认 MEM_SIZE 只有 16KB,SNMP GETNEXT 遍历 MIB 树时内存耗尽,设备重启。解决:将 MEM_SIZE 调至 32KB 以上,限制 MIB 节点数量。
  2. UDP 发送阻塞 SNMP:UDP 发送大帧(如带历史数据)时占用 LwIP 内部锁,SNMP GET 超时。解决:UDP 帧固定为最小长度(15字节),不在 UDP 中发送历史数据。
  3. SNMP Community 字符串硬编码:固件中写死 "public",部署后无法修改,存在安全风险。解决:从 Flash 读取配置,支持 Web 界面修改。
  4. MIB 树设计不合理导致 GETNEXT 死循环:OID 排序错误,GETNEXT 返回自身或前一个节点,NMS 陷入死循环。解决:严格按字典序排列 OID,测试用 snmpwalk 验证。
  5. Trap 发送失败导致任务阻塞:Trap 目标不可达时,LwIP 内部重试耗尽资源。解决:Trap 发送使用非阻塞模式,超时立即返回。
  6. 双协议时间不同步:UDP 用系统 Tick 计时,SNMP 用 sysUpTime,两者漂移导致数据时间戳不一致。解决:统一使用 sys_now() 作为时间源。
  7. Flash 写操作阻塞网络:保存配置时擦除 Flash 耗时数百毫秒,网络中断。解决:使用双缓冲 Flash 扇区,后台擦除,或写入 RAM 缓存后延迟刷盘。
  8. PHY 自动协商时间过长:上电后 PHY 协商需要 3~5 秒,SNMP Agent 在此期间无法响应。解决:在 netif_set_up() 后等待 NETIF_FLAG_LINK_UP 事件再启动 SNMP。
  9. CRC 计算与网关不匹配:固件用 CRC-16/CCITT,网关用 CRC-16/MODBUS,校验失败。解决:统一使用 CRC-16/MODBUS(0x8005多项式),双方对齐。
  10. 忘记实现 SNMP SET:运维人员希望通过 SNMP 修改采集周期,但固件只实现了 GET。解决:实现 SET 方法,写入 Flash,重启生效或热更新。

八、小结

电力中心配电柜场景下,SNMP+UDP 双协议以太网温湿度变送器的固件开发,核心挑战是在资源受限的 MCU 上让两个协议高效共存。关键设计决策:LwIP 作为统一协议栈底座,UDP 用 raw API 实现最小开销的主动上报,SNMP 用 LwIP 内置 Agent 实现标准化管理,两者通过队列解耦传感器采集任务。MIB 设计要精简实用,Trap 机制要可靠不阻塞。固件开发中最容易踩的坑是内存配置不足和时序协调不当,需要充分的压力测试和长时间运行验证。

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

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

目录
  • 电力中心配电柜:SNMP、UDP双协议以太网温湿度变送器固件开发经验
    • 一、为什么要在固件层同时实现 SNMP 和 UDP
    • 二、硬件平台与软件架构
      • 2.1 典型硬件选型
      • 2.2 软件架构
      • 2.3 任务划分与优先级
    • 三、LwIP 配置要点
      • 3.1 lwipopts.h 关键配置
      • 3.2 LwIP 线程与中断
    • 四、UDP 协议实现
      • 4.1 UDP 发送任务
      • 4.2 UDP 接收(可选,用于接收配置命令)
    • 五、SNMP 协议实现
      • 5.1 MIB 设计
      • 5.2 SNMP 核心实现(基于 LwIP 内置 SNMP Agent)
      • 5.3 SNMP Trap 主动上报
    • 六、双协议共存的关键设计
      • 6.1 资源隔离
      • 6.2 时序协调
      • 6.3 看门狗策略
    • 七、典型坑
    • 八、小结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档