0

programmatically triggering adafruit soundboard via serial f
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

programmatically triggering adafruit soundboard via serial f

by heaversm on Fri Nov 09, 2018 9:02 pm

I purchased an adafruit sound board, which allows you to trigger audio via the serial monitor.

Product Tutorial Link

Image


I can successfully play files by using the Serial monitor. I can also successfully play sound files by (separately) wiring up a momentary button to the Sound Board. But what I want to do is programmatically trigger various sound files based on reading the input of a push button from arduino.

I am adapting their sample code, attempting to do the following:

Code: Select all | TOGGLE FULL SIZE
void handleButton(){ //detect state of button push
 
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastButtonState) {
    if (buttonState == LOW) { // if the current state is LOW then the button went from on to off:
      if (buttonPushCounter < numModes){
        buttonPushCounter++;
      } else {
        buttonPushCounter = 0;
      }
      //Serial.println("#1"); //doesn't work
      //ss.println("#1"); //doesn't work
    }
    // Delay a little bit for debouncing
    //delay(buttonDelay);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;
}



Where #1 would trigger the first sound file. But I have a feeling that because the code is listening for keyboard input within the serial monitor itself, that I'm either sending this info wrong or it's not possible to do this way. How would I programmatically trigger these files via Serial? Or is there some way to do it by sending signals to the board's GPIO without wiring up separate buttons for each file?

I noticed that the example's read looks like this:

Code: Select all | TOGGLE FULL SIZE
while (!Serial.available());
  char cmd = Serial.read();
 
  flushInput();
 
  switch (cmd) {
    case '#': {
      Serial.print("Enter track #");
      uint8_t n = readnumber();

      Serial.print("\nPlaying track #"); Serial.println(n);
      if (! sfx.playTrack(n) ) {
        Serial.println("Failed to play track?");
      }
      break;
    }
Attachments
Screen Shot 2018-11-09 at 4.46.56 PM.png
Screen Shot 2018-11-09 at 4.46.56 PM.png (95.53 KiB) Viewed 136 times

heaversm
 
Posts: 31
Joined: Thu Apr 12, 2012 10:58 am

Re: programmatically triggering adafruit soundboard via seri

by adafruit_support_bill on Sat Nov 10, 2018 6:50 am

You haven't posted your complete code, so we can't see how you are calling your handleButton() function. Your approach should work if you are checking for button presses within your loop() function.

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

Re: programmatically triggering adafruit soundboard via seri

by heaversm on Sat Nov 10, 2018 10:11 am

Here is all the code:

Code: Select all | TOGGLE FULL SIZE
/*
  Menu driven control of a sound board over UART.
  Commands for playing by # or by name (full 11-char name)
  Hard reset and List files (when not playing audio)
  Vol + and - (only when not playing audio)
  Pause, unpause, quit playing (when playing audio)
  Current play time, and bytes remaining & total bytes (when playing audio)

  Connect UG to ground to have the sound board boot into UART mode
*/

//https://learn.adafruit.com/adafruit-audio-fx-sound-board/serial-audio-control

#include <SoftwareSerial.h>
#include "Adafruit_Soundboard.h"


// Choose any two pins that can be used with SoftwareSerial to RX & TX
#define SFX_TX 5
#define SFX_RX 6

// Connect to the RST pin on the Sound Board
#define SFX_RST 4

// You can also monitor the ACT pin for when audio is playing!

const int buttonPin = 2;     // the number of the pushbutton pin
int buttonPushCounter = 0;   // counter for the number of button presses - translates to the different modes we activate
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
int numModes = 8;


// we'll be using software serial
SoftwareSerial ss = SoftwareSerial(SFX_TX, SFX_RX);

// pass the software serial to Adafruit_soundboard, the second
// argument is the debug port (not used really) and the third
// arg is the reset pin
Adafruit_Soundboard sfx = Adafruit_Soundboard(&ss, NULL, SFX_RST);
// can also try hardware serial with
// Adafruit_Soundboard sfx = Adafruit_Soundboard(&Serial1, NULL, SFX_RST);

void setup() {
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
  Serial.begin(115200);
  Serial.println("Adafruit Sound Board!");
 
  // softwareserial at 9600 baud
  ss.begin(9600);
  // can also do Serial1.begin(9600)

  if (!sfx.reset()) {
    Serial.println("Not found");
    while (1);
  }
  Serial.println("SFX board found");
}

void handleButton(){ //detect state of button push
 
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastButtonState) {
    if (buttonState == LOW) { // if the current state is LOW then the button went from on to off:
      if (buttonPushCounter < numModes){
        buttonPushCounter++;
      } else {
        buttonPushCounter = 0;
      }
      Serial.println("#1"); //doesn't work
      //ss.println("#1"); //doesn't work
    }
    // Delay a little bit for debouncing
    //delay(buttonDelay);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;
}


void loop() {
  flushInput();
  handleButton();
  Serial.println(F("What would you like to do?"));
  Serial.println(F("[r] - reset"));
  Serial.println(F("[+] - Vol +"));
  Serial.println(F("[-] - Vol -"));
  Serial.println(F("[L] - List files"));
  Serial.println(F("[P] - play by file name"));
  Serial.println(F("[#] - play by file number"));
  Serial.println(F("[=] - pause playing"));
  Serial.println(F("[>] - unpause playing"));
  Serial.println(F("[q] - stop playing"));
  Serial.println(F("[t] - playtime status"));
  Serial.println(F("> "));
 
  while (!Serial.available());
  char cmd = Serial.read();
 
  flushInput();
 
  switch (cmd) {
    case 'r': {
      if (!sfx.reset()) {
        Serial.println("Reset failed");
      }
      break;
    }
   
    case 'L': {
      uint8_t files = sfx.listFiles();
   
      Serial.println("File Listing");
      Serial.println("========================");
      Serial.println();
      Serial.print("Found "); Serial.print(files); Serial.println(" Files");
      for (uint8_t f=0; f<files; f++) {
        Serial.print(f);
        Serial.print("\tname: "); Serial.print(sfx.fileName(f));
        Serial.print("\tsize: "); Serial.println(sfx.fileSize(f));
      }
      Serial.println("========================");
      break;
    }
   
    case '#': {
      Serial.print("Enter track #");
      uint8_t n = readnumber();

      Serial.print("\nPlaying track #"); Serial.println(n);
      if (! sfx.playTrack(n) ) {
        Serial.println("Failed to play track?");
      }
      break;
    }
   
    case 'P': {
      Serial.print("Enter track name (full 12 character name!) >");
      char name[20];
      readline(name, 20);

      Serial.print("\nPlaying track \""); Serial.print(name); Serial.print("\"");
      if (! sfx.playTrack(name) ) {
        Serial.println("Failed to play track?");
      }
      break;
   }

   case '+': {
      Serial.println("Vol up...");
      uint16_t v;
      if (! (v = sfx.volUp()) ) {
        Serial.println("Failed to adjust");
      } else {
        Serial.print("Volume: "); Serial.println(v);
      }
      break;
   }

   case '-': {
      Serial.println("Vol down...");
      uint16_t v;
      if (! (v=sfx.volDown()) ) {
        Serial.println("Failed to adjust");
      } else {
        Serial.print("Volume: ");
        Serial.println(v);
      }
      break;
   }
   
   case '=': {
      Serial.println("Pausing...");
      if (! sfx.pause() ) Serial.println("Failed to pause");
      break;
   }
   
   case '>': {
      Serial.println("Unpausing...");
      if (! sfx.unpause() ) Serial.println("Failed to unpause");
      break;
   }
   
   case 'q': {
      Serial.println("Stopping...");
      if (! sfx.stop() ) Serial.println("Failed to stop");
      break;
   } 

   case 't': {
      Serial.print("Track time: ");
      uint32_t current, total;
      if (! sfx.trackTime(&current, &total) ) Serial.println("Failed to query");
      Serial.print(current); Serial.println(" seconds");
      break;
   } 

   case 's': {
      Serial.print("Track size (bytes remaining/total): ");
      uint32_t remain, total;
      if (! sfx.trackSize(&remain, &total) )
        Serial.println("Failed to query");
      Serial.print(remain); Serial.print("/"); Serial.println(total);
      break;
   } 

  }
}






/************************ MENU HELPERS ***************************/

void flushInput() {
  // Read all available serial input to flush pending data.
  uint16_t timeoutloop = 0;
  while (timeoutloop++ < 40) {
    while(ss.available()) {
      ss.read();
      timeoutloop = 0;  // If char was received reset the timer
    }
    delay(1);
  }
}

char readBlocking() {
  while (!Serial.available());
  return Serial.read();
}

uint16_t readnumber() {
  uint16_t x = 0;
  char c;
  while (! isdigit(c = readBlocking())) {
    //Serial.print(c);
  }
  Serial.print(c);
  x = c - '0';
  while (isdigit(c = readBlocking())) {
    Serial.print(c);
    x *= 10;
    x += c - '0';
  }
  return x;
}

uint8_t readline(char *buff, uint8_t maxbuff) {
  uint16_t buffidx = 0;
 
  while (true) {
    if (buffidx > maxbuff) {
      break;
    }

    if (Serial.available()) {
      char c =  Serial.read();
      //Serial.print(c, HEX); Serial.print("#"); Serial.println(c);

      if (c == '\r') continue;
      if (c == 0xA) {
        if (buffidx == 0) {  // the first 0x0A is ignored
          continue;
        }
        buff[buffidx] = 0;  // null term
        return buffidx;
      }
      buff[buffidx] = c;
      buffidx++;
    }
  }
  buff[buffidx] = 0;  // null term
  return buffidx;
}
/************************ MENU HELPERS ***************************/

heaversm
 
Posts: 31
Joined: Thu Apr 12, 2012 10:58 am

Re: programmatically triggering adafruit soundboard via seri

by adafruit_support_bill on Sat Nov 10, 2018 10:20 am

This line will prevent you from monitoring buttons effectively, since it will block waiting for serial input.
Code: Select all | TOGGLE FULL SIZE
  while (!Serial.available());


You should re-structure your loop something like this:

Code: Select all | TOGGLE FULL SIZE
void loop()
{
  handleButton();  // handle any button inputs
 
  if(!Serial.available()) // handle any serial inputs
  {
    char cmd = Serial.read();
    flushInput();
   
    // code for handling serial commands goes here  . . .
   
   }
}

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

Re: programmatically triggering adafruit soundboard via seri

by heaversm on Sun Nov 11, 2018 4:27 pm

Thanks for your response. I still can't seem to get it working. This is my updated code - I've tried to adjust it numerous ways:

Code: Select all | TOGGLE FULL SIZE
//https://learn.adafruit.com/adafruit-audio-fx-sound-board/serial-audio-control

#include <SoftwareSerial.h>
#include "Adafruit_Soundboard.h"


// Choose any two pins that can be used with SoftwareSerial to RX & TX
#define SFX_TX 5
#define SFX_RX 6

// Connect to the RST pin on the Sound Board
#define SFX_RST 4

// You can also monitor the ACT pin for when audio is playing!

const int buttonPin = 2;     // the number of the pushbutton pin
int buttonPushCounter = 0;   // counter for the number of button presses - translates to the different modes we activate
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
int numModes = 8;
int buttonDelay = 50;

SoftwareSerial ss = SoftwareSerial(SFX_TX, SFX_RX);
Adafruit_Soundboard sfx = Adafruit_Soundboard(&ss, NULL, SFX_RST);

void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(115200);
  Serial.println("Adafruit Sound Board!");
 
  ss.begin(9600);
 
  if (!sfx.reset()) {
    Serial.println("Not found");
    while (1);
  }
  Serial.println("SFX board found");
}

void loop() {
  //flushInput();
  handleButton();
  handleSerial();
}


void handleButton(){ //detect state of button push
 
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastButtonState) {
    if (buttonState == LOW) { // if the current state is LOW then the button went from on to off:
      if (buttonPushCounter < numModes){
        buttonPushCounter++;
      } else {
        buttonPushCounter = 0;
      }
      //Serial.println("#1");
      Serial.println("#");
      Serial.println("1");
    }
    // Delay a little bit for debouncing
    //delay(buttonDelay);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;
}

void handleSerial(){
  flushInput();
  if (!Serial.available()){
 
    char cmd = Serial.read();

    flushInput();
   
    switch (cmd) {
      case 'r': {
        if (!sfx.reset()) {
          Serial.println("Reset failed");
        }
        break;
      }
     
      case 'L': {
        uint8_t files = sfx.listFiles();
     
        Serial.println("File Listing");
        Serial.println("========================");
        Serial.println();
        Serial.print("Found "); Serial.print(files); Serial.println(" Files");
        for (uint8_t f=0; f<files; f++) {
          Serial.print(f);
          Serial.print("\tname: "); Serial.print(sfx.fileName(f));
          Serial.print("\tsize: "); Serial.println(sfx.fileSize(f));
        }
        Serial.println("========================");
        break;
      }
     
      case '#': {
        Serial.print("Enter track #");
        uint8_t n = readnumber();
 
        Serial.print("\nPlaying track #"); Serial.println(n);
        if (! sfx.playTrack(n) ) {
          Serial.println("Failed to play track?");
        }
        break;
      }
     
      case 'P': {
        Serial.print("Enter track name (full 12 character name!) >");
        char name[20];
        readline(name, 20);
 
        Serial.print("\nPlaying track \""); Serial.print(name); Serial.print("\"");
        if (! sfx.playTrack(name) ) {
          Serial.println("Failed to play track?");
        }
        break;
     }
 
     case '+': {
        Serial.println("Vol up...");
        uint16_t v;
        if (! (v = sfx.volUp()) ) {
          Serial.println("Failed to adjust");
        } else {
          Serial.print("Volume: "); Serial.println(v);
        }
        break;
     }
 
     case '-': {
        Serial.println("Vol down...");
        uint16_t v;
        if (! (v=sfx.volDown()) ) {
          Serial.println("Failed to adjust");
        } else {
          Serial.print("Volume: ");
          Serial.println(v);
        }
        break;
     }
     
     case '=': {
        Serial.println("Pausing...");
        if (! sfx.pause() ) Serial.println("Failed to pause");
        break;
     }
     
     case '>': {
        Serial.println("Unpausing...");
        if (! sfx.unpause() ) Serial.println("Failed to unpause");
        break;
     }
     
     case 'q': {
        Serial.println("Stopping...");
        if (! sfx.stop() ) Serial.println("Failed to stop");
        break;
     } 
 
    }
  }
}


/************************ MENU HELPERS ***************************/

void flushInput() {
  // Read all available serial input to flush pending data.
  uint16_t timeoutloop = 0;
  while (timeoutloop++ < 40) {
    while(ss.available()) {
      ss.read();
      timeoutloop = 0;  // If char was received reset the timer
    }
    delay(1);
  }
}

char readBlocking() {
  while (!Serial.available());
  return Serial.read();
}

uint16_t readnumber() {
  uint16_t x = 0;
  char c;
  while (! isdigit(c = readBlocking())) {
    //Serial.print(c);
  }
  Serial.print(c);
  x = c - '0';
  while (isdigit(c = readBlocking())) {
    Serial.print(c);
    x *= 10;
    x += c - '0';
  }
  return x;
}

uint8_t readline(char *buff, uint8_t maxbuff) {
  uint16_t buffidx = 0;
 
  while (true) {
    if (buffidx > maxbuff) {
      break;
    }

    if (Serial.available()) {
      char c =  Serial.read();
      //Serial.print(c, HEX); Serial.print("#"); Serial.println(c);

      if (c == '\r') continue;
      if (c == 0xA) {
        if (buffidx == 0) {  // the first 0x0A is ignored
          continue;
        }
        buff[buffidx] = 0;  // null term
        return buffidx;
      }
      buff[buffidx] = c;
      buffidx++;
    }
  }
  buff[buffidx] = 0;  // null term
  return buffidx;
}
/************************ MENU HELPERS ***************************/


I can switch back and forth between the example sketch and mine and it plays properly with the example, so it's not a hardware issue.

I suppose there's a few things tripping me up:

* the `!Serial.available()`. Why the `!`? Shouldn't it be "While Serial Is Available"?

* The flushInput function. What exactly does this function do? The name seems to imply that it clears out any input. I'm trying to understand the code, and it seems like it's saying that it reads the data somehow though, and maybe blocks any further commands?

* ss.read() vs Serial.read - Looks like ss reads at 9600 baud and Serial.read is at 115200, and my guess is the former handles commands from the code to the sound board, and the latter handles user input commands?

Anyway - I've tried moving around flush input and read commands, toggling the while and if Serial available is true and false, printing lines individually and together, nothing seems to be working. If you have any additional pointers, that'd be great. Thanks!

heaversm
 
Posts: 31
Joined: Thu Apr 12, 2012 10:58 am

Re: programmatically triggering adafruit soundboard via seri

by heaversm on Sun Nov 11, 2018 4:54 pm

Ah! I figured it out. Needed to call `ss.println("#1")`, not Serial.println("#1");

Thanks for your help!

heaversm
 
Posts: 31
Joined: Thu Apr 12, 2012 10:58 am

Re: programmatically triggering adafruit soundboard via seri

by adafruit_support_bill on Sun Nov 11, 2018 7:20 pm

Good to hear that works for you. Thanks for the follow-up.

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

Please be positive and constructive with your questions and comments.