Arduino UNO, how to interrupt long lasting subs

Arduino uno

How can I interrupt a long procedure when some event occurs

( for example change on input pin)

need to do something like this:

break the procedure;

run some interrupt code;

continue the procedure;

Programm should looks like this:

...
setup{
   // some initial settings
}
loop{

   myLongLastingProcedure();
 
   // for instance, play some music or print something
   // need to brake on external event 
   //and continue to run after interrupt procedure is finished

}
...

That would be a pin change interrupt.

In general, you cannot do any serial I/O within an interrupt routine (e.g. serial monitor, I2C or SPI device access).

Hello 7frappe

Welcome to the best Arduino forum ever :slight_smile:

One trick is to write the procedure as short as possible so that computing time for the CPU can be allocated to another procedure.

A typical real-time processing killer is the delay() function that uses the while() calls.

Common advice for the contents of the ISR is to set a flag and exit. Outside the ISR, check the flag and act on its state.

1 Like

Thank, this will solve my problem

Typical solution:
Make your procedure an automaton (state machine) and insert the event recognition.

Take a look at a state machine. You might also want the ISR to set a flag and do the interrupt processing as part of the main loop.

You will have to use non blocking code.

good general advice!
however, some microcontrollers have the facility to set interrupt priority levels where a device interrupt service routine can be interrupted by higher priority devices

for example, some time ago used a PIC24FJ256GA705 acquiring samples from a LSM9DS1 9-axis iNEMO inertial module every 20mSec

  1. timer interrupted every 20mSec
  2. timer ISR called LSM9DS1 library functions which used I2C
  3. sensor readings were written into a double buffer
  4. while one buffer was being filled in the timer ISR the main() function transmitted the other buffer over BLE to a smart phone

using the MPLABX IDE the interrupt priorities were set using the MMC (MPLAB Code Configurator)

timer interrupt is level 1 the I2C interface is level 2 therefore I2C can interrupt timer ISR

however, in general the advice is to keep ISR as short as possible and avoid calling complex library function - in particular no Serial Monitor IO!
set a volatile flag variable in the ISR and do complex operations in loop()

Seems to be an XY problem. The general hint is - like some advice above - not to have a long lasting procedure. You never ever need that. It can always be written in such a way, that loop() loops really fast. And that's the way to do several things 'in parallel'.
So the main question is, what does your 'myLongLastingProcedure();' do? And why is it long lasting?

Interrupts are not the way to circumwent bad programming. They have many pitfalls, and if you use them, you should really know what you are doing.

5 Likes

you may have consider moving to a faster more powerful processor (also may require a floating point processor or a DSP)
e.g. Uno > Arduino Due > ESP32 > Teensey > Raspberry Pi

Hi, @7frappe
Welcome to the forum.

https://forum.arduino.cc/t/how-to-get-the-best-out-of-this-forum

Can yopu please tell us what your project is?
In many cases an interupt is not necessary.

Can mean many things, may be long length of code but actually be a very short period of time.

Tom.... :smiley: :+1: :coffee: :australia:

What would this interrupt code do?

Main function is sending text in Morse code and/or RTTY code through port 4, wich can take 5-20 seconds depending on text length.

By interrupt I want to stop sending somwhere in the middle of text, also want to change the sending speed (by rotary encoder, or potencimeter) while text is beeing transmitted.

I tried out pin change interrupt but seems it doesn’t work.

Some tested and working example will be fine.

But the time between character is never fixed and during that time you can code to check your changed speed.

You need to post your code so we can see what you are doing. Your long lasting sub will have to be modified for this to work whether you use interrupts or not.

//for Arduino UNO
// to get work, connect NO NC pushbutton to pins 2,3 and gnd
// check output on pin 4

#pragma once
#include "libraries/common.h" 
#include "libraries/rtty.h"
#include "libraries/cw.h"



Common co;
CW cw;
Rtty rt;
int typeSelectPin; // switch btw CW and RTTY
int contentSelectPin1; // output
int contentSelectPin2; // inverse output (rtty only)
void setup() {
      // put your setup code here, to run once:
    co.setup(9600);  //serial 9600
    co.setPinSet(2); // pushbutton for start, NO-pin2, NC-pin3, common to GND
    co.setPinReset(3); //pushbutton NO
    cw.setCwOutputPin(4); //cw out
    cw.setWps(60); //words per minute 60,70, 80, 90 110 or 120,, select during interrupt
    cw.setLedPin(13);
    cw.setPermToSend(true); // permission to send cw, this should be changed by interrupt
    rt.setRttyOutputPin(4); //rtty out
    rt.setRttyOutputPinInverse(5); //rtty inverse out
    rt.setBitRate(50); //rtty bit rate 45,50,75, 100 or 150 bps , select during interrupt
    rt.setLedPin(13);
    rt.setPermToSend(true); // stop sending when false, this should be changed by interrupt
    typeSelectPin=6; //HIGH=sending telegraphy, LOW=teletype
    contentSelectPin1=7; // combination 7 ,8 will determine the output text
    contentSelectPin2=8;
    pinMode(typeSelectPin,INPUT_PULLUP);
    pinMode(contentSelectPin1,INPUT_PULLUP);
    pinMode(contentSelectPin2,INPUT_PULLUP);
  
}

//******************************************************
void loop() {
  //when pushbutton pressed
  sendOut(); 
  //wait for next press 
  
}
//=================================================

void sendOut(){
  int sp=0; // select what to send
 
  sp=2*digitalRead(contentSelectPin2)+digitalRead(contentSelectPin1); //0,1,2,3
  //Serial.println(sp);
  if (co.once()){ // press the pushbutton and go through loop only once
    if (digitalRead(typeSelectPin)==true){
      Serial.println("CW");
      Serial.print("sp=");
      Serial.println(sp);
      if (sp==0) cw.sendAbc();
      if (sp==1) cw.sendRy();
      if (sp==2) cw.sendFox();
      if (sp==3) cw.sendChar("a");
      
    }
    else{
      Serial.println("RTTY");
      Serial.print("sp=");
      Serial.println(sp);

      if (sp==0) rt.sendAbc();
      if (sp==1) rt.sendRY();
      if (sp==2) rt.sendFox();
      if (sp==3) rt.sendChar("ry");
      
    }
  }
}

//**********************************************************************

class Common{
 
  private: int pinSet=2;
  private: int pinReset=3;
  //public: const int PIN_CW=4;
  //public: const int PIN_RTTY=4;
  //public: const int PIN_RTTY_INVERSE=5;
  private: boolean Q=false;
  // pin assigments
  public: void setPinSet(int newPin){
      pinSet=newPin;
  }
  public: void setPinReset(int newPin){
      pinReset=newPin;
  }

  public: boolean once(){
    return once(pinSet,pinReset);
  }
  private: boolean once(int pinSet, int pinReset){
    int i=0;
    setInput_pullup(pinSet);
    setInput_pullup(pinReset);

    if (Q==false && digitalRead(pinSet)==false){
      i=2;
    }
    if (Q==true && digitalRead(pinReset)==false){
      i=3;
    }
    if (digitalRead(pinSet)==false && digitalRead(pinReset)==true) Q=true;
    if (digitalRead(pinReset)==false && digitalRead(pinSet)==true) Q=false;
    if (i==2) return true;
    return false;
  }
  public: void setup(int bps) {
    //9600
    Serial.begin(bps);  // Initialize serial communication at 115200 baud
  }

  private: void setInput_pullup(int pinNumber){
    pinMode(pinNumber,INPUT_PULLUP);
  }


};

//****************************************************************************

class CW{
  private: const int CW_CODE[96]={      
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    0xf8,0x78,0x38,0x18,0x8,00,0x80,0xc0,\
    0xe0,0xf0,0,0,0,0,0,0,\		
    0,0x40,0x80,0xa0,0x80,00,0x20,0xc0,\ 
    0,0,0x70,0xa0,0x40,0xc0,0x80,0xe0,\	
    0x60,0xd0,0x40,0,0x80,0x20,0x10,0x60,\
    0x90,0xb0,0xc0,0,0,0,0,0		//x-z
    // a=._ = 

  };
  private: const int CW_DUZINA[96]={
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,\
    0x5,0x5,0,0,0,0,0,0,\
    0,0x2,0x4,0x4,0x3,0x1,0x4,0x3,\
    0x4,0x2,0x4,0x3,0x4,0x2,0x2,0x3,\
    0x4,0x4,0x3,0x3,0x1,0x3,0x4,0x3,\
    0x4,0x4,0x4,0,0,0,0,0
  };

  // ascii (A)= 61; CW_CODE[61]= 0x40 = 0b01000000; 
  //CW_DUZINA (length) [61]=0x2=2; output= first 2 bites of b01000000
  //output = dot, dash
  private: const char ABC[36]={
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "
    };

  private: const char FOX[43]={
    "The quick brown fox jumps over the lazy dog"
  };

  private: const char RY[36]={
    "ryryryryryryryryryryryryryryryryryry"
  };

  private: int cwDotLength=100; //ms
  private: int cwOutputPin=4;
  private: int ledPin=13;
  private: boolean permissionToSend=true;

  public: void setCwOutputPin(int newOutputPin){
    cwOutputPin=newOutputPin;
  }

  public: void setPermToSend(boolean newPerm){
    permissionToSend=newPerm;
  }
  //public: void setDotLength(int newLength){
  //    cwDotLength=newLength;
  //}

  public: void setWps(int newWps){
    if (newWps==60) cwDotLength=75;
    if (newWps==70) cwDotLength=72;
    if (newWps==80) cwDotLength=64;
    if (newWps==90) cwDotLength=56;
    if (newWps==110) cwDotLength=50;
    if (newWps==120) cwDotLength=40;
  }

  public: void setLedPin(int newPin){
    ledPin=newPin;
  }
  public: void sendAbc(){
    sendCW(ABC);
  }

  public: void sendFox(){
    sendCW(FOX);
  }

  public: void sendRy(){
    sendCW(RY);
  }

  public: void sendChar(char sToSend[]){
      sendCW(sToSend);
  }
  public: void sendCW(char sToSend[]){
    int length=strlen(sToSend);
    for (int i=0; i<length; i=i+1){
      if (permissionToSend) {
        sendCW1a(sToSend[i]);
        delay(cwDotLength*2);
      }
    }
    
  }

  private: int getAscii(char c){
    return (int)c;
  }
  private: void sendCW1a(char c){
    if (c==' '){
      delay (cwDotLength*5);
      return;
    }
    char upperC=toupper(c);
    int ascii=getAscii(upperC);
    int cwKod=CW_CODE[ascii];
    int cwDuzina=CW_DUZINA[ascii];
    boolean bi[8];
    byte b=(byte)cwKod;
    boolean bitToSend;
    for (int i=7; i>=0;i=i-1){
      bi[i]=((b >>i) & 0x1);
    }
    for (int i=0 ;i<cwDuzina;i=i+1){
      sendCW0(bi[7-i]);
    }
  return;
}

  private: void sendCW0( boolean bitToSend){
      if (bitToSend==true){
        digitalWrite(cwOutputPin, HIGH);
        digitalWrite(ledPin,HIGH);
        delay(cwDotLength*3);
        digitalWrite(cwOutputPin, LOW);
        digitalWrite(ledPin,LOW);
        delay(cwDotLength);
        //Serial.println("dash");

        return;
      }
      else{
        digitalWrite(cwOutputPin, HIGH);
        digitalWrite(ledPin,HIGH);
        delay(cwDotLength);
        digitalWrite(cwOutputPin, LOW);
        digitalWrite(ledPin,LOW);
        delay(cwDotLength);
        //Serial.println("dot0");
        return;
      }
  
  }
};

//********************************************************

class Rtty{
 
  private: const int RTTY[96]={
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    0x4,0,0,0,0x1f,0,0,0,0,0,0,0,0,0,0,0,\
    0xd,0x1d,0x19,0x10,0xa,0x1,0x15,0x1c,\
    0xc,0x3,0,0,0,0,0,0,\
    0,0x18,0x13,0xe,0x12,0x10,0x16,0xb,\
    0x5,0xc,0x1a,0x1e,0x9,0x7,0x6,0x3,\
    0xd,0x1d,0xa,0x14,0x1,0x1c,0xf,0x19,\
    0x17,0x15,0x11,0,0,0,0x1b,0
  };

  private: const char ABC[36]={
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "
    };
  private: const char FOX[43]={
    "The quick brown fox jumps over the lazy dog"
  };

  private: const char RY[36]={
    "ryryryryryryryryryryryryryryryryryry"
  };

  private: int rttyBitDuration=20; //20 ms trajanje
  private: int rttyOutputPin=4;
  private: int rttyOutputPinInverse=5;
  private: int ledPin=13;
  private: boolean permissionToSend=true;
  public: setRttyOutputPin(int newOutputPin){
    rttyOutputPin=newOutputPin;
  }
  public: void setPermToSend(boolean newPerm){
    permissionToSend=newPerm;
  }
  //public: setRttyBitLength(int newLength){
  //  rttyBitDuration=newLength;
  //}
  public: void setBitRate(int newBitRate){
    if (newBitRate==45){}
    if (newBitRate==50) rttyBitDuration=20;
    if (newBitRate==75){}
    if (newBitRate==100){}
    if (newBitRate==150){}
    return;
  }
  public: void setRttyOutputPinInverse(int newPin){
    rttyOutputPinInverse=newPin;
  }
  public: void sendRY(){
    sendRtty(RY);
  }
  public: void sendFox(){
    sendRtty(FOX);
  }

  public: void sendAbc(){
    sendRtty(ABC);
  }

  public: void sendChar(char sToSend[]){
      sendRtty(sToSend);
  }
  public: void sendRtty(char sToSend[]){
    int length=strlen(sToSend);
    for (int i=0; i<length; i=i+1){
      if (permissionToSend) sendRtty1(sToSend[i]);
    }
  }

  public: void setLedPin(int newPin){
    ledPin=newPin;
  }
  private: void sendRtty1(char c){
    //start,least,...,most,stop,sop
    int ascii=getAscii(toupper(c));
    int rttyCode=RTTY[ascii];
    byte b=rttyCode;
    boolean bi[5];
    for (int i=4; i>=0;i=i-1){
      bi[i]=((b >>i) & 0x1);
    }
    rtty0(false);
    for (int i=0; i<=4; i=i+1){
      rtty0(bi[i]);
    }
    rtty0(true);
    rtty0(true);
  }
  private: void rtty0(boolean bitToSend){
    if (bitToSend==true){
      digitalWrite(rttyOutputPin, HIGH);
      digitalWrite(rttyOutputPinInverse, LOW);
      digitalWrite(ledPin,HIGH);
    }
    else {
      digitalWrite(rttyOutputPin, LOW);
      digitalWrite(rttyOutputPinInverse, HIGH);
      digitalWrite(ledPin,LOW);
    }
    delay(rttyBitDuration);
    return;
  }
  private: int getAscii(char c){
    return (int)c;
  }
};

Obviously, you made one or more mistakes in the code or the wiring.

Always start with a library example and verify that it works, before making any changes or writing any code of your own.

Your code is very confusing and you should turn on warnings. There really are a lot of warnings when compiling, and some of them are in fact errors.

Some comments could help understanding. Why do you make things so unnecessarily complicated?

The main problem regarding your question is that the code is blocking. Send your bits without blocking in loop() with millis(). Then you can always look in loop() for your buttons/switches.

Sorry for the confused code, I’m just a hobbyst

On the other hand, the code is working well on my equipment.

The classes Common, RTTy and CW must be saved in appropriate header files

It was written in Arduino IDE version 2.3.6

Almost everyone who asks questions here in the forum is a hobbyist. That's not a reason to write confused code. I think you have to learn a bit more :wink:

This does not mean that it is correct and cannot be improved. And with regard to your desired extension, you are stuck in a dead end with this code. When I look at all the warnings, it seems to be a coincidence that it works.