0

Music Maker Affects Timing
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Music Maker Affects Timing

by augydoggy on Thu Dec 22, 2016 5:23 pm

I'm using a Mega 2560 with a Music Maker and Motor shield (v2.3) to combine sounds with motor driven actions. I've found that if I run the program without the Music Maker the motor commands will run on the programmed time every time. When I include the Music Maker it throws off the motor timing by as much as 9 seconds. I'm trying to synchronize the motor to the sounds but none of my changes are consistent with the commands I put in. If I remove the Music Maker from the sketch the timing adjustments follow what I program in.

I'll include my sketch here. Please note that this sketch is made to run 7 different scenes activated by pushbuttons, one scene at a time. The scene I'm working with is "void waterspout", second from last in the sketch.
The timing notes in the sketch don't match the actual commands because I've been making changes.

-Tom

Code: Select all | TOGGLE FULL SIZE
// Model railroad animations sketch




// include SPI, MP3 and SD libraries

#include <SPI.h>

#include <Adafruit_VS1053.h>

#include <SD.h>

#include <Wire.h>

#include <Adafruit_MotorShield.h>

#include "utility/Adafruit_MS_PWMServoDriver.h"




// define the pins used

//#define CLK 13       // SPI Clock, shared with SD card

//#define MISO 12      // Input data, from VS1053/SD card

//#define MOSI 11      // Output data, to VS1053/SD card

// Connect CLK, MISO and MOSI to hardware SPI pins.

// See http://arduino.cc/en/Reference/SPI "Connections"




// These are the pins used for the music maker shield

#define SHIELD_RESET  -1      // VS1053 reset pin (unused!)

#define SHIELD_CS     7      // VS1053 chip select pin (output)

#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)




// These are common pins between breakout and shield

#define CARDCS 4     // Card chip select pin

// DREQ should be an Int pin, see http://arduino.cc/en/Reference/attachInterrupt

#define DREQ 3       // VS1053 Data request, ideally an Interrupt pin




Adafruit_VS1053_FilePlayer musicPlayer =

  // create shield-example object!

  Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);




// Create the motor shield object with the default I2C address

Adafruit_MotorShield AFMS = Adafruit_MotorShield();




// Assign some motors.

Adafruit_DCMotor *sandChuteMotor = AFMS.getMotor(1);

Adafruit_DCMotor *coalChuteMotor = AFMS.getMotor(2);

Adafruit_DCMotor *coalBucketMotor = AFMS.getMotor(3);

Adafruit_DCMotor *waterSpoutMotor = AFMS.getMotor(4);




int runXTimes = 0;




const int BUTTON_A = 21; // pin 21

const int BUTTON_B = 22; // pin 22

const int BUTTON_C = 23; // pin 23

const int BUTTON_D = 24; // pin 24

const int BUTTON_E = 25; // pin 25

const int BUTTON_F = 26; // pin 26

const int BUTTON_G = 27; // pin 27  //not connected


void setup() {
 
const int BUTTON_A = 21;  // ashpit

const int BUTTON_B = 22;  // sandhouse chute

const int BUTTON_C = 23;  // sandhouse compressor

const int BUTTON_D = 24;  // coaling tower chute

const int BUTTON_E = 25;  // coaling tower bucket

const int BUTTON_F = 26;  // water tank spout

const int BUTTON_G = 27;  // water tank spout, not connected







 // initialize seven inputs for use with pushbuttons. Normally HIGH, if pushed, goes to LOW.

  pinMode(BUTTON_A, INPUT_PULLUP);

  pinMode(BUTTON_B, INPUT_PULLUP);

  pinMode(BUTTON_C, INPUT_PULLUP);

  pinMode(BUTTON_D, INPUT_PULLUP);

  pinMode(BUTTON_E, INPUT_PULLUP);

  pinMode(BUTTON_F, INPUT_PULLUP);

  pinMode(BUTTON_G, INPUT_PULLUP);

  Serial.begin(9600);

  if (! musicPlayer.begin()) {
   
    // initialise the music player

     Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));

     while (1);   // Note that this loops forever if it can't find the musicPlayer.

  }
 
  SD.begin(CARDCS); 
 
  // initialise the SD card

  // Set volume for left, right channels. lower numbers == louder volume!

  musicPlayer.setVolume(30,30);




  // If DREQ is on an interrupt pin (on uno, #2 or #3) we can do background audio playing

  musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int




  AFMS.begin();  // create with the default frequency 1.6KHz

  sandChuteMotor->setSpeed(100);

  coalChuteMotor->setSpeed(100);

  coalBucketMotor->setSpeed(100);

  waterSpoutMotor->setSpeed(100);

 
}

void loop() {
  if (digitalRead(BUTTON_A) == LOW) {

    ashPit();   // button A was pressed

  }

  if (digitalRead(BUTTON_B) == LOW) {

    sandChute();    // button B was pushed

  }

  if (digitalRead(BUTTON_C) == LOW) {

    sandCompressor();    // button C was pressed

  }

  if (digitalRead(BUTTON_D) == LOW) {

    coalChute();    // button D was pressed

  }

  if (digitalRead(BUTTON_E) == LOW) {

    coalBucket();    // button E was pressed

  }

  if (digitalRead(BUTTON_F) == LOW) {

    waterSpout();    // button F was pressed

  }

  if (digitalRead(BUTTON_G) == LOW) {

    coalPit();    // button G was pressed

  }




}




void ashPit() {
  // play sound of fire and coals
  // sound file to drive LEDs, simulate fire and coals
  musicPlayer.startPlayingFile("track007.wav");
  // stop playing sound file
  musicPlayer.stopPlaying();

  }




void sandChute() {
  // play sand blowing sounds to go with chute operation
  musicPlayer.startPlayingFile("track001.wav");
  delay(15000);
  // turn on motor, chute lowers to sand dome 10 seconds 
  sandChuteMotor ->run(FORWARD);
  delay(2500);
  // stop motor
  sandChuteMotor ->run(RELEASE);
  // sand flows into dome 15 seconds
  delay(2500);
  // chute raises to stored position 10 seconds
  sandChuteMotor ->run(BACKWARD);
  delay(2500);
  // stop motor
  sandChuteMotor ->run(RELEASE);
  delay(2500);
  // stop playing sound file since the animation is done
  musicPlayer.stopPlaying();

}




void sandCompressor() {
  // sounds associated with sand drying process.
  // furnace and compressor sounds to go with door operation
  musicPlayer.startPlayingFile("track004.wav");
  // stop playing sound file since the animation is done
  musicPlayer.stopPlaying();

}







void coalChute() {
  // play sounds of coaling tower chute operation
  // run motor to animate coal chute. Use coalChuteMotor.
  // coal chute sound to go with chute operation
  musicPlayer.startPlayingFile("track002.wav");
  delay(5000);
  // turn on motor, chute lowers 10 seconds
  coalChuteMotor ->run(FORWARD);
  delay(10000);
  // stop motor
  coalChuteMotor ->run(RELEASE);
  // coal drops into tender 45 seconds
  delay(45000);
  // chute raises to stored position 10 seconds
  coalChuteMotor ->run(BACKWARD);
  delay(10000);
  // stop motor
  coalChuteMotor ->run(RELEASE);
  // stop playing sound file since the animation is done
  musicPlayer.stopPlaying();

  }




void coalBucket() {
  // play sounds of coaling tower bucket operation
  // run motor to animate coal bucket. Use coalBucketMotor.
  // coal bucket sounds to go with bucket operation
  musicPlayer.startPlayingFile("track003.wav");
  delay(5000);
  for (; runXTimes < 8; runXTimes++) {
  delay(4000);
  coalBucketMotor ->run(FORWARD);
  // turn on motor, bucket raises to top of tower
  delay(10000);
  coalBucketMotor ->run(RELEASE);
  delay(4000);
  // coal is dumped into tower
  coalBucketMotor ->run(BACKWARD);
  delay(10000);
  // bucket is lowered into pit
  coalBucketMotor ->run(RELEASE);}
  // stop playing sound file since the animation is done
  musicPlayer.stopPlaying();

  }




void waterSpout() {
  // play sounds of water filling operation

  delay(10000);
  // lower spout 8 seconds
  waterSpoutMotor ->run(FORWARD);
  delay(8000);
  // stop motor
  waterSpoutMotor ->run(RELEASE);
  // water flows 90 seconds
  delay(30000);
  // spout raises to stored position 8 seconds
  waterSpoutMotor ->run(BACKWARD);
  delay(8000);
  // stop motor
  waterSpoutMotor ->run(RELEASE);
  delay(10000);
  // stop playing sound file
  musicPlayer.stopPlaying();

  }   




void coalPit() {
  // play sounds of coal pit operation
  musicPlayer.startPlayingFile("track001.wav");
  delay(15000);
  // stop playing sound file since the animation is done
  musicPlayer.stopPlaying();

}

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Re: Music Maker Affects Timing

by adafruit_support_bill on Fri Dec 23, 2016 7:04 am

The interrupt handling in the music layer can cause contention with the timer interrupts and affect the millisecond timer used by delay(). You can try to add a 'fudge' factor to compensate for that. But the best way to assure accurate timing is to use an RTC: https://www.adafruit.com/products/3013

adafruit_support_bill
 
Posts: 77422
Joined: Sat Feb 07, 2009 10:11 am

Re: Music Maker Affects Timing

by augydoggy on Sat Dec 24, 2016 12:36 pm

Bill - I'll order the RTC, thanks.

I saw a blurb about not using "delay()" (https://learn.adafruit.com/multi-taskin ... y?view=all) because it sucks up all the processing time. Is this what's causing my timing problems? Is there a way to make this work with the DC motors I have in my sketch?

-Tom

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Re: Music Maker Affects Timing

by adafruit_support_bill on Sat Dec 24, 2016 3:36 pm

My first instinct was to post that same link for you to read. The problem is, I don't think it will help in this case. In my "multitasking the Arduino" guide I recommend using millis() instead of delay. But the millis() timer is also updated via timer interrupts. So if the Music Maker interrupt overhead is affecting the delay(), it is also most likely affecting millis().

That said, there are other benefits to using millis() instead of delay() as described in the multitasking guide series. So it is a worthwhile technique to learn.

adafruit_support_bill
 
Posts: 77422
Joined: Sat Feb 07, 2009 10:11 am

Re: Music Maker Affects Timing

by augydoggy on Sat Dec 24, 2016 4:09 pm

Bill - I was wondering if that was your article. I read it through several times and will try applying it on another project. Thanks mucho for your help here. Have a merry Christmas and enjoy the holidays.

-Tom

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Re: Music Maker Affects Timing

by augydoggy on Fri Jan 13, 2017 9:48 am

Bill - I've added a DS3231 precision RTC to my Mega 2560 as you suggested above. I downloaded the RTCLib and attempted to run the sketch ds3231 to check things out. It won't compile giving an "error compiling for board Arduino Mega 2560". I can see in void setup that the sketch is for Leonardo/Micro/Zero. Do I need to specify something different for the Mega?

- Tom

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Re: Music Maker Affects Timing

by adafruit_support_bill on Fri Jan 13, 2017 10:20 am

There should be more detail about the error. Usually the compiler gives you a list of errors specifying the nature of the error(s) and the line number(s) where they were detected. It is best to start at the top of the list, since the later errors may be just complications from the earlier ones.

adafruit_support_bill
 
Posts: 77422
Joined: Sat Feb 07, 2009 10:11 am

Re: Music Maker Affects Timing

by augydoggy on Fri Jan 13, 2017 12:17 pm

Bill - thanks, got it running. In my sketch above (Model Railroad animations sketch) to get the RTC taking care of the timing do I only need to add:

#include <RTCLib.cpp>

#include <RTCLib.h>

- Tom

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Re: Music Maker Affects Timing

by adafruit_support_bill on Fri Jan 13, 2017 12:28 pm

Good news. Thanks for the follow-up.

adafruit_support_bill
 
Posts: 77422
Joined: Sat Feb 07, 2009 10:11 am

Re: Music Maker Affects Timing

by augydoggy on Fri Jan 13, 2017 4:41 pm

Bill - I got the RTC working with the Mega and also integrated into my sketch. I still have the same problem with major time delays between the Music Maker and the Motor shield. I also discovered that after one or two attempts the sound file becomes corrupt with distortion but only through the Music Maker. Still plays fine on my laptop.

I went back and searched some more and found someone else here who experienced a similar problem (viewtopic.php?f=31&t=85185&p=430778&hilit=music+maker+distortion#p430778) under the heading "wav files distortion on music maker". The problem turned out to be the bit rate versus the system clock. The bit rate for 16 bit 44.1KHz is 1.4Mbps x 2 (read and write) = 2.8Mbps.

I re-recorded my wav file as an Mp3 and it worked fine. So is this saying that the precision RTC is still too slow for playing 16 bit wav files or is there something I can do to fix it? At this point I have a lot of time and cash invested in my system and really prefer to use wav files for my project.

- Tom

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Re: Music Maker Affects Timing

by adafruit_support_bill on Fri Jan 13, 2017 6:00 pm

The RTC is not a factor in the music playback, since the resolution of the RTC is only 1 second. It sounds like you have found a solution for the music distortion, but are still having problems with the motor timing issue mentioned in your original post.

As mentioned above, the RTC resolution is only 1 second. But that very precise one-second pulse can be used to correct for timer drift. In your original post you said that your motor timing was off by 9 seconds. You can correct for that drift by using the RTC 'pulse-per-second' signal to re-sync the Arduino time.

adafruit_support_bill
 
Posts: 77422
Joined: Sat Feb 07, 2009 10:11 am

Re: Music Maker Affects Timing

by augydoggy on Fri Jan 13, 2017 6:32 pm

Bill - when I use the Mp3 file the distortion disappears and the timing problem goes away. I can synchronize the motor to the sound files to the millisecond. Adding the RTC made no difference at all when using a WAV file.

If there is something I can do with programming to make this work I'd really like to hear about it. How would you recommend I resync the Arduino time?

- Tom

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Re: Music Maker Affects Timing

by adafruit_support_bill on Sat Jan 14, 2017 7:17 am

In the code posted above, you are using delay() to time your motors. Delay uses the Arduino's internal timer which is subject to drift even in ideal conditions - and even more so when there is interrupt contention. Using the RTC, and the RTC Library, your motor timing will not be affected by interrupt contention from the Music Maker.

From what I can see, all of your timing is in multiples of 1 second, so that works nicely with the 1 second resolution of the RTC.

https://github.com/adafruit/RTClib

The datecalc example sketch shows how to use the DateTime and TimeSpan classes to do time calculatons.
https://github.com/adafruit/RTClib/blob ... tecalc.ino

adafruit_support_bill
 
Posts: 77422
Joined: Sat Feb 07, 2009 10:11 am

Re: Music Maker Affects Timing

by augydoggy on Sat Jan 14, 2017 9:19 am

Bill - I'm sorry if I'm being dense here. Are you saying I should ditch the delay() and replace it with the clock timing? Could you give me an example of how that should be written out?

- Tom

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Re: Music Maker Affects Timing

by augydoggy on Sat Jan 14, 2017 10:18 am

Is there a way to take the current DateTime and define it as T0 (or something similar) in setup? Maybe then I could put that at the beginning of each "scene" and use T0 with the TimeSpan throughout the rest of the scene. Is that possible? My concern is that there are seven separate "scenes" (void ashpit, void coalchute, etc) and I don't want things to get too complicated trying to serve all of them.

augydoggy
 
Posts: 64
Joined: Mon Jun 06, 2016 6:36 am

Please be positive and constructive with your questions and comments.