---
title: "How Do You Get SNMPv3 authPriv on an ESP32-S3-ETH When ESP-IDF Has No Switch for It?"
url: "https://maker.wiznet.io/Grace_Koo/projects/how-do-you-get-snmpv3-authpriv-on-an-esp32-s3-eth-when-esp-idf-has-no-switch-for-it/"
markdown_url: "https://maker.wiznet.io/Grace_Koo/projects/how-do-you-get-snmpv3-authpriv-on-an-esp32-s3-eth-when-esp-idf-has-no-switch-for-it/md"
type: "UCC: User Created Content"
author: "vrngpb"
author_url: "https://github.com/vrngpb/snmpv3_agent_s3eth"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "vrngpb"
original_url: "https://github.com/vrngpb/snmpv3_agent_s3eth"
published: "2026-10-23"
language: "en"
tags: ["ESP32-S3", "SNMPv3", "ESP-IDF", "lwIP", "Server room monitoring"]
hardware: ["WIZnet W5500"]
likes: 0
views: 11
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# How Do You Get SNMPv3 authPriv on an ESP32-S3-ETH When ESP-IDF Has No Switch for It?

> A server-room monitor on the Waveshare ESP32-S3-ETH serving SNMPv3 authPriv from lwIP's own agent, with commit-confirm network changes, set beside WIZnet's EOE-W55RP20 module.

Original author: vrngpb (source: https://github.com/vrngpb/snmpv3_agent_s3eth)

## Components

- **WIZnet W5500** x 1: Onboard on the Waveshare ESP32-S3-ETH, SPI2 ([docs](https://docs.wiznet.io/Product/Chip/Ethernet/W5500))

WIZnet parts: W5500 ([Datasheet](https://docs.wiznet.io/Product/Chip/Ethernet/W5500/datasheet?utm_source=maker&utm_medium=project&utm_campaign=w5500), [product hub](https://maker.wiznet.io/products/w5500/))

## Article

> 📚 **Context**: [vrngpb/snmpv3_agent_s3eth](https://github.com/vrngpb/snmpv3_agent_s3eth) — firmware for a Waveshare ESP32-S3-ETH that reports a server room's temperature, water leak and door state over SNMPv3 authPriv, and takes network settings by SNMP SET with a five-minute confirm-or-revert.

---

## 01 — What the repository builds

A monitoring node that sits in a server room. It reads three sensors, serves them over SNMP, and a management system collects them with SNMPv3.

| Sensor | Part | How it is read |
| --- | --- | --- |
| Temperature | DS18B20 | 1-Wire, every 2 s |
| Water leak | Gidrolock WSP | GPIO behind a 2N3904 transistor |
| Two doors | reed switches | dry contact, pull-up |

The README is firm that the leak sensor must never be wired straight to a GPIO: its loop runs at 5–24 V and burns the port.

### Pins

| GPIO | Use |
| --- | --- |
| 11 / 12 / 13 / 14 | W5500 MOSI / MISO / SCK / CS |
| 10 / 9 | W5500 INT / RST |
| 16 | DS18B20 data, 4.7 kΩ pull-up |
| 17 | leak input |
| 18 / 21 | door 1 / door 2 |
| 2 / 1 | network LED / alarm LED |
| 8 | factory-reset button, hold 5 s |

GPIO 4–7 belong to the TF slot, 33–37 to the octal PSRAM and 19/20 to USB, so the design routes around them. The button is on 8 because 5 is the TF card's MISO.

### The MIB

Standard MIB-2 plus one enterprise subtree at `.1.3.6.1.4.1.9999.1`.

| Last OID | Type | Access | Meaning |
| --- | --- | --- | --- |
| 1 | INTEGER | RO | temperature × 10 (246 = 24.6 °C) |
| 2 | GAUGE32 | RO | free heap, bytes |
| 3 | TIMETICKS | RO | uptime |
| 4 | GAUGE32 | RO | heap used, % |
| 5 | OCTET STRING | RO | MAC |
| 6 | INTEGER | RO | leak (1 = alarm) |
| 7 / 8 | INTEGER | RO | door 1 / door 2 (1 = open) |
| 10 / 11 / 12 | OCTET STRING | RW | IP / gateway / mask |
| 13 | INTEGER | RW | config confirm — GET reports trial mode, SET 1 confirms |

The alarm LED blinks on a leak, an open door or anything above 45 °C. The README ships a bash monitor for net-snmp and a Python one pinned to pysnmp 4.4.12.

---

## 02 — ESP-IDF has no switch for SNMP

lwIP carries an SNMP agent, but **ESP-IDF exposes no Kconfig option to turn it on.** So the repository adds a component that pulls lwIP's own sources into the build.

```plaintext
set(LWIP_PATH "$ENV{IDF_PATH}/components/lwip/lwip")
file(GLOB SNMP_SOURCES
    "${LWIP_PATH}/src/apps/snmp/*.c"
    "${LWIP_PATH}/src/apps/snmp/snmp_mib2/*.c")
idf_component_register(SRCS ${SNMP_SOURCES} INCLUDE_DIRS "." REQUIRES lwip mbedtls)
target_compile_definitions(${COMPONENT_LIB} PUBLIC
    -DLWIP_SNMP=1 -DSNMP_LWIP=1 -DSNMP_USE_RAW=1
    -DLWIP_SNMP_V3=1 -DLWIP_SNMP_V3_MBEDTLS=1
    -DMIB2_STATS=1 -DLWIP_STATS=1)
```

- It globs `apps/snmp` and `snmp_mib2` out of the lwIP tree that IDF already ships.

- `SNMP_USE_RAW=1` — the agent attaches through lwIP's raw API, not sockets.

- `LWIP_SNMP_V3_MBEDTLS=1` — SHA-1 and AES come from IDF's mbedTLS.

- A line converting Windows backslashes and a `-Wno-dangling-pointer` are the traces of it not building cleanly as-is.

The component's own description reads "for IDF v6, no Kconfig option".

---

## 03 — The USM callbacks are filled in by hand

lwIP's SNMPv3 asks the application for the user table and the engine state through callbacks. `snmpv3_usm.c` supplies them.

**Engine ID** — exactly the RFC 3411 layout.

```plaintext
eid[0] = 0x80 | ((SNMP_ENTERPRISE >> 24) & 0x7F);   /* enterprise number, top bit set */
eid[1..3] = SNMP_ENTERPRISE;
eid[4] = 0x03;                                      /* format: MAC address */
memcpy(&eid[5], mac, 6);
```

**Engine boots** — kept in NVS and incremented on every boot. It is what replay protection relies on, so it has to survive a power cycle.

**Engine time** — a 1-second `esp_timer`. The comment notes that lwIP refreshes its internal time on every inbound message, so the tick deliberately does not call into the stack.

**Keys** — the passphrases are localized with lwIP's helper.

| Step | Action |
| --- | --- |
| 1 | repeat the passphrase out to 1 MB |
| 2 | SHA-1 → 20-byte Ku |
| 3 | Kl = SHA-1(Ku ‖ engineID ‖ Ku) |

The auth key is all 20 bytes; the privacy key uses **the first 16** for AES-128. The two passphrases are independent.

There is one user. Its name and both passphrases go into the build through `cmake -D`. An empty username is treated as an engine-discovery probe and let through.

---

## 04 — Change the IP and you have five minutes to confirm it

This is the most carefully built part. Change a remote device's address over SNMP SET with a typo in it and the device is gone for good. So the repository borrows JunOS's `commit confirmed`.

| Step | What happens |
| --- | --- |
| SET | IP, gateway and mask are stored in NVS as candidates; the live settings stay. Reboot after 1 s |
| Trial boot | comes up on the candidates. The state is moved from TRIAL to REVERT before they are applied |
| Confirm | SET 1 to .13 at the new address, within five minutes. Candidates become the main settings, no reboot |
| Not confirmed | timer expiry, a panic, the watchdog or a power loss — whichever it is, the next boot runs the old settings |

**The ordering is the point.** The trial boot writes REVERT into NVS before it applies the candidates, so anything that dies during the trial leaves the next boot on the revert path. Confirmation runs the other way round — the main keys are written first and the NORMAL flag **last**, so a crash mid-confirm still leaves consistent main settings.

lwIP's two-phase scalar nodes are used as intended. `test` checks for a strict dotted quad, a contiguous mask and a length of 7 to 15; `set` stores. Sending IP, gateway and mask in one PDU calls the callback three times and **schedules the reboot once.** Confirming outside trial mode returns `inconsistentValue`.

Anything left out of the SET inherits the current value. Changing only the IP and leaving the gateway alone works.

---

## 05 — The W5500 side

| Item | Value |
| --- | --- |
| Driver | ESP-IDF in-tree, CONFIG_ETH_SPI_ETHERNET_W5500 in sdkconfig |
| Bus | SPI2, 36 MHz, mode 0, queue 20 |
| INT / RST | GPIO 10 / 9, PHY address 1 |
| MAC | ESP_MAC_ETH from the ESP32-S3 eFuse |
| IP | static; on link-up, dhcpc is stopped and the NVS values are applied |

The comment explains the eFuse MAC — **the W5500 has no MAC burned in.** If the driver fails to install, the log prints all six pins so wiring is the first thing checked.

The chip runs MACRAW and lwIP does TCP/IP. SNMP is UDP 161, one socket, and the agent itself lives inside that same lwIP.

The task watchdog is 5 s and panics into a reboot. Both the sensor task and the button task register with it.

---

## 06 — Beside WIZnet's EOE-W55RP20 SNMP module

There is a [WIZnet module](https://docs.wiznet.io/Product/Industrial-Solutions/SNMP) that does the same job. A W55RP20 packages an RP2040 and a W5500 together, and the host MCU exchanges AT commands over UART and nothing else. The overlap with this repository is large enough that putting them side by side shows who carries what.

| Item | snmpv3_agent_s3eth | EOE-W55RP20 |
| --- | --- | --- |
| Where SNMP runs | lwIP on the ESP32-S3 | inside the module |
| TCP/IP | lwIP, chip in MACRAW | W5500 hardware |
| What the host does | everything | exchanges values over UART |
| Versions | v3 only | v1 / v2c / v3, only v3 on by default |
| Auth / privacy | SHA-1 / AES-128 | SHA-1 or SHA-256 / AES-128 |
| Users | one, fixed at build | up to 4, four roles, passwords not stored |
| Objects | 13, in code | up to 1024, created by number or from CSV |
| Dangerous SETs | 5-minute commit-confirm | Confirmed SET — the host must approve |
| Traps | none | Trap and Inform to 4 receivers, Informs retried |
| Configuration | build options and SNMP SET | 61 AT commands, web UI off by default |
| Firmware update | idf.py flash | verified, staged, installed at next boot |

One problem gets two different answers: **how to accept a write safely.** The repository puts a clock on it — a confirmation at the new address within five minutes keeps the change, otherwise it reverts. The module puts a person on it — a written value is offered to the host MCU first and applied only on approval. For values that cut the connection the moment they change, like network settings, the first fits; for values with an immediate physical effect, like a relay, the second does.

The rest is division of labour. Here the ESP32-S3 carries the SNMP stack, the USM and TCP/IP. The module lifts all of that off and leaves the host with a UART. A small host, a need for SHA-256 and several users, or hundreds of objects point that way; a handful of sensors and one MIB is lighter done directly, as this repository does.

---

## 07 — Where it stands

- **There is no licence file.** Reusing the code needs a word with the author.

- The README is in Russian; the comments mix Russian and English.

- **One user, with passphrases baked into the build.** Changing them means reflashing.

- **SHA-1 only.** That is what lwIP supports; there is no SHA-256.

- **No traps.** Alarms have to be polled with GET.

- sysContact and sysLocation are fixed in code.

- No DHCP, static IP only; the defaults live in `ethernet_app.c`.

It opens a door IDF leaves shut by building lwIP's sources directly, fills in the USM by hand, and bolts a revert onto address changes. **The three places anyone bringing up SNMPv3 on ESP-IDF hits first are all left in the code.**

---

## 08 — Similar Projects on WIZnet Makers

- [**How to Build an Industrial SNMP Agent with WIZnet W5500 on LPC1758**](https://maker.wiznet.io/ronpang/projects/how-to-build-an-industrial-snmp-agent-with-wiznet-w5500-on-lpc1758/) brings up the same SNMPv3 on a different MCU. That one stacks an agent on the W5500's hardware sockets; this one uses the agent inside lwIP.

- [**SNMP Environmental Monitor**](https://maker.wiznet.io/mason/projects/snmp-environmental-monitor/) is closest in purpose — an environment node exported over SNMP. This repository adds v3 authentication and privacy and remote reconfiguration on top.

- [**STM32 + W5500 SNMP**](https://maker.wiznet.io/mark/projects/stm32-w5500-snmp/) is the fundamentals end, SNMP on the same chip. Read together, they show what grows when v1 becomes v3 authPriv.

---

## Q&A

**Q. In what mode is the W5500 used?**

MACRAW, through ESP-IDF's in-tree driver. lwIP handles TCP/IP and the SNMP agent runs inside that same lwIP. It is on SPI2 at 36 MHz, INT on GPIO10, RST on GPIO9, with the MAC taken from the ESP32-S3 eFuse.

**Q. Why does enabling SNMP on ESP-IDF need its own step?**

lwIP has an SNMP agent, but IDF does not expose a Kconfig option for it. The repository adds a component that globs the `apps/snmp` sources out of IDF's lwIP tree, compiles them directly, and attaches mbedTLS with `LWIP_SNMP_V3_MBEDTLS=1`.

**Q. What happens when network settings are changed over SNMP?**

The new values are stored as candidates in NVS and the device reboots into trial mode after one second. A SET of 1 to `.13` at the new address within five minutes makes them permanent; without it the device returns to the previous settings. A panic, watchdog or power loss during the trial also lands on the revert path at the next boot.

**Q. How does it differ from WIZnet's SNMP module?**

In where SNMP and TCP/IP run. Here it is lwIP on the ESP32-S3; on the EOE-W55RP20 it is inside the module and the host only uses a UART. The module is broader on users, SHA-256, object count, traps and web configuration; for a few sensors and one MIB, doing it directly as this repository does is lighter.

**Q. Can it be reused?**

There is no licence file. The structure and settings can be read and applied; reusing the code needs the author's confirmation.

---

**Original Link**: https://github.com/vrngpb/snmpv3_agent_s3eth

**lwIP SNMP build**: `components/snmp_lwip/CMakeLists.txt` · **USM**: `main/snmpv3_usm.c` · **commit-confirm**: `main/ethernet_app.c`, `main/main.c` · **Compared against**: [EOE-W55RP20 SNMP module](https://docs.wiznet.io/Product/Industrial-Solutions/SNMP)

---

Source: https://maker.wiznet.io/Grace_Koo/projects/how-do-you-get-snmpv3-authpriv-on-an-esp32-s3-eth-when-esp-idf-has-no-switch-for-it/
