diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000000000000000000000000000000000000..47b4a23bca16c98fc532aaf337ddca22400bcc7f
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+IndentWidth: 4
+ColumnLimit: 100
+Language: Cpp
+AllowShortIfStatementsOnASingleLine: false
diff --git a/README.md b/README.md
index 1a33f6fe75e2ea77bace3586dff234abd55c4a58..dabb980fccbe8b58172136a5534cce5f3bea627f 100644
--- a/README.md
+++ b/README.md
@@ -100,3 +100,11 @@ CI artefacts can be uploaded so:
 
     unzip icbm-0.0.0.zip
     arduino-cli upload -p /dev/ttyUSB1 -b esp32:esp32:esp32wroverkit --input-dir icbm-0.0.0
+
+
+## Contributing
+
+Use `clang-format` to format the project:
+
+    find . -regex '.*\.\(ino\|cpp\|h\)' -exec clang-format -i {} +
+
diff --git a/esp32_ethernet.cpp b/esp32_ethernet.cpp
index ec384a46f6b5950a858b31c90cc5834c82f19f20..15b2e629ce95fa51e54f906b1539d7c681e265c7 100644
--- a/esp32_ethernet.cpp
+++ b/esp32_ethernet.cpp
@@ -7,46 +7,42 @@ We use Ethernet, but reuse much of the networking utilities.
 
 bool eth_connected = false;
 
-
-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 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 initializeNetwork()
-{
-  WiFi.onEvent(EthernetEvent);
-  ETH.begin(ETH_TYPE, ETH_ADDR, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_POWER_PIN, ETH_CLK_MODE);
+void initializeNetwork() {
+    WiFi.onEvent(EthernetEvent);
+    ETH.begin(ETH_TYPE, ETH_ADDR, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_POWER_PIN, ETH_CLK_MODE);
 }
-
diff --git a/icbm.ino b/icbm.ino
index e980bd0e8b1aa67e854f41947c875822a8825d6d..4b1580e5f3de40d1586a04229b7900ef9ee8cf0e 100644
--- a/icbm.ino
+++ b/icbm.ino
@@ -1,8 +1,7 @@
 #include "esp32_ethernet.hpp"
-#include "rest.hpp"
 #include "mpod.hpp"
 #include "pins.hpp"
-
+#include "rest.hpp"
 
 unsigned long start;
 extern bool eth_connected;
@@ -21,7 +20,6 @@ void setup() {
     poll_port_expander();
 }
 
-
 enum {
     NONE,
     ON,
@@ -29,11 +27,10 @@ enum {
 };
 
 void serial_loop() {
-    if (millis() - start  >= 1000) {
+    if (millis() - start >= 1000) {
         start = millis();
         Serial.print(eth_connected ? "." : "-");
     }
-
 }
 
 void loop() {
diff --git a/mpod.cpp b/mpod.cpp
index c7d66b66473687c90d7e82f7f3b69f01153aee1e..aaf667bbfc576eead85f8a17aa61a44b306239cf 100644
--- a/mpod.cpp
+++ b/mpod.cpp
@@ -1,4 +1,5 @@
 #include "mpod.hpp"
+
 #include <SNMP.h>
 #include <WiFiUdp.h>
 
@@ -13,10 +14,10 @@ MPOD::MPOD() {
     float _voltageRiseRate = 0;
 }
 
-
 // Create an SNMP GETREQUEST message
-SNMP::Message* MPOD::read(uint16_t channel) {
-    SNMP::Message* message = new SNMP::Message(SNMP::Version::V2C, "public", SNMP::Type::GetRequest);
+SNMP::Message *MPOD::read(uint16_t channel) {
+    SNMP::Message *message =
+        new SNMP::Message(SNMP::Version::V2C, "public", SNMP::Type::GetRequest);
     // In GETREQUEST, values are always of type NULL.
     String snmp_cmd = OID::NAMES[OID::OUTPUTSTATUS];
     snmp_cmd += channel;
@@ -34,8 +35,8 @@ SNMP::Message* MPOD::read(uint16_t channel) {
 }
 
 // Create an SNMP SETREQUEST message to switch on or off the MPOD
-SNMP::Message* MPOD::output(const uint16_t channel, const bool on) {
-    SNMP::Message* message = new SNMP::Message(SNMP::Version::V2C, "guru", SNMP::Type::SetRequest);
+SNMP::Message *MPOD::output(const uint16_t channel, const bool on) {
+    SNMP::Message *message = new SNMP::Message(SNMP::Version::V2C, "guru", SNMP::Type::SetRequest);
     // In SETREQUEST, use node type and set the value.
     // OUTPUT SWITCH, integer type, 0 is OFF and 1 is ON.
     String snmp_cmd = OID::NAMES[OID::OUTPUTSWITCH];
@@ -58,97 +59,80 @@ bool MPOD::message(const SNMP::Message *message) {
         // 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;
+            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;
         }
 
         // Get the channel ID from the last value in the varbind name
         // No validation is done here as the varbind must be legal to get
         // that far.
-        const char* channel = strrchr(varbind->getName(), '.') + 1;
+        const char *channel = strrchr(varbind->getName(), '.') + 1;
         _channel = atoi(channel);
     }
     // Return true if nodes found, that means this is a valid response from MPOD
     return found;
 }
 
-bool MPOD::isOn() const {
-    return _on;
-}
+bool MPOD::isOn() const { return _on; }
 
-bool MPOD::isUp() const {
-    return _up;
-}
+bool MPOD::isUp() const { return _up; }
 
-bool MPOD::isDown() const {
-    return _down;
-}
+bool MPOD::isDown() const { return _down; }
 
-float MPOD::getMeasurementSenseVoltage() const {
-    return _measurementSenseVoltage;
-}
+float MPOD::getMeasurementSenseVoltage() const { return _measurementSenseVoltage; }
 
-float MPOD::getMeasurementCurrent() const {
-    return _measurementCurrent;
-}
+float MPOD::getMeasurementCurrent() const { return _measurementCurrent; }
 
-float MPOD::getVoltage() const {
-    return _voltage;
-}
+float MPOD::getVoltage() const { return _voltage; }
 
-float MPOD::getCurrent() const {
-    return _current;
-}
+float MPOD::getCurrent() const { return _current; }
 
-float MPOD::getVoltageRiseRate() const {
-    return _voltageRiseRate;
-}
+float MPOD::getVoltageRiseRate() const { return _voltageRiseRate; }
 
-uint16_t MPOD::getChannel() const {
-    return _channel;
-}
+uint16_t MPOD::getChannel() const { return _channel; }
 
 // Use appropriate cast to get integer value
 unsigned int MPOD::getIntegerFromVarBind(const VarBind *varbind) {
-    return static_cast<IntegerBER*>(varbind->getValue())->getValue();
+    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();
+    return static_cast<OpaqueFloatBER *>(static_cast<OpaqueBER *>(varbind->getValue())->getBER())
+        ->getValue();
 }
 
 String MPOD::toJSON() {
@@ -167,7 +151,6 @@ String MPOD::toJSON() {
     return json;
 }
 
-
 WiFiUDP udp;
 SNMP::Manager snmp;
 MPOD mpod;
diff --git a/pins.cpp b/pins.cpp
index 3f0837a19dc7609c97bd0aa7079d3d59b3654794..c64146c4b3562c038a48d1fa582a581abb837675 100644
--- a/pins.cpp
+++ b/pins.cpp
@@ -4,102 +4,91 @@ MCP23S08 MCP(SS, MISO, MOSI, SCK);
 volatile byte ISR_FLAG = 0;
 struct pins PINS;
 
-void IRAM_ATTR isr(){
-  ISR_FLAG = 1;
-}
-
-void initializeMCP()
-{
-  pinMode(ISR_PIN, INPUT_PULLUP);
-  attachInterrupt(digitalPinToInterrupt(ISR_PIN), isr, CHANGE);
+void IRAM_ATTR isr() { ISR_FLAG = 1; }
 
-  SPI.begin();
-  MCP.begin();
+void initializeMCP() {
+    pinMode(ISR_PIN, INPUT_PULLUP);
+    attachInterrupt(digitalPinToInterrupt(ISR_PIN), isr, CHANGE);
 
-  // Enable inputs and interrupts on pins 0-2
-  MCP.pinMode8(0b00000111);
-  MCP.enableInterrupt(0, CHANGE);
-  MCP.enableInterrupt(1, CHANGE);
-  MCP.enableInterrupt(2, CHANGE);
-  Serial.println("MCP23S08 Started");
+    SPI.begin();
+    MCP.begin();
 
+    // Enable inputs and interrupts on pins 0-2
+    MCP.pinMode8(0b00000111);
+    MCP.enableInterrupt(0, CHANGE);
+    MCP.enableInterrupt(1, CHANGE);
+    MCP.enableInterrupt(2, CHANGE);
+    Serial.println("MCP23S08 Started");
 }
 
-
-void isr_check_loop()
-{
-  if (ISR_FLAG == 1){
-    PINS.sib = false;
-    PINS.plc = false;
-    PINS.ups = false;
-    PINS.last_triggered = millis();
-
-    Serial.print("Interrupt: ");
-    uint8_t regval = MCP.getInterruptCaptureRegister();  //INTCAP
-    Serial.print(regval, BIN);
-
-    uint8_t mask = 1 << 0;
-    if (!(regval & mask)) {
-      Serial.print(" UPS");
-      PINS.ups = true;
-    }
-
-    mask = 1 << 1;
-    if (!(regval & mask)) {
-      Serial.print(" PLC");
-      PINS.plc = true;
+void isr_check_loop() {
+    if (ISR_FLAG == 1) {
+        PINS.sib = false;
+        PINS.plc = false;
+        PINS.ups = false;
+        PINS.last_triggered = millis();
+
+        Serial.print("Interrupt: ");
+        uint8_t regval = MCP.getInterruptCaptureRegister();  // INTCAP
+        Serial.print(regval, BIN);
+
+        uint8_t mask = 1 << 0;
+        if (!(regval & mask)) {
+            Serial.print(" UPS");
+            PINS.ups = true;
+        }
+
+        mask = 1 << 1;
+        if (!(regval & mask)) {
+            Serial.print(" PLC");
+            PINS.plc = true;
+        }
+
+        mask = 1 << 2;
+        if (!(regval & mask)) {
+            Serial.print(" SIB");
+            PINS.sib = true;
+        }
+
+        Serial.println();
+        ISR_FLAG = 0;
     }
-
-    mask = 1 << 2;
-    if (!(regval & mask)) {
-      Serial.print(" SIB");
-      PINS.sib = true;
-    }
-
-    Serial.println();
-    ISR_FLAG = 0;
-  }
 }
 
-
-void poll_port_expander()
-{
+void poll_port_expander() {
     PINS.sib = false;
     PINS.plc = false;
     PINS.ups = false;
     PINS.last_triggered = millis();
     int regval = MCP.read8();
-    
+
     uint8_t mask = 1 << 0;
     if (!(regval & mask)) {
-      Serial.print(" UPS");
-      PINS.ups = true;
+        Serial.print(" UPS");
+        PINS.ups = true;
     }
 
     mask = 1 << 1;
     if (!(regval & mask)) {
-      Serial.print(" PLC");
-      PINS.plc = true;
+        Serial.print(" PLC");
+        PINS.plc = true;
     }
 
     mask = 1 << 2;
     if (!(regval & mask)) {
-      Serial.print(" SIB");
-      PINS.sib = true;
+        Serial.print(" SIB");
+        PINS.sib = true;
     }
 
     Serial.println();
-    
-
 }
 
-
 bool on = true;
 unsigned long toggle_start = 0;
 void toggle_status_led() {
-    if (millis() - toggle_start  >= 1000) {
+    if (millis() - toggle_start >= 1000) {
         toggle_start = millis();
-    	on = !on;
+        on = !on;
         MCP.write1(STATUS_LED_PIN, on);
-     }
+    }
 }
diff --git a/rest.cpp b/rest.cpp
index b3c5247b97d321c10d48d4a0e4f64d95ddcf4dd3..9d5504053f1c0a5249c09b7441b08291163c2af3 100644
--- a/rest.cpp
+++ b/rest.cpp
@@ -1,8 +1,10 @@
 #include "rest.hpp"
+
+#include <ETH.h>
+
 #include "mpod.hpp"
 #include "panel.hpp"
 #include "pins.hpp"
-#include <ETH.h>
 
 #ifndef ICBM_GIT_VERSION
 #define ICBM_GIT_VERSION "DEVEL"
@@ -26,7 +28,6 @@ void initializeRoutes() {
     Serial.println("REST Server Started");
 }
 
-
 void identify() {
     int seconds = millis() / 1000;
     int hours = seconds / 3600;
@@ -86,12 +87,7 @@ void identify() {
 
 void panel() {
     unsigned long elapsed_seconds = (millis() - PINS.last_triggered) / 1000;
-    String content = buildPanel(
-        PINS.sib,
-        PINS.plc,
-        PINS.ups,
-        elapsed_seconds
-    );
+    String content = buildPanel(PINS.sib, PINS.plc, PINS.ups, elapsed_seconds);
     restServer.send(200, "text/html", content);
 }
 
@@ -106,101 +102,100 @@ void notFound() {
     for (uint8_t i = 0; i < restServer.args(); i++) {
         message += "\n\"" + restServer.argName(i) + "\":\"" + restServer.arg(i) + "\",";
     }
-	if (restServer.args() != 0) {
-		message.remove(message.length()-1);  // remove trailing comma for valid json
-	}
+    if (restServer.args() != 0) {
+        message.remove(message.length() - 1);  // remove trailing comma for valid json
+    }
 
-	message += "\n}\n}";
+    message += "\n}\n}";
     restServer.send(404, "text/json", message);
-	Serial.println(message);
+    Serial.println(message);
 }
 
 void restart() {
-	restServer.send(200);
-	ESP.restart();
+    restServer.send(200);
+    ESP.restart();
 }
 
-
 enum {
-	OFF,
-	ON,
-	NONE,
+    OFF,
+    ON,
+    NONE,
 };
 
 void sendSNMP() {
-	uint8_t output = NONE;
+    uint8_t output = NONE;
     uint16_t channel = 0;
-	bool success = false;
+    bool success = false;
 
     for (uint8_t i = 0; i < restServer.args(); i++) {
-		if(restServer.argName(i) == "output") {
-			if(restServer.arg(i) == "on") {
-				output = ON;
-			} else if (restServer.arg(i) == "off") {
-				output = OFF;
-			}
-		} else if(restServer.argName(i) == "ch") {
+        if (restServer.argName(i) == "output") {
+            if (restServer.arg(i) == "on") {
+                output = ON;
+            } else if (restServer.arg(i) == "off") {
+                output = OFF;
+            }
+        } else if (restServer.argName(i) == "ch") {
             channel = restServer.arg(i).toInt();
         }
-	}
+    }
 
-	auto ipAddr = IPAddress(192,168,140,79);
+    auto ipAddr = IPAddress(192, 168, 140, 79);
     if (output != NONE && channel != 0) {
-		SNMP::Message *snmp_msg = mpod.output(channel, output);
-		snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP);
-		delete snmp_msg;
-		success = true;
-	}
+        SNMP::Message *snmp_msg = mpod.output(channel, output);
+        snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP);
+        delete snmp_msg;
+        success = true;
+    }
 
     if (success) {
-		SNMP::Message *snmp_msg = mpod.read(channel);
-		snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP);
-		delete snmp_msg;
+        SNMP::Message *snmp_msg = mpod.read(channel);
+        snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP);
+        delete snmp_msg;
     }
 
-	String http_msg = "{\n";
-	http_msg += "\"target\":\"" + ipAddr.toString() + "\",\n";
-	http_msg += "\"arguments\":{";
+    String http_msg = "{\n";
+    http_msg += "\"target\":\"" + ipAddr.toString() + "\",\n";
+    http_msg += "\"arguments\":{";
     http_msg += "\"channel\":" + String(channel) + ",";
     http_msg += "\"output\":" + String(output) + "},\n";
-	http_msg += "\"success\":" + String(success); 
+    http_msg += "\"success\":" + String(success);
 
     if (success) {
         http_msg += ",\n";
         http_msg += mpod.toJSON();
     }
 
-	http_msg += "\n}";
+    http_msg += "\n}";
 
-	restServer.send(200, "text/json", http_msg);
+    restServer.send(200, "text/json", http_msg);
 }
 
 void pollMPODChannel() {
     uint16_t channel = 0;
-    for(uint8_t i = 0; i < restServer.args(); i++) {
-        if(restServer.argName(i) == "ch") {
+    for (uint8_t i = 0; i < restServer.args(); i++) {
+        if (restServer.argName(i) == "ch") {
             channel = restServer.arg(i).toInt();
         }
     }
 
     String ret;
-    if(!channel) {
+    if (!channel) {
         ret = "\"reason\": \"Invalid channel\"";
     } else {
-	    auto ipAddr = IPAddress(192,168,140,79);
+        auto ipAddr = IPAddress(192, 168, 140, 79);
         SNMP::Message *snmp_msg = mpod.read(channel);
-		snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP);
-		delete snmp_msg;
-	    ret = mpod.toJSON();  // TODO: This has stale info at this stage!
+        snmp.send(snmp_msg, ipAddr, SNMP::Port::SNMP);
+        delete snmp_msg;
+        ret = mpod.toJSON();  // TODO: This has stale info at this stage!
     }
 
-	String http_msg = "{\n";
-	http_msg += ret;
-	http_msg += ",\"success\": ";
+    String http_msg = "{\n";
+    http_msg += ret;
+    http_msg += ",\"success\": ";
     http_msg += channel ? 1 : 0;
     http_msg += ",\"channel\": ";
     http_msg += channel;
-	http_msg += "\n}";
+    http_msg += "\n}";
 
-	restServer.send(200, "text/json", http_msg);
+    restServer.send(200, "text/json", http_msg);
 }