Breaking out of attachInterrupt void during countdown

#include <Adafruit_NeoPixel.h> //include needed library for making ring-led's control easier
const byte S = 2;
const byte SW = 5;
const byte DT = 4;
const byte CLK = 3;
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h>//Required for 16 MHz Adafruit Trinket
#endif
#define LED_COUNT 24//number of LEDs on the ring
#define LED_PIN    6
#define CHUNK 300

int focus_timer = (50); //default focus time in seconds
int break_timer = (10); //default break time in seconds
int number_of_sessions = 2; //default number of sessions
int mode = 0; //changes ui 0(focus), 1(break), 2(number of sessions)
int delta = 0; // decides if the value will increase or decrease
unsigned long last_run=0; // stops accidental double input
unsigned long second_counter=0; //core of the countdown, makes sure loops runned every second
int temp_focus = 0;//makes multiple sessions possible with the benefit of on-the-move focus-time changes
int temp_break = 0;//makes multiple sessions possible with the benefit of on-the-move break-time changes
int temp_session = 0;//makes multiple sessions possible with the benefit of on-the-move session quantity changes
int button_state = 1;//start position of the knob button
int last_button_state = 1; //start position of the knob button
int led_counter = 1;// number of LEDs that will light up
int second = 100;//delay on the countdown loop


Adafruit_NeoPixel pixels(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  Serial.begin(9600);
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);
  pinMode(S,INPUT);
  pixels.begin();           // INITIALIZE NeoPixel pixels object (REQUIRED)
  pixels.show();            // Turn OFF all pixels ASAP
  pixels.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)

  //INTERRUPTS ONLY WORK ON PIN 2 & 3 !!!
  attachInterrupt(digitalPinToInterrupt(CLK), knob_turned, CHANGE);//interrupt the code when the knob is turned, and run the knob_turned Interrupt Service Routine (ISR)
  attachInterrupt(digitalPinToInterrupt(S), countdown, RISING);//interrupt the code when the device is tilted, and run the countdown Interrupt Service Routine (ISR)

}

void loop(){
  
  switch(mode){
    case 0:{
      pixels.clear();
      led_counter = 2*((focus_timer + CHUNK-1) / CHUNK);//displays the time by 5 minutes chunks on the LED-ring
      //Serial.println(led_counter); debug code for LED count
      for(int l = 0; l < led_counter; l++) {
        pixels.setPixelColor(l, pixels.Color(10, 0, 0));
      }
      pixels.show();
      break;
    }

    case 1:{
      pixels.clear();
      led_counter = 2*((break_timer + CHUNK-1) / CHUNK);//displays the time by 5 minutes chunks on the LED-ring
      //Serial.println(led_counter); debug code for LED count
      for(int l = 0; l < led_counter; l++) {
        pixels.setPixelColor(l, pixels.Color(0, 0, 10));
      }
      pixels.show();
      break;
    }

    case 2:{
      pixels.clear();
      led_counter = number_of_sessions;
      //Serial.println(led_counter); debug code for LED count
      for(int l = 0; l < led_counter; l++) {//displays the number of sessions on the LED-ring
        pixels.setPixelColor(l, pixels.Color(0, 10, 0));
      }
      pixels.show();
      break;
    }
  }
 
 if(millis()-last_run > 50){//to stop accidental double clicks the code will update every x milisecs 
  button_state=digitalRead(SW); //read the button state
  if(button_state != last_button_state && button_state == 0){
    mode++;
    mode = mode % 3;
    if(mode == 0){
      Serial.println("Focus period");
    }
    else if(mode == 1){
      Serial.println("Break period");
    }
    else if(mode == 2){
      Serial.println("Number of sessions");
    }
  }
  last_button_state = button_state;
 }
  
  //Serial.println(digitalRead(S));//Debug code for pin testing
}

  
  
  

void knob_turned(){
  if(millis()-last_run >50){
    if(digitalRead(CLK) != digitalRead(DT)){
      delta++;
    }
      else{
      delta--;
    }
    
    switch(mode) {
      case 0:{
        focus_timer = constrain((focus_timer+(delta*300)), 300, 3600);// changes the length of sessions
        Serial.println((String)"New focus length is: " + focus_timer);
        temp_focus=focus_timer;
        break;
      }

      case 1:{
        break_timer = constrain((break_timer+(delta*300)), 0, 3600); // changes the length of breaks
        Serial.println((String)"New break length is: " + break_timer);
        temp_break=break_timer;
        break;
      }

      case 2:{
        number_of_sessions = constrain((number_of_sessions+delta), 1, 24); //changes the number of session when it's in the session mode(2). Added 1, so there can't be a zero session.
        Serial.println((String)"New number of sessions is: " + number_of_sessions);
        temp_session=number_of_sessions;
        break;
      }
    }

    //Serial.println(delta);//debug code for delta
    delta=0;
    last_run=millis();
  }
}

void countdown(){
  for(number_of_sessions; number_of_sessions > 0; number_of_sessions--){
    Serial.println((String)"Focus time");
    for(focus_timer; focus_timer > 0; focus_timer--){
      while(digitalRead(S) != HIGH){//puts the system into infinite loop when device is tilted back up to pause the countdown
        delay(1);
      }
      mode = 0;
      pixels.clear();
      led_counter = 2*((focus_timer + CHUNK-1) / CHUNK);
      for(int l = 0; l < led_counter; l++) {
        pixels.setPixelColor(l, pixels.Color(10, 0, 0));
      }
      pixels.show();
      Serial.println(focus_timer);//debug code for focus countdown
      delay(second);
    }

    if(number_of_sessions > 1){//Last break is not needed
      Serial.println((String)"Break time");
      for(break_timer; break_timer > 0; break_timer--){
        while(digitalRead(S) != HIGH){//puts the system into infinite loop when device is tilted back up to pause the countdown
          delay(1);
        }
        mode = 1;
        pixels.clear();
        led_counter = 2*((break_timer + CHUNK-1) / CHUNK);
        for(int l = 0; l < led_counter; l++) {
          pixels.setPixelColor(l, pixels.Color(10, 0, 0));
        }
        pixels.show();
        Serial.println(break_timer);//debug code for break countdown
        delay(second);
      }
    }

    if(number_of_sessions > 1){
      Serial.println((String)(number_of_sessions-1) + " sessions left.");
    }
    else{
      Serial.println((String)"No sessions left.");
    }
  }

}

I am trying to make myself a pomodoro timer using Arduino uno, NeoPixel 24-LED ring, mercuy tilt-sensor(S) and rotary encoder(SW DT CLK). Rotary encoder is used to set up the time periods and number of focus sessions. Mercury-tilt sensor is used to start the countdown when the device is turned upside down.

I want to be able to stop the countdown at any point, add or substract timer if i want to and continue the countdown. But i couldnt find a way to break the interrupt command block.

Can i make this work or should i move the countdown to the main loop and make the "settings" menu to an interrupt void

I don't understand your question, but I do know you have waaaaaaaay too much code in your ISRs. I typically have one instruction that sets a flag that the main loop then interrogates.
Of course, no delay statements; use the millis () function to decide when to execute various pieces of code, including the code you used to have in the ISR. If you combine that technique with a state machine, you have a very efficient design.
Check the forum tutorial section to see several examples of state machines and how not use the delay statement.

Anything to do with Serial from an interrupt handler: bad. Fatal on some cores.

Any calls to delay from an interrupt handler: don't work.

Any calls to methods in class instances from an interrupt handler: disaster awaits.

Would you change all your "S" variables to "TILT" because searching for "S" is difficult.

Describe "number of sessions", "break period", "focus time" and how you start it, program it, and stop it. I don't see any neopixel lighting happening.

... also... you have two entries for Adafruit_Neopixel.h

... and... you have two conditions that are the same: if (number_of_sessions > 1)

Hint: When you are in a interrupt assume most everything is not going to work (not correct but it helps). The best trick is to simply set a flag in the ISR (Interrupt Service Routine) and then process it as part of the main loop.

I also thought making the sensor variable a word too, but it was there so i can remember the name of the soket that gives out the input.
I work on it at night, so it helps. But i agree with you on that %100

Neo pixel lighting happens on both countdown and loop void. Name of my stribçng is "pixels" maybe that's why it was hard to notice?

Basicly i light up LEDs based on the time left/time set, and have different colors for each mode(focus, break, number of sessions)

Focus_timer is the length of the focus session tin seconds, can be change by increment of 5 mins with the encoder

Break_timer is the length of the breaks between each focus session, can be adjusted the same way.

Number of sessions is the number of times the focus-break timer will repeat. Can be adjusted with the encoder with the increments of 1

Again, thank you for pointing out. I was sleep deprived, didnt even notice the same if condition was writen back to back

Yeah, figured as much tbh. But couldnt find something else that can help me stop the timer loop anytime i wanted and change the values as i like. Probably should change what is the main loop and what is the interrupt command

And i dont know how to implement what you are saying into my code. Can you think of an example stop i can try it.

My question: in the "void countdown", countdown gets into an infinite loop to stop the countdown with the while statement. But when that statement ends it starts from the begining of the two "for" loops its nested in. And does not continue from the code below. Why is that, and how can i fix it?

Second question: Countdown starts when the tilt sensor is turned upside down, and because this happens with an "interrupt" command, it does not stop until the countdown ends even if i tilt the sensor back up. Is there a way to record the state that the "for" loop is in and stop the loop. Later continue from where its left of when i tilt the sensor upside-down again.
Like a "break" functin for the "interrupt" loop.

Also i read that milliz work but delay and others dont. But i only could get result with delay and not the millis, that's why i put it there, but probably i am missing something. Will lookup further into it.

Basicly all the Serial code is there for me to debug the system, this is my first project so i am trying to see what is the state of the system, because 24 LEDs is not enough to debug. But otherwise all of those code will be turned into comments when i am confident the parts they are connedted are working as intented.

If you have other suggestions for me to debug, im all ears tbh.

I read that, and somebody else said it too. But im gettting result with delay and not with the millis. Idm whats up with that but will look further into it

I dont understand your third point, can you dummer it down a little bit more?

Although your ISRs are too long and you should redo everything, you can exit an ISR anytime by executing a return

void myISR()
{
  bool x, y;
  // do stuff
  if (x == y) return;
  // do other stuff if x!=y
}

I agree with others: This is the worst abuse of interrupts I have seen in a long time. You should re-think the whole design.

Just some observations.
A proper Interrupt Service Handler (ISR) has very few statements in it, I aim for ONE.

You first need to master the language elements. In severalo places you express the desire to break out of a loop. There are specific commands to do that.

The fact you need to break out of a loop is a strong indicator the design is wrong.

I told you to look at the tutorial here on the forum to learn about 'state machines' and the use of millis() to control the flow of code you. There is no indication you have.

Bottom line is re-write based on well known and proven designs.

I am enclosing screen shots of a few of the tutorials. Spend a few days studying these and then rewrite your code.



I'll simply point out the fallacy here. You cannot debug a system using a tool that breaks the system being debugged. Wrong approach, will not work (though it may give results in some erratic, unpredictable, and flawed circumstances, which will also appear/disappear capriciously); will only confuse, confound, and frustrate. You came here for advice...

Pardon me, but why bother? You seem to be intent upon selecting only the advice that suits your worldview. This abuse of Serial is so flagrant, fixing that is necessary before anything else - though frankly, the whole approach is so 'upside-down' fixing the serial abuse might be a waste of time.

IOW, If you must instrument an interrupt handler with printouts, you've made it too complex. Full stop, no caveats. KISS. Let the interrupt handler set/clear a flag, increment a counter, set a debug variable if needed, whatever, but keep that as the full extent; do those actions only, then finito.

Code, components, et c. Overview | A NeoPixel Pomodoro Timer | Adafruit Learning System

I am sorry if i sound rude or ignorant but english is not my mother-language and this is really my first project. Many things that people are telling me is going over my head tbh.

I am not used to a system where i need to define a consistent loop to always work and needing to break out of it. I am a civil engineer student who uses matlab from time to time and this is my personal project. Matlab doesnt require the use of semicolon, and when i dont, it just shows me the output of the calculation, and i am used to that.

So, i am genuenly asking here, what tool should i use to see if my calculations are working?
Because when i write a for loop that uses a math function, i cant figure out where did i made the mistake; if its because i used = instead of == in the comparision part of the for loop, or is it just my coding mistake. I cant see the difference in the 24 led ring, so i tried to use Serial.

Yes it is stated that not everything works in interrupt but it was not explained in the documentation why (at least i didnt catch it), so i assumed you were talking about cluttering the interrupt instead of breaking it. and because i assumed that it would break all the time if it was a buggy function, i said "let's not fix the "working" thing" because i wasn't going to use Serial in the end product regarless.

What do you guys use to debug a code?

Thank you for the answer!

I will be rewriting tonight, but i am thankfull for the mention of the return fuction regarless

Thank you for the tutorials!

I didnt know which terms to search for. Its a lifesaver

Most use Serial...
But not inside ISR...
And put compiler warnings to verbose...
And take them seriuously...

Thank you for telling me about verbose.
I guess nobody needs Serial it inside the ISR when they dont cram everything in it :smiling_face_with_tear:

For debug most of us just use Serial.println.

BTW, it is rare to need to explicitly break out of any loop. The for loop has a builtg in exit, a do while can be c oded to break out under program control. Your need to do that is a stroing indication that the code is not well thought out. I can't remember using return to break out of a loop ever, I may have but it's so long ago I can't recall it.
One trick to have the compiler catch the = vs == bug is to put the constant part on the left like the following.

  if (var = TRUE). // incorrect but compiler accepts 

  if (TRUE = var). // still incorrect but you will get a compiler error

  if (TRUE == var). // correct code as is
  if (var == TRUE)  // but if you forget the 2nd = the compiler will not catch it

The error msg you get is
error: lvalue required as left operand of assignment