{"id":1830,"date":"2014-12-30T00:34:19","date_gmt":"2014-12-30T00:34:19","guid":{"rendered":"http:\/\/41j.com\/blog\/?p=1830"},"modified":"2014-12-30T00:34:19","modified_gmt":"2014-12-30T00:34:19","slug":"arduino-snmp-voltage-monitor","status":"publish","type":"post","link":"https:\/\/41j.com\/blog\/2014\/12\/arduino-snmp-voltage-monitor\/","title":{"rendered":"Arduino SNMP Voltage Monitor"},"content":{"rendered":"<p><a href=\"http:\/\/41j.com\/blog\/wp-content\/uploads\/2014\/12\/netshield-e1419899069801.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/41j.com\/blog\/wp-content\/uploads\/2014\/12\/netshield-768x1024.jpg\" alt=\"netshield\" width=\"700\" height=\"933\" class=\"aligncenter size-large wp-image-1832\" \/><\/a><\/p>\n<p>As a quick proof of concept I wanted to try using the Arduino with a Wiznet TCP\/IP Ethernet chip as a SNMP server to monitor an analog input. The eventual use-case being a battery monitor. Turns out there&#8217;s already an SNMP library for the Arduino called <a href=\"https:\/\/code.google.com\/p\/agentuino\/\">Agentuino<\/a>. It hasn&#8217;t been updated in a couple of years, but seems to work well. I installed the library and modified the code to catch the OID associated with UPS battery charge level (1.3.6.1.4.1.318.1.1.1.2.2.1.0). I then just return the value of input A0. This can then be read using snmpget (available in the snmp package on Debian):<\/p>\n<pre>\r\nroot@navlaptop:\/home\/new# snmpget -v 1 -r 1 -c public 192.168.2.64 1.3.6.1.4.1.318.1.1.1.2.2.1.0 # NC\r\niso.3.6.1.4.1.318.1.1.1.2.2.1.0 = INTEGER: 78\r\nroot@navlaptop:\/home\/new# snmpget -v 1 -r 1 -c public 192.168.2.64 1.3.6.1.4.1.318.1.1.1.2.2.1.0 # ~ 2.5V\r\niso.3.6.1.4.1.318.1.1.1.2.2.1.0 = INTEGER: 444\r\nroot@navlaptop:\/home\/new# snmpget -v 1 -r 1 -c public 192.168.2.64 1.3.6.1.4.1.318.1.1.1.2.2.1.0 #NC\r\niso.3.6.1.4.1.318.1.1.1.2.2.1.0 = INTEGER: 88\r\n<\/pre>\n<p>So the setup appears to work quite well. One concern is the power concumption which with the Arduino and Wiznet is around 170mA. This might not be suitable for the solar deployment I&#8217;ve been looking at.<\/p>\n<p>Anyway the code is below (it&#8217;s just a modified version of the Agentuino sample):<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n\/**\r\n* Agentuino SNMP Agent Library Prototyping...\r\n*\r\n* Copyright 2010 Eric C. Gionet &lt;lavco_eg@hotmail.com&gt;\r\n*\r\n*\/\r\n#include &lt;Streaming.h&gt;         \/\/ Include the Streaming library\r\n#include &lt;Ethernet.h&gt;          \/\/ Include the Ethernet library\r\n#include &lt;SPI.h&gt;\r\n#include &lt;MemoryFree.h&gt;\r\n#include &lt;Agentuino.h&gt; \r\n#include &lt;Flash.h&gt;\r\n\/\/\r\n#define DEBUG\r\n\/\/\r\nstatic byte mac&#x5B;] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };\r\nstatic byte ip&#x5B;] = { 192, 168, 2, 64 };\r\nstatic byte gateway&#x5B;] = { 192, 168, 2, 1 };\r\nstatic byte subnet&#x5B;] = { 255, 255, 255, 0 };\r\n\/\/\r\n\/\/ tkmib - linux mib browser\r\n\/\/\r\n\/\/ RFC1213-MIB OIDs\r\n\/\/ .iso (.1)\r\n\/\/ .iso.org (.1.3)\r\n\/\/ .iso.org.dod (.1.3.6)\r\n\/\/ .iso.org.dod.internet (.1.3.6.1)\r\n\/\/ .iso.org.dod.internet.mgmt (.1.3.6.1.2)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2 (.1.3.6.1.2.1)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2.system (.1.3.6.1.2.1.1)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2.system.sysDescr (.1.3.6.1.2.1.1.1)\r\nconst static char sysDescr&#x5B;] PROGMEM      = &quot;1.3.6.1.2.1.1.1.0&quot;;  \/\/ read-only  (DisplayString)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2.system.sysObjectID (.1.3.6.1.2.1.1.2)\r\nconst static char sysObjectID&#x5B;] PROGMEM   = &quot;1.3.6.1.2.1.1.2.0&quot;;  \/\/ read-only  (ObjectIdentifier)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2.system.sysUpTime (.1.3.6.1.2.1.1.3)\r\nconst static char sysUpTime&#x5B;] PROGMEM     = &quot;1.3.6.1.2.1.1.3.0&quot;;  \/\/ read-only  (TimeTicks)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2.system.sysContact (.1.3.6.1.2.1.1.4)\r\nconst static char sysContact&#x5B;] PROGMEM    = &quot;1.3.6.1.2.1.1.4.0&quot;;  \/\/ read-write (DisplayString)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2.system.sysName (.1.3.6.1.2.1.1.5)\r\nconst static char sysName&#x5B;] PROGMEM       = &quot;1.3.6.1.2.1.1.5.0&quot;;  \/\/ read-write (DisplayString)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2.system.sysLocation (.1.3.6.1.2.1.1.6)\r\nconst static char sysLocation&#x5B;] PROGMEM   = &quot;1.3.6.1.2.1.1.6.0&quot;;  \/\/ read-write (DisplayString)\r\n\/\/ .iso.org.dod.internet.mgmt.mib-2.system.sysServices (.1.3.6.1.2.1.1.7)\r\nconst static char sysServices&#x5B;] PROGMEM   = &quot;1.3.6.1.2.1.1.7.0&quot;;  \/\/ read-only  (Integer)\r\n\r\n\/\/ .1.3.6.1.4.1.318.1.1.1.2.2.1.0\r\nconst static char upsBatPnct&#x5B;] PROGMEM = &quot;1.3.6.1.4.1.318.1.1.1.2.2.1.0&quot;;\r\n\r\n\/\/\r\n\/\/ Arduino defined OIDs\r\n\/\/ .iso.org.dod.internet.private (.1.3.6.1.4)\r\n\/\/ .iso.org.dod.internet.private.enterprises (.1.3.6.1.4.1)\r\n\/\/ .iso.org.dod.internet.private.enterprises.arduino (.1.3.6.1.4.1.36582)\r\n\/\/\r\n\/\/\r\n\/\/ RFC1213 local values\r\nstatic char locDescr&#x5B;]              = &quot;Agentuino, a light-weight SNMP Agent.&quot;;  \/\/ read-only (static)\r\nstatic char locObjectID&#x5B;]           = &quot;1.3.6.1.3.2009.0&quot;;                       \/\/ read-only (static)\r\nstatic uint32_t locUpTime           = 0;                                        \/\/ read-only (static)\r\nstatic char locContact&#x5B;20]          = &quot;Eric Gionet&quot;;                            \/\/ should be stored\/read from EEPROM - read\/write (not done for simplicity)\r\nstatic char locName&#x5B;20]             = &quot;Agentuino&quot;;                              \/\/ should be stored\/read from EEPROM - read\/write (not done for simplicity)\r\nstatic char locLocation&#x5B;20]         = &quot;Nova Scotia, CA&quot;;                        \/\/ should be stored\/read from EEPROM - read\/write (not done for simplicity)\r\nstatic int32_t locServices          = 7;                                        \/\/ read-only (static)\r\n\r\nuint32_t prevMillis = millis();\r\nchar oid&#x5B;SNMP_MAX_OID_LEN];\r\nSNMP_API_STAT_CODES api_status;\r\nSNMP_ERR_CODES status;\r\n\r\nvoid pduReceived()\r\n{\r\n  SNMP_PDU pdu;\r\n  \/\/\r\n  #ifdef DEBUG\r\n    Serial &lt;&lt; F(&quot;UDP Packet Received Start..&quot;) &lt;&lt; F(&quot; RAM:&quot;) &lt;&lt; freeMemory() &lt;&lt; endl;\r\n  #endif\r\n  \/\/\r\n  api_status = Agentuino.requestPdu(&amp;pdu);\r\n  \/\/\r\n  if ( pdu.type == SNMP_PDU_GET || pdu.type == SNMP_PDU_GET_NEXT || pdu.type == SNMP_PDU_SET\r\n    &amp;&amp; pdu.error == SNMP_ERR_NO_ERROR &amp;&amp; api_status == SNMP_API_STAT_SUCCESS ) {\r\n    \/\/\r\n    pdu.OID.toString(oid);\r\n    \/\/\r\n    \/\/Serial &lt;&lt; &quot;OID: &quot; &lt;&lt; oid &lt;&lt; endl;\r\n    \/\/\r\n    if ( strcmp_P(oid, sysDescr ) == 0 ) {\r\n      \/\/ handle sysDescr (set\/get) requests\r\n      if ( pdu.type == SNMP_PDU_SET ) {\r\n        \/\/ response packet from set-request - object is read-only\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = SNMP_ERR_READ_ONLY;\r\n      } else {\r\n        \/\/ response packet from get-request - locDescr\r\n        status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locDescr);\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      }\r\n      \/\/\r\n      #ifdef DEBUG\r\n        Serial &lt;&lt; F(&quot;sysDescr...&quot;) &lt;&lt; locDescr &lt;&lt; F(&quot; &quot;) &lt;&lt; pdu.VALUE.size &lt;&lt; endl;\r\n      #endif\r\n    } else if ( strcmp_P(oid, sysUpTime ) == 0 ) {\r\n      \/\/ handle sysName (set\/get) requests\r\n      if ( pdu.type == SNMP_PDU_SET ) {\r\n        \/\/ response packet from set-request - object is read-only\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = SNMP_ERR_READ_ONLY;\r\n      } else {\r\n        \/\/ response packet from get-request - locUpTime\r\n        status = pdu.VALUE.encode(SNMP_SYNTAX_TIME_TICKS, locUpTime);\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      }\r\n      \/\/\r\n      #ifdef DEBUG\r\n        Serial &lt;&lt; F(&quot;sysUpTime...&quot;) &lt;&lt; locUpTime &lt;&lt; F(&quot; &quot;) &lt;&lt; pdu.VALUE.size &lt;&lt; endl;\r\n      #endif\r\n    } else if ( strcmp_P(oid, sysName ) == 0 ) {\r\n      \/\/ handle sysName (set\/get) requests\r\n      if ( pdu.type == SNMP_PDU_SET ) {\r\n        \/\/ response packet from set-request - object is read\/write\r\n        status = pdu.VALUE.decode(locName, strlen(locName)); \r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      } else {\r\n        \/\/ response packet from get-request - locName\r\n        status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locName);\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      }\r\n      \/\/\r\n      #ifdef DEBUG\r\n        Serial &lt;&lt; F(&quot;sysName...&quot;) &lt;&lt; locName &lt;&lt; F(&quot; &quot;) &lt;&lt; pdu.VALUE.size &lt;&lt; endl;\r\n      #endif\r\n    } else if ( strcmp_P(oid, sysContact ) == 0 ) {\r\n      \/\/ handle sysContact (set\/get) requests\r\n      if ( pdu.type == SNMP_PDU_SET ) {\r\n        \/\/ response packet from set-request - object is read\/write\r\n        status = pdu.VALUE.decode(locContact, strlen(locContact)); \r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      } else {\r\n        \/\/ response packet from get-request - locContact\r\n        status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locContact);\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      }\r\n      \/\/\r\n      #ifdef DEBUG\r\n        Serial &lt;&lt; F(&quot;sysContact...&quot;) &lt;&lt; locContact &lt;&lt; F(&quot; &quot;) &lt;&lt; pdu.VALUE.size &lt;&lt; endl;\r\n      #endif\r\n    } else if ( strcmp_P(oid, sysLocation ) == 0 ) {\r\n      \/\/ handle sysLocation (set\/get) requests\r\n      if ( pdu.type == SNMP_PDU_SET ) {\r\n        \/\/ response packet from set-request - object is read\/write\r\n        status = pdu.VALUE.decode(locLocation, strlen(locLocation)); \r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      } else {\r\n        \/\/ response packet from get-request - locLocation\r\n        status = pdu.VALUE.encode(SNMP_SYNTAX_OCTETS, locLocation);\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      } \r\n      \/\/\r\n      #ifdef DEBUG\r\n        Serial &lt;&lt; F(&quot;sysLocation...&quot;) &lt;&lt; locLocation &lt;&lt; F(&quot; &quot;) &lt;&lt; pdu.VALUE.size &lt;&lt; endl;\r\n      #endif\r\n    } else if ( strcmp_P(oid, sysServices) == 0 ) {\r\n      \/\/ handle sysServices (set\/get) requests\r\n      if ( pdu.type == SNMP_PDU_SET ) {\r\n        \/\/ response packet from set-request - object is read-only\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = SNMP_ERR_READ_ONLY;\r\n      } else {\r\n        \/\/ response packet from get-request - locServices\r\n        status = pdu.VALUE.encode(SNMP_SYNTAX_INT, locServices);\r\n        pdu.type = SNMP_PDU_RESPONSE;\r\n        pdu.error = status;\r\n      }\r\n      \/\/\r\n      #ifdef DEBUG\r\n        Serial &lt;&lt; F(&quot;locServices...&quot;) &lt;&lt; locServices &lt;&lt; F(&quot; &quot;) &lt;&lt; pdu.VALUE.size &lt;&lt; endl;\r\n      #endif\r\n    } else if ( strcmp_P(oid, upsBatPnct) == 0) {\r\n    \r\n      \/\/ battery voltage output\r\n      int val = analogRead(0);\r\n      status = pdu.VALUE.encode(SNMP_SYNTAX_INT, val);\r\n      pdu.type = SNMP_PDU_RESPONSE;\r\n      pdu.error = status;      \r\n    }\r\n    \r\n    else {\r\n      \/\/ oid does not exist\r\n      \/\/\r\n      \/\/ response packet - object not found\r\n      pdu.type = SNMP_PDU_RESPONSE;\r\n      pdu.error = SNMP_ERR_NO_SUCH_NAME;\r\n    }\r\n    \/\/\r\n    Agentuino.responsePdu(&amp;pdu);\r\n  }\r\n  \/\/\r\n  Agentuino.freePdu(&amp;pdu);\r\n  \/\/\r\n  \/\/Serial &lt;&lt; &quot;UDP Packet Received End..&quot; &lt;&lt; &quot; RAM:&quot; &lt;&lt; freeMemory() &lt;&lt; endl;\r\n}\r\n\r\nvoid setup()\r\n{\r\n  Serial.begin(9600);\r\n  Ethernet.begin(mac, ip);\r\n  \/\/\r\n  api_status = Agentuino.begin();\r\n  \/\/\r\n  if ( api_status == SNMP_API_STAT_SUCCESS ) {\r\n    \/\/\r\n    Agentuino.onPduReceive(pduReceived);\r\n    \/\/\r\n    delay(10);\r\n    \/\/\r\n    Serial &lt;&lt; F(&quot;SNMP Agent Initalized...&quot;) &lt;&lt; endl;\r\n    \/\/\r\n    return;\r\n  }\r\n  \/\/\r\n  delay(10);\r\n  \/\/\r\n  Serial &lt;&lt; F(&quot;SNMP Agent Initalization Problem...&quot;) &lt;&lt; status &lt;&lt; endl;\r\n}\r\n\r\nvoid loop()\r\n{\r\n  \/\/ listen\/handle for incoming SNMP requests\r\n  Agentuino.listen();\r\n  \/\/\r\n  \/\/ sysUpTime - The time (in hundredths of a second) since\r\n  \/\/ the network management portion of the system was last\r\n  \/\/ re-initialized.\r\n  if ( millis() - prevMillis &gt; 1000 ) {\r\n    \/\/ increment previous milliseconds\r\n    prevMillis += 1000;\r\n    \/\/\r\n    \/\/ increment up-time counter\r\n    locUpTime += 100;\r\n  }\r\n}\r\n<\/pre>\n<p>Arduino SNMP:<br \/>\nhttps:\/\/code.google.com\/p\/agentuino\/<\/p>\n<p>Arduino SNMP Temp:<br \/>\nhttp:\/\/openhardware.gridshield.net\/home\/arduino-snmp-temperature-sensor<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As a quick proof of concept I wanted to try using the Arduino with a Wiznet TCP\/IP Ethernet chip as a SNMP server to monitor an analog input. The eventual use-case being a battery monitor. Turns out there&#8217;s already an SNMP library for the Arduino called Agentuino. It hasn&#8217;t been updated in a couple of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-1830","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1RRoU-tw","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1830","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/comments?post=1830"}],"version-history":[{"count":2,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1830\/revisions"}],"predecessor-version":[{"id":1834,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1830\/revisions\/1834"}],"wp:attachment":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/media?parent=1830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/categories?post=1830"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/tags?post=1830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}