Skip to content

feat(snmp): battery, MCU temperature, reset reason, mesh, filter and duty gate OIDs - #16

Merged
Elektr0Vodka merged 3 commits into
dmc-observer-devfrom
feature/dmc-observer-dev-snmp-battery
Oct 16, 2026
Merged

Elektr0Vodka merged 3 commits into
dmc-observer-devfrom
feature/dmc-observer-dev-snmp-battery

Conversation

@Elektr0Vodka

@Elektr0Vodka Elektr0Vodka commented Oct 16, 2026 •

Copy link
Copy Markdown

What

Adds battery voltage and the other device health data the firmware already keeps to the SNMP agent, so a poller can see why a node rebooted, whether it is hot or congested, and what the packet filter and region gating are doing.

All under .1.3.6.1.4.1.99999:

Group OIDs Type
System .1.4.0 battery mV (0 when the board cannot measure it), .1.5.0 MCU temperature °C (-127 when there is no sensor), .1.6.0 reset reason code, .1.7.0 reset reason text INTEGER / STRING
Radio .2.12.0 TX queue length, .2.16.0 TX duty cycle %, .2.17.0 error flags bitmask INTEGER
Radio .2.13.0 flood dups, .2.14.0 direct dups, .2.15.0 RX airtime s Counter32
MQTT .3.4.0 clock set by NTP (1/0) INTEGER
Memory .4.5.0 lowest free heap since boot INTEGER
Duty gate (repeater only) .6.1.0-.6.5.0 enabled, gated layers, max layer, threshold, hysteresis INTEGER
Filter (repeater only) .7.1.0 enabled, .7.2.0 dry-run INTEGER
Filter (repeater only) .7.3.0-.7.13.0 drop totals (hops, rate, channel, hash, malformed, advert, path, sender, text, age) and airtime saved (ms) Counter32
Neighbours .8.1.0 neighbour count INTEGER
  • src/helpers/SNMPAgent.{h,cpp}: new fields, setters and handlers. The duty gate and filter groups are only registered after enableRepeaterGroups(), so room servers return noSuchObject for them instead of a misleading 0.
  • examples/simple_repeater/MyMesh.cpp, examples/simple_room_server/MyMesh.cpp: the push code moves into MyMesh::pushSNMPStats(). The reset reason is set once at startup.
  • src/helpers/bridges/MQTTBridge.cpp: passes _ntp_synced to updateMQTTStats().
  • MQTT_SNMP.md: OID tables, a note on types and counter resets, updated architecture notes, and the out-of-date board list replaced with a pointer to WITH_SNMP=1.

Why

The agent exposed radio, MQTT, memory and Wi-Fi stats, but no battery, temperature or reboot cause, and nothing about filter or region gating. A solar node with a dying pack, a node that keeps resetting on brownouts, or a repeater shedding traffic could not be seen from LibreNMS, Zabbix or RTFM-EV.

Design notes

  • Battery and temperature are read every 30 s, not every 2 s. HeltecV4Board::getBattMilliVolts() calls delay(10), which held up the mesh task every 2 s in the first version of this PR. Both values are read on the mesh task (Core 1), so the ADC is never touched from the SNMP loop on Core 0. The first reading is pushed straight away.
  • New running totals are Counter32 so pollers graph them as rates. The existing INTEGER totals (.2.1-.2.11, .3.3) are unchanged, to avoid breaking RTFM-EV's app/snmp/mib.py. Changing those types would be a separate PR.
  • Filter hop and rate totals are summed across payload types with a saturating add, matching the filter's own counters, which saturate instead of wrapping.
  • Counters start again from 0 after a reboot or clear stats.
  • The meshcomod companion SNMP agent (Feat/companion-snmp) is being aligned to the same numbers so one poller reads both. RTFM-EV's mib.py needs the new scalars to show them.

Testing

  • pio run -e Heltec_v3_repeater_observer_mqtt -e Heltec_v3_room_server_observer_mqtt: both build, no new warnings.
  • Not yet tested on hardware (no snmpwalk against a running node).

Known issue, not fixed here

ESP32Board::getMCUTemperature() adds the float readings into a uint32_t, so below 0 °C die temperature .1.5.0 will read wrong. The die usually runs well above ambient, so this is rare. It is upstream code, so I left it alone.

Firmware impact

Observer builds with WITH_SNMP=1 only. About 160 bytes of static RAM, plus heap for 31 more OID handlers on repeaters (13 on room servers, which skip the 18 duty gate and filter OIDs). No prefs or wire format change. SNMPAgent.* comes from agessaman's observer firmware, so expect it on the next catch-up merge.

🤖 Generated with Claude Code

The SNMP agent had no battery reading, so solar or battery powered
observers could not be monitored for a dying pack. Add battery voltage
in millivolts to the system group as .1.4.0 (0 when the board cannot
measure it).

The value is pushed from the mesh task together with the existing
2 second radio stats update, so the ADC is only read on Core 1, never
from the SNMP loop on Core 0. The meshcomod companion SNMP agent uses
the same OID, so one poller reads battery from both.
Monitoring tools could see radio, MQTT and memory stats, but not why a
node rebooted, whether it is overheating or congested, or what the
packet filter and region gating are doing. Expose the values the
firmware already keeps:

- system: MCU temperature (.1.5), reset reason code and text (.1.6/.1.7)
- radio: TX queue, flood/direct dups, RX airtime, TX duty cycle, error
  flags (.2.12-.2.17)
- mqtt: NTP synced (.3.4); memory: lowest free heap since boot (.4.5)
- duty gate (.6) and packet filter drop totals (.7), repeater only
- neighbour count (.8.1)

New running totals are Counter32 so pollers graph them as rates; the
older INTEGER totals are unchanged. Battery and temperature are now
read every 30 s instead of every 2 s, because the Heltec V4 battery
read blocks the mesh task for 10 ms. The push code moves into
MyMesh::pushSNMPStats() in both the repeater and the room server.
@Elektr0Vodka Elektr0Vodka changed the title feat(snmp): expose battery voltage as .1.4.0 feat(snmp): battery, MCU temperature, reset reason, mesh, filter and duty gate OIDs Oct 16, 2026
The room server's neighbour table only exists with WITH_MQTT_NEIGHBORS, so
pushSNMPStats() broke the build on non-PSRAM room-server observers
(Heltec_v2_room_server_observer_mqtt). Report 0 there instead.
@Elektr0Vodka

Copy link
Copy Markdown
Author

Pushed 7a4928e: the room server's neighbour table only exists with WITH_MQTT_NEIGHBORS, so pushSNMPStats() broke Heltec_v2_room_server_observer_mqtt (no PSRAM). It now reports .8.1.0 = 0 there (documented in MQTT_SNMP.md).

Verified merged onto dmc-observer-dev 9895e83: Heltec_v2 repeater + room and heltec_v4 repeater + room observer builds SUCCESS; pio test -e native 709/709. Not hardware-tested.

🤖 Generated with Claude Code

@Elektr0Vodka
Elektr0Vodka merged commit 5d7949b into dmc-observer-dev Oct 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant