全部例程

W55MH32使用SNMP管理其他设备

W55MH32

更新于 2026年9月15日

本篇总结

在 W55MH32 上实现 SNMP Agent 功能,通过 MIB Browser 管理 W55MH32 的 LED 等设备状态。

🔑 KEIL MDK、UART调试、硬件TOE引擎、SNMP、DHCP

硬件平台:本例程基于 W55MH32L-EVB 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB

本示例演示如何在 W55MH32 上实现 SNMP(简单网络管理协议)功能。通过实战例程,为大家讲解如何使用 MIB Browser 管理 W55MH32,实现远程读取和设置 LED 状态等设备管理操作。该例程用到的其他网络协议,例如 DHCP,请参考相关章节。

前置工具准备

开始本例程前,请确认以下工具已安装:

  • iReasoning MIB Browser:SNMP 管理工具,用于向 W55MH32 发送 GET/SET 请求:点击下载
  • WIZ UartTool V1.0:串口调试工具:点击下载

SNMP 协议简介

SNMP(Simple Network Management Protocol)是应用层的网络管理协议,用于在网络设备之间交换管理信息。它工作在 UDP 之上,Agent 端口为 161,Trap 端口为 162。通过 SNMP,管理站可以远程监控和配置网络设备。

组成角色说明
管理站(Manager)发起请求运行 MIB Browser 的 PC,向设备发送 GET/SET 请求
代理(Agent)响应请求W55MH32,监听 UDP 161,接收并处理管理站的请求
MIB管理信息库以 OID 树形结构组织的管理对象集合

W55MH32 的优势

W55MH32 SDK 内置了完整的 SNMP Agent 库(snmpd.c/h),报文组包/解包、OID 匹配、Trap 发送全部由库函数处理。你只需要:注册定时器 → 添加 MIB 数据项 → 初始化 SNMP → 在主循环中调用 snmpd_run() 驱动状态机。

硬件连接与拓扑

请注意:

PC(运行 MIB Browser)和 W55MH32 需处于同一网段。W55MH32 需先完成网络初始化(如通过 DHCP 获取 IP 地址)。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器 LAN 口
┌──────────────────────────────────┐
│ 路由器(DHCP Server) │
│ 192.168.1.1 │
└────┬───────────────────┬──────────┘
│ │
┌────┴──────┐ ┌────┴──────────────┐
│ W55MH32 │ │ PC │
│(SNMP Agent)│ │ (iReasoning MIB │
│ UDP :161 │ │ Browser) │
└───────────┘ └───────────────────┘
↑
DEBUG USB ──→ WIZ UartTool
网络拓扑:PC 运行 MIB Browser 作为 SNMP Manager,W55MH32 作为 SNMP Agent 监听 UDP 161

工程结构

工程位于 SDK 内 1.SDK\ModuleDemo\WZTOE\SNMP\,核心文件如下:

SNMP/
├── Project/
│   └── Project.uvprojx            ← Keil 工程文件
└── USER/
    ├── main.c                     ← 硬件初始化 + 网络配置 + 主循环
    ├── wiz_interface/
    │   ├── wiz_interface.c        ← PHY 检测 / 网络信息打印 / DHCP
    │   └── wiz_interface.h
    ├── snmp/
    │   ├── snmp_custom.c          ← MIB 数据定义 + GET/SET 回调函数
    │   ├── snmp_custom.h          ← dataEntryType 结构体定义
    │   ├── snmpd.c                ← SNMP Agent 初始化与主循环
    │   └── snmpd.h
    ├── ioLibrary/
    │   └── Internet/SNMP/         ← SNMP 协议库(报文解析/组包)
    ├── tim/
    │   └── bsp_tim.c/h            ← TIM3 定时器(DHCP 1s + SNMP 10ms)
    └── uart/
        └── bsp_uart.c/h           ← 调试串口 + printf 重定向

本例程的核心:在 snmp_custom.c 中定义 MIB 数据项并注册 GET/SET 回调函数,通过 snmpd_run() 驱动 SNMP Agent 状态机处理请求。

步骤1:初始化LED并注册到SNMP

文件:main.c — 在网络初始化完成后,调用 LED 初始化函数,并将 LED 的读取和设置回调注册到 SNMP 系统中。

user_led_init();
user_led_control_init(get_user_led_status, set_user_led_status);

user_led_init() 完成 LED 的 GPIO 初始化,user_led_control_init() 将读取和设置 LED 状态的回调函数注册到 SNMP 系统中,后续 MIB 数据项通过函数指针调用这些回调。

步骤2:注册SNMP定时器

文件:main.c — SNMP 协议的超时处理需要 10ms 级别的定时器。我们在 TIM3 的 1ms 中断中累加实现,每 10ms 调用一次 SNMP_time_handler()。

/**
* @brief   1ms timer IRQ Handler
* @param   none
* @return  none
*/
void TIM3_IRQHandler(void)
{
    static uint32_t tim3_1ms_count = 0;
    static uint8_t tim3_10ms_count = 0;
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
    {
        tim3_1ms_count++;
        tim3_10ms_count++;
        if (tim3_1ms_count >= 1000)
        {
            DHCP_time_handler();
            tim3_1ms_count = 0;
        }
        if(tim3_10ms_count>=10)
        {
            SNMP_time_handler();
            tim3_10ms_count=0;
        }
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
    }
}

我们需要 10ms 调用一次 SNMP_time_handler() 函数,方便 SNMP 进行超时处理。同时 1 秒累加调用 DHCP_time_handler() 供 DHCP 使用。

步骤3:添加MIB功能函数

文件:snmp_custom.c — 在 snmpData 结构体变量中添加 MIB 数据项,包括系统信息和 LED 的 GET/SET 功能。

dataEntryType snmpData[] =
{
    // System MIB
    // SysDescr Entry
    {   8, {0x2b, 6, 1, 2, 1, 1, 1, 0}, SNMPDTYPE_OCTET_STRING, 30,
        {"WIZnet Embedded SNMP Agent"}, NULL, NULL},
    // SysObjectID Entry
    {   8, {0x2b, 6, 1, 2, 1, 1, 2, 0}, SNMPDTYPE_OBJ_ID, 8,
        {"\x2b\x06\x01\x02\x01\x01\x02\x00"}, NULL, NULL},
    // SysUptime Entry
    {   8, {0x2b, 6, 1, 2, 1, 1, 3, 0}, SNMPDTYPE_TIME_TICKS, 0,
        {""}, currentUptime, NULL},
    // sysContact Entry
    {   8, {0x2b, 6, 1, 2, 1, 1, 4, 0}, SNMPDTYPE_OCTET_STRING, 30,
        {"http://www.wizwiki.net/forum"}, NULL, NULL},
    // sysName Entry
    {   8, {0x2b, 6, 1, 2, 1, 1, 5, 0}, SNMPDTYPE_OCTET_STRING, 30,
        {"http://www.wiznet.co.kr"}, NULL, NULL},
    // Location Entry
    {   8, {0x2b, 6, 1, 2, 1, 1, 6, 0}, SNMPDTYPE_OCTET_STRING, 30,
        {"4F Humax Village"}, NULL, NULL},
    // SysServices
    {   8, {0x2b, 6, 1, 2, 1, 1, 7, 0}, SNMPDTYPE_INTEGER, 4,
        {""}, NULL, NULL},
    // getLED - 读取LED状态
    {   8, {0x2b, 6, 1, 2, 1, 12, 2, 0}, SNMPDTYPE_OCTET_STRING, 30,
        {""}, get_LEDStatus_UserLED, NULL},
    // setLED - 设置LED状态
    {   8, {0x2b, 6, 1, 2, 1, 12, 1, 0}, SNMPDTYPE_INTEGER, 4,
        {""}, NULL, set_LEDStatus_UserLED},
    // OID Test #1 (long-length OID example, 19865)
    {0x0a, {0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0x9b, 0x19, 0x01, 0x00},
        SNMPDTYPE_OCTET_STRING, 30,
        {"long-length OID Test #1"}, NULL, NULL},
    // OID Test #2 (long-length OID example, 22210)
    {0x0a, {0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xad, 0x42, 0x01, 0x00},
        SNMPDTYPE_OCTET_STRING, 35,
        {"long-length OID Test #2"}, NULL, NULL},
    // OID Test #2: SysObjectID Entry
    {0x0a, {0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xad, 0x42, 0x02, 0x00},
        SNMPDTYPE_OBJ_ID, 0x0a,
        {"\x2b\x06\x01\x04\x01\x81\xad\x42\x02\x00"}, NULL, NULL},
};

结构体变量 snmpData 的结构体 dataEntryType 定义如下所示:

typedef struct {
  uint8_t oidlen;
  uint8_t oid[MAX_OID];
  uint8_t dataType;
  uint8_t dataLen;
  union {
    uint8_t octetstring[MAX_STRING];
    uint32_t intval;
  } u;
  void (*getfunction)(void *, uint8_t *);
  void (*setfunction)(int32_t);
} dataEntryType;

关键说明

getLED 的 OID 为 1.3.6.1.2.1.12.2.0,注册了 get_LEDStatus_UserLED 回调函数用于读取 LED 状态;setLED 的 OID 为 1.3.6.1.2.1.12.1.0,注册了 set_LEDStatus_UserLED 回调函数用于设置 LED 状态。

步骤4:初始化SNMP协议

文件:snmpd.c — 调用 snmpd_init() 初始化 SNMP Agent,注册 Socket 号、管理站 IP 和代理 IP。

void snmpd_init(uint8_t * managerIP, uint8_t * agentIP,
                uint8_t sn_agent, uint8_t sn_trap)
{
#ifdef _SNMP_DEBUG_
  printf("\r\n - SNMP : Start SNMP Agent Daemon\r\n");
#endif
  SOCK_SNMP_AGENT = sn_agent;
  SOCK_SNMP_TRAP = sn_trap;
  if((SOCK_SNMP_AGENT > _WIZCHIP_SOCK_NUM_) ||
     (SOCK_SNMP_TRAP > _WIZCHIP_SOCK_NUM_)) return;
  startTime = getSNMPTimeTick(); // Start time (unit: 10ms)
  initTable(); // Settings for OID entry values

  initial_Trap(managerIP, agentIP);
  /*
  // Example Codes for SNMP Trap
  dataEntryType enterprise_oid = {0x0a, {0x2b, 0x06, 0x01, 0x04, 0x01,
      0x81, 0x9b, 0x19, 0x01, 0x00}, SNMPDTYPE_OBJ_ID, 0x0a,
      {"\x2b\x06\x01\x04\x01\x81\x9b\x19\x10\x00"}, NULL, NULL};
  dataEntryType trap_oid1 = {8, {0x2b, 6, 1, 4, 1, 0, 11, 0},
      SNMPDTYPE_OCTET_STRING, 30, {""}, NULL, NULL};
  dataEntryType trap_oid2 = {8, {0x2b, 6, 1, 4, 1, 0, 12, 0},
      SNMPDTYPE_INTEGER, 4, {""}, NULL, NULL};
  strcpy((char *)trap_oid1.u.octetstring, "Alert!!!");
  trap_oid2.u.intval = 123456;
  // Generic Trap: warmStart
  snmp_sendTrap((void *)"192.168.0.214", (void *)"192.168.0.112",
      (void *)"public", enterprise_oid, SNMPTRAP_WARMSTART, 0, 0);
  // Enterprise-Specific Trap
  snmp_sendTrap((void *)"192.168.0.214", (void *)"192.168.0.112",
      (void *)"public", enterprise_oid, 6, 0, 2, &trap_oid1, &trap_oid2);
  */
}

这一步,主要是将使用的 Socket 号、管理站 IP 地址、请求 IP 地址等参数注册进去,并且记录开始时间。如果想使用 Trap 主动上报,可以参考注释中的示例代码。

步骤5:在主循环中运行snmpd_run()

文件:snmpd.c — snmpd_run() 函数执行一个 UDP 状态机,当收到 SNMP 管理站的消息后执行解析以及回复操作。

int32_t snmpd_run(void)
{
    int32_t ret;
    int32_t len = 0;

    uint8_t svr_addr[6];
    uint16_t svr_port;
    if(SOCK_SNMP_AGENT > _WIZCHIP_SOCK_NUM_) return -99;

    switch(getSn_SR(SOCK_SNMP_AGENT))
    {
        case SOCK_UDP :
            if ( (len = getSn_RX_RSR(SOCK_SNMP_AGENT)) > 0)
            {
                request_msg.len = recvfrom(SOCK_SNMP_AGENT,
                    request_msg.buffer, len, svr_addr, &svr_port);
            }
            else
            {
                request_msg.len = 0;
            }
            if (request_msg.len > 0)
            {
#ifdef _SNMP_DEBUG_
                dumpCode((void *)"\r\n[Request]\r\n", (void *)"\r\n",
                    request_msg.buffer, request_msg.len);
#endif
                // Initialize
                request_msg.index = 0;
                response_msg.index = 0;
                errorStatus = errorIndex = 0;
                memset(response_msg.buffer, 0x00, MAX_SNMPMSG_LEN);
                // Received message parsing and send response process
                if (parseSNMPMessage() != -1)
                {
                    sendto(SOCK_SNMP_AGENT, response_msg.buffer,
                        response_msg.index, svr_addr, svr_port);
                }
#ifdef _SNMP_DEBUG_
                dumpCode((void *)"\r\n[Response]\r\n", (void *)"\r\n",
                    response_msg.buffer, response_msg.index);
#endif
            }
            break;
        case SOCK_CLOSED :
            if((ret = socket(SOCK_SNMP_AGENT, Sn_MR_UDP,
                PORT_SNMP_AGENT, 0x00)) != SOCK_SNMP_AGENT)
                return ret;
#ifdef _SNMP_DEBUG_
            printf(" - [%d] UDP Socket for SNMP Agent, port [%d]\r\n",
                SOCK_SNMP_AGENT, PORT_SNMP_AGENT);
#endif
            break;
        default :
            break;
    }
    return 1;
}

snmpd_run() 函数会执行一个 UDP 状态机,当收到 SNMP 管理的消息后会执行解析以及回复操作。在 SOCK_UDP 状态下接收数据,调用 parseSNMPMessage() 解析请求并生成响应,然后通过 sendto() 发送响应。

运行验证

烧录例程运行后,首先可以看到打印了 PHY 链路检测和 DHCP 获取网络信息,然后运行 SNMP 程序。打开 iReasoning MIB Browser,输入 W55MH32 的 IP 地址进行以下操作:

W55MH32 SNMP example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
 W55MH32 network configuration : DHCP

 MAC         : 00:08:DC:00:0E:9A
 IP          : 192.168.1.6
 Subnet Mask : 255.255.255.0
 Gateway     : 192.168.1.1
 DNS         : 192.168.1.1
====================================================================================================


 - SNMP : Start SNMP Agent Daemon
 - [0] UDP Socket for SNMP Agent, port [161]

测试前提

测试实例需要 PC 端(运行 MIB Browser)和 W55MH32 处于同一网段。W55MH32 需先完成网络初始化(如通过 DHCP 获取 IP 地址)。

  • 打开 MIB Browser,输入 W55MH32 的地址,然后依次点击 system 分支下的各个节点,获取到的结果与代码定义相同。
  • 找到 User 分支下的 setLED 指令,右键指令,点击 set,类型选择 integer,Value 填 1,点击 OK。
  • getLED 指令,右键指令,点击 get,即可读出 LED 状态。
iReasoning MIB Browser — SNMP SET 操作(OID setLED,Value=1,目标 192.168.1.123)— 远程点亮 LED
iReasoning MIB Browser — SNMP GET 操作(OID getLED)— 查询 LED 状态(USER LED[off] / USER LED[on])

常见问题

Q: SNMP有哪些版本,本例程使用哪个版本

SNMP主要有三个版本:SNMPv1、SNMPv2c和SNMPv3。本例程使用的是SNMPv1版本(Version字段值为0)。SNMPv1和SNMPv2c使用社区字符串进行简单身份验证,SNMPv3提供了更强的安全机制(认证和加密)。不同版本的报文格式存在差异。

Q: 什么是社区字符串,public和private有什么区别

社区字符串是SNMPv1和SNMPv2c的一种简单身份验证机制,用于限制对管理对象的访问权限。public通常表示只读访问权限,private通常表示读写访问权限。它相当于一个简单的"密码",安全性较低,SNMPv3中已被更完善的USM安全模型取代。

Q: OID是什么,如何查询和设置设备信息

OID(对象标识符)是唯一标识MIB中管理对象的分层命名方案,由点号分隔的整数组成(如1.3.6.1.2.1.1.1.0)。使用SNMP GET请求可查询特定OID的值,使用SET请求可修改OID的值(需设备支持),使用WALK请求可遍历一个OID下的所有子节点。使用前最好查阅设备的MIB文档了解支持的OID。

Q: W55MH32如何主动上报事件

W55MH32可以通过SNMP Trap机制主动向管理站上报事件。在snmpd_init()初始化时配置管理站IP和代理IP,然后调用snmp_sendTrap()函数发送Trap报文。支持通用Trap(如warmStart)和企业自定义Trap。Trap使用UDP端口发送,无需管理站主动请求。

Q: 测试SNMP功能有什么前提条件

测试实例需要PC端(运行MIB Browser)和W55MH32处于同一网段。W55MH32需要先完成网络初始化(如通过DHCP获取IP地址),并确保SNMP Agent的UDP Socket(端口161)正常打开。snmpd_run()需要在主循环中持续调用以处理来自管理站的请求。

总结

  • 协议概念:SNMP是应用层网络管理协议,具有简单性、互操作性、跨平台支持等特点
  • 架构组成:包括管理站(Manager)、代理(Agent)、管理信息库(MIB)和协议本身四部分
  • OID详解:分层命名方案(如1.3.6.1.2.1.1.1.0),通过GET/SET/WALK操作管理设备
  • 报文格式:基于ASN.1/BER编码,包含Version、Community、PDU、变量绑定等字段
  • 实现步骤:初始化LED注册 → 注册10ms定时器 → 添加MIB功能函数 → 初始化SNMP → 主循环运行snmpd_run()
  • 应用场景:故障报警、工业自动化环境监控、数据中心机房管理

下一篇文章将讲解如何使用 W55MH32 的 IPRAW 模式实现 PING 操作,敬请期待!

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5 配套开发板

Andy

W5500 技术支持专家