Skip to content
Snippets Groups Projects
pins.cpp 4.18 KiB
Newer Older
#include "pins.hpp"

MCP23S08 MCP(SS, MISO, MOSI, SCK);

// 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() { MCP_ISR_FLAG = 1; }
Cyril Danilevski's avatar
Cyril Danilevski committed
void initializeMCP() {
    pinMode(ISR_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(ISR_PIN), isr, CHANGE);
Cyril Danilevski's avatar
Cyril Danilevski committed
    SPI.begin();
    MCP.begin();
Cyril Danilevski's avatar
Cyril Danilevski committed
    // Enable inputs and interrupts on pins 0-2
    MCP.pinMode8(0b00000111);
    MCP.enableInterrupt(0, CHANGE);
    MCP.enableInterrupt(1, CHANGE);
    MCP.enableInterrupt(2, CHANGE);

    // Inform SIB we're ready
    MCP.write1(ALERT_SIB_PIN, HIGH);

Cyril Danilevski's avatar
Cyril Danilevski committed
    Serial.println("MCP23S08 Started");
Cyril Danilevski's avatar
Cyril Danilevski committed
void isr_check_loop() {
    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;
        PINS.canDoPowerFromREST = true;
Cyril Danilevski's avatar
Cyril Danilevski committed
void poll_port_expander() {
    PINS.sib = false;
    PINS.plc = false;
    PINS.ups = false;
    int regval = MCP.read8();
Cyril Danilevski's avatar
Cyril Danilevski committed

    uint8_t mask = 1 << 0;
    if (!(regval & mask)) {
Cyril Danilevski's avatar
Cyril Danilevski committed
        Serial.print(" UPS");
        PINS.ups = true;
    }

    mask = 1 << 1;
    if (!(regval & mask)) {
Cyril Danilevski's avatar
Cyril Danilevski committed
        Serial.print(" PLC");
        PINS.plc = true;
    }

    mask = 1 << 2;
    if (!(regval & mask)) {
Cyril Danilevski's avatar
Cyril Danilevski committed
        Serial.print(" SIB");
        PINS.sib = true;
bool on = true;
unsigned long toggle_start = 0;
void toggle_status_led() {
Cyril Danilevski's avatar
Cyril Danilevski committed
    if (millis() - toggle_start >= 1000) {
        toggle_start = millis();
Cyril Danilevski's avatar
Cyril Danilevski committed
        on = !on;
        MCP.write1(STATUS_LED_PIN, on);
Cyril Danilevski's avatar
Cyril Danilevski committed
    }

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);
}