Can't figure out what's wrong with my wave shield code

I’m in the process of building a remote control robot with a speaker on it. Here are the details of the design:

  • I’m using an Arduino Uno
  • I have a Wave Shield stacked on top of a motor controller shield.
  • I need pins 2 and 3 for the interrupts that read the from the RC receiver, so I had to move the wave shield DAC from pins 2-5 to 4-7. I did this in “WavePinDefs.h” Not sure if this is relevant but figured it was worth mentioning.
  • I tried a couple of WaveHC example files and they all work so I’m pretty sure it’s not a hardware problem

Ideally, I’d want one stick on my transmitter to control the drive motor (forward/reverse) and the other stick to control the audio. However I have a couple problems that may or may not be related. First of all, the audio won’t play at all unless I include a while loop directly following the play() function call. Unfortunately, this means I can’t drive the robot until the song is finished. Weirder: it has to be right after the play()… if I move the while loop to the main loop just following the call to playfile() (marked “THIS SPOT” in the code below), then I don’t get any audio and I haven’t the faintest idea what the difference is. The robot just completely dies as soon as the audio stick is pushed.

Worse, even after the song is done playing, I can’t get the robot to drive forward/reverse any more (until I reset). My debug statements clearly show that the interrupts are working and that it’s mapping the inputs properly, but apparently the analogwrites aren’t working? Apparently one of the WaveHC calls breaks something? No clue.

To summarize, the code below does the following: At first the drive works normally. If at any point the audio stick is pushed, the song plays and it doesn’t respond until the song is over. After the song is over, the drive no longer operates even though the debug statements show the mapping is working. Pushing the audio stick starts the song again.

Any help making this code work as intended would be much appreciated! I have no idea what’s wrong!

#include "WaveUtil.h"
#include "WaveHC.h"

#define dir 8             // pin 8
#define pwm_cy 9          // pin 9
#define Chan1Interrupt 1  // corresponds to pin 3 (int.1 = 3)
#define Chan2Interrupt 0  // corresponds to pin 2 (int.0 = 2)

// Remember: LCS, CLK, DI, and LAT were moved to pins 4-7
// Untouched: CCS, MOSI, MISO, SCK (pins 10-13)

SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the volumes root directory
WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time

// Define macro to put error messages in flash memory
#define error(msg) error_P(PSTR(msg))

unsigned long Chan1_startPulse;
volatile double Chan1_val;
volatile double Chan1_val_last;
unsigned long Chan2_startPulse;
volatile double Chan2_val;
volatile double Chan2_val_last;
int pwmout = 0;

void setup()
{
  Serial.begin(9600);           // set up Serial library at 9600 bps for debugging
  
  putstring_nl("\nWave test!");  // say we woke up!
  
  putstring("Free RAM: ");       // This can help with debugging, running out of RAM is bad
  Serial.println(FreeRam());

  //  if (!card.init(true)) { //play with 4 MHz spi if 8MHz isn't working for you
  if (!card.init()) {         //play with 8 MHz spi (default faster!)  
    error("Card init. failed!");  // Something went wrong, lets print out why
  }
  
  // enable optimize read - some cards may timeout. Disable if you're having problems
  card.partialBlockRead(true);
  
  // Now we will look for a FAT partition!
  uint8_t part;
  for (part = 0; part < 5; part++) {   // we have up to 5 slots to look in
    if (vol.init(card, part)) 
      break;                           // we found one, lets bail
  }
  if (part == 5) {                     // if we ended up not finding one  :(
    error("No valid FAT partition!");  // Something went wrong, lets print out why
  }
  
  // Lets tell the user about what we found
  putstring("Using partition ");
  Serial.print(part, DEC);
  putstring(", type is FAT");
  Serial.println(vol.fatType(), DEC);     // FAT16 or FAT32?
  
  // Try to open the root directory
  if (!root.openRoot(vol)) {
    error("Can't open root dir!");      // Something went wrong,
  }
  
  // Whew! We got past the tough parts.
  putstring_nl("Files found (* = fragmented):");

  // Print out all of the files in all the directories.
  root.ls(LS_R | LS_FLAG_FRAGMENTED);
  
  attachInterrupt(Chan1Interrupt, Chan1_begin, RISING);
  attachInterrupt(Chan2Interrupt, Chan2_begin, RISING);
  pinMode(dir, OUTPUT);
  pinMode(pwm_cy, OUTPUT);
}

void loop()
{ 
  //stick is pushed forward
  if (Chan1_val > 1488 && Chan1_val <= 1910) {
    pwmout = map(Chan1_val,1488,1910,5,255);
//    if (pwmout >= 240)
//      pwmout = 255;
    digitalWrite(dir, 0);
    analogWrite(pwm_cy, pwmout);
  }
  //stick is pushed reverse
  else if (Chan1_val >= 1040 && Chan1_val < 1460) {
    pwmout = map(Chan1_val,1460,1040,5,255);
//    if (pwmout >= 240)
//      pwmout = 255;
    digitalWrite(dir, 1);
    analogWrite(pwm_cy, pwmout);
  }
  //stick is neutral
  else {
    analogWrite(pwm_cy, 0);
    pwmout = 0;
  }
 
  //report out 
  Serial.print(Chan1_val);
  Serial.print(" || ");
  Serial.println(pwmout);
  
  //play the wav file if stick 2 is pressed down
  if(Chan2_val > 1650 && Chan2_val <= 1910) {
    playfile("BRASS.WAV");
    //while(wave.isplaying); {} <<<< THIS SPOT
  }
  
}

void Chan1_begin()           // enter Chan1_begin when interrupt pin goes HIGH.
        {
          Chan1_startPulse = micros();     // record microseconds() value as Chan1_startPulse
          detachInterrupt(Chan1Interrupt);  // after recording the value, detach the interrupt from Chan1_begin
          attachInterrupt(Chan1Interrupt, Chan1_end, FALLING); // re-attach the interrupt as Chan1_end, so we can record the value when it goes low
        }

void Chan1_end() 
        {
         Chan1_val = micros() - Chan1_startPulse;  // when interrupt pin goes LOW, record the total pulse length by subtracting previous start value from current micros() vlaue. 
         detachInterrupt(Chan1Interrupt);  // detach and get ready to go HIGH again
         attachInterrupt(Chan1Interrupt, Chan1_begin, RISING);
         if (Chan1_val < 1000 || Chan1_val > 3000) { Chan1_val = Chan1_val_last;}
         else {Chan1_val_last = Chan1_val;}
        }
        
void Chan2_begin()           // enter Chan1_begin when interrupt pin goes HIGH.
        {
          Chan2_startPulse = micros();     // record microseconds() value as Chan1_startPulse
          detachInterrupt(Chan2Interrupt);  // after recording the value, detach the interrupt from Chan1_begin
          attachInterrupt(Chan2Interrupt, Chan2_end, FALLING); // re-attach the interrupt as Chan1_end, so we can record the value when it goes low
        }

void Chan2_end() 
        {
         Chan2_val = micros() - Chan2_startPulse;  // when interrupt pin goes LOW, record the total pulse length by subtracting previous start value from current micros() vlaue. 
         detachInterrupt(Chan2Interrupt);  // detach and get ready to go HIGH again
         attachInterrupt(Chan2Interrupt, Chan2_begin, RISING);
         if (Chan2_val < 1000 || Chan2_val > 3000) { Chan2_val = Chan2_val_last;}
         else {Chan2_val_last = Chan2_val;}
        }
        
        
//print error message and halt
void error_P(const char *str) {
  PgmPrint("Error: ");
  SerialPrint_P(str);
  sdErrorCheck();
  while(1);
}

// print error message and halt if SD I/O error, great for debugging!
void sdErrorCheck(void) {
  if (!card.errorCode()) return;
  PgmPrint("\r\nSD I/O error: ");
  Serial.print(card.errorCode(), HEX);
  PgmPrint(", ");
  Serial.println(card.errorData(), HEX);
  while(1);
}

void playfile(char *name) {
  FatReader f;
    // see if the wave object is currently doing something
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it
  }
  // look in the root directory and open the file
  if (!f.open(root, name)) {
    putstring("Couldn't open file "); Serial.print(name); return;
  }
  // OK read the file and turn it into a wave object
  if (!wave.create(f)) {
    putstring_nl("Not a valid WAV"); return;
  }
  
  // ok time to play! start playback
  Serial.println("Should play now...");
  wave.play();
  while(wave.isplaying); {}
}

Hi, welcome to the forum.

You can copy a link in your post.
Is this the Wave Shield ? Adafruit Wave Shield for Arduino Kit [v1.1] : ID 94 : $22.00 : Adafruit Industries, Unique & fun DIY electronics and kits
But which Motor Controller Shield are you using ?

Sorry, yes that's the wave shield. The motor controller is this one: http://cytron.com.my/viewProduct.php?pcode=SHIELD-MD10&name=Cytron%2010A%20Motor%20Driver%20Shield

That motor driver is for a single motor, and they made it very flexible with the jumpers.
You have pin 8 for direction and pin 9 for PWM. So far so good, pin 9 is indeed PWM on the Arduino Uno.
And a simple analogWrite() is used for the PWM : http://arduino.cc/en/Reference/analogWrite

I can't see in the schematic which pins are used by the wave shield. It seems to be 2...7. And maybe a red led at pin 9 ?
There is MMC_CS (pin 6) and POTSW (pin 7).
The file "WavePinDefs.h" is in the old libraries. It is not in the new library.
You can use this to see which port and pin it is: http://arduino.cc/en/Hacking/PinMapping

I think you have to look at the analog inputs for free pins. They can be used as digital pins.
Using A0 and A1 would be something like this:

// pin 2 is DAC chip select -> now A0 !

/** Data direction register for DAC chip select. */
#define MCP_DAC_CS_DDR  DDRC
/** Port register for DAC chip select. */
#define MCP_DAC_CS_PORT PORTC
/** Port bit number for DAC chip select. */
#define MCP_DAC_CS_BIT  PORTC0

// pin 3 is DAC serial clock -> now A1 !
/** Data direction register for DAC clock. */
#define MCP_DAC_SCK_DDR  DDRC
/** Port register for DAC clock. */
#define MCP_DAC_SCK_PORT PORTC
/** Port bit number for DAC clock. */
#define MCP_DAC_SCK_BIT  PORTC1

I'm having troubles understanding the schematics and code. I think Adafruit should improve both. I have even second thoughts about the hardware design of the board. I hope you understand it better than I do :roll_eyes:
You can try the new libraries, but I did not check that for the definition of the pins.

I think I have the most recent version of the library? I got it from here (this is what is linked on adafruit) : Google Code Archive - Long-term storage for Google Code Project Hosting.

Some of the pins for the wave shield are flexible. You can connect them to whichever pin on the header you want using physical wire jumpers. In my case, I'm using pins 4-7 for LCS, CLK, DI, and LAT. There should be no pin conflicts; the motor controller only uses pins 8 and 9.

In any case, I'm pretty sure it's not a hardware problem because the example code provided with the library works perfectly. It's only when I try to integrate audio into my working motor driver code that things get weird.

You are right, that is the new library. Sorry, I got confused with the old and new code.

I want to be sure that the hardware is okay, but I’m not sure…
However, I took a look at your sketch.

I have not seen an attachInterrupt() and detachInterrupt() inside its own interrupt service routine yet. Can you use a single interrupt routine, and trigger at CHANGE ? I think you can do a digitalRead() to check the value of the pin.

The value ‘double’ is actually a ‘float’. But please use unsigned long when you use millis() or micros().

You use ‘volatile’ for the global variables that are changed in the interrupt routine. That is very good, but that’s not all.
For example, when you test such a variable in loop() with an if-statement, the value could change in between the retrieving of that 4-byte value.
In the example below, a local variable is used to test in the if-statement, and the value of the volatile variable is copied while the interrupts are off.

volatile unsgined long Chan1_value;
...
void loop() {
  ...
  noInterrupts();
  unsigned long value = Chan1_value;
  interrupts();

  if ( value > 100 && value < 500 ) {
     ...