SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By djairjr
#195038
Firstly, I wanted to comment that I'm loving this board!
I bought it to play some tricks with talking automats, using the Arduino to control it.
It took me a while to figure out how to do what I need, but I ended up finding a way.
I'm making a little choir in three voices, with eighteen dolls that are controlled by servo motors. The sound part of this coral is made with the Tsunami. And the control of the engines with the Adafruit PWM Servo Motor Drive.
I'm having some difficulty understanding the correct use of the tsunami.update () and tsunami.isTrackPlaying (int trk) functions. It seems that some kind of delay is needed after invoking them, but I can not understand exactly how long it lasts.
I'm having a problem with my code and I get the impression that it's something related to communicating with the Tsunami.
The program should work like this: when something is in front of an ultrasonic sensor, the Tsunami plays five mono tracks simultaneously. The Arduino must detect that one of these bands is playing and when this happens, it must position the servo motors according to the signals that enter its analog inputs.
I connected three mono Tsunami outputs to the three analog inputs of the Arduino (using circuits to set the voltage offset). The other two outputs are connected to a speaker.
The three mono outputs connected on the Arduino are control voices, one for each voice of the chorus (bass, mid and treble). The other two are the mixed voices and go to the sound output.
I'm using a while (!tsunami.isTrackPlaying (1) {to put all the code of what needs to be done when I have no music playing.) And another while (tsunami.isTrackPlaying (1) {to do what it takes when Some music is playing.
The problem is that my program is not detecting this correctly.
There are five tracks on the SD card, all mono, all named correctly. The ini file is also correct.
The program operated using only three servo motors (one for each voice). But when I increased the servos and created the routine that divides the voices into groups of six, the whole thing got complicated.
How do I use these two functions?

:pray:

Here is the code...
Code: Select all
/*When something is in front of Ultrasound Sensor, play 5 tracks poly at TSUNAMI SUPER WAV TRIGGER
if there is any audio playing at TSUNAMI, move 18 Servo Motors that is attached to a FROG DOLL mouth
And make the FROGS sing.
*/

#include <Wire.h>                    // I2C to Adafruit 16 Channel PWM Servo Driver
#include <Adafruit_PWMServoDriver.h> // Adafruit 16 Channel PWM Servo Driver
#include <Tsunami.h>                 // Tsunami Super Wav Trigger
#include <Ultrasonic.h>              // Ultrasound Sensor HC-SR04

/* Relay Board */
#define ABAJUR1 8                     // Lights
#define ABAJUR2 9                     // Lights

/* Tsunami Super Wav Trigger */
Tsunami tsunami;                      // Create tsunami object
int  gRateOffset = 0;                 // Offset Rate Tsunami
int  gNumTracks;                      // Number of SD Card Tracks
char gTsunamiVersion[VERSION_STRING_LEN];    // Tsunami Version String

/* Sensor Ultrasonico HC-SR04 */
#define SENSORMIN 10                  // Minimum distance to Trigger
#define SENSORMAX 50                  // Maximum distance to Trigger
#define TRIGGER_PIN  6
#define ECHO_PIN     7
Ultrasonic ultrasonic(TRIGGER_PIN, ECHO_PIN); // Create Ultrasonic Object

/* Servo Parameters */
#define VOICECONTROL 2                // Number of control voices (index 0)
#define SERVONUM 17                   // Number of SERVO MOTORS
#define VOZAGUDAMIN 264               // Mouth CLOSED for Small FROG
#define VOZAGUDAMAX 380               // Mouth OPENED for Small FROG

#define VOZMEDIAMIN 264               // Mouth CLOSED for Medium FROG
#define VOZMEDIAMAX 410               // Mouth OPENED for Medium FROG

#define VOZGRAVEMIN 264               // Mouth CLOSED for Big FROG
#define VOZGRAVEMAX 371               // Mouth OPENED for Big FROG

/* Trying to find mid position - just for testing
int VOZAGUDAMED = ((VOZAGUDAMAX - VOZAGUDAMIN) / 2) + VOZAGUDAMIN;
int VOZMEDIAMED = ((VOZMEDIAMAX - VOZMEDIAMIN) / 2) + VOZMEDIAMIN;
int VOZGRAVEMED = ((VOZGRAVEMAX - VOZGRAVEMIN) / 2) + VOZGRAVEMIN;
*/
int servomin;
int actmax;
int actmin;
int Audio[] = {1, 2, 3, 4, 5}; // Array with audio track number
int Servos[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; // Array with servo number

/*Arrays for Motor Position and Amplitude of Audio Signal*/
int Posicao[VOICECONTROL];
int Amplitude[VOICECONTROL];

Adafruit_PWMServoDriver sapo = Adafruit_PWMServoDriver(); // Create an PWM Servo object called "sapo" (frog)

/* Preparing Arduino Audio Readings */

int Offset = 520; // Voltage offset for audio - measured in tests
int AudioMax = 690; // Audio Peak

void setup() {  
  Serial.println ("Started");
  Serial.begin (9600);            
  sapo.begin();                   // Start Adafruit PWM Servo Driver
  sapo.setPWMFreq(60);            // Servos at 60hz
  yield ();
  
  /* Init Tsunami Super Wav Trigger */
  delay(1000);                    // Start TSUNAMI
  tsunami.start();               
  delay(10);
  tsunami.stopAllTracks();        // Stop all tracks
  tsunami.samplerateOffset(0, 0); // Reset Sample Rate Offset
  tsunami.setReporting(true);     // Track Report Enabled
  Serial.println ("Tsunami inicializada");
  delay(1000);                     // Wait for Tsunami Report

  /* Testing Arduino and Tsunami communication */

  if (tsunami.getVersion(gTsunamiVersion, VERSION_STRING_LEN)) {
    Serial.println ("Estabelecendo comunicação com a Tsunami");
    Serial.print(gTsunamiVersion);
    Serial.println("\n");
    gNumTracks = tsunami.getNumTracks();
    Serial.print("Numero de pistas = ");
    Serial.println(gNumTracks);
    Serial.println ("*************************************");
  }
  /* Preparing the Relay Board */
  pinMode(ABAJUR1, OUTPUT);
  pinMode(ABAJUR2, OUTPUT);
  digitalWrite(ABAJUR1, LOW);
  digitalWrite(ABAJUR2, LOW);
  Serial.println ("Relay Board Preparada");
  Serial.println ("*************************************");
  Serial.println ("Setup das placas OK");
  Serial.println ("*************************************");
} /* SETUP FINISHED */

/* Setting Servos at Miliseconds  */

void setServoPulse(uint8_t n, double pulse) {
  double pulselength;
  pulselength = 1000000;   // 1,000,000 us por segundo
  pulselength /= 60;   // 60 Hz
  Serial.print(pulselength);
  Serial.println(" us por periodo");
  pulselength /= 4096;  // 12 bits de resolution
  Serial.print(pulselength);
  Serial.println(" us por bit");
  pulse *= 1000;
  pulse /= pulselength;
  Serial.println(pulse);
  sapo.setPWM(n, 0, pulse);
}

void loop() {
  tsunami.update();                            // Update Tsunami Status

  /* Preparo o Sensor Ultrasonico */
  float cmMsec;
  long microsec = ultrasonic.timing();
  cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM); // Read Ultrasound Sensor

  while (!tsunami.isTrackPlaying(1)) {
    // When Tsunami is not playing Track 1
    digitalWrite(ABAJUR1, LOW);                       // Keep Lights Off
    digitalWrite(ABAJUR2, LOW);
    for (int fecha = 0; fecha <= SERVONUM; fecha++) { // Keep Frogs with closed Mouth.
      sapo.setPWM (Servos[fecha], 0, VOZAGUDAMIN);
    }
    if ((cmMsec >= SENSORMIN) && (cmMsec <= SENSORMAX)) { // Test if there is an object in front of sensor
      for (int a = 0; a < gNumTracks; a++) {
        tsunami.trackLoad(Audio[a], a, true);              // Load Audio Track
        Serial.print ("Faixa ");
        Serial.print (Audio [a]);
        Serial.println (" Carregada...");
      }
      tsunami.resumeAllInSync();                              // Play All Tracks
      tsunami.update();
      Serial.print ("Objeto detectado a ");                   // Just for show Object Distance
      Serial.print (cmMsec);
      Serial.println (" Cm");
    } 
  }

  /* But, if Tsunami is playing... Lights ON, read ANALOG INPUT and Position SERVOS based on sound input - LipSync!!! */
  while (tsunami.isTrackPlaying(1)) {
    digitalWrite(ABAJUR1, HIGH);      // Lights ON
    digitalWrite(ABAJUR2, HIGH);                              
    for (int i = 0; i <= VOICECONTROL; i++) {               // Starting loop that control motors based on Analog Read
      Amplitude[i] = analogRead (i);                        
      if (Amplitude[i] >= Offset) {
        if (i == 0) { 
          actmin = 0;
          actmax = 6;
          servomin = VOZAGUDAMIN;
          Posicao[i] = map (Amplitude[i], Offset, AudioMax, VOZAGUDAMIN, VOZAGUDAMAX); // Position SERVOS 0 to 5
        } else if (i == 1) {
          actmin = 6;
          actmax = 12;
          servomin = VOZMEDIAMIN;
          Posicao[i] = map (Amplitude[i], Offset, AudioMax, VOZMEDIAMIN, VOZMEDIAMAX); // Position SERVOS 6 to 11
        } else if (i == 2) {
          actmin = 12;
          actmax = 18;
          servomin = VOZGRAVEMIN;
          Posicao[i] = map (Amplitude[i], Offset, AudioMax, VOZGRAVEMIN, VOZGRAVEMAX); // Position SERVOS 12 to 17
        }
        for (int s = servomin; s < Posicao[i]; s++) {                               // Move SERVOS
          for (int act = actmin; act < actmax; act++) {                            
            sapo.setPWM (Servos[act], 0, s);
          }
        }
      }
    }
    tsunami.update();
    delay(50); // 
  }
}
By djairjr
#195042
Few changes in code, improving the general performance.
But still don't have good communication with Tsunami.... (tsunami.update() issues?)
Change while to if... else if...
Code: Select all
/*
    ___                     ___          _
   / __| __ _ _ __  ___ ___/ __|__ _ _ _| |_ ___ _ _ ___ ___
   \__ \/ _` | '_ \/ _ (_-< (__/ _` | ' \  _/ _ \ '_/ -_|_-<
   |___/\__,_| .__/\___/__/\___\__,_|_||_\__\___/_| \___/__/
             |_|

    Lipsync exercise using
    Tsunami Wav Trigger, Ultrasound Sensor, Relay Board and PWM Servo Driver
    By DJAIR GUILHERME

   Connections between Arduino and other boards

   PWM SERVO I2C | ARDUINO
      SCL        |    3
      SDA        |    2

   HC-SR04 | ARDUINO
    TRIG   |    6
    ECHO   |    7

   Tsunami is operating at MONO mode.

   TSUNAMI MONO | ARDUINO
      1L        |    A0
      1R        |    A1
      2L        |    A2

  TSUNAMI MONO | AMPLIFIED OUT
      2R        |    RIGHT CHANNEL
      3L        |    LEFT CHANNEL

   TSUNAMI SERIAL | ARDUINO (Using logic bi-directional converter 3,3V para 5V with Serial1)
           TX        |    1 (RX)
           RX        |    0 (TX)
         GND       |   GND

   Mouth position of FROGS
   SAPO GRANDE    SERVOMIN = 264 | SERVOMAX = 380
   SAPO MEDIO     SERVOMIN = 264 | SERVOMAX = 410
   SAPO PEQUENO   SERVOMIN = 264 | SERVOMAX = 371

   RELAY BOARD    | ARDUINO
       RL1        |   8
       RL2        |   9

  When something is in front of Ultrasound Sensor, play 5 tracks poly at TSUNAMI SUPER WAV TRIGGER
  if there is any audio playing at TSUNAMI, move 18 Servo Motors that is attached to a FROG DOLL mouth
  And make the FROGS sing.
*/

#include <Wire.h>                    // I2C to Adafruit 16 Channel PWM Servo Driver
#include <Adafruit_PWMServoDriver.h> // Adafruit 16 Channel PWM Servo Driver
#include <Tsunami.h>                 // Tsunami Super Wav Trigger
#include <Ultrasonic.h>              // Ultrasound Sensor HC-SR04

/* Relay Board */
#define ABAJUR1 8                     // Lights
#define ABAJUR2 9                     // Lights

/* Tsunami Super Wav Trigger */
Tsunami tsunami;                      // Create tsunami object
int  gRateOffset = 0;                 // Offset Rate Tsunami
int  gNumTracks;                      // Number of SD Card Tracks
char gTsunamiVersion[VERSION_STRING_LEN];    // Tsunami Version String

/* Sensor Ultrasonico HC-SR04 */
#define SENSORMIN 10                  // Minimum distance to Trigger
#define SENSORMAX 50                  // Maximum distance to Trigger
#define TRIGGER_PIN  6
#define ECHO_PIN     7
Ultrasonic ultrasonic(TRIGGER_PIN, ECHO_PIN); // Create Ultrasonic Object

/* Preparando Parâmetros para Servos */
#define VOICECONTROL 2                // Number of control voices (index 0)
#define SERVONUM 17                   // Number of SERVO MOTORS
#define VOZAGUDAMIN 264               // Mouth CLOSED for Small FROG
#define VOZAGUDAMAX 380               // Mouth OPENED for Small FROG

#define VOZMEDIAMIN 264               // Mouth CLOSED for Medium FROG
#define VOZMEDIAMAX 410               // Mouth OPENED for Medium FROG

#define VOZGRAVEMIN 264               // Mouth CLOSED for Big FROG
#define VOZGRAVEMAX 371               // Mouth OPENED for Small FROG

/* Trying to find mid position - just for testing
  int VOZAGUDAMED = ((VOZAGUDAMAX - VOZAGUDAMIN) / 2) + VOZAGUDAMIN;
  int VOZMEDIAMED = ((VOZMEDIAMAX - VOZMEDIAMIN) / 2) + VOZMEDIAMIN;
  int VOZGRAVEMED = ((VOZGRAVEMAX - VOZGRAVEMIN) / 2) + VOZGRAVEMIN;
*/
int servomin;
int actmax;
int actmin;
int Audio[] = {1, 2, 3, 4, 5}; // Array with audio track number
int Servos[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; // Array with servo number

/* Arrays with Servo position and Analog Signal amplitude */
int Posicao[VOICECONTROL];
int Amplitude[VOICECONTROL];

Adafruit_PWMServoDriver sapo = Adafruit_PWMServoDriver(); // Create an PWM Servo object called "sapo" (frog)

/* Preparing Arduino Audio Readings */

int Offset = 520; // Voltage offset for audio - measured in tests
int AudioMax = 690; // Audio Peak

void setup() {
  Serial.println ("Started");
  Serial.begin (57600);
  sapo.begin();                   // Start Adafruit PWM Servo Driver
  sapo.setPWMFreq(60);            // Servos at 60hz
  yield ();

  /* Init Tsunami Super Wav Trigger */
  delay(1000);                    // Start TSUNAMI
  tsunami.start();
  tsunami.flush();
  delay(1000);
  tsunami.stopAllTracks();        // Stop all tracks
  tsunami.samplerateOffset(0, 0); // Reset Sample Rate Offset
  tsunami.setReporting(true);     // Track Report Enabled
  Serial.println ("Tsunami inicializada");
  delay(1000);                     // Wait for Tsunami Report

  /* Testing Arduino and Tsunami communication */

  if (tsunami.getVersion(gTsunamiVersion, VERSION_STRING_LEN)) {
    Serial.println ("Estabelecendo comunicação com a Tsunami");
    Serial.print(gTsunamiVersion);
    Serial.println("\n");
    gNumTracks = tsunami.getNumTracks();
    Serial.print("Numero de pistas = ");
    Serial.println(gNumTracks);
    Serial.println ("*************************************");
  }

  /* Preparing the Relay Board */
  pinMode(ABAJUR1, OUTPUT);
  pinMode(ABAJUR2, OUTPUT);
  digitalWrite(ABAJUR1, LOW);
  digitalWrite(ABAJUR2, LOW);
  Serial.println ("Relay Board Preparada");
  Serial.println ("*************************************");
  Serial.println ("Setup das placas OK");
  Serial.println ("*************************************");


} /* SETUP FINISHED */

/* Setting Servos at Miliseconds  */

void setServoPulse(uint8_t n, double pulse) {
  double pulselength;
  pulselength = 1000000;   // 1,000,000 us por segundo
  pulselength /= 60;   // 60 Hz
  Serial.print(pulselength);
  Serial.println(" us por periodo");
  pulselength /= 4096;  // 12 bits de resolution
  Serial.print(pulselength);
  Serial.println(" us por bit");
  pulse *= 1000;
  pulse /= pulselength;
  Serial.println(pulse);
  sapo.setPWM(n, 0, pulse);
}

void loop() {
  tsunami.update();                            // Update Tsunami Status
  delay (100);
  /* Preparo o Sensor Ultrasonico */
  float cmMsec;
  long microsec = ultrasonic.timing();
  cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM); // Read Ultrasound Sensor
  if ((cmMsec >= SENSORMIN) && (cmMsec <= SENSORMAX)) { // Test if there is an object in front of sensor
    tsunami.resumeAllInSync();                              // Play All Tracks
    tsunami.update();
    delay (100);
    Serial.print ("Objeto detectado a ");                   // Just for show Object Distance
    Serial.print (cmMsec);
    Serial.println (" Cm");
  }
  if (tsunami.isTrackPlaying(1) == false) {
    // When Tsunami is not playing Track 1
    Serial.println ("Nao está tocando");
    for (int a = 0; a < gNumTracks; a++) {
      tsunami.trackLoad(Audio[a], a, true);              // Load Audio Track
      Serial.print ("Faixa ");
      Serial.print (Audio [a]);
      Serial.println (" Carregada...");
    }
    digitalWrite(ABAJUR1, LOW);                       // Keep Lights Off - This works better here... Why?
    digitalWrite(ABAJUR2, LOW);
    tsunami.update();
    delay(100);

    for (int fecha = 0; fecha <= SERVONUM; fecha++) { // Keep Frogs with closed Mouth.
      sapo.setPWM (Servos[fecha], 0, VOZAGUDAMIN);
    }


  } else if (tsunami.isTrackPlaying(1) == true) {
    tsunami.update();
    delay(1000);
    digitalWrite(ABAJUR1, HIGH);      // Lights ON - This is not working in this position of code... But if I put it at the end of this end if, works...
    digitalWrite(ABAJUR2, HIGH);
    Serial.println ("Tocando");
    for (int i = 0; i <= VOICECONTROL; i++) {               // Starting loop that control motors based on Analog Read
      Amplitude[i] = analogRead (i);
      if (Amplitude[i] >= Offset) {
        if (i == 0) {
          actmin = 0;
          actmax = 6;
          servomin = VOZAGUDAMIN;
          Posicao[i] = map (Amplitude[i], Offset, AudioMax, VOZAGUDAMIN, VOZAGUDAMAX); // Position SERVOS 0 to 5
        } else if (i == 1) {
          actmin = 6;
          actmax = 12;
          servomin = VOZMEDIAMIN;
          Posicao[i] = map (Amplitude[i], Offset, AudioMax, VOZMEDIAMIN, VOZMEDIAMAX); // Position SERVOS 6 to 11
        } else if (i == 2) {
          actmin = 12;
          actmax = 18;
          servomin = VOZGRAVEMIN;
          Posicao[i] = map (Amplitude[i], Offset, AudioMax, VOZGRAVEMIN, VOZGRAVEMAX); // Position SERVOS 12 to 17
        }
        for (int s = servomin; s < Posicao[i]; s++) {                               // Move SERVOS
          for (int act = actmin; act < actmax; act++) {
            sapo.setPWM (Servos[act], 0, s);
          }
        }
      }
    }
    tsunami.update();
    delay(100); // Sync with Tsunami
  }
}
By djairjr
#195043
Maybe the Tsunami is detecting the tracks that I carried before as if they were playing? Even if they are paused ...
Makes sense?

In my understanding, this method should return TRUE only if the track was running, but not paused ... Is that the problem?

A quick measure in pin "playing" of Tsunami reveal that this pin is only HIGH (3V3) when the board is playing something (not paused). In my script, the SERIAL MONITOR keeps showing "Tocando" (which means "playing" in portuguese) even when there is no sound at Tsunami (and playing pin is GND).

A quick fix would be to use the "playing" pin on one of the digital inputs of the Arduino, making the card execute the loop.
User avatar
By robertsonics
#195044
I won't be able to really dig into this until the weekend. Until then, a quick comment about .update(). Track reporting messages from Tsunami are received by the library via an interrupt and placed in a circular buffer. The interrupt routine does not parse or otherwise process the messages. This way, messages can arrive asynchronously and not be lost. The .update() routine needs to be called to pull messages from this buffer and process them in the foreground task. So even though a track report message has been received, the internal track state variables won't be updated until you call .update(). A single call to .update() will process all of the messages in the buffer, so you just need to be calling it regularly to make sure the internal track states remain up-to-date.
User avatar
By robertsonics
#195061
A followup: If you call a function that starts or stops a track and then wish to call .isTrackPlaying() on that track, you have to wait long enough for the track report message to be returned and then call .update() which allows the library's internal track state variables to be updated. I'm guessing 10 ms should be enough.

It is true that paused tracks are considered "playing", so that if you call loadTrack(), isTrackPlaying() will return true for that track. The "playing" digital output pin is the same function as the status LED, namely it is active only when any tracks are actually playing.