// Interfacing MCP23017 E/SP with arrays in Arduino IDE
// https://forum.arduino.cc/t/interfacing-mcp23017-e-sp-with-arrays-in-arduino-ide/1309545


#include <MCP23017.h>

MCP23017 mcp[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27};
#define FISRT_MCP_PIN 100

// Cable Tester by "jurs" for Arduino forum
enum {PASS, FAIL_NOTCONNECTED, FAIL_WRONGCONNECTED, FAIL_SHORTENED };

// pin numbers for use at begin and end of cable
const byte pinCableBegin[] = { 2, 3, 4, 5, 100 };
const byte pinsCableEnd[]  = {11, 12, 13, A0, 101 };
const byte NUMCABLES = sizeof(pinCableBegin);

void setup() {
  Serial.begin(115200);
  if (sizeof(pinCableBegin) != sizeof(pinsCableEnd))
  {
    Serial.println("Wrong cable pin configuration!");
    Serial.println("Fix declaration of pinCableBegin[] and pinsCableEnd[] arrays!");
    while (1); // error stop with endless loop
  }

  uint8_t MCP23017_count = sizeof(mcp) / sizeof(* mcp);
  // Serial.print(MCP23017_count);
  // Serial.println(" MCP23017 declared");
  for (size_t mcpIndex = 0; mcpIndex < MCP23017_count; mcpIndex++)
  {
    mcp[mcpIndex].init();
  }

  Serial.println();
  Serial.println("################################################");
  Serial.println("#                CABLE TESTER                  #");
  Serial.println("################################################");
  Serial.println();
}

void myPinMode(uint8_t pinNumber, uint8_t pinModeVal)
{
  if (pinNumber < FISRT_MCP_PIN)
  {
    pinMode(pinNumber, pinModeVal);
  }
  else
  {
    mcp[(pinNumber - FISRT_MCP_PIN) / 16].pinMode((pinNumber - FISRT_MCP_PIN) % 16, pinModeVal, false);
  }
}

void myDigitalWrite(uint8_t pinNumber, uint8_t pinState)
{
  if (pinNumber < FISRT_MCP_PIN)
  {
    digitalWrite(pinNumber, pinState);
  }
  else
  {
    mcp[(pinNumber - FISRT_MCP_PIN) / 16].digitalWrite((pinNumber - FISRT_MCP_PIN) % 16, pinState);
  }
}

int8_t myDigitalRead(uint8_t pinNumber)
{
  if (pinNumber < FISRT_MCP_PIN)
  {
    return digitalRead(pinNumber);
  }
  else
  {
    return mcp[(pinNumber - FISRT_MCP_PIN) / 16].digitalRead((pinNumber - FISRT_MCP_PIN) % 16);
  }
}

void allPinsInputHigh()
{ // set all pins to INPUT_PULLUP in a for-loop
  for (byte i = 0; i < NUMCABLES; i++)
  {
    myPinMode(pinCableBegin[i], INPUT_PULLUP);
    myPinMode(pinsCableEnd[i], INPUT_PULLUP);
  }
}


void DoOneTest()
{
  byte result;
  Serial.println();
  Serial.println("### TEST ###");
  for (byte i = 0; i < NUMCABLES; i++) // test each pin
  {
    result = PASS; // initially there is no error found, assume PASS
    allPinsInputHigh();
    // first test is for continuity and OUTPUT/HIGH
    myPinMode(pinCableBegin[i], OUTPUT);
    myDigitalWrite(pinCableBegin[i], HIGH); // <- Don't forget to set the level too !!!
    // some delay() may be required here !!!
    if (myDigitalRead(pinsCableEnd[i]) != HIGH)
    {
      bitSet(result, FAIL_NOTCONNECTED);
    }
    // then check for continuity and OUTPUT/LOW
    myDigitalWrite(pinCableBegin[i], LOW);
    // some delay() may be required here !!!
    if (myDigitalRead(pinsCableEnd[i]) != LOW)
    {
      bitSet(result, FAIL_NOTCONNECTED);
    }

    // next test: check for wrong connections to other pins
    for (byte j = 0; j < NUMCABLES; j++)
    {
      if (j != i && myDigitalRead(pinsCableEnd[j]) == LOW)
      {
        bitSet(result, FAIL_WRONGCONNECTED);
      }
    }
    // final test for short circuit against other pins
    for (byte j = 0; j < NUMCABLES; j++)
    {
      if (j != i && myDigitalRead(pinCableBegin[j]) == LOW)
      {
        bitSet(result, FAIL_SHORTENED);
      }
    }
    Serial.print("Line ");
    Serial.print(i + 1);
    if (result == PASS) Serial.print(" PASS");
    else Serial.print(" FAIL");
    if (bitRead(result, FAIL_NOTCONNECTED)) Serial.print(" BREAK");
    if (bitRead(result, FAIL_WRONGCONNECTED)) Serial.print(" WRONG");
    if  (bitRead(result, FAIL_SHORTENED)) Serial.print(" SHORT");
    Serial.println();
  }
  Serial.println("Test finished.");
  Serial.println();
}

void loop() {
  if (Serial.available())
  {
    DoOneTest();
    delay(20);
    while (Serial.available()) Serial.read(); // clear Serial input buffer
  }
}