Problem with Interrupts in a Morse Code Sketch

Hello Everyone,

This is my first real attempt to use an interrupt with Arduino, and so far it has been a dismal failure. I am trying to create a Morse Code sketch that will have two modes. I am using the Morse.h library from Mark Fickett

https://github.com/markfickett/arduinomorse

Mode 1 will output a sting in Morse code and should keep doing that until the mode switch has been changed.
Mode 2 will ouput a string in Morse code and wait for user input, then loop back through. I will use this mode eventually to add an interactive feature.

The issue I am having is the Arduino sees the change in the button state, but it sounds like it is blasting through all the dots and dashes when it uses the morse.h library to beep out the string.

I would appreciate any help or ideas on how I can do this.

/*  
 Code Generator/Simulator

 This sketch has two modes that are controlled by a switch connected to pin 3. There
 is also a speaker connected to pin 6 that has a 220ohm resistor on it. 
 Mode 1 will ouput the message "Welcome to the communications school in Morse code.
 Mode 2 will output a message in Morse code wait for a user to interact with the Morse 
 key button on pin 2 - Still working on that part. 

 This sketch uses the Morse.h library by Mark Fickett

 https://github.com/markfickett/arduinomorse
 
*/


#include <Morse.h>

#define MORSE_PIN 6
#define MORSE_WPM 8

Morse morse(MORSE_PIN, MORSE_WPM, 1);

// Setup the interupt in pin2 to change modes
const int modeButton = 3;    // Changes modes
const uint8_t morseKey = 2;  // future use to allow a person to type out morse responses
int modestate = LOW;
int lastButtonState = LOW;

long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers


void setup()
{
  // initialize the pin as an input
  pinMode(morseKey, INPUT);  // sets pin 2 to input
  pinMode(modeButton, INPUT_PULLUP); // sets pin 3 to input and uses the pullup builtin
  attachInterrupt(digitalPinToInterrupt(modeButton), ISRMode, CHANGE);  // trigger an interupt based on the pin2 button change state
 
  // initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial)
  {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("Connecting");
  Serial.println();
}

void loop()
{
}

// This function gets called in the ISRMode interupt function and loops a generic message. 
void welcome()  
{
  while (modestate == LOW) {    // as long as the modeButton is LOW keep looping
    Serial.println("welcome to the communications school"); // prints the mode for debugging
    morse.sendmsg("welcome to the communications school"); // using morse.h library to beep out this string
    delay(3000);
    Serial.println("Restart Loop");
  }
}

// This function gets called in the ISRMode interupt function and loops a request for listener input and waits for some time. I want to eventually make this translate what
// is input and have the arduino respond
void interactive() {   
  while (modestate == HIGH) {    // as long as the modeButton is HIGH keep looping
    Serial.println("Interactive Mode"); // prints the mode for debugging
    morse.sendmsg("V V V BOB DE HEB    INTQSA"); // using morse.h library to beep out this string
    delay(10000); // Give user time to input code
    morse.sendmsg("DE HEV QSA 5");        // using morse.h library to beep out this string
    delay(5000); // Give user time to input code
    morse.sendmsg("BOB QRX 3");    // using morse.h library to beep out this string
    delay(3000);
    Serial.println("Restart Loop");
  }
}

// This is called in the interupt on pin 3 and decides what function it should call based on the status of modeButton 
void ISRMode() {
  int modestate = digitalRead(modeButton);  
        if (modestate == HIGH) {
        interactive();
      }
      else
        welcome();
    }

Why are you using an interrupt?

Coding Badly,

I thought it would be the best thing to use so that if the button changes state it will change to that mode. Is there an easier way to do that? Or any ideas? Thanks in advance.

Yes. Start here…

Thanks Coding,

So if i use the readButton function in my main loop, it will continue to pool that button even if the readbutton function has called one of the funtions in my code that beeps out the morse code? Or will it wait until that function is done and then go back to the main loop and repool the button via the readButton function.

Sorry if these questions are dumb. I took a class on programming, but it just taught the basics. So i am trying to learn as much as I can.

Thanks.

Thales1975:
So if i use the readButton function in my main loop, it will continue to pool that button even if the readbutton function has called one of the funtions in my code that beeps out the morse code? Or will it wait until that function is done and then go back to the main loop and repool the button via the readButton function.

No, it will check button state when the loop comes back there.

You need to understand that your interrupt will not be interrupted by an interrupt of the same level. So when you trigger your interrupt, it runs its code (you can't be using Serial.print in the interrupt by the way) and conceptually as you have a while in there your interrupt routine won't be stopped until the button changes state... Can't work.

In general you want to keep interrupt's code to something super short and fast to execute, like setting a flag on a volatile variable that you can check in the rest of your code to see if something happened.

An interrupt does not stop the current execution of a program, just pauses it to do something else, then the original program continues. So in your case, even if you were just setting up a flag and looping in the main loop to send morse code, it would resume where you interrupted it.

The library you are using is most likely NOT the one you are referencing at the beginning of your code but rather this one which is a blocking send you can't stop and uses delay() to manage the timing... but Inside the interrupt function, delay() won't work and the value returned by millis() will not increment...

So long story short your whole design is flawed...

Don't use interrupt for this, use a library that is asynchronous so that sending a string returns immediately and you call a polling function in the library often enough to send the next bit in due time, which the library manages for you such as the one in the link you referenced but don't use. In your loop you check for the state of the button - if you insist on using interrupt raise a flag in your interrupt and check for that flag but it's totally useless for a button (which you need to debounce)) - and if state has changed dismiss the current sending request and go do something else you want to achieve.