From c8cde2a06236774e1cace4ca17f9d9bca9b45d68 Mon Sep 17 00:00:00 2001
From: Cyril Danilevski <cyril.danilevski@xfel.eu>
Date: Tue, 25 Feb 2025 16:56:05 +0100
Subject: [PATCH] Perform power down sequence from port expander interrupt

---
 icbm.ino      |  13 +++--
 pins.cpp      | 138 ++++++++++++++++++++++++++++++++++++++------------
 pins.hpp      |   6 ++-
 powerproc.hpp |   2 +
 4 files changed, 121 insertions(+), 38 deletions(-)

diff --git a/icbm.ino b/icbm.ino
index c43b3cf..cb83fd8 100644
--- a/icbm.ino
+++ b/icbm.ino
@@ -15,11 +15,13 @@ void setup() {
     initializeRoutes();
     initializeSNMP();
 
-    start = millis();
-
+    // Poll port expander to initialize the PINS struct.
     poll_port_expander();
+    PINS.last_triggered = millis();
     PINS.stage = "";
     PINS.ramping = false;
+
+    start = millis();
 }
 
 enum {
@@ -28,7 +30,7 @@ enum {
     OFF,
 };
 
-void serial_loop() {
+void serialLoop() {
     if (millis() - start >= 1000) {
         start = millis();
         Serial.print(eth_connected ? "." : "-");
@@ -40,5 +42,8 @@ void loop() {
     snmp.loop();
     restServer.handleClient();
     toggle_status_led();
-    serial_loop();
+    serialLoop();
+    if (eth_connected) {  // We may be triggered, but disconnected from the network.
+        powerOffCheckLoop();
+    }
 }
diff --git a/pins.cpp b/pins.cpp
index c64146c..03a94cc 100644
--- a/pins.cpp
+++ b/pins.cpp
@@ -1,10 +1,16 @@
 #include "pins.hpp"
 
+#include "powerproc.hpp"
+
 MCP23S08 MCP(SS, MISO, MOSI, SCK);
-volatile byte ISR_FLAG = 0;
+
+// Semaphores for handling interrupts and power down events.
+volatile byte MCP_ISR_FLAG = 0;
+volatile byte PERFORM_PROCEDURE_FROM_INTERRUPT = 0;
+
 struct pins PINS;
 
-void IRAM_ATTR isr() { ISR_FLAG = 1; }
+void IRAM_ATTR isr() { MCP_ISR_FLAG = 1; }
 
 void initializeMCP() {
     pinMode(ISR_PIN, INPUT_PULLUP);
@@ -18,40 +24,54 @@ void initializeMCP() {
     MCP.enableInterrupt(0, CHANGE);
     MCP.enableInterrupt(1, CHANGE);
     MCP.enableInterrupt(2, CHANGE);
+
+    // Inform SIB we're ready
+    MCP.write1(ALERT_SIB_PIN, HIGH);
+
     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;
-        }
-
-        mask = 1 << 2;
-        if (!(regval & mask)) {
-            Serial.print(" SIB");
-            PINS.sib = true;
-        }
-
-        Serial.println();
-        ISR_FLAG = 0;
+    if (MCP_ISR_FLAG == 0) {
+        return;
+    }
+    // False means triggered (not ok).
+    PINS.sib = false;
+    PINS.plc = false;
+    PINS.ups = false;
+    PINS.last_triggered = millis();
+
+    // Get the pins status at the time of the interrupt, and check their statuses.
+    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();
+
+    // This interrupt was handled
+    MCP_ISR_FLAG = 0;
+
+    // If not all ok, set the power down flag, later handled.
+    if (!(PINS.sib && PINS.plc && PINS.ups)) {
+        PERFORM_PROCEDURE_FROM_INTERRUPT = 1;
     }
 }
 
@@ -59,7 +79,6 @@ 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;
@@ -92,3 +111,56 @@ void toggle_status_led() {
         MCP.write1(STATUS_LED_PIN, on);
     }
 }
+
+void powerOffCheckLoop() {
+    if (!PERFORM_PROCEDURE_FROM_INTERRUPT) {
+        return;
+    }
+    // Disable interrupts while performing power procedure.
+    // Once we've been triggered, we perform the power down procedure, uninterrupted,
+    // regardless of changes in the environment.
+    detachInterrupt(digitalPinToInterrupt(ISR_PIN));
+
+    // Update status on serial interface and status webpage.
+    PINS.ramping = true;
+    Serial.println("POWERING DOWN DUE TO TRIGGER.");
+
+    // Inform SIB that we're about to perform a procedure.
+    MCP.write1(ALERT_SIB_PIN, LOW);
+    Serial.println("asked sib down");
+
+    // Wait for SIB to acknowledge by checking its input (should go to triggered).
+    /* This require hardware changes, not implemented yet.
+    // TODO: apply a timeout and go ahead anyway?
+    do {
+        delay(200);
+        poll_port_expander();
+    } while(!(PINS.sib));
+    Serial.println("SIB ACK");
+     */
+
+    // Iterate through each group and power down.
+    String group;
+    String groups;
+    for (int8_t groupIdx = pproc.stagesCount - 1; groupIdx >= 0; groupIdx--) {
+        group = pproc.stages[groupIdx].name;
+        groups += group;
+        groups += ",";
+        PINS.stage = group;
+        pproc.powerOff(group);
+        delay(1000);
+    }
+
+    // Clear statuses on webpage and serial interfaces.
+    PINS.ramping = false;
+    Serial.print("Finished powering down from interrupt: ");
+    Serial.println(groups);
+    PINS.stage = "Ramped down";
+
+    // This event has been handled.
+    PERFORM_PROCEDURE_FROM_INTERRUPT = 0;
+    // Inform SIB that we're done and it can take data again.
+    MCP.write1(ALERT_SIB_PIN, HIGH);
+    // Re-enable interrupts before exiting.
+    attachInterrupt(digitalPinToInterrupt(ISR_PIN), isr, CHANGE);
+}
diff --git a/pins.hpp b/pins.hpp
index 0cd10be..e1efb05 100644
--- a/pins.hpp
+++ b/pins.hpp
@@ -13,9 +13,12 @@
 //AlCon Logic Board interrupt
 #define TRIGGER_SIGNAL 14
 
-// LED pin on MCP23S08 extender
+// LED pin on MCP23S08 expander
 #define STATUS_LED_PIN 6
 
+// Pin to inform SIB that a power down is about to happen, on MCP23S08 expander
+#define ALERT_SIB_PIN 7
+
 struct pins {
    bool sib;
    bool plc;
@@ -32,3 +35,4 @@ void initializeMCP();
 void isr_check_loop();
 void poll_port_expander();
 void toggle_status_led();
+void powerOffCheckLoop();
diff --git a/powerproc.hpp b/powerproc.hpp
index 979df46..8e91b88 100644
--- a/powerproc.hpp
+++ b/powerproc.hpp
@@ -34,4 +34,6 @@ public:
     String toJSON();
 };
 
+void powerOffCheckLoop();
+
 extern PowerProcedure pproc;
-- 
GitLab