Skip to content
Snippets Groups Projects
Commit 301d2cac authored by Cyril Danilevski's avatar Cyril Danilevski
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
*.swp
# IMC Arduino Firmware
ESP32 firmware for the IMC, based on Arduino and the `ESP32-Ethernet-Kit_A_V1.2`.
This project depends on [`Arduino_SNMP`](https://github.com/patricklaf/SNMP).
### Installation
Either use the Arduino IDE and use its library manager to find the SNMP library, or use `arduino-cli`:
```bash
# Download arduino-cli
wget https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Linux_64bit.tar.gz
# Extract the downloaded archive
tar -xzf arduino-cli_latest_Linux_64bit.tar.gz
# Verify installation
arduino-cli config init
```
Then set up the required environment. Edit `~/.arduino15/arduino-cli.yaml` and add the following:
```bash
board_manager:
additional_urls:
- https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
- http://arduino.esp8266.com/stable/package_esp8266com_index.json
```
Update the arduino index with:
```bash
arduino-cli core update-index
```
The required libraries are:
arduino-cli lib install ethernet
arduino-cli lib install SNMP
The project can be compiled so:
arduino-cli compile --fqbn esp32:esp32:esp32wroverkit icbm.ino -v
And uploaded so:
arduino-cli upload -p /dev/ttyUSB1 --fqbn esp32:esp32:esp32wroverkit .
/*
Arduinos are more often used with wifi, and much of the utilities are written with Wifi in mind.
We use Ethernet, but reuse much of the networking utilities.
*/
#include "esp32_ethernet.hpp"
void EthernetEvent(WiFiEvent_t event)
{
switch (event) {
case ARDUINO_EVENT_ETH_START:
Serial.println("ETH Started");
ETH.setHostname("esp32-ethernet");
break;
case ARDUINO_EVENT_ETH_CONNECTED:
Serial.println("ETH Connected");
break;
case ARDUINO_EVENT_ETH_GOT_IP:
Serial.print("ETH MAC: ");
Serial.print(ETH.macAddress());
Serial.print(", IPv4: ");
Serial.print(ETH.localIP());
if (ETH.fullDuplex()) {
Serial.print(", FULL_DUPLEX");
}
Serial.print(", ");
Serial.print(ETH.linkSpeed());
Serial.println("Mbps");
eth_connected = true;
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("ETH Disconnected");
eth_connected = false;
break;
case ARDUINO_EVENT_ETH_STOP:
Serial.println("ETH Stopped");
eth_connected = false;
break;
default:
break;
}
}
void connectNetwork()
{
WiFi.onEvent(EthernetEvent);
ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK_MODE);
}
/*
Arduinos are more often used with wifi, and much of the utilities are written with Wifi in mind.
We use Ethernet, but reuse much of the networking utilities.
*/
#ifndef esp32_ethernet_h
#define esp32_ethernet_h
#include "Arduino.h"
#include "ETH.h"
#define ETH_ADDR 1
#define ETH_POWER_PIN 5
#define ETH_MDC_PIN 23
#define ETH_MDIO_PIN 18
#define ETH_TYPE ETH_PHY_IP101
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
static bool eth_connected = false;
void EthernetEvent(WiFiEvent_t event);
void connectNetwork();
#endif
icbm.ino 0 → 100644
#include <EthernetUdp.h>
#include <SNMP.h>
#include "esp32_ethernet.hpp"
EthernetUDP udp;
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
start = millis();
// MPOD
//SNMP::Message *message = mpod.setup();
//snmp.send(message, IPAddress(10, 42, 0, 3), SNMP::PORT::SNMP);
//delete message;
}
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, 3), 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, 3), SNMP::PORT::SNMP);
delete message;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment