PinTest

7 minute read

Where I write an Arduino program to test pins on a specific board in order to confirm a Forth HAL for the same board.

Pin Testing

When writing software which will control a circuit, it is best to confirm the behavior of circuit using known or familar test software before attempting to write with a new language. For example, I am developing a HAL/Primitive/User layer for the ESP32 using ESPForth. As the board, the Adafruit HUZZAH32 has an Arduino framework already developed for it, it is a good idea to test the board using the Arduino C++ code before testing it with the ESPForth words.

To do this, I wrote two programs, ManPinTest.ino and PinTest.ino. The former is a modified Blink program where the program will blink a pin at 5HZ to demonstrate the pin works as an output. The “Man” part is that it is manual. It requires a code change for a new pin, which means a compile/load cycle for every new pin.

PinTest on the other hand, is interactive. It will ask for which pin to test and will offer several ways to test the pin.

Ultimately, both programs work well, PinTest is more versatile. Both also showed that the HUZZAH32 board was working well. All output pins were verified to work.

Background (More Detail)

While attempting to confirm the HAL for this entry, I ran across problems confirming my Forth code was setting the pins properly. Specific pins such as pin 12/A11 didn’t seem to be responding to my words, while others 13/LED, did.

I realized after a time, could it be my board? No, I checked another HUZZAH32 and it exhibited similar issues.

OK, could it be that those specific ports can’t be configured as outputs. I reviewed a considerable amount of documentation including and didn’t find anything which would confirm those ports had a problem.

Do those ports work using the Arduino code? Aha! Now we are making progress. I created a very simple blink program, which I called ManPinTest. This program is a simple evolution of the common Blink example, however it’s designed to have a single line change for changing which port to confirm. Its essentially a “minimum viable test” to confirm a pin is operational as an output. I also added code to show the bit position of the bin in the port in both binary and hexadecimal. (Still hate C++ char/string handling!)

// ManPinTest.ino - blink pin at 5Hz
// identify the pin to be tested
// either by pin i.e; 27 or pins_arduino.h address A10
// also identifies bit position by binary or hex
int Pin = A8;

void setup() {
  // initialize pin and confirm as output
  char port[2] = "P";
  char port0[] = "0";
  char port1[] = "1";
  Serial.begin(115200);
  pinMode(Pin, OUTPUT);
  Serial.print("Pin ");
  Serial.println(Pin);
  if (Pin > 33) {
    Serial.println("Pins 34-39 input only");
    strcat(port, port1);
  }
  else {
        strcat(port, port0);
  }
  long portPin = 0;
  portPin = 1 << Pin;
  
  Serial.print(port);
  Serial.print(" Bit Binary: %");
  Serial.println(portPin, BIN);
  Serial.print(port);
  Serial.print(" Bit Hex: $");
  Serial.println(portPin, HEX);
}

// with a delay of 100, the pin will have a 5Hz frequency
void loop() {
  if (Pin < 34) {
      digitalWrite(Pin, HIGH);   // turn pin HIGH
      delay(100);                // wait for a .1 second
      digitalWrite(Pin, LOW);    // turn pin LOW
      delay(100);                // wait for a .1 second
   }
}

Output

Pin 14
Bit Binary: %100000000000000
Bit Hex: $4000

Frankly, this code works well, for simple testing I recommend using this one. The more elaborate one below is great for extensive testing as it doesn’t require a new compile for each pin.

More Elaborate Version

While the ManTestPin version worked extremely well. I had to re-compile and load each time I wanted to use a new pin. I also couldn’t change the test easily. So I developed one using a concept of a state machine, where I follow the following states:

  1. Start - Print header and ask for pin to be tested
  2. getPin - continue to loop waiting for pin to be entered, exit on pin
  3. getTest - continue to loop waiting for test to be entered, perform test and wait for new test.

Tests

  1. Restart and ask for new pin
  2. Set pin high
  3. Set pin low
  4. Blink pin on then off

Issues (Deprecated, see note at top.)

At first, I thought I was finding the same issue as what I had with the Forth HAL version. Specific pins were failing the tests. However, what is happening is that the test i.e; HIGH to LOW, would take 10-12 seconds, while LOW to HIGH would take less than a second.

Lesson

Keep things simple. That said, writing the more complicated program reminded me of using state machines. It is also something that can be fixed, however, I’m not going to continue with this version for now.

New Plan

Rewrite my Forth tests similar to the ManPinTest using Forth primitives for calling Arduino functions. If these primitives work, then examine specifically what the Arduino does on some pins to enable output that others don’t require.

Elaborate Version Code

// Arduino Pin Test 
// Use to test a new board identifying each pin

char recBuff[3];
int pinN = 0;
char test = '0';
boolean startState = true;
boolean pinState = false;
boolean testState = false;
boolean reqTestState = true;
const int minTest = 0;
const int maxTest = 4;
const int maxPins = 33; // Pins 34-39 are input only on ESP32

int readline(int readch, char *recBuff, int len) {
    static int pos = 0;
    int rpos;

    if (readch > 0) {
        switch (readch) {
            case '\r': // Ignore CR
                break;
            case '\n': // Return on new-line
                rpos = pos;
                pos = 0;  // Reset position index ready for next time
                return rpos;
            default:
                if ((readch < 48) || (readch > 57)) {
                  Serial.println("Character must be a number");
                  break;
                }
                if (pos < len-1) {
                    recBuff[pos++] = readch;
                    recBuff[pos] = 0;
                }
        }
    }
    return 0;
}

void getPin() {
    if (readline(Serial.read(), recBuff, 3) > 0) {
        pinN = atoi(recBuff);
        if (pinN > maxPins) {
          Serial.print("Possible error, pin requested greater than number of output pins ");
          Serial.println(pinN);
        }
        else if (pinN < 0) {
          Serial.print("Possible error, pin requested is less than 0");
        }
        pinState = true;
        pinMode(pinN, OUTPUT);
        Serial.print(pinN);
        Serial.println(" enabled as Output");
    }
}

void getTest() {
    if (readline(Serial.read(), recBuff, 2) > 0) {
        test = atoi(recBuff);
        if (test > maxTest) {
          Serial.print("Possible error, test requested greater than tests allowed ");
          Serial.println(test);
        }
        else if (test < minTest) {
          Serial.print("Possible error, test requested less than 0");
        }
        testState = true;
        reqTestState = false;
    }
}
void blink() {
  digitalWrite(pinN, HIGH); // sets the digital pinN on
  delay(1000);            // waits for a second
  digitalWrite(pinN, LOW);  // sets the digital pinN off
  delay(1000);            // waits for a second
}

void pinBits() {
    // initialize pin and confirm as output
  char port[2] = "P";
  char port0[] = "0";
  char port1[] = "1";
  Serial.print("Pin ");
  Serial.println(pinN);
  if (pinN > 31) {
    strcat(port, port1);
  }
  else {
        strcat(port, port0);
  }
  long portPin = 0;
  portPin = 1 << pinN;
  
  Serial.print(port);
  Serial.print(" Bit Binary: %");
  Serial.println(portPin, BIN);
  Serial.print(port);
  Serial.print(" Bit Hex: $");
  Serial.println(portPin, HEX);
}

void runTest_1() {
    digitalWrite(pinN, HIGH);
    Serial.print(pinN);
    Serial.println(" is now High");
}

void runTest_2() {
    digitalWrite(pinN, LOW);
    Serial.print(pinN);
    Serial.println(" is now LOW");
}

void runTest_3() {
    blink();
    Serial.print(pinN);
    Serial.println(" will blink once");
}

void runTest_4() {
    pinBits();
}

void setup() {
    Serial.begin(2000000);
    Serial.println();
    Serial.println("Pin Test is ready");
    Serial.print("Tests are: 0 for new pin | 1 for pin High | 2 for Pin Low | 3 for Pin Blink | 4 for Port Bits");
}

void loop() {
  if (startState) {
    startState = false;
    Serial.println();
    Serial.print("Enter Pin: ");
  }
  else if (not pinState) {
    getPin();
  }
  else if (not testState) {
    if (reqTestState) {
      Serial.print("Enter test (0-4): ");
      reqTestState = false;
    }
      getTest();
      if (testState) {
          switch(test) {
              case  minTest: // 0 - restart with new pin
                  startState = true;
                  pinState = false;
                  testState = false;
                  reqTestState = true;
                  break;
              case minTest + 1 : // 1 - set pin HIGH
                runTest_1();
                testState = false;
                reqTestState = true;
                break;
              case minTest + 2: // 2 - set pin LOW
                runTest_2();
                testState = false;
                reqTestState = true;
                break;
              case minTest + 3: // 3 - blink pin off then on at 5Hz
                runTest_3();
                testState = false;
                reqTestState = true;
                break;
              case maxTest: // 4 - print pin Port bits in binary and hex
                runTest_4();
                testState = false;
                reqTestState = true;
                break;
              default: // print error, not a valid test number
                Serial.print(test);
                Serial.println(" Must be 0, 1, 2, 3, or 4");
//                delay(2000);
                testState = false;
                reqTestState = true;
      }
  }
}
}

Comments powered by Talkyard.