An SNMP Manager for ESP, Arduino and similar MCU. Providing simple SNMP Get-Request support for specified OIDs.
The library supports:
If you find this useful, consider providing some support:
Changelog: CHANGELOG.md
An SNMPManager object is created for listening (on UDP port 162) to and parsing the SNMP GetResponses. This is initialised with the SNMP community string.
SNMPManager snmpManager = SNMPManager("public");
An SNMPGet object is created to make SNMP GetRequest calls (from UDP port 161 (by default)). This is initialised with the SNMP community string and an SNMP version. Note SNMPv1 = 0, SNMPv2 = 1. The port scan be changed if required using setPort(<port number>)
SNMPGet snmpRequest = SNMPGet("public", 1);
The handlers and callbacks for receiving the incoming SNMP GetResponse are configured in setup()
ValueCallback *callbackSysName; // Blank Callback for each OID
void setup()
{
IPAddress target(192, 168, 200, 187);
callbackSysName = snmpManager.addStringHandler(target, ".1.3.6.1.2.1.1.5.0", &sysNameResponse); // Callback for SysName for target host
}
Within the main program snmpManager.loop()
needs to be called frequently to capture and parse incoming GetResponses. GetRequests can be sent as needed, though typically a significantly lower rate than the main loop.
void loop()
{
snmpManager.loop(); // Call frequently
getSNMP();
}
void getSNMP()
{
// Check to see if it is time to send an SNMP request.
if ((timeLast + pollInterval) <= millis())
{
// Send SNMP Get request
snmpRequest.addOIDPointer(callbackSysName);
snmpRequest.setIP(WiFi.localIP()); //IP of the arduino
snmpRequest.setUDP(&udp);
snmpRequest.setRequestID(rand() % 5555);
snmpRequest.sendTo(router);
snmpRequest.clearOIDList();
// Display response (first call might be empty)
Serial.print("sysNameResponse: ");
Serial.println(sysNameResponse);
Serial.println("----------------------");
timeLast = millis();
}
}
You can add multiple OID to be queried in a single request by calling snmpRequest.addOIDPointer(another_callback);
This approach ensures all the requested OID are returned in the same response. Though I expect there are limits on the maximum packet sizes, so some experimentation may be required with large numbers of OID.
It's important to note that even if you make GetRequests every n seconds, that the response may not arrive in the allotted time period. SNMP responses are often deprioritised by devices when under load. As such your poll interval shouldn't be used for any calculations of time, instead using the devices uptime counter will show the time elapsed between data collections. For example if I want to calculate the bandwidth utilisation of my ADSL connection, then we need to look calculate: Utilisation = Amount of Data in time period / Max Possible data in time period
Which can be performed with the following:
// Note: Calculation will be incorrect if inOctets counter has wrapped.
bandwidthUtilisationPercent = ((float)((inOctets - lastInOctets) * 8) / (float)(downSpeed * ((uptime - lastUptime) / 100)) * 100);
What does this mean? Well lets explain the variables:
This can be broken down as:
((inOctets - lastInOctets) * 8)
calculates the delta in data received and converts Bytes to Bits by multiplying by 8.((uptime - lastUptime) / 100)
calculates the time between two samples and converts it to seconds.(downSpeed * ((uptime - lastUptime) / 100))
We calculate how much data could have been theoretically received in the elapsed time for a given maximum download speed.When working with SNMP Counters (COUNTER32 or COUNTER64) they can only be used for measuring a change between two values. A device reboot may reset the counter such that the calculating a delta would give an incorrect reading. Or depending on the sample period and the rate of change, the counter can wrap.
To compensate for wrapping, you can:
(((4294967295 - lastInOctets) + inOctets) * 8)
if (inOctets > lastInOctets)
{
// Note: Calculation will be incorrect if inOctets counter has wrapped.
bandwidthUtilisationPercent = ((float)((inOctets - lastInOctets) * 8) / (float)(downSpeed * ((uptime - lastUptime) / 100)) * 100);
}
else if (lastInOctets > inOctets)
{
// This handles 32bit counters wrapping a maximum of one time.
bandwidthUtilisationPercent = (((float)((4294967295 - lastInOctets) + inOctets) * 8) / (float)(downSpeed * ((uptime - lastUptime) / 100)) * 100);
}
To compensate for device reset:
SNMP can be used to query strings, however long strings lead to larger packet sizes needing larger buffers and increased memory usage. The ESP8266 appears to have a bug in the WiFi or UDP protocol support, leading to a maximum UDP packet size that can be received being 1024 bytes. As there are can be multiple OID responses in a single packet along with headers etc, this will reduce the maximum string size that can be received. Reading strings in to a character arrays can use a significant amount of memory, which may not be available on some MCUs. As such query strings should will likely need to be limited.
#define DEBUG
before the library include #include <Arduino_SNMP_Manager.h>
#define DEBUG_BER
before the library include #include <Arduino_SNMP_Manager.h>
#define SUPPRESS_ERROR_SHORT_PACKET
before #include <Arduino_SNMP_Manager.h>
#define SUPPRESS_ERROR_FAILED_PARSE
before #include <Arduino_SNMP_Manager.h>
The examples folder contains an SNMP GetRequest example for each of the data types. Note that the OID will need to be adapted the device you are querying. To understand what OID your device supports and the data type of each one, I'd recommend walking to the device with standard SNMP tools:
The following devices have been confirmed to work with this library (these are affiliate links that help support my work):
I'd love to hear about projects that find this library useful.
This project a derived from an SNMP Agent project. With Manager functionality adapted from work by Niich's fork.