Interrupts to play WAV on Arduino Zero

Hi Guys,

I'm trying to get interrupts working without much success on a Firecricket (similar to a Zero - Cortex M0+ uC). What I'm ultimately trying to do is use this board as a lightsaber sound card, i.e. I want Hum to play after Open plays, based on a switch pulling pin 1 low, and have Close interrupt Hum when the switch is reset by playing and then stopping all sound output.

Can anyone be of any assistance with this? This is my code:

SdFat SD;
SamdAudio AudioPlayer;

const unsigned int sampleRate = 22050;
const int LEDpin = 26;
const int buttonpin = 1;
int startSound = 0;

void setup() {
pinMode(LEDpin, OUTPUT);
pinMode(buttonpin, INPUT_PULLUP);

attachInterrupt(digitalPinToInterrupt(buttonpin), turnon, FALLING);
attachInterrupt(digitalPinToInterrupt(buttonpin), turnoff, RISING);

Serial.begin(9600);

if (!SD.begin(10)) {
digitalWrite(LEDpin, HIGH);
delay(250);
digitalWrite(LEDpin, LOW);
}
}

void loop() {
if (startSound == 1) {
digitalWrite(LEDpin, HIGH);
AudioPlayer.begin(sampleRate);
AudioPlayer.play("Hum.wav", 0);
delay(95000);
}
}

void turnon() {
Serial.println("Turn On");
startSound = 1;
digitalWrite(LEDpin, HIGH);
AudioPlayer.begin(sampleRate);
AudioPlayer.play("Open.wav", 0);
}

void turnoff() {
Serial.println("Turn Off");
AudioPlayer.begin(sampleRate);
AudioPlayer.play("Close.wav", 0);
digitalWrite(LEDpin, LOW);
startSound = 0;
AudioPlayer.end();
}

Cheers!

Edit: Should have said the input is changing from 1 to 0 when I push the switch, but for some reason the interrupt isn't being triggered...

Ok I've figured out the interrupt - needed to specify pin 13 instead of 1 in the attachinterrupt function.

The next issues I'm having is having the audio loop Hum play without a 'click' noise every time it starts over. I'm guessing this will be near impossible to eliminate given the cpu needs time to execute?

Try the CHANGE interrupt and then inside the interrupt yourself read the pin to find out if it just went HIGH or just went LOW.

I don't know much about the Zero but it's normally an exceptionally bad idea to spend more than a hundred microseconds inside an interrupt. Set an indicator of which sound to play and get out of the interrupt as quickly as possible.

Ok thanks, so below is my updated code. What I was aiming for was to use the interrupt to stop the playing of the Hum audio file which runs for 90 seconds with the use of a delay() function. Without the delay function it tries to play the audio file every cpu cycle, ie I just get static effectively.

const int LEDpin = 26;
const int buttonpin = 1;
const int iopin = 13;

int state = 3;

void setup() {
pinMode(LEDpin, OUTPUT);
pinMode(buttonpin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(iopin), nextState, CHANGE);

AudioPlayer.begin(sampleRate);
Serial.begin(9600);
}
void loop() {
if (state == 0) {
Serial.println("Opened Saber");
AudioPlayer.play("Open.wav", 0);
digitalWrite(LEDpin, HIGH);
delay(600);
state = 1;
} else if (state == 1) {
Serial.println("Saber Humming");
AudioPlayer.play("Hum.wav",0);
delay(90000);
} else if (state == 2) {
Serial.println("Close Saber");
AudioPlayer.play("Close.wav",0);
digitalWrite(LEDpin, LOW);
AudioPlayer.end();
state = 3;
} else {
Serial.println("Waiting");
delay(1000);
}
}

void nextState() {
if (state == 3) {
state = 0;
} else {
state = 2;
}
}

Is there anyway I can use an interrupt to, well, interrupt the delay()?

The better way would be not to use delays at all.

http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html

Can you give an example in the context of my application? I'm not understanding how this example could be applied to my application given when the switch is deactivated is not fixed in time.

OK, let's assume you're in State 0. The "open" sound is playing. You need to stay in that state for 600ms. So the big switch-case should look at the time it entered state 0 and only proceed to state 1 after 600ms has passed.

You're going to end up with more states, but that's no problem. Just draw them all on a piece of paper with arrows going between them.

So, after 600ms in state 0, you should start playing the hum sound, record the time you started it and then make the state move on to state 1.

State 1 waits 90,000ms then plays the last sound, records the time and moves to the next state.

This won't change your current code by very much. However it does mean that you have to duplicate a lot of the start-sound-record-time stuff in the interrupt. It would be better to have a LOT more states, so that the interrupt can direct you to the intermediate state which starts the sound and then immediately exits that state into the appropriate waiting state.

Thanks guys, all I found I really needed to do was:

void hum() {
AudioPlayer.play("Hum.wav",0);
while (digitalRead(buttonpin) == LOW && count < 900) {
delay(100);
count++;
}
}