Newer
Older
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
SNMP::Manager snmp;
// Use some SNMP classes
using SNMP::IntegerBER;
using SNMP::OctetStringBER;
using SNMP::OpaqueBER;
using SNMP::OpaqueFloatBER;
using SNMP::VarBind;
using SNMP::VarBindList;
// This class encapsulates the MPOD SNMP interface
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
SNMP::Message* 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* 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* 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 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;
}
// Getters
bool isOn() const {
return _on;
}
bool isUp() const {
return _up;
}
bool isDown() const {
return _down;
}
float getMeasurementSenseVoltage() const {
return _measurementSenseVoltage;
}
float getMeasurementCurrent() const {
return _measurementCurrent;
}
float getVoltage() const {
return _voltage;
}
float getCurrent() const {
return _current;
}
float getVoltageRiseRate() const {
return _voltageRiseRate;
}
private:
// Use appropriate cast to get integer value
unsigned int getIntegerFromVarBind(const VarBind *varbind) {
return static_cast<IntegerBER*>(varbind->getValue())->getValue();
}
// Use appropriate casts to get embedded opaque float value
float getFloatFromVarBind(const VarBind *varbind) {
return static_cast<OpaqueFloatBER*>(static_cast<OpaqueBER*>(varbind->getValue())->getBER())->getValue();
}
bool _on = false;
bool _up = false;
bool _down = false;
float _measurementSenseVoltage = 0;
float _measurementCurrent = 0;
float _voltage = 0;
float _current = 0;
float _voltageRiseRate = 0;
};
MPOD mpod;
// Event handler to process SNMP messages
void onMessage(const SNMP::Message *message, const IPAddress remote, const uint16_t port) {
// Check if message is from MPOD
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");
}
}
unsigned long start;
void setup() {
Serial.begin(115200);
connectNetwork();
// SNMP
snmp.begin(&udp);
snmp.onMessage(onMessage);
// Start
while (!eth_connected) {
Serial.print("-");
delay(100);
}
SNMP::Message *message = mpod.setup();
snmp.send(message, IPAddress(10, 42, 0, 1), SNMP::PORT::SNMP);
delete message;
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
}
enum {
NONE,
ON,
OFF,
};
void loop() {
// Manager loop function must be called to process incoming messages
snmp.loop();
// Serial
if (Serial.available()) {
uint8_t output = NONE;
// Read command from serial
String string = Serial.readString();
string.toLowerCase();
// Only two commands
if (string == "on") {
output = ON;
} else if (string == "off") {
output = OFF;
}
Serial.print(string);
if (output != NONE) {
// If ON or OFF, send SETREQUEST to MPOD
SNMP::Message *message = mpod.output(output == ON);
Serial.print("Sending message ");
snmp.send(message, IPAddress(10, 42, 0, 1), SNMP::PORT::SNMP);
Serial.println("Sent");
delete message;
}
}
// Send a request every second
if (millis() - start >= 1000) {
start = millis();
// Create message to query MPOD and send it
SNMP::Message* message = mpod.read();
snmp.send(message, IPAddress(10, 42, 0, 1), SNMP::PORT::SNMP);