diff --git a/mpod.cpp b/mpod.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f65d2241e8f05c8cfe28e37432e31daa0d6a0a8 --- /dev/null +++ b/mpod.cpp @@ -0,0 +1,186 @@ +#include "mpod.hpp" +#include <SNMP.h> +#include <WiFiUdp.h> + +MPOD::MPOD() { + bool _on = false; + bool _up = false; + bool _down = false; + float _measurementSenseVoltage = 0; + float _measurementCurrent = 0; + float _voltage = 0; + float _current = 0; + float _voltageRiseRate = 0; +} + + +// Create an SNMP SETREQUEST message to setup MPOD +SNMP::Message* MPOD::setup() { + // Use read/write community, not read-only + SNMP::Message* message = new SNMP::Message(SNMP::VERSION2C, "guru", SNMP::TYPE_SETREQUEST); + // In SETREQUEST, use node type and set the value. + // OUTPUT SWITCH, integer type, 0 is OFF and 1 is ON. + message->add(OID::NAMES[OID::OUTPUTSWITCH], new IntegerBER(0)); + // OUTPUT VOLTAGE is an opaque float embedded in an opaque node + message->add(OID::NAMES[OID::OUTPUTVOLTAGE], new OpaqueBER(new OpaqueFloatBER(10.0))); + // OUTPUT CURRENT is an opaque float embedded in an opaque node + message->add(OID::NAMES[OID::OUTPUTCURRENT], new OpaqueBER(new OpaqueFloatBER(0.001))); + // OUTPUT VOLTAGE RISE RATE is negative. Don't ask me why... + message->add(OID::NAMES[OID::OUTPUTVOLTAGERISERATE], new OpaqueBER(new OpaqueFloatBER(-1.0))); + return message; +} + + // Create an SNMP GETREQUEST message +SNMP::Message* MPOD::read() { + SNMP::Message* message = new SNMP::Message(SNMP::VERSION2C, "public", SNMP::TYPE_GETREQUEST); + // In GETREQUEST, values are always of type NULL. + message->add(OID::NAMES[OID::OUTPUTSTATUS]); + message->add(OID::NAMES[OID::OUTPUTMEASUREMENTSENSEVOLTAGE]); + message->add(OID::NAMES[OID::OUTPUTMEASUREMENTCURRENT]); + return message; +} + + // Create an SNMP SETREQUEST message to switch on or off the MPOD +SNMP::Message* MPOD::output(const bool on) { + SNMP::Message* message = new SNMP::Message(SNMP::VERSION2C, "guru", SNMP::TYPE_SETREQUEST); + // In SETREQUEST, use node type and set the value. + // OUTPUT SWITCH, integer type, 0 is OFF and 1 is ON. + message->add(OID::NAMES[OID::OUTPUTSWITCH], new IntegerBER(on ? 1 : 0)); + return message; +} + + // Parse incoming message +bool MPOD::message(const SNMP::Message *message) { + unsigned int found = 0; + unsigned int index = 0; + // Get the variable binding list from the message. + VarBindList *varbindlist = message->getVarBindList(); + for (unsigned int index = 0; index < varbindlist->count(); ++index) { + // Each variable binding is a sequence of 2 objects: + // - First one is and ObjectIdentifierBER. It holds the OID + // - Second is the value of any type + VarBind *varbind = (*varbindlist)[index]; + // There is a convenient function to get the OID as a const char* + const char *name = varbind->getName(); + switch (OID::match(name)) { + case OID::OUTPUTSTATUS: { + // OUTPUTSTATUS is defined in MIB as BITS but encoded as OCTETSTRING by MPOD + OctetStringBER *status = static_cast<OctetStringBER*>(varbind->getValue()); + _on = status->getBit(0); + _up = status->getBit(11); + _down = status->getBit(12); + } + found++; + break; + case OID::OUTPUTMEASUREMENTSENSEVOLTAGE: + // Use private helper function to extract float value + _measurementSenseVoltage = getFloatFromVarBind(varbind); + found++; + break; + case OID::OUTPUTMEASUREMENTCURRENT: + _measurementCurrent = getFloatFromVarBind(varbind); + found++; + break; + case OID::OUTPUTSWITCH: + // Use private helper function to extract integer value + _on = getIntegerFromVarBind(varbind); + found++; + break; + case OID::OUTPUTVOLTAGE: + _voltage = getFloatFromVarBind(varbind); + found++; + break; + case OID::OUTPUTCURRENT: + _current = getFloatFromVarBind(varbind); + found++; + break; + case OID::OUTPUTVOLTAGERISERATE: + _voltageRiseRate = getFloatFromVarBind(varbind); + found++; + break; + } + } + // Return true if nodes found, that means this is a valid response from MPOD + return found; +} + +bool MPOD::isOn() const { + return _on; +} + +bool MPOD::isUp() const { + return _up; +} + +bool MPOD::isDown() const { + return _down; +} + +float MPOD::getMeasurementSenseVoltage() const { + return _measurementSenseVoltage; +} + +float MPOD::getMeasurementCurrent() const { + return _measurementCurrent; +} + +float MPOD::getVoltage() const { + return _voltage; +} + +float MPOD::getCurrent() const { + return _current; +} + +float MPOD::getVoltageRiseRate() const { + return _voltageRiseRate; +} + +// Use appropriate cast to get integer value +unsigned int MPOD::getIntegerFromVarBind(const VarBind *varbind) { + return static_cast<IntegerBER*>(varbind->getValue())->getValue(); +} + +// Use appropriate casts to get embedded opaque float value +float MPOD::getFloatFromVarBind(const VarBind *varbind) { + return static_cast<OpaqueFloatBER*>(static_cast<OpaqueBER*>(varbind->getValue())->getBER())->getValue(); +} + + +WiFiUDP udp; +SNMP::Manager snmp; +MPOD mpod; + +// Event handler to process SNMP messages +void onMessage(const SNMP::Message *message, const IPAddress remote, const uint16_t port) { + if (mpod.message(message)) { + Serial.println(); + Serial.print("MPOD status"); + Serial.print(mpod.isOn() ? " on" : " off"); + if (mpod.isUp()) { + Serial.print(" up"); + } + if (mpod.isDown()) { + Serial.print(" down"); + } + Serial.println(); + Serial.print("HV voltage "); + Serial.print(mpod.getMeasurementSenseVoltage()); + Serial.print(" V ("); + Serial.print(mpod.getVoltage()); + Serial.print(") current "); + Serial.print(mpod.getMeasurementCurrent()); + Serial.print(" A ("); + Serial.print(mpod.getCurrent()); + Serial.print(") rise rate "); + Serial.print(mpod.getVoltageRiseRate()); + Serial.println(" V/s"); + } else { + Serial.println("Received non-MPOD traffic"); + } +} + +void initializeSNMP() { + snmp.begin(&udp); + snmp.onMessage(onMessage); +} diff --git a/mpod.hpp b/mpod.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bc51a80a932c02c2fe7b11374f9fca608569b5cb --- /dev/null +++ b/mpod.hpp @@ -0,0 +1,85 @@ +#pragma once +#include <SNMP.h> + +using SNMP::IntegerBER; +using SNMP::OctetStringBER; +using SNMP::OpaqueBER; +using SNMP::OpaqueFloatBER; +using SNMP::VarBind; +using SNMP::VarBindList; + + +class MPOD { + public: + // Simple helper class to handle OIDs + class OID { + public: + enum { + OUTPUTSTATUS, + OUTPUTMEASUREMENTSENSEVOLTAGE, + OUTPUTMEASUREMENTCURRENT, + OUTPUTSWITCH, + OUTPUTVOLTAGE, + OUTPUTCURRENT, + OUTPUTVOLTAGERISERATE, + UNKNOWN, + COUNT = UNKNOWN, + }; + + static inline const char *NAMES[] = { + "1.3.6.1.4.1.19947.1.3.2.1.4.1", + "1.3.6.1.4.1.19947.1.3.2.1.5.1", + "1.3.6.1.4.1.19947.1.3.2.1.7.1", + "1.3.6.1.4.1.19947.1.3.2.1.9.1", + "1.3.6.1.4.1.19947.1.3.2.1.10.1", + "1.3.6.1.4.1.19947.1.3.2.1.12.1", + "1.3.6.1.4.1.19947.1.3.2.1.13.1", + }; + + // Returns index of OID equals to name + // Returns UNKNOWN if none + static unsigned int match(const char *name) { + for (unsigned int index = 0; index < COUNT; ++index) { + if (strcmp(NAMES[index], name) == 0) { + return index; + } + } + return UNKNOWN; + } + }; + + // Create an SNMP SETREQUEST message to setup MPOD + MPOD(); + SNMP::Message* setup(); + SNMP::Message* read(); + SNMP::Message* output(const bool); + bool message(const SNMP::Message*); + bool isOn() const; + bool isUp() const; + bool isDown() const; + float getMeasurementSenseVoltage() const; + float getMeasurementCurrent() const; + float getVoltage() const; + float getCurrent() const; + float getVoltageRiseRate() const; + + private: + unsigned int getIntegerFromVarBind(const VarBind*); + float getFloatFromVarBind(const VarBind*); + bool _on; + bool _up; + bool _down; + float _measurementSenseVoltage; + float _measurementCurrent; + float _voltage; + float _current; + float _voltageRiseRate; +}; + +extern SNMP::Manager snmp; +extern MPOD mpod; + +// Event handler to process SNMP messages +void onSNMPMessage(const SNMP::Message *message, const IPAddress remote, const uint16_t port); +void initializeSNMP(); +