Program not returning from attachInterrupt

in the code below, the stepper functions called from within the interrupt routine are from the FastAccelStepper library and are claimed to be interrupt-safe. If I run the code as shown, the stepper runs continuously and the attachInterrupt call never returns. If i comment out the body except for the

   interrupt_called = true;

everything works fine, although the interrupt function obviously doesn't do what it needs to.

It appears findZero is being called during the attachInterrupt call, which makes no sense. Further confusing matters, the two Serial.println debug calls to print the value of interrupt_called both show false (0), as if the function is not being called.

A header file defines ZERO_DETECT_PIN as 2
The code is being loaded to a Nano, and for now nothing is actually connected to pin 2, so it should be in the pull-up high state.

I'm at a loss as to what to try next.

volatile bool interrupt_called;
void findZero()
{
  interrupt_called = true;
  //stop the stepper
  stop();
  //back off until we break contact. Contact is active low.
  //move one full step at a time. 
  while (digitalRead(ZERO_DETECT_PIN ))
  {
    move(-MICRO_STEPS);
  }
  stepper_counts_from_zero = 0;
  inch_position_x_100000 = 0L;
  setPosition(0);
}

void setup()
 {
   Serial.begin(19200);
   Serial.println("Starting setup");
  initializeButtons();
  initializeMenu();
  initializeStepper();

  //set up the interrupt routine for zero finding
  pinMode(ZERO_DETECT_PIN, INPUT_PULLUP);
  Serial.println(digitalRead(ZERO_DETECT_PIN));
  Serial.println(interrupt_called);
  attachInterrupt(digitalPinToInterrupt(ZERO_DETECT_PIN), findZero, FALLING); 
  Serial.println(interrupt_called);

  
  //display Height label
  lcd.setCursor(7,1);
  lcd.print("HEIGHT");
  printHeight();
  Serial.println("Starting LiftStepper");
 }

None of this code should be in an interrupt routine.

  //stop the stepper
  stop();
  //back off until we break contact. Contact is active low.
  //move one full step at a time. 
  while (digitalRead(ZERO_DETECT_PIN ))
  {
    move(-MICRO_STEPS);
  }
  stepper_counts_from_zero = 0;
  inch_position_x_100000 = 0L;
  setPosition(0);

The loop function should just check the interrupt_called flag and do all the required actions.

Please post a complete sketch that illustrates the problem

we can discuss what should go in an interrupt routine, but that is irrelevant to the question of why this is occurring. I was hoping we could focus on that issue first.

Interrupts are turned off in an interrupt routine, and anything that depends on them being on, like timed stepper motor code, WILL NOT work.

That is essentially the whole sketch. You can comment out

  initializeButtons();
  initializeMenu();

and nothing changes.
Here's the definition of initializeStepper, which is in a separate file:

void initializeStepper() 
{
  engine.init();
  stepper = engine.stepperConnectToPin(STEP_PIN);
  stepper->setDirectionPin(STEP_DIR);
  stepper->setEnablePin(STEP_ENABLE);
  stepper->setAutoEnable(true);
  stepper->setSpeedInUs(uS_PER_STEP);  // the parameter is μs/step 
  stepper->setAcceleration(ACCEL_STEPS_SEC2);
  stepper->setCurrentPosition(0);
}

The loop portion of the sketch, which is never reached when attachInterrupt fails to return is:

void loop() 
{
  menuTick();
  buttonTicks();
}

With the two initialization routines commented out as above, these two lines can be deleted as well with no effect.

I will try to post a minimal standalone example later today.

Where is its state set ?

Yes. It's very frustrating trying to debug somebody else's code from random snippets.

Try an empty ISR:

void findZero() {
}

Does attachInterrupt() return with that?

No, this is NOT irrelevant to the question, it is the cause of the issue.

I think you found it - I called digitalWrite(.., HIGH) before attachInterrupt and that seems to have solved the problem.

What I don't understand is that the docs seem to say pinMode(x, INPUT_PULLUP) sets the internal pullup resistor, so why should the digitalWrite be needed? Lots of code examples on the web don't show this.
Thanks.

Whoever claimed that is wrong. The FastAccelStepper library function 'move()' is NOT interrupt safe. It calls a function which calls a function which enables interrupts:

  noInterrupts();
  if ((_rw.ramp_state == RAMP_STATE_IDLE) && (target_pos != curr_target_pos)) {
    // Only start the ramp generator, if the target position is different
    _rw.ramp_state = RAMP_STATE_ACCELERATE;
    _rw.curr_ticks = TICKS_FOR_STOPPED_MOTOR;
    _rw.performed_ramp_up_steps = 0;
  }
  _ro = new_ramp;
  interrupts();

The original post says

If i comment out the body except for the

   interrupt_called = true;

everything works fine, although the interrupt function obviously doesn't do what it needs to.

"Everything works fine" = attachInterrupt returned normally.

So, obviously, that which you said is irrelevant (your bad bode in the ISR) is very relevant and indeed the cause of your problem.

The interrupt flag was already set when attachInterrupt() is called. Thus, the ISR fires immediately. The code in the ISR then hangs the processor.

I went back and re-read the .h file more carefully. Only a very limited subset of functions are claimed to be interrupt safe. In particular, the stopMove() function called in the ISR is one of these.

As you yourself note, the interrupt flag must have been already set and the loop in the ISR hangs the processor. But why is the flag set? The input pin for the interrupt is set to INPUT_PULLUP, which the docs say ties an internal pull-up to the pin, bringing it HIGH unless actively pulled down to ground.

Contrary to your statement, the content of the ISR is irrelevant as to why the interrupt was called in the first place. It was that sort of distraction that I was seeking to avoid when I asked not to focus the discussion on the more general question of how much should be inside an interrupt.

While is generally sound practice to avoid putting too much inside an ISR, there are cases, particularly in safety critical functions, where you want to complete all the relevant actions before returning from the ISR. Setting a flag and reading it the next time around the loop introduces an unknown delay, as well as the possibility of another, lower priority interrupt further delaying action or some other function in the loop being executed, possibly exacerbating a dangerous situation.

Where exactly in the sketch is the input pin pinMode() set ?

Why not try clearing the flag before calling attachInterrupt()?

If the code in the ISR was valid, the processor would not have hung. Sounds relevant to me.

As has been stated countless times on this forum, and on any other programming forum and textbook, as little as possible. And especially, nothing that depends on interrupts.

But you are most welcome to ignore our comments and learn these lessons the hard way.

indeed. But without knowing the context you can't know what constitutes "as little as possible". In your original post you assert that "none of this" belongs in the ISR. Really? The code makes clear we're driving a stepper motor. The first function call you wish to delete in the ISR stops the motor. How are you so sure that nothing harmful will occur if we merely send back a flag and wait for the next opportunity in the loop to check the flag?

Make the change and test it. If you run into a problem, post the complete code, and forum members will be glad to help.

It can't work the way you have it now, so you can only gain.