LCD hangs program

I am calling a function from an interrupt routine.
All works as intended, until I add the following code:

lcd.clear();
lcd.setCursor(0, 1);
lcd.print(countLabels);

the Interrupt routine:

void pedal_ISR(void) {
  if (runningLabels) {
    detachInterrupt(digitalPinToInterrupt(pedalPin));
    runLabels();
  }
}

The function where the problem presents itself:

void runLabels() {
  digitalWrite(enable17, HIGH);  // Enable stepper17
  digitalWrite(enable23, LOW);   // Enable stepper23

  irPinStatus = digitalRead(irPin);

  if (irPinStatus == 0) {  // Ensure a label is loaded
    timeBegin = micros();
    do {
      irPinStatus = digitalRead(irPin);
      stepper23.setMaxSpeed(5000);
      stepper23.setSpeed(-5000);
      stepper23.runSpeed();
      stepper17.setMaxSpeed(3000);
      stepper17.setSpeed(-2000);
      stepper17.runSpeed();
    } while (irPinStatus == 0);

  } else if (irPinStatus == 1) {
    runningLabels = false;
    timeEnd = micros();
    stepper23.stop();
    stepper17.stop();
    digitalWrite(enable17, LOW);  // Disable stepper17
  }
  unsigned long duration = timeEnd - timeBegin;
  Serial.print("Time: ");
  Serial.println(duration);
  countLabels++;
  attachInterrupt(digitalPinToInterrupt(pedalPin), pedal_ISR, LOW);
  Serial.print("countLabels: ");
  Serial.println(countLabels);
  // lcd.clear();
  // lcd.setCursor(0, 1);
  // lcd.print(countLabels);
}

I am able to write to the LCD within other functions that are not associated to an interrupt.
Could the problem be calling the void runLabels() from an interrupt?

Post your complete code.

Within ISR no millis() or micros() will be updated (and all variables changed should be declared as "volatile"). You should call runLabels() from the loop() function, but unless you post the full code I can't tell you how it might be modified.

Complete code:

#include <Hardware.h>
#include "AccelStepper.h"
#include <Wire.h>
#include <LiquidCrystal_I2C_Menu.h>
LiquidCrystal_I2C_Menu lcd(0x27, 16, 2);

#define pinCLK 8
#define pinDT 9
#define pinSW 7

#define enable23 13
#define STEP_PIN_23 11
#define DIR_PIN_23 10

#define enable17 12
#define STEP_PIN_17 4
#define DIR_PIN_17 5

AccelStepper stepper23(1, STEP_PIN_23, DIR_PIN_23);
AccelStepper stepper17(1, STEP_PIN_17, DIR_PIN_17);

#define pedalPin 3
int pedalPinStatus;
bool runningLabels = false;
volatile bool pedalIsPressed = false;
int countLabels = 0;

#define irPin 2
bool loadingLabels = false;
int irPinStatus;
bool loadingFlag = 0;

unsigned long timeEnd;
unsigned long timeBegin;

enum { mkBack,
       mkRoot,
       mkLoadLabels,
       mkRunLabels,
       mkExit,
};

sMenuItem menu[] = {
  { mkBack, mkRoot, "Menu Select:" },
  { mkRoot, mkLoadLabels, "Load Labels" },
  { mkRoot, mkRunLabels, "Run Labels" },
  { mkRoot, mkExit, "Exit" },

};

uint8_t menuLen = sizeof(menu) / sizeof(sMenuItem);

void pedal_ISR(void) {
  if (runningLabels) {
    detachInterrupt(digitalPinToInterrupt(pedalPin));
    runLabels();
  }
}

void setup() {
  Serial.begin(115200);

  lcd.begin();
  lcd.attachEncoder(pinDT, pinCLK, pinSW);
  lcd.clear();
  lcd.print("   BREW  BRO");
  lcd.setCursor(0, 1);
  lcd.print("    WELCOME");
  delay(2000);

  pinMode(enable23, OUTPUT);
  digitalWrite(enable17, LOW);  // Disable NEMA 17
  pinMode(enable17, OUTPUT);
  digitalWrite(enable23, HIGH);  // Disable NEMA 23 so user can manually rotate shaft to Load Labels
  pinMode(irPin, INPUT);

  pinMode(pedalPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pedalPin), pedal_ISR, LOW);
}

void loadLabels() {
  irPinStatus = digitalRead(irPin);
  Serial.println("Loading");
  digitalWrite(enable23, LOW);  // Enable Stepper23
  if (irPinStatus == 1) {
    timeBegin = micros();
    do {
      irPinStatus = digitalRead(irPin);
      stepper23.setMaxSpeed(3000);
      stepper23.setSpeed(-3000);
      stepper23.runSpeed();
    } while (irPinStatus == 1);

  } else if (irPinStatus == 0) {
    timeEnd = micros();
    loadingLabels = false;
    stepper23.stop();
  }
  unsigned long duration = timeEnd - timeBegin;
  Serial.print("Time: ");
  Serial.println(duration);

  lcd.clear();
  lcd.print("Loading Complete");
  lcd.setCursor(2, 1);
  lcd.print("Click to Exit");
  delay(2000);
  loadingLabels = false;
}

void runLabels() {
  digitalWrite(enable17, HIGH);  // Enable stepper17
  digitalWrite(enable23, LOW);   // Enable stepper23

  irPinStatus = digitalRead(irPin);

  if (irPinStatus == 0) {  // Ensure a label is loaded
    timeBegin = micros();
    do {
      irPinStatus = digitalRead(irPin);
      stepper23.setMaxSpeed(5000);
      stepper23.setSpeed(-5000);
      stepper23.runSpeed();
      stepper17.setMaxSpeed(3000);
      stepper17.setSpeed(-2000);
      stepper17.runSpeed();
    } while (irPinStatus == 0);

  } else if (irPinStatus == 1) {
    runningLabels = false;
    timeEnd = micros();
    stepper23.stop();
    stepper17.stop();
    digitalWrite(enable17, LOW);  // Disable stepper17
  }
  unsigned long duration = timeEnd - timeBegin;
  Serial.print("Time: ");
  Serial.println(duration);
  countLabels++;
  attachInterrupt(digitalPinToInterrupt(pedalPin), pedal_ISR, LOW);
  Serial.print("countLabels: ");
  Serial.println(countLabels);
  // lcd.clear();
  // lcd.setCursor(0, 1);
  // lcd.print(countLabels);
}


void loop() {
  uint8_t selectedMenuItem = lcd.showMenu(menu, menuLen, 1);
  if (selectedMenuItem == mkLoadLabels) {
    lcd.print("Loading Labels");
    loadingLabels = true;
    runningLabels = false;
    pedalIsPressed = false;
    loadLabels();
  } else if (selectedMenuItem == mkRunLabels) {
    lcd.print("Run Labels");
    runningLabels = true;
    loadingLabels = false;
    pedalIsPressed = false;
    countLabels = 0;
    Serial.println(runningLabels);
  } else if (selectedMenuItem == mkExit) {
    lcd.print("Exit OK?");
    lcd.setCursor(2, 1);
    lcd.print("Click to Exit");
    runningLabels = false;
    loadingLabels = false;
    pedalIsPressed = false;
    mkRoot;
  }
  while (lcd.getEncoderState() == eNone)
    ;
}

After a quick look, I think you should just use the "pedalIsPressed" variable you already have but don't use (and it's "volatile" for a reason)...
This means that you may change the code to move the istructions from the ISR to the loop() and see if it works:

...
void pedal_ISR(void) {
  pedalIsPressed = true;
}
...
void loop() {
  if (pedalIsPressed) {
    if (runningLabels) {
      detachInterrupt(digitalPinToInterrupt(pedalPin));
      runLabels();
      // Don't know if this is required/useful here
      pedalIsPressed = false;
    }
  }
  uint8_t selectedMenuItem = lcd.showMenu(menu, menuLen, 1);
...

When I incorporate the above changes into my sketch, I have no response from the code.
Edited code:

#include <Hardware.h>
#include "AccelStepper.h"
#include <Wire.h>
#include <LiquidCrystal_I2C_Menu.h>
LiquidCrystal_I2C_Menu lcd(0x27, 16, 2);

#define pinCLK 8
#define pinDT 9
#define pinSW 7

#define enable23 13
#define STEP_PIN_23 11
#define DIR_PIN_23 10

#define enable17 12
#define STEP_PIN_17 4
#define DIR_PIN_17 5

AccelStepper stepper23(1, STEP_PIN_23, DIR_PIN_23);
AccelStepper stepper17(1, STEP_PIN_17, DIR_PIN_17);

#define pedalPin 3
int pedalPinStatus;
bool runningLabels = false;
volatile bool pedalIsPressed = false;
int countLabels = 0;

#define irPin 2
bool loadingLabels = false;
int irPinStatus;
bool loadingFlag = 0;

unsigned long timeEnd;
unsigned long timeBegin;

enum { mkBack,
       mkRoot,
       mkLoadLabels,
       mkRunLabels,
       mkExit,
};

sMenuItem menu[] = {
  { mkBack, mkRoot, "Menu Select:" },
  { mkRoot, mkLoadLabels, "Load Labels" },
  { mkRoot, mkRunLabels, "Run Labels" },
  { mkRoot, mkExit, "Exit" },

};

uint8_t menuLen = sizeof(menu) / sizeof(sMenuItem);

// void pedal_ISR(void) {
//   if (runningLabels) {
//     detachInterrupt(digitalPinToInterrupt(pedalPin));
//     runLabels();
//   }
// }

void pedal_ISR(void) {
  pedalIsPressed = true;
}

void setup() {
  Serial.begin(115200);

  lcd.begin();
  lcd.attachEncoder(pinDT, pinCLK, pinSW);
  lcd.clear();
  lcd.print("   BREW  BRO");
  lcd.setCursor(0, 1);
  lcd.print("    WELCOME");
  delay(2000);

  pinMode(enable23, OUTPUT);
  digitalWrite(enable17, LOW);  // Disable NEMA 17
  pinMode(enable17, OUTPUT);
  digitalWrite(enable23, HIGH);  // Disable NEMA 23 so user can manually rotate shaft to Load Labels
  pinMode(irPin, INPUT);

  pinMode(pedalPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pedalPin), pedal_ISR, LOW);
}

void loadLabels() {
  irPinStatus = digitalRead(irPin);
  Serial.println("Loading");
  digitalWrite(enable23, LOW);  // Enable Stepper23
  if (irPinStatus == 1) {
    timeBegin = micros();
    do {
      irPinStatus = digitalRead(irPin);
      stepper23.setMaxSpeed(3000);
      stepper23.setSpeed(-3000);
      stepper23.runSpeed();
    } while (irPinStatus == 1);

  } else if (irPinStatus == 0) {
    timeEnd = micros();
    loadingLabels = false;
    stepper23.stop();
  }
  unsigned long duration = timeEnd - timeBegin;
  Serial.print("Time: ");
  Serial.println(duration);

  lcd.clear();
  lcd.print("Loading Complete");
  lcd.setCursor(2, 1);
  lcd.print("Click to Exit");
  delay(2000);
  loadingLabels = false;
}

void runLabels() {
  digitalWrite(enable17, HIGH);  // Enable stepper17
  digitalWrite(enable23, LOW);   // Enable stepper23

  irPinStatus = digitalRead(irPin);

  if (irPinStatus == 0) {  // Ensure a label is loaded
    timeBegin = micros();
    do {
      irPinStatus = digitalRead(irPin);
      stepper23.setMaxSpeed(5000);
      stepper23.setSpeed(-5000);
      stepper23.runSpeed();
      stepper17.setMaxSpeed(3000);
      stepper17.setSpeed(-2000);
      stepper17.runSpeed();
    } while (irPinStatus == 0);

  } else if (irPinStatus == 1) {
    runningLabels = false;
    timeEnd = micros();
    stepper23.stop();
    stepper17.stop();
    digitalWrite(enable17, LOW);  // Disable stepper17
  }
  unsigned long duration = timeEnd - timeBegin;
  Serial.print("Time: ");
  Serial.println(duration);
  countLabels++;
  attachInterrupt(digitalPinToInterrupt(pedalPin), pedal_ISR, LOW);
  Serial.print("countLabels: ");
  Serial.println(countLabels);
  // lcd.clear();
  // lcd.setCursor(0, 1);
  // lcd.print(countLabels);
}


void loop() {

  if (pedalIsPressed) {
    if (runningLabels) {
      detachInterrupt(digitalPinToInterrupt(pedalPin));
      runLabels();
      // Don't know if this is required/useful here
      pedalIsPressed = false;
    }
  }

  uint8_t selectedMenuItem = lcd.showMenu(menu, menuLen, 1);
  if (selectedMenuItem == mkLoadLabels) {
    lcd.print("Loading Labels");
    loadingLabels = true;
    runningLabels = false;
    pedalIsPressed = false;
    loadLabels();
  } else if (selectedMenuItem == mkRunLabels) {
    lcd.print("Run Labels");
    runningLabels = true;
    loadingLabels = false;
    pedalIsPressed = false;
    countLabels = 0;
    Serial.println(runningLabels);
  } else if (selectedMenuItem == mkExit) {
    lcd.print("Exit OK?");
    lcd.setCursor(2, 1);
    lcd.print("Click to Exit");
    runningLabels = false;
    loadingLabels = false;
    pedalIsPressed = false;
    mkRoot;
  }
  while (lcd.getEncoderState() == eNone)
    ;
}

It seems that I do not get to the loop() when interrupt is fired:

  if (pedalIsPressed) {
    if (runningLabels) {
      detachInterrupt(digitalPinToInterrupt(pedalPin));
      runLabels();
      // Don't know if this is required/useful here
      pedalIsPressed = false;
    }
  }

I would be amazed if you could successfully call Wire code (which is what the I2C LCD calls will eventually get to) in an interrupt context. I just cannot see that working.

Far better to do nothing but set a flag in the interrupt handler, check the flag in the main thread and do all that processing there.

That is what I tried in the above sketch:

void pedal_ISR(void) {
  pedalIsPressed = true;
}

and in loop():

  if (pedalIsPressed) {
    if (runningLabels) {
      detachInterrupt(digitalPinToInterrupt(pedalPin));
      runLabels();
      // Don't know if this is required/useful here
      pedalIsPressed = false;
    }
  }

It seems that the interrupt does not fire?

In this case the culprit it isn't the ISR. Add some "Serial.print()" around (the loop, but also inside "runLabels()"") to better understand what happens. Then post again your new "debug" code together with the serial output.

Were you able to clear your other issues with this project?

  1. Running two stepper motors simultaneously
  2. Buggy Stepper code

I switched to the AccelStepper library.
I have updated those posts

It apears that the problem is the following code.
If I comment it out, it works, but I loose my menu functions.

  uint8_t selectedMenuItem = lcd.showMenu(menu, menuLen, 1);
  if (selectedMenuItem == mkLoadLabels) {
    lcd.print("Loading Labels");
    loadingLabels = true;
    runningLabels = false;
    pedalIsPressed = false;
    loadLabels();
  } else if (selectedMenuItem == mkRunLabels) {
    lcd.print("Run Labels");
    runningLabels = true;
    loadingLabels = false;
    pedalIsPressed = false;
    countLabels = 0;
    // Serial.println(runningLabels);
  } else if (selectedMenuItem == mkExit) {
    lcd.print("Exit OK?");
    lcd.setCursor(2, 1);
    lcd.print("Click to Exit");
    runningLabels = false;
    loadingLabels = false;
    pedalIsPressed = false;
    mkRoot;
  }

  while (lcd.getEncoderState() == eNone)
    ;

Complete code:

#include <Hardware.h>
#include "AccelStepper.h"
#include <Wire.h>
#include <LiquidCrystal_I2C_Menu.h>
LiquidCrystal_I2C_Menu lcd(0x27, 16, 2);

#define pinCLK 8
#define pinDT 9
#define pinSW 7

#define enable23 13
#define STEP_PIN_23 11
#define DIR_PIN_23 10

#define enable17 12
#define STEP_PIN_17 4
#define DIR_PIN_17 5

AccelStepper stepper23(1, STEP_PIN_23, DIR_PIN_23);
AccelStepper stepper17(1, STEP_PIN_17, DIR_PIN_17);

#define pedalPin 3
int pedalPinStatus;
bool runningLabels = false;
volatile bool pedalIsPressed = false;
int countLabels = 0;


#define irPin 2
bool loadingLabels = false;
int irPinStatus;
bool loadingFlag = 0;

unsigned long timeEnd;
unsigned long timeBegin;

enum { mkBack,
       mkRoot,
       mkLoadLabels,
       mkRunLabels,
       mkExit,
};

sMenuItem menu[] = {
  { mkBack, mkRoot, "Menu Select:" },
  { mkRoot, mkLoadLabels, "Load Labels" },
  { mkRoot, mkRunLabels, "Run Labels" },
  { mkRoot, mkExit, "Exit" },

};

uint8_t menuLen = sizeof(menu) / sizeof(sMenuItem);

void pedal_ISR(void) {
  if (runningLabels) {
    detachInterrupt(digitalPinToInterrupt(pedalPin));
    pedalIsPressed = true;
  }
}

void setup() {
  Serial.begin(115200);

  lcd.begin();
  lcd.attachEncoder(pinDT, pinCLK, pinSW);
  lcd.clear();
  lcd.print("   BREW  BRO");
  lcd.setCursor(0, 1);
  lcd.print("    WELCOME");
  delay(2000);

  pinMode(enable23, OUTPUT);
  digitalWrite(enable17, LOW);  // Disable NEMA 17
  pinMode(enable17, OUTPUT);
  digitalWrite(enable23, HIGH);  // Disable NEMA 23 so user can manually rotate shaft to Load Labels
  pinMode(irPin, INPUT);

  pinMode(pedalPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pedalPin), pedal_ISR, LOW);
}

void loadLabels() {
  irPinStatus = digitalRead(irPin);
  Serial.println("Loading");
  digitalWrite(enable23, LOW);  // Enable Stepper23
  if (irPinStatus == 1) {
    timeBegin = micros();
    do {
      irPinStatus = digitalRead(irPin);
      stepper23.setMaxSpeed(3000);
      stepper23.setSpeed(-3000);
      stepper23.runSpeed();
    } while (irPinStatus == 1);

  } else if (irPinStatus == 0) {
    timeEnd = micros();
    loadingLabels = false;
    stepper23.stop();
  }
  unsigned long duration = timeEnd - timeBegin;
  Serial.print("Time: ");
  Serial.println(duration);

  lcd.clear();
  lcd.print("Loading Complete");
  lcd.setCursor(2, 1);
  lcd.print("Click to Exit");
  delay(2000);
  loadingLabels = false;
}

void runLabels() {
  digitalWrite(enable17, HIGH);  // Enable stepper17
  digitalWrite(enable23, LOW);   // Enable stepper23

  irPinStatus = digitalRead(irPin);

  if (irPinStatus == 0) {  // Ensure a label is loaded
    timeBegin = micros();
    do {
      irPinStatus = digitalRead(irPin);
      stepper23.setMaxSpeed(5000);
      stepper23.setSpeed(-5000);
      stepper23.runSpeed();
      stepper17.setMaxSpeed(3000);
      stepper17.setSpeed(-2000);
      stepper17.runSpeed();
    } while (irPinStatus == 0);

  } else if (irPinStatus == 1) {
    runningLabels = false;
    timeEnd = micros();
    stepper23.stop();
    stepper17.stop();
    digitalWrite(enable17, LOW);  // Disable stepper17
  }
  unsigned long duration = timeEnd - timeBegin;
  Serial.print("Time: ");
  Serial.println(duration);
  countLabels++;

  Serial.print("countLabels: ");
  Serial.println(countLabels);
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print(countLabels);

  attachInterrupt(digitalPinToInterrupt(pedalPin), pedal_ISR, LOW);
}



void loop() {
  pedalPinStatus = digitalRead(pedalPin);
  if (pedalPinStatus == 0) {
    Serial.print("Pedal Fired: ");
    Serial.println(pedalPinStatus);
    runLabels();
  }

  // uint8_t selectedMenuItem = lcd.showMenu(menu, menuLen, 1);
  // if (selectedMenuItem == mkLoadLabels) {
  //   lcd.print("Loading Labels");
  //   loadingLabels = true;
  //   runningLabels = false;
  //   pedalIsPressed = false;
  //   loadLabels();
  // } else if (selectedMenuItem == mkRunLabels) {
  //   lcd.print("Run Labels");
  //   runningLabels = true;
  //   loadingLabels = false;
  //   pedalIsPressed = false;
  //   countLabels = 0;
  //   // Serial.println(runningLabels);
  // } else if (selectedMenuItem == mkExit) {
  //   lcd.print("Exit OK?");
  //   lcd.setCursor(2, 1);
  //   lcd.print("Click to Exit");
  //   runningLabels = false;
  //   loadingLabels = false;
  //   pedalIsPressed = false;
  //   mkRoot;
  // }

  // while (lcd.getEncoderState() == eNone)
  //   ;
}

Serial Monitor:

Pedal Fired: 0
Time: 4285145468
countLabels: 1
Pedal Fired: 0
Time: 4281719528
countLabels: 2

I found the following in the Github of the "LiquidCrystal_I2C_Menu" library.
I don't know if this would help in disabling the menu polling to enable the print to LCD.

attachIdleFunc
When calling most library functions, an action is expected from the user: selecting an item in the menu, entering a value, and so on. In this case, we are essentially in a loop and the execution of the rest of the program is impossible until we exit this loop. This is logical. But there may be cases when such logic is not suitable. For example, if we need to poll some sensors even while the menu is running. In such cases, the attachIdleFunc function will help. It allows you to specify a function that the library will call again and again while we are in the menu, entering or selecting values. And inside this function itself, you can poll sensors or anything that will not be executed for a long time, so as not to interfere with the library polling the buttons and encoder.

To demonstrate the functionality described, the example below turns the Arduino's built-in LED on and off while we are inside the inputVal and printMultiline functions.

# include  <Wire.h>
 # include  <LiquidCrystal_I2C_Menu.h> 
LiquidCrystal_I2C_Menu lcd( 0x27 , 20 , 4 );

// Pins to which the encoder is connected 
# define pinCLK 2 
# define pinDT 3 
# define pinSW 4

unsigned  long tm = 0 ;
 bool ledState = false;

// This function will be called from the library when idle 
void myIdleFunc() {
   if ( millis () - tm >= 500 ) {
     // Turn on and off the built-in LED on the Arduino 
    tm = millis ();
    ledState = !ledState;
    digitalWrite ( LED_BUILTIN , ledState);
  }
}

void  setup () {
  lcd.begin ( );
  lcd.attachEncoder(pinDT, pinCLK, pinSW);
  lcd.attachIdleFunc(myIdleFunc);
  pinMode ( LED_BUILTIN , OUTPUT );
  lcd. print ( "Press the button" );
}

int x = 0 ;

void  loop () {
  myIdleFunc();
  if (lcd.getEncoderState() == eButton) {
     // To check, call any library function 
    // that waits for user actions: 
    x = lcd.inputVal( "Input some val" , 0 , 100 , x);
    lcd.printMultiline( "Some text here" );
    lcd.clear () ;
    lcd. print ( "Press the button" );
  }
}

Initially, myIdleFunc is called inside the loop function, causing the LED to blink. When the encoder button is pressed, the library functions are called, and loop execution is suspended. However, the LED continues to blink because myIdleFunc is called by the library. This does not interfere with the operation of the inputVal and printMultiline functions.

It looks like it's something related to the LCD menu library functionality: I have never used it, but I suppose that last "lcd.getEncoderState()" is a blocking call. If this is the case, I think you need to find how to use non-blocking functions at all. Sorry, I can't tell you more, I can't analyze the library it's something you might ask to the library author...

Take a look at this thread. Might help.

Fix your Post #13 where you have comments inside a code block.

attachIdleFunc
When calling most library functions, an action is expected from the user: selecting an item in the menu, entering a value, and so on. In this case, we are essentially in a loop and the execution of the rest of the program is impossible until we exit this loop. This is logical. But there may be cases when such logic is not suitable. For example, if we need to poll some sensors even while the menu is running. In such cases, the attachIdleFunc function will help. It allows you to specify a function that the library will call again and again while we are in the menu, entering or selecting values. And inside this function itself, you can poll sensors or anything that will not be executed for a long time, so as not to interfere with the library polling the buttons and encoder.
To demonstrate the functionality described, the example below turns the Arduino's built-in LED on and off while we are inside the inputVal and printMultiline functions.

Your "completed code" in Post #12 has two attachInterrupt() calls... one of them in loop()...

Where do you find Hardware.h? That's an evil name to give a library, since they all have "hardware" in their description.

Thanks - fixed

???

Dunno where this cropped up - removed

In SETUP()

in LOOP()

in runLabels()

Therefore " you attach interrupt in loop()"