๐Ÿ“ฃ๐Ÿ“ช ๐Ÿšš Adafruit will not be shipping USPS orders Monday October 14, 2019 ๐Ÿ—“. Expedited USPS orders placed after 11am ET ๐Ÿ•š Friday October 11 will go out Tuesday October 15 ๐Ÿ“ฃ๐Ÿ“ช๐Ÿšš
0

Feather M0 Bluefruit LE + Music Maker Wing Woes
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Feather M0 Bluefruit LE + Music Maker Wing Woes

by clydgate on Thu Jan 04, 2018 4:22 am

I am working on bluetooth control for an astromech droid. User will make R2D2 advance, retreat, turn, and play R2 bleeps etc from the Bluefruit iphone app.

The Bluefruit stuff is working great. Same with the motorshield wing-- the motors spin etc. So overall I'm making progress. But I'm having trouble with the VS1053 board. On startup it successfully plays test sine waves and a couple of WAV files from the SD card. But after playing one or two files, it craps out and won't play any more files.

I am kinda concerned that the interrupt pin I'm using on the VS1053 is not compatible with the Bluefruit board... I'm using the default, copied from examples like simpleplayer

I hate to do this, but I'm gonna paste my code in hopes that someone will spot something obvious.

MAny thanks for any ideas...

here is the insanely long file:



Code: Select all | TOGGLE FULL SIZE
/*
  R2D2 rides again!
  the idea is to resurrect an old HASBRO R2D2 droid that has been inoperative for many parsecs.
  We tore out the guts but kept the following circuitry intact:
    a left motor
    a right motor,
    a motor that spins his head
    a speaker for bleeps and bloops.

    R2D2's original manual is at https://www.hasbro.com/common/instruct/87245.pdf

    The rebooted R2 will respond to commands from an iPhone App!
    We'll use Adafruit Bluefruit app.
    this code is adapted from Ada_BLE_RC, Adafruit VS1053 simple player, and bunch other stuff.

    Hardware:
      Adafruit Feather M0 Bluefruit LE microcontroller
      Feather Wing Motorshield
      Feather Wing VS1053 sound board
      we'll reuse R2's motors and speakers and battery case.

    Revision history:
      v1: feather plays nice with motorshield.
      v2: added bluetooth control thru the BLE object.
          you can control R2 with iphone app. also added primitive
          sound thru an old Adafruit Sound FX board.
      v3: installed Adafruit VS1053 board instead. This gives us sound files
          that we read from a disk. It also introduces a ton of weird defines
          that gum up the code.

    Known problems/bugs:
      soundboard craps out after playing a couple of sounds. maybe the delay() stuff is screwing it up?
      should get rid of the duration argument to the movement functions

    Future ideas:
    Chris Lydgate: Dec 27 2017
*/
#include <string.h>
#include <Arduino.h>
#include <SPI.h>
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"
#include "BluefruitConfig.h"

#if SOFTWARE_SERIAL_AVAILABLE
#include "SoftwareSerial.h"
#endif

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"
#include <SD.h>
#include <Adafruit_VS1053.h>

// Next comes a bunch of defines for the VS1053 featherwing. a lot of it seems preoccupied
// with switching around pins for different micros. we could probably take a bunch of it out
// since this code relies on a Feather M0.
#define VS1053_RESET   -1     // VS1053 reset pin (not used!)

// Feather M0 or 32u4
#if defined(__AVR__) || defined(ARDUINO_SAMD_FEATHER_M0)
#define VS1053_CS       6     // VS1053 chip select pin (output)
#define VS1053_DCS     10     // VS1053 Data/command select pin (output)
#define CARDCS          5     // Card chip select pin
// DREQ should be an Int pin *if possible* (not possible on 32u4)
#define VS1053_DREQ     9     // VS1053 Data request, ideally an Interrupt pin

// Feather ESP8266
#elif defined(ESP8266)
#define VS1053_CS      16     // VS1053 chip select pin (output)
#define VS1053_DCS     15     // VS1053 Data/command select pin (output)
#define CARDCS          2     // Card chip select pin
#define VS1053_DREQ     0     // VS1053 Data request, ideally an Interrupt pin

// Feather ESP32
#elif defined(ESP32)
#define VS1053_CS      32     // VS1053 chip select pin (output)
#define VS1053_DCS     33     // VS1053 Data/command select pin (output)
#define CARDCS         14     // Card chip select pin
#define VS1053_DREQ    15     // VS1053 Data request, ideally an Interrupt pin

// Feather Teensy3
#elif defined(TEENSYDUINO)
#define VS1053_CS       3     // VS1053 chip select pin (output)
#define VS1053_DCS     10     // VS1053 Data/command select pin (output)
#define CARDCS          8     // Card chip select pin
#define VS1053_DREQ     4     // VS1053 Data request, ideally an Interrupt pin

// WICED feather
#elif defined(ARDUINO_STM32_FEATHER)
#define VS1053_CS       PC7     // VS1053 chip select pin (output)
#define VS1053_DCS      PB4     // VS1053 Data/command select pin (output)
#define CARDCS          PC5     // Card chip select pin
#define VS1053_DREQ     PA15    // VS1053 Data request, ideally an Interrupt pin

#elif defined(ARDUINO_FEATHER52)
#define VS1053_CS       30     // VS1053 chip select pin (output)
#define VS1053_DCS      11     // VS1053 Data/command select pin (output)
#define CARDCS          27     // Card chip select pin
#define VS1053_DREQ     31     // VS1053 Data request, ideally an Interrupt pin
#endif

Adafruit_VS1053_FilePlayer musicPlayer =
  Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS);

/* Create the motor shield object with the default I2C address
    then set up 3 motors. one for left, one for right, one for ht ehead.
*/
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// set the left motor to M1
Adafruit_DCMotor *leftMotor = AFMS.getMotor(1);
// set the right motor to M2
Adafruit_DCMotor *rightMotor = AFMS.getMotor(2);
// set the head motor to M3
Adafruit_DCMotor *headMotor = AFMS.getMotor(3);

/* next, set up the bluetooth stuff. First we set our device's name. it will be set via the
    AT command thingy. Then we create a brand new shiny Bluefruit object, named 'ble'.

*/
String BROADCAST_NAME = "R2D2";
String BROADCAST_CMD = String("AT+GAPDEVNAME=" + BROADCAST_NAME);

Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);

// A small helper
void error(const __FlashStringHelper*err) {
  Serial.println(err);
  while (1);
}
// function prototypes over in packetparser.cpp
uint8_t readPacket(Adafruit_BLE *ble, uint16_t timeout);
float parsefloat(uint8_t *buffer);
void printHex(const uint8_t * data, const uint32_t numBytes);

// the packet buffer. each time we loop, we fill this with the packet received from iphone app.
extern uint8_t packetbuffer[];
char buf[60];

long lastPress = 0; // the timestamp in ms of the last time a button was pressed.
int MAX_SPEED = 50; // don't want R2 to move too fast!
int CRUISING_SPEED = 100;
int BUTTON_PIN = 6; // temp usage for user pressing a button
//
// the setup function runs once when you press reset or power the board
//
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(19200);           // set up Serial library at 19200 bps
  while (!Serial) {
    ;  //  wait for it to begin
  }

  lastPress = millis();
  //
  // set up the card on the VS1053.
  //
  if (!SD.begin(CARDCS)) {
    Serial.println(F("SD failed, or not present"));
  }
  Serial.println("SD OK!");

  if (! musicPlayer.begin()) { // initialise the music player
    Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
  } else {

    Serial.println(F("VS1053 found"));
    int i;
    for (i = 1; i < 5; i++) {
      musicPlayer.sineTest(i, 10);    // Make a tone to indicate VS1053 is working
    }
    musicPlayer.setVolume(20, 20);   // Set volume for left, right channels. lower numbers == louder volume!
    musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int
    musicPlayer.startPlayingFile("R2D2-01.wav");
  }

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

  // warm up the motors. speed to start, from 0 (off) to 255 (max speed)
  leftMotor->setSpeed(0);
  leftMotor->run(RELEASE);
  rightMotor->setSpeed(0);
  rightMotor->run(RELEASE);
  headMotor->setSpeed(0);
  headMotor->run(RELEASE);

  Serial.println(F("setup: warming up the engines."));
  Serial.println(F("-----------------------------------------"));
  BLEsetup();
}
/* next function copied direct from adafruit robot example.

*/
void BLEsetup() {
  Serial.print(F("Initialising the Bluefruit LE module: "));

  if ( !ble.begin(VERBOSE_MODE) )
  {
    error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
  }
  Serial.println( F("OK!") );

  /* Perform a factory reset to make sure everything is in a known state */
  Serial.println(F("Performing a factory reset: "));
  if (! ble.factoryReset() ) {
    error(F("Couldn't factory reset"));
  }

  //Convert the name change command to a char array
  BROADCAST_CMD.toCharArray(buf, 60);

  //Change the broadcast device name here!
  if (ble.sendCommandCheckOK(buf)) {
    Serial.println("name changed");
  }
  delay(250);

  //reset to take effect
  if (ble.sendCommandCheckOK("ATZ")) {
    Serial.println("resetting");
  }
  delay(250);

  //Confirm name change
  ble.sendCommandCheckOK("AT+GAPDEVNAME");

  /* Disable command echo from Bluefruit */
  ble.echo(false);


  /*
   *  Wait for connection. In other words, loop forever until user launches Bluefruit App on iphone and connects to us.
  */
  while (! ble.isConnected()) {
    Serial.print("launch your iphone app... ");
    delay(500);
  }
  Serial.println("Requesting Bluefruit info:");
  /* Print Bluefruit information */
  ble.info();

  Serial.println(F("Please use Adafruit Bluefruit LE app to connect in Controller mode"));
  Serial.println(F("Then activate/use the sensors, color picker, game controller, etc!"));
  Serial.println();

  ble.verbose(false);  // debug info is a little annoying after this point!

  Serial.println(F("*****************"));

  // Set Bluefruit to DATA mode
  Serial.println( F("Switching to DATA mode!") );
  ble.setMode(BLUEFRUIT_MODE_DATA);

  Serial.println(F("*****************"));
  ble.println("R2D2 at your service.");
}
/*
    here is the loop function that is the heart of R2. We loop forever, checking for packets
    sent from the phone. right now we will just implement the "controller" mode, which means
    user might press one of the 8 buttons on the "controller" in the Adafruit app. The 8
    buttons are 1 2 3 4 up down left right. Let's have up = forward, down = reverse, left = spinleft
    and right = spinright. The number buttons can make R2 spin his head or make bleeps.

    Each packet should contain 5 chars. Example:
    Button 4 pressed:  [โ€˜!โ€™] [โ€˜Bโ€™] [โ€˜4โ€™] [โ€˜1โ€™] [CRC]
    There's a ! followed by B for button. the button number here is 4 and the 1 means it was pressed
    (0 = released). Finally there's a CRC.
    Up = 5, Down = 6, Left = 7, Right = 8;

    right now the command to move lasts for a set time, ie advance is set for 500 ms. probably instead we
    should advance forever but just halt whenver we get a "liftup" signal.
*/

bool isMoving;
int lastButton = 0; // this is a way to check if the user has hit a new button.

void loop() {
  // is there a physical override?
  if (digitalRead(BUTTON_PIN) == LOW) {
    Serial.println("override!");
    advance();
    return;
  }
  long elapsed = 0;
  elapsed = millis() - lastPress;
  Serial.println(elapsed);
  lastPress += elapsed;

  //next read new packet data into packetbuffer
  uint8_t len = readPacket(&ble, BLE_READPACKET_TIMEOUT);
  if (len == 0) return;
  // OK, we should now have packet data in packetbuffer.
  // Next make sure it's a button press and not something else.
  //

  if (packetbuffer[1] = ! 'B') {
    Serial.println("not a button!");
    return;
  }
  uint8_t buttnum = packetbuffer[2] - '0';
  boolean pressed = packetbuffer[3] - '0';
  if (pressed == 0) {   //the button was released. stop moving!
    lastButton = 0;
    halt();
    return;
  }
  if (buttnum == lastButton) {    // same old button. do nothing!
    return;
  }
  // if we're here, it means a new button was pressed.
  Serial.println("button press!");
  musicPlayer.stopPlaying();
  switch (buttnum) {
    case 1:
      if (!musicPlayer.startPlayingFile("HELP.wav")) {
        Serial.print("no help");
      } else {
        ble.println("Help me, Obi Wan Kenobi.");
      }
      break;
    case 2:
      musicPlayer.startPlayingFile("CHANCES.wav");
      ble.println("Outlook is grim.");
      break;
    case 3:
      ble.println("Cue the music.");
      musicPlayer.startPlayingFile("starwars.wav");
      break;
    case 4:
      ble.println("resetting vs1053");
      if(musicPlayer.playingMusic) {
         ble.println("it was playing.");
      }
      musicPlayer.begin();
      break;
    case 5:
      musicPlayer.startPlayingFile("R2D2-01.wav");
      advance();
      ble.println("R2D2 advances!");
      break;
    case 6:
      musicPlayer.startPlayingFile("R2D2-02.wav");
      retreat();
      ble.println("R2D2 retreats!");
      break;
    case 7:
      musicPlayer.startPlayingFile("R2D2-03.wav");
      spinLeft();
      ble.println("R2D2 left.");
      break;
    case 8:
      musicPlayer.startPlayingFile("R2D2-04.wav");
      spinRight();
      ble.println("R2D2 right.");
      break;
    default:
      ble.println("This droid is tricky.");
      break;
  }

}

void advance() {
  int i;
  Serial.println("forward!");
  leftMotor->run(FORWARD);
  rightMotor->run(FORWARD);
  // first, slowly get up to speed;
  for (i = 0; i < CRUISING_SPEED; i++) {
    leftMotor->setSpeed(i);
    rightMotor->setSpeed(i);
    delay(10);
  }
}
void retreat() {
  int i;
  Serial.println("retreat!");
  leftMotor->run(BACKWARD);
  rightMotor->run(BACKWARD);
  // first, slowly get up to speed;
  for (i = 0; i < CRUISING_SPEED; i++) {
    leftMotor->setSpeed(i);
    rightMotor->setSpeed(i);
    delay(10);
  }
}
void spinLeft() {
  int i;
  Serial.println("left!");
  leftMotor->run(BACKWARD);
  // first, slowly get up to speed;
  for (i = 0; i < CRUISING_SPEED; i++) {
    leftMotor->setSpeed(i);
    delay(10);
  }
}
void spinRight() {
  int i;
  Serial.println("right!");
  leftMotor->run(FORWARD);
  // first, slowly get up to speed;
  for (i = 0; i < CRUISING_SPEED; i++) {
    leftMotor->setSpeed(i);
    delay(10);
  }
}
void spinHead() {
  int i;
  Serial.println("spin head!");
  headMotor->run(FORWARD);
  // first, slowly get up to speed;
  for (i = 0; i < CRUISING_SPEED; i++) {
    headMotor->setSpeed(i);
    delay(10);
  }
}
void halt() {
  Serial.println("halt!");
  isMoving = 0;
  leftMotor->run(RELEASE);
  rightMotor->run(RELEASE);
  headMotor->run(RELEASE);
}
Attachments
tripler.jpg
tripler.jpg (120.37 KiB) Viewed 826 times

clydgate
 
Posts: 30
Joined: Fri Jul 21, 2017 3:12 am

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by adafruit_support_mike on Thu Jan 04, 2018 6:04 am

As a sanity check, try disconnecting the Motor FeatherWing and see if you still have the same problems. You might be seeing a side effect of noise in the supply rails.

adafruit_support_mike
 
Posts: 59116
Joined: Thu Feb 11, 2010 2:51 pm

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by clydgate on Thu Jan 04, 2018 1:04 pm

Never thought of that. Will give it a shot tonight. Can I just disconnect the power supply to the motorshield or do you mean physically removing motorshield from the tripler?

clydgate
 
Posts: 30
Joined: Fri Jul 21, 2017 3:12 am

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by clydgate on Fri Jan 05, 2018 2:44 am

I commented out the lines referring to the VS1053 and pulled the wing from the sockets.

I do get different behavior.

Sine wave and first soundfile play fine. (note that that takes place before initializing the ble object and connection.)

After that, the files I play with startPlayingFile() only play for ~500 ms or so. After 3-4 files, even that stops working.

It typically takes the loop() about 500 ms to execute... could it be that somehow my loop is interrupting the soundboard?

For debugging purposes, I gave user a button which resets the VS20153. That is successful, but doesn't solve the weird 500-ms problem.

When I plug the motorshield back in, once again the initial sounds work fine, but I get funky behavior once we enter the loop.

Many thanks for any crazy ideas.

clydgate
 
Posts: 30
Joined: Fri Jul 21, 2017 3:12 am

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by adafruit_support_mike on Fri Jan 05, 2018 5:25 am

That sounds like a power issue.

Speakers and motors use lots of current, and the amount they use changes quickly. That creates voltage spikes in all the wiring between the power source and the motor/speaker, and those spikes can get big enough to make digital circuits like microcontroller's glitch or crash.

What are you using for power sources?

adafruit_support_mike
 
Posts: 59116
Joined: Thu Feb 11, 2010 2:51 pm

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by clydgate on Fri Jan 05, 2018 11:40 am

During development I have mostly used USB cable from computer to power the project.

I do also have a battery pack onboard the Bluefruit board, and a separate 4.5 volt battery pack for the motorshield.

the speaker has its own batteries, too, natch.
Attachments
feathers-wires-v3.jpg
feathers-wires-v3.jpg (477.33 KiB) Viewed 770 times

clydgate
 
Posts: 30
Joined: Fri Jul 21, 2017 3:12 am

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by adafruit_support_mike on Sat Jan 06, 2018 3:42 am

Hmm..

First, try swapping the positions of the Music Maker and Motor FeatherWings. The Music Maker gets its power from the Feather, and having the motor controller between them might be having some effect on the system.

Also try adding a 100uF to 1000uF capacitor between the Music Maker's USB and GND pins. That will smooth out the worst of any spikes created by the speaker.

adafruit_support_mike
 
Posts: 59116
Joined: Thu Feb 11, 2010 2:51 pm

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by clydgate on Thu Jan 11, 2018 12:03 am

Good suggestions. I don't have capacitors but will obtain.

In the meantime, I did switch the position of the motorshield and the music maker so that the music maker is now next to the Feather. Sadly the music maker is still crapping out.

However, I have to say I'm not sure that voltage spike due to motorshield is the culprit, because I just ran a test case where the user triggers sounds but not motors. The music maker plays the sound file fine the first time around, then refuses to emit any more sound.

Just reassure me: you don't think it's possible that the music maker interrupt is somehow set to a Feather pin that needs to be doing something else, like listening to Bluetooth? (just a stab in the dark)

clydgate
 
Posts: 30
Joined: Fri Jul 21, 2017 3:12 am

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by adafruit_support_mike on Thu Jan 11, 2018 5:19 am

The BLE module and SAMD21 are more or less independent, and only communicate through the SPI interface. The BLE module does have CS and IRQ pins, but those aren't broken out to the side of the Feather.

It's possible you might have an interrupt timing conflict between the BLE module and the Music Maker though. Try turning off the interrupt-based updates for the Music Maker in yout code, and adding a call to .feedBuffer() at the top of loop(). That will eliminate any chance of one interrupt happening while the other's ISR is executing.

adafruit_support_mike
 
Posts: 59116
Joined: Thu Feb 11, 2010 2:51 pm

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by clydgate on Sat Jan 13, 2018 4:12 am

Thanks for your suggestions. I was getting error msgs re the SD card, so I decided to simplify my life and use sineTest() instead of startPlayingFile(). I also turned off the power supply to the motorshield. Sure enough, under these conditions, the music maker responds properly. So job #1 is to get those capacitors installed.

However, startPlayingFile() just isn't reliable even when the motorshield has no power. I am a novice and don't quite grok what you mean by "turning off interrupt-based updates"... How do I do this?

Another idea would be to use playFullFile() but some of my soundfiles are several seconds long, and it seems a shame to have R2 unresponsive while the sound is playing. But perhaps that is the price of freedom...

clydgate
 
Posts: 30
Joined: Fri Jul 21, 2017 3:12 am

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by adafruit_support_mike on Sat Jan 13, 2018 5:30 am

The VS1053 can't hold a complete MP3 in memory at once. Instead, it has an input buffer that holds enough data for the next few seconds of sound. When it starts getting close to the end of the data in its buffer, the VS1053 sets the voltage on one of its pins high as a signal that it needs more bytes.

That signal is connected to one of the Feather's GPIO pins (#6 IIRC), and the microcontroller uses that to control data transmission to the VS1053. There are two ways it can do that:

The first is to call the function .feedBuffer() every few milliseconds to read GPIO 6 and see if the "feed me" signal has gone high. If it has, the code will jump to a function that pulls more data from the SD card and sends it to the VS1053. That approach is called 'polling the pin'. It's simple and fairly bulletproof, but it puts some limits on how long the rest of your code can run before it has to stop and check the pin again.

The other option is to assign .feedBuffer() to an interrupt handler on GPIO 6.

Interrupts begin with circuits that detect signals like rising or falling edges on a GPIO pin. If the circuit sees the kind of signal it wants, it sends a message to the microcontroller's CPU that says, "stop what you're doing, execute this function right now, then pick up where you left off." It's a powerful technique that uses the microcontroller's time efficiently, but it does make things a bit more complicated.

One big problem with interrupts is the question, "what happens if an interrupt arrives while the CPU is executing the handler function for another interrupt?" generically known as an 'interrupt collision'. The CPU has to save a snapshot of what it was doing when the interrupt arrived so it knows how to pick up where it left off, and there's a limit to how many times any machine can do that. In general, microcontrollers avoid that problem by shutting off the system that listens for new interrupts before executing the code for the interrupt that just came in. That means a CPU executing the handler for one interrupt can miss the signal that should generate another interrupt.

The ARM CPU architecture has a system that does let one interrupt suspend another, called the Nested Vector Interrupt Controller, or NVIC. 'Nested' means higher-priority interrupts can suspend lower-priority interrupt handlers, and 'Vector' means the system uses pointers to handler functions instead of blocks of code the CPU executes directly. It's an elegant system, but mostly irrelevant in this case because all signal-to-a-pin interrupts have the same priority and can't suspend each other.

For your system, it's technically possible that the VS1053's "feed me" signal could arrive while the microcontroller is talking to the BLE module in an interrupt handler from that library, and when you're doing things 48 million times per second, low probability just means it takes a few more seconds to happen.

Modifying your code so the microcontroller polls the "feed me" signal instead of handing the job off to an interrupt handler will eliminate the possibility of an interrupt collision between the VS1053 and the BLE module.

adafruit_support_mike
 
Posts: 59116
Joined: Thu Feb 11, 2010 2:51 pm

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by clydgate on Sun Jan 14, 2018 4:14 am

Wow. What a great intro to the dark art of the interrupt().

Thinking about your suggestion, I put musicPlayer.feedBuffer() at the top of my loop. No dice. Then I started to wonder why my loop was always taking approx 500ms to execute, even when it did nothing. I realized that my loop included:

readPacket(&ble, LE_READPACKET_TIMEOUT);

and that LE_READPACKET_TIMEOUT was set to 500 ms. So basically the whole show stops for 500 ms to check if there's anything coming through on the bluefruit channel. While that's happening, poor music maker is starved for affection. So I set this to 50 ms, so that we can do feedBuffer() more often.

Second, I realized that I my setup() included this line:

musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT); // DREQ int

Which I copied from the example. I was never really doing anything with this pin, so what the hell-- I just commented it out.

Result: the system works! music maker plays sound files with musicPlayer.startPlayingFile() and while they're playing it continues to listen to user input through bluefruit channel. Plus my loop is 10 times faster. YAY!

Remaining bugs:
    If we play a sound, then play another sound before the first is done, the second sound is distorted. This is despite an intervening call to musicPlayer.stopPlaying(); fortunately, resetting the music maker seems to fix this.
    there is a slight stutter in some of the sounds... I can live with it, but may tinker with the BLE timeout some more to see if I can eliminate it entirely.
    music maker is definitely not happy when I turn power on to the motorshield. Hoping that installing a capacitor will fix this.

Thanks for your great insights. I will definitely post a vid of this project when I'm done.

clydgate
 
Posts: 30
Joined: Fri Jul 21, 2017 3:12 am

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by adafruit_support_mike on Mon Jan 15, 2018 5:23 am

The BLE timeout would definitely cause problems for the Music Maker. Glad to hear you got that sorted out. The stutter does sound like some residual timing error, but at least you know where to look for the solution.

We've had some reports of wierdness when switching from one sound to another, and it runs in my mind that there may be some connection to the signal timing.. try putting a few milliseconds of delay between the call to .stopPlaying() and starting the new track. You might also try adding a half-second clip of silence and play that between sounds.

The Motor Shield does sound like a job for a capacitor. Start with 100uF and see if it works, but be prepared to move up. You might need something in the 4700uF to 10,000uF range.

adafruit_support_mike
 
Posts: 59116
Joined: Thu Feb 11, 2010 2:51 pm

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by clydgate on Sun Jan 21, 2018 6:03 pm

I've made a lot of progress thanks to your help.

I put a 100uF capacitor between USB and GND pins of the music maker wing, which seems to help. But I'm still seeing some flakiness, depending on the speaker/amplifier setup. I'm happy to install a beefier capacitor, but... should I just go to 10,000uF immediately? Why screw around with the intervening values? Is there some benefit to smaller values that I do not understand?

Once I have installed the board into R2, I want the system to be as resilient as possible.

Side note: during development, I've been testing the system with a powered speaker. But this R2 unit came with its own builtin (unpowered) speaker. I bought a little PAM 8302 mono class D amp and hooked it up to the system using an independent power source, and connected it to R2's speaker. It sounded terrible--horribly distorted. Just as I was cursing myself for choosing such a low-cost option (it was $4) I checked the power supply. I was supplying 9v but it actually wants 2-5v. I switched power to the proper range, and voila! Sounds terrific. Here's to the virtues of reading the documentation! I will never speak ill of a PAM 8302 again.

clydgate
 
Posts: 30
Joined: Fri Jul 21, 2017 3:12 am

Re: Feather M0 Bluefruit LE + Music Maker Wing Woes

by adafruit_support_mike on Mon Jan 22, 2018 4:20 am

clydgate wrote:should I just go to 10,000uF immediately? Why screw around with the intervening values? Is there some benefit to smaller values that I do not understand?

Hardware design is an endless process of making tradeoffs. Bigger capacitors are better, but there's a point of diminishing returns. You generally balance that against cost and space concerns.

It's easier to make the tradeoffs if you know how the things you're using work though. In this case capacitors tend to follow a 1/C rule, where C is the size of the capacitor. To make the answer fit your question a little better, I'm going to turn 1/C into 1/nC where 'n' is a scaling factor you can change any way you like, and 'C' is the value of the first capacitor you tried.

Starting from the beginning, you have a circuit with voltage spikes. You measure the spikes without any capacitor, then connect a randomly selected capacitor and measure them again. You find that with the capacitor, the spikes are V.new/V.old the size they originally were. You can convert that to K/C, where 'K' is some constant that gives you the same fraction.

If you double the size of the capacitor, you expect the spikes to be about K/2C as big as the undamped spikes. If you use a capacitor 10 times bigger, you expect the spikes to be K/10C as big, and so on.

At that point you have to start looking at what you get in exchange for scaling up to the next bigger capacitor. If the undamped spikes were +/-1V, and the first capacitor dropped them to +/-500mV (K/C=1V/2), making the cap 50 times bigger would give you about K/50C=1v/2*50=10mV. That change would reduce the size of the spikes by 490mV, which is probably worth doing.

Going to n=500 would reduce the spikes to 1V/2*500=1mV. That's certainly low, but only 9mV better than the n=50 value.

Going to N=5000 would reduce the spikes to 1V/2*5000=100uV, which is 900uV lower than n=500 and 9.9mV lower than n=50.

The major design question is, "how much do you want that 900uV of improvement?"

There's no single answer. If you're building a precision measurement system, it could be worth going to n=1,000,000 to drop the noise below 1uV. Most circuits don't notice 100mV of ripple on the supply rails, so n=10 would keep you comfortably below that level.

If you already have a 10mF capacitor you can plug into the circuit, go ahead and try it.. there's no real down side. You can expect it to drop the spikes to about 1% the size you get from the 100uF cap. If the circuit works and the cap will fit into the R2 unit with all the other hardware, go ahead and use it.


clydgate wrote:I was supplying 9v but it actually wants 2-5v. I switched power to the proper range, and voila! Sounds terrific. Here's to the virtues of reading the documentation! I will never speak ill of a PAM 8302 again.

Ooh.. yeah. I'm a little surprised the PAM8302 didn't die outright at 9V. Was it coming from a 9V battery? Those have high internal resistance, and driving the speaker would probably have pulled the battery down to a level that was safe for the PAM8302.

That's a PWM amp though, so you might have been getting several volts of noise on the supposedly-9V rail, which would play merry hell with the amp's ability to read an input signal.

adafruit_support_mike
 
Posts: 59116
Joined: Thu Feb 11, 2010 2:51 pm

Please be positive and constructive with your questions and comments.