Finite state machine tuning, audio menu navigation & playback

Hi it’s been a while; I’m working on a project that uses a general aviation headset (mike and headphones), an easyVR module, a sparkfun mp3 shield and an arduino mega attached to 16 pushbuttons and LEDs as a checklist aid. the code regarding the easyVR and the mp3 playback is okay, but I’m throwing a lot of spaghetti at the wall in order to get my finite state machine to work right.

Here it is in principle: machine scans buttons, if a button is pressed, it records whether or not it’s a unique press and moves on. It then plays a checklist mp3 file based on the button pressed, and how many times it has been to this checklist. It then waits for a keypress or an easyVR command before saying ‘copy’ and moving on. I threw in some basic serial print instructions to see what’s going on, and it simply doesn’t want to sit in the ‘scan buttons’ loop until a button is scanned, but rather starts playing an mp3 straightaway. I’m working on a soldered together version of this hardware and am quite happy with it but would like to get this code sorted out (and add things like PAUSE if analogRead(7) (output from a comparator on radio calls) is greater than 70 or so).

the code works well enough as a series of nested if loops, but I think a switch/case scenario may be helpful to make sense of the code and to add things like blinking LEDs and pause functions during playback or listening for commands. Here’s my spaghetti code, there are a bunch of variables that aren’t being used and will be pulled out of it eventually. Any input would be greatly appreciated and I’d be glad to share my hardware tricks if anyone is interested. I think it’s probably me not understanding switch, or making a series of other uninformed mistakes.

//include libraries, mp3, SD, FAT, SPI
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h> 
#include <SFEMP3Shield.h>
#include "Arduino.h"
SdFat sd;
SFEMP3Shield MP3player;

//libraries for easyVR
  #include "SoftwareSerial.h"
  SoftwareSerial port(62, 63);
  #include "EasyVR.h"
EasyVR easyvr(port);
//has the word been detected?
int checkword = 0;
//is it active?
int active = 1;
        int16_t idx;
//button scanner for 16 buttons
int scan = 0;
//hits
int thishit;
int lasthit =256;
int hitcounter;
//how many hits?

//number of entries per heading
int menucount[] = 
{
  17, 29, 42, 49, 67, 80, 98, 111, 117, 124, 129, 137, 145, 148, 152, 162
};

int menuoption[] =
{
  12, 13, 7, 18, 13, 18, 13, 6, 7, 6, 7, 8, 2, 5, 10, 13
};
//button pins
int buttonpins[] = 
{
  23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 66, 68
};
//LED pins
int LEDpins[] = 
{
  22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 67, 69
};

//buttonstates
int thisbuttonstate[16];
int lastbuttonstate[16];
void setup() {
  //diagnostics to serial monitor
  Serial.begin(9600);      // open the serial port at 9600 bps:    
  //set mic distance for easyVR
      easyvr.setMicDistance(1);
        easyvr.setLanguage(EasyVR::ENGLISH);
        easyvr.setTrailingSilence(EasyVR::TRAILING_MIN);
  easyvr.setCommandLatency(EasyVR::MODE_FAST);

      port.begin(9600);
  easyvr.setTimeout(0);

  sd.begin(SD_SEL, SPI_HALF_SPEED);
  MP3player.begin();
   delay(500);
       MP3player.setVolume(1,1);
//initialize buttons, LEDs
for (int i = 0; i < 16; i++)
{
  pinMode (buttonpins[i], INPUT_PULLUP);
//toggle check, to see if we made it this far
//digitalWrite(LEDpins[i], HIGH);

    pinMode (LEDpins[i], OUTPUT);

  }
}


 void loop ()
  {
switch (active)
{
  //---------------------------------CASE1-----------------BUTTON SCANNER
  case 1:
  //diagnostic:
  Serial.println("1");
  //no buttons hit, no menus active, nothing to play- scan buttons and wait for a hit
//scan buttons for a hit. LOW value = hit.
    for (scan = 0; scan < 16; scan++)
    {
     lasthit = thishit;
lastbuttonstate[scan] = thisbuttonstate[scan];
thisbuttonstate[scan] = digitalRead[scan];
if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
//------------------BUTTON PUSH DETECTION
{
  active = 2;
  lasthit = thishit;
  thishit = scan;
  break;
}
else
{
active = 1;
break;
}
    }
    
    //-------------------------------CASE 2--------------BUTTON ANALYZER
  case 2:
  Serial.println("2");
  //a button has been hit. which one? and how many times? set cases for playback
  //analyse data
  
if (thishit != lasthit)
//unique button press, new checklist
{
  hitcounter = 0;
  active = 3;
  break;
}
if (thishit == lasthit)
//the same button has been pushed as last time
{
hitcounter++;
active = 4;
break;
}
if (hitcounter == menuoption[scan])
//we've reached the last menu entry for that checklist
{
hitcounter = 0;
active = 5;
break;
}
if (hitcounter > menuoption[scan])
{
  //we've advanced past the end of a checklist, reset
  hitcounter = 0;
  active = 1;
  break;
}


//-----------------------------------------CASE3--------START A CHECKLIST
  case 3:
  Serial.println("3");
  //play the first file, if user says CHECK lock into that checklist, if user presses another button, make a new case 3 under that heading instead
  MP3player.playTrack(scan+1);
  while ((MP3player.isPlaying()))
{
  digitalWrite(LEDpins[scan], LOW);
}

 easyvr.recognizeWord(4);
  //easyvr.recognizeCommand(group); // used for SD commands/trigger or for built-in trigger word
  do
  {
digitalWrite (LEDpins[scan], HIGH);
//CHECK TO SEE IF A BUTTON HAS BEEN PRESSED
    for (scan = 0; scan < 16; scan++)
thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
{
  if (thishit != lasthit)
  {
    active = 2;
    break;
  }
if (thishit = scan)
{
 active = 2;
 break;
    }
}
  }
  while (!easyvr.hasFinished());
digitalWrite(LEDpins[scan], LOW);
  MP3player.playTrack(176);
  //wait til track is done
  while ((!MP3player.isPlaying()))
  active = 2;
  break;

  case 4:
  Serial.println("4");
  //---------------------------------------------------------CASE4--------------CHECKLIST
  //user has entered a checklist, lock out other buttons
MP3player.playTrack(menucount[scan]+(hitcounter-1));MP3player.playTrack(menucount[scan]+(hitcounter-1));
  while ((MP3player.isPlaying()))
{
  digitalWrite(LEDpins[scan], LOW);

}
 easyvr.recognizeWord(4);
  //easyvr.recognizeCommand(group); // used for SD commands/trigger or for built-in trigger word
  do
  {
digitalWrite (LEDpins[scan], HIGH);
//CHECK TO SEE IF *THIS* BUTTON HAS BEEN PRESSED

thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
{
hitcounter++;
 active = 2;
break;
}
    }

  while (!easyvr.hasFinished());
digitalWrite(LEDpins[scan], LOW);
  MP3player.playTrack(176);
  //wait til track is done
  while ((!MP3player.isPlaying()))
  active = 1;
  break;


  case 5: 
    Serial.println("5");
  //-------------------------------------------CASE 5------------------------LAST ITEM IN LIST
  //play the last file in the checklist, once complete, play file 'CHECKLIST COMPLETE and reset everything to case 1

MP3player.playTrack(menucount[scan]+(menuoption[scan]));

  while ((MP3player.isPlaying()))
{
  digitalWrite(LEDpins[scan], LOW);
}

 easyvr.recognizeWord(4);
  //LISTEN FOR THe WORD CHECK
  do
  {
digitalWrite (LEDpins[scan], HIGH);
//CHECK TO SEE IF *THIS* BUTTON HAS BEEN PRESSED

thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
{
hitcounter = 0;

 active = 1;
  MP3player.playTrack(177);
    while ((!MP3player.isPlaying()))
    {
      break;
    }
}
}
  while (!easyvr.hasFinished());
digitalWrite(LEDpins[scan], LOW);
  MP3player.playTrack(177);
  //wait til track is done
  while ((!MP3player.isPlaying()))
  active = 1;
  hitcounter = 0;
  break;
}
  }

The formatting on that code is not easy to read. Use the auto-format tool available on the Tools menu in your Arduino IDE. It is very good. If it seems like it scrambled your code then the code was wrong, not the autoformat.

Give your states names, for example:

#define STATE_IDLE 1
#define STATE_TAKEOFF_CHECKLIST 2
...

That will make it easier to read the code and understand where you are in all of the states.

Thank you!
Gave it a quick format, redid the state names, and updated the print string to reflect the activity.
I did get it to do the scan routine, now it just doesn’t exit it. Would be nice to get this together enough to blink LEDs while listening and have some other options inside each state; first to get them working the right way.

//include libraries, mp3, SD, FAT, SPI
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include <SFEMP3Shield.h>
#include "Arduino.h"
SdFat sd;
SFEMP3Shield MP3player;

//libraries for easyVR
#include "SoftwareSerial.h"
SoftwareSerial port(62, 63);
#include "EasyVR.h"
EasyVR easyvr(port);
//has the word been detected?
int checkword = 0;
//is it active?

int16_t idx;
//button scanner for 16 buttons
int scan = 0;
//hits
int thishit;
int lasthit = 255;
int hitcounter;
//how many hits?
//DEFINE STATES
#define STATE_BUTTONSCAN 1
#define STATE_CHECK_HITS 2
#define STATE_FIRST_ENTRY 3
#define STATE_IN_CHECKLIST 4
#define STATE_LAST_ENTRY 5
int active = STATE_BUTTONSCAN;
//number of entries per heading
int menucount[] =
{
  17, 29, 42, 49, 67, 80, 98, 111, 117, 124, 129, 137, 145, 148, 152, 162
};

int menuoption[] =
{
  12, 13, 7, 18, 13, 18, 13, 6, 7, 6, 7, 8, 2, 5, 10, 13
};
//button pins
int buttonpins[] =
{
  23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 66, 68
};
//LED pins
int LEDpins[] =
{
  22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 67, 69
};

//buttonstates
int thisbuttonstate[16];
int lastbuttonstate[16];
void setup() {
  //diagnostics to serial monitor
  Serial.begin(9600);      // open the serial port at 9600 bps:
  //set mic distance for easyVR
  easyvr.setMicDistance(1);
  easyvr.setLanguage(EasyVR::ENGLISH);
  easyvr.setTrailingSilence(EasyVR::TRAILING_MIN);
  easyvr.setCommandLatency(EasyVR::MODE_FAST);

  port.begin(9600);
  easyvr.setTimeout(0);

  sd.begin(SD_SEL, SPI_HALF_SPEED);
  MP3player.begin();
  delay(500);
  MP3player.setVolume(1, 1);
  //initialize buttons, LEDs
  for (int i = 0; i < 16; i++)
  {
    pinMode (buttonpins[i], INPUT_PULLUP);
    //toggle check, to see if we made it this far
    //digitalWrite(LEDpins[i], HIGH);

    pinMode (LEDpins[i], OUTPUT);

  }
}


void loop ()
{
  switch (active)
  {
    //---------------------------------CASE1-----------------BUTTON SCANNER
    case STATE_BUTTONSCAN:
    #define STATE_BUTTONSCAN 1
#define STATE_CHECK_HITS 2
#define STATE_FIRST_ENTRY 3
#define STATE_IN_CHECKLIST 4
#define STATE_LAST_ENTRY 5
      //diagnostic:
      Serial.println("1");
      //no buttons hit, no menus active, nothing to play- scan buttons and wait for a hit
      //scan buttons for a hit. LOW value = hit.
      for (scan = 0; scan < 16; scan++)
      {
        lasthit = thishit;
        lastbuttonstate[scan] = thisbuttonstate[scan];
        thisbuttonstate[scan] = digitalRead[scan];
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
          //------------------BUTTON PUSH DETECTION
        {
          active = STATE_CHECK_HITS;
          lasthit = thishit;
          thishit = scan;
          break;
        }
        else
        {
          //active = STATE_BUTTONSCAN;
          return;
        }
      }

    //-------------------------------CASE 2--------------BUTTON ANALYZER
    case STATE_CHECK_HITS:
      Serial.println("2");
      //a button has been hit. which one? and how many times? set cases for playback
      //analyse data

      if (thishit != lasthit)
        //unique button press, new checklist
      {
        hitcounter = 0;
        active = STATE_FIRST_ENTRY;
        break;
      }
      if (thishit == lasthit)
        //the same button has been pushed as last time
      {
        hitcounter++;
        active = STATE_IN_CHECKLIST;
        break;
      }
      if (hitcounter == menuoption[scan])
        //we've reached the last menu entry for that checklist
      {
        //hitcounter = 0;
        active = STATE_LAST_ENTRY;
        break;
      }
      if (hitcounter > menuoption[scan])
      {
        //we've advanced past the end of a checklist, reset
        hitcounter = 0;
        active = STATE_BUTTONSCAN;
        break;
      }


    //-----------------------------------------CASE3--------START A CHECKLIST
    case 3:
      Serial.println("3");
      //play the first file, if user says CHECK lock into that checklist, if user presses another button, make a new case 3 under that heading instead
      MP3player.playTrack(scan + 1);
      while ((MP3player.isPlaying()))
      {
        digitalWrite(LEDpins[scan], LOW);
      }

      easyvr.recognizeWord(4);
      //easyvr.recognizeCommand(group); // used for SD commands/trigger or for built-in trigger word
      do
      {
        digitalWrite (LEDpins[scan], HIGH);
        //CHECK TO SEE IF A BUTTON HAS BEEN PRESSED
        for (scan = 0; scan < 16; scan++)
          thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
        {
          if (thishit != lasthit)
          {
            active = STATE_FIRST_ENTRY;
            break;
          }
          if (thishit = scan)
          {
            active = STATE_IN_CHECKLIST;
            break;
          }
        }
      }
      while (!easyvr.hasFinished());
      digitalWrite(LEDpins[scan], LOW);
      MP3player.playTrack(176);
      //wait til track is done
      while ((!MP3player.isPlaying()))
        active = STATE_CHECK_HITS;
      break;

    case STATE_IN_CHECKLIST:
      Serial.println("4");
      //---------------------------------------------------------CASE4--------------CHECKLIST
      //user has entered a checklist, lock out other buttons
      MP3player.playTrack(menucount[scan] + (hitcounter - 1)); MP3player.playTrack(menucount[scan] + (hitcounter - 1));
      while ((MP3player.isPlaying()))
      {
        digitalWrite(LEDpins[scan], LOW);

      }
      easyvr.recognizeWord(4);
      //easyvr.recognizeCommand(group); // used for SD commands/trigger or for built-in trigger word
      do
      {
        digitalWrite (LEDpins[scan], HIGH);
        //CHECK TO SEE IF *THIS* BUTTON HAS BEEN PRESSED

        thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
        {
          hitcounter++;
          active = STATE_CHECK_HITS;
          break;
        }
      }

      while (!easyvr.hasFinished());
      digitalWrite(LEDpins[scan], LOW);
      MP3player.playTrack(176);
      //wait til track is done
      while ((!MP3player.isPlaying()))
        active = STATE_BUTTONSCAN;
      break;


    case STATE_LAST_ENTRY:
      Serial.println("5");
      //-------------------------------------------CASE 5------------------------LAST ITEM IN LIST
      //play the last file in the checklist, once complete, play file 'CHECKLIST COMPLETE and reset everything to case 1

      MP3player.playTrack(menucount[scan] + (menuoption[scan]));

      while ((MP3player.isPlaying()))
      {
        digitalWrite(LEDpins[scan], LOW);
      }

      easyvr.recognizeWord(4);
      //LISTEN FOR THe WORD CHECK
      do
      {
        digitalWrite (LEDpins[scan], HIGH);
        //CHECK TO SEE IF *THIS* BUTTON HAS BEEN PRESSED

        thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
        {
          hitcounter = 0;

          active = STATE_BUTTONSCAN;
          MP3player.playTrack(177);
          while ((!MP3player.isPlaying()))
          {
            break;
          }
        }
      }
      while (!easyvr.hasFinished());
      digitalWrite(LEDpins[scan], LOW);
      MP3player.playTrack(177);
      //wait til track is done
      while ((!MP3player.isPlaying()))
        active = STATE_BUTTONSCAN;
      hitcounter = 0;
      break;
  }
}

typo in the last code, forgot to delete my reference #defines

//include libraries, mp3, SD, FAT, SPI
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include <SFEMP3Shield.h>
#include "Arduino.h"
SdFat sd;
SFEMP3Shield MP3player;

//libraries for easyVR
#include "SoftwareSerial.h"
SoftwareSerial port(62, 63);
#include "EasyVR.h"
EasyVR easyvr(port);
//has the word been detected?
int checkword = 0;
//is it active?

int16_t idx;
//button scanner for 16 buttons
int scan = 0;
//hits
int thishit;
int lasthit = 255;
int hitcounter;
//how many hits?
//DEFINE STATES
#define STATE_BUTTONSCAN 1
#define STATE_CHECK_HITS 2
#define STATE_FIRST_ENTRY 3
#define STATE_IN_CHECKLIST 4
#define STATE_LAST_ENTRY 5
int active = STATE_BUTTONSCAN;
//number of entries per heading
int menucount[] =
{
  17, 29, 42, 49, 67, 80, 98, 111, 117, 124, 129, 137, 145, 148, 152, 162
};

int menuoption[] =
{
  12, 13, 7, 18, 13, 18, 13, 6, 7, 6, 7, 8, 2, 5, 10, 13
};
//button pins
int buttonpins[] =
{
  23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 66, 68
};
//LED pins
int LEDpins[] =
{
  22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 67, 69
};

//buttonstates
int thisbuttonstate[16];
int lastbuttonstate[16];
void setup() {
  //diagnostics to serial monitor
  Serial.begin(9600);      // open the serial port at 9600 bps:
  //set mic distance for easyVR
  easyvr.setMicDistance(1);
  easyvr.setLanguage(EasyVR::ENGLISH);
  easyvr.setTrailingSilence(EasyVR::TRAILING_MIN);
  easyvr.setCommandLatency(EasyVR::MODE_FAST);

  port.begin(9600);
  easyvr.setTimeout(0);

  sd.begin(SD_SEL, SPI_HALF_SPEED);
  MP3player.begin();
  delay(500);
  MP3player.setVolume(1, 1);
  //initialize buttons, LEDs
  for (int i = 0; i < 16; i++)
  {
    pinMode (buttonpins[i], INPUT_PULLUP);
    //toggle check, to see if we made it this far
    //digitalWrite(LEDpins[i], HIGH);

    pinMode (LEDpins[i], OUTPUT);

  }
}


void loop ()
{
  switch (active)
  {
    //---------------------------------CASE1-----------------BUTTON SCANNER
    case STATE_BUTTONSCAN:
      //diagnostic:
      Serial.println("scanning");
      //no buttons hit, no menus active, nothing to play- scan buttons and wait for a hit
      //scan buttons for a hit. LOW value = hit.
      for (scan = 0; scan < 16; scan++)
      {
        lasthit = thishit;
        lastbuttonstate[scan] = thisbuttonstate[scan];
        thisbuttonstate[scan] = digitalRead[scan];
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
          //------------------BUTTON PUSH DETECTION
        {
          active = STATE_CHECK_HITS;
          lasthit = thishit;
          thishit = scan;
          break;
        }
        else
        {
          //active = STATE_BUTTONSCAN;
          return;
        }
      }

    //-------------------------------CASE 2--------------BUTTON ANALYZER
    case STATE_CHECK_HITS:
      Serial.println("checking hits");
      //a button has been hit. which one? and how many times? set cases for playback
      //analyse data

      if (thishit != lasthit)
        //unique button press, new checklist
      {
        hitcounter = 0;
        active = STATE_FIRST_ENTRY;
        break;
      }
      if (thishit == lasthit)
        //the same button has been pushed as last time
      {
        hitcounter++;
        active = STATE_IN_CHECKLIST;
        break;
      }
      if (hitcounter == menuoption[scan])
        //we've reached the last menu entry for that checklist
      {
        //hitcounter = 0;
        active = STATE_LAST_ENTRY;
        break;
      }
      if (hitcounter > menuoption[scan])
      {
        //we've advanced past the end of a checklist, reset
        hitcounter = 0;
        active = STATE_BUTTONSCAN;
        break;
      }


    //-----------------------------------------CASE3--------START A CHECKLIST
    case 3:
      Serial.println("first entry/header");
      //play the first file, if user says CHECK lock into that checklist, if user presses another button, make a new case 3 under that heading instead
      MP3player.playTrack(scan + 1);
      while ((MP3player.isPlaying()))
      {
        digitalWrite(LEDpins[scan], LOW);
      }

      easyvr.recognizeWord(4);
      //easyvr.recognizeCommand(group); // used for SD commands/trigger or for built-in trigger word
      do
      {
        digitalWrite (LEDpins[scan], HIGH);
        //CHECK TO SEE IF A BUTTON HAS BEEN PRESSED
        for (scan = 0; scan < 16; scan++)
          thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
        {
          if (thishit != lasthit)
          {
            active = STATE_FIRST_ENTRY;
            break;
          }
          if (thishit = scan)
          {
            active = STATE_IN_CHECKLIST;
            break;
          }
        }
      }
      while (!easyvr.hasFinished());
      digitalWrite(LEDpins[scan], LOW);
      MP3player.playTrack(176);
      //wait til track is done
      while ((!MP3player.isPlaying()))
        active = STATE_CHECK_HITS;
      break;

    case STATE_IN_CHECKLIST:
      Serial.println("inside the checklist");
      //---------------------------------------------------------CASE4--------------CHECKLIST
      //user has entered a checklist, lock out other buttons
      MP3player.playTrack(menucount[scan] + (hitcounter - 1)); MP3player.playTrack(menucount[scan] + (hitcounter - 1));
      while ((MP3player.isPlaying()))
      {
        digitalWrite(LEDpins[scan], LOW);

      }
      easyvr.recognizeWord(4);
      //easyvr.recognizeCommand(group); // used for SD commands/trigger or for built-in trigger word
      do
      {
        digitalWrite (LEDpins[scan], HIGH);
        //CHECK TO SEE IF *THIS* BUTTON HAS BEEN PRESSED

        thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
        {
          hitcounter++;
          active = STATE_CHECK_HITS;
          break;
        }
      }

      while (!easyvr.hasFinished());
      digitalWrite(LEDpins[scan], LOW);
      MP3player.playTrack(176);
      //wait til track is done
      while ((!MP3player.isPlaying()))
        active = STATE_BUTTONSCAN;
      break;


    case STATE_LAST_ENTRY:
      Serial.println("last entry, checklist complete");
      //-------------------------------------------CASE 5------------------------LAST ITEM IN LIST
      //play the last file in the checklist, once complete, play file 'CHECKLIST COMPLETE and reset everything to case 1

      MP3player.playTrack(menucount[scan] + (menuoption[scan]));

      while ((MP3player.isPlaying()))
      {
        digitalWrite(LEDpins[scan], LOW);
      }

      easyvr.recognizeWord(4);
      //LISTEN FOR THe WORD CHECK
      do
      {
        digitalWrite (LEDpins[scan], HIGH);
        //CHECK TO SEE IF *THIS* BUTTON HAS BEEN PRESSED

        thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
        {
          hitcounter = 0;

          active = STATE_BUTTONSCAN;
          MP3player.playTrack(177);
          while ((!MP3player.isPlaying()))
          {
            break;
          }
        }
      }
      while (!easyvr.hasFinished());
      digitalWrite(LEDpins[scan], LOW);
      MP3player.playTrack(177);
      //wait til track is done
      while ((!MP3player.isPlaying()))
        active = STATE_BUTTONSCAN;
      hitcounter = 0;
      break;
  }
}

I guess I’m not understanding the switch case thing, I was hoping this would keep looping until it hits the state change but it seems to just run through once, and go to the other routines whether a button has been pressed or not. Converting it to a FOR loop means it will keep running and never exit, even if a key has been pressed.

  switch(active)
  {
  case STATE_BUTTONSCAN:
  {


    //scan buttons for a hit. LOW value = hit.
    for (scan = 0; scan < 16; scan++)
    {
      //diagnostic:
      Serial.println(scan);
      lasthit = thishit;
      lastbuttonstate[scan] = thisbuttonstate[scan];
      thisbuttonstate[scan] = digitalRead[scan];

      if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
        //------------------BUTTON PUSH DETECTION
      {
        active = STATE_CHECK_HITS;
        lasthit = thishit;
        thishit = scan;
        //        break;
      }
      //else
      //{
      //    active = STATE_BUTTONSCAN;
      // scan++;
      // break;
      //}

    }
  }

Meanwhile this code works fine, no switch/case stuff, but I would like more control and for the code to be neatly defined functionwise in a manner that allows subroutines like blinkwithoutdelay to happen while waiting; and to handle different cases (blink all the LEDs when a checklist is complete, or pause if incoming audio is detected)…

//include libraries, mp3, SD, FAT, SPI
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include <SFEMP3Shield.h>
#include "Arduino.h"
SdFat sd;
SFEMP3Shield MP3player;

//libraries for easyVR
#include "SoftwareSerial.h"
SoftwareSerial port(62, 63);
#include "EasyVR.h"
EasyVR easyvr(port);
//has the word been detected?
int checkword = 0;
//is it active?
int active = 0;
int16_t idx;
//button scanner for 16 buttons
int scan = 0;
//hits
int thishit;
int lasthit = 256;
int hitcounter;
//how many hits?

//number of entries per heading
int menucount[] =
{
  17, 29, 42, 49, 67, 80, 98, 111, 117, 124, 129, 137, 145, 148, 152, 162
};

int menuoption[] =
{
  12, 13, 7, 18, 13, 18, 13, 6, 7, 6, 7, 8, 2, 5, 10, 13
};
//button pins
int buttonpins[] =
{
  23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 66, 68
};
//LED pins
int LEDpins[] =
{
  22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 67, 69
};

//buttonstates
int thisbuttonstate[16];
int lastbuttonstate[16];
void setup() {
  //set mic distance for easyVR
  easyvr.setMicDistance(1);
  easyvr.setLanguage(EasyVR::ENGLISH);
  easyvr.setTrailingSilence(EasyVR::TRAILING_MIN);
  easyvr.setCommandLatency(EasyVR::MODE_FAST);

  port.begin(9600);
  easyvr.setTimeout(0);

  sd.begin(SD_SEL, SPI_HALF_SPEED);
  MP3player.begin();
  delay(500);
  MP3player.setVolume(1, 1);
  //initialize buttons, LEDs
  for (int i = 0; i < 16; i++)
  {
    pinMode (buttonpins[i], INPUT_PULLUP);
    //toggle check, to see if we made it this far
    //digitalWrite(LEDpins[i], HIGH);

    pinMode (LEDpins[i], OUTPUT);

  }
}

void doscan()
{

  for (scan = 0; scan < 16; scan++)
  {
    lastbuttonstate[scan] = thisbuttonstate[scan];
    thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
    if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
    {
      active = 1;
      thishit = scan;
      if (thishit == lasthit)
      {
        hitcounter++;
        if (hitcounter > menuoption[scan])
        {
          hitcounter = 0;
          active = 0;
        }
        break;
      }
      if (thishit != lasthit)
      {
        hitcounter = 0;
        break;
      }
    }
  }
}
void loop ()
{
  if (active == 0);
  {
    doscan();
  }
  if (active == 1)
  {
    lasthit = thishit;

    if (hitcounter == 0)
    {
      MP3player.playTrack(scan + 1);
    }
    if (hitcounter > 0)
    {

      MP3player.playTrack(menucount[scan] + (hitcounter - 1));
    }
    while ((MP3player.isPlaying()))
    {
      digitalWrite(LEDpins[scan], HIGH);

    }

    easyvr.recognizeWord(4);
    //easyvr.recognizeCommand(group); // used for SD commands/trigger or for built-in trigger word

    do
    {
      digitalWrite (LEDpins[scan], HIGH);
    }

    while (!easyvr.hasFinished());
    digitalWrite(LEDpins[scan], LOW);
    MP3player.playTrack(176);
    //say 'COPY'
    active = LOW;

  }

}

I don’t fully understand the problem you are having, but I did notice this…

        digitalWrite (LEDpins[scan], HIGH);

//CHECK TO SEE IF A BUTTON HAS BEEN PRESSED
        for (scan = 0; scan < 16; scan++)
          thisbuttonstate[scan] = digitalRead(buttonpins[scan]);
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
        {
          if (thishit != lasthit)
          {
            active = STATE_FIRST_ENTRY;
            break;
          }
          if (thishit = scan)
          {
            active = STATE_IN_CHECKLIST;
            break;
          }
        }
      }
      while (!easyvr.hasFinished());
      digitalWrite(LEDpins[scan], LOW);

So you turn on one LED based on the value of scan then you use scan to scan all the other buttons, looking for something. At the end of that for() loop, scan will be 16. Then it looks like you might have intended to turn off the LED that you turned on earlier. Except you’ve destroyed the value of scan.

Use a separate, local varaible[/tt] inside the loop OR keep a copy of which LED number you turned on at the top.

Thank you for at least having a look!

The one thing that seems to be working just fine is the LED and the scan value- in fact everything about this works EXCEPT when I rearrange the code into a switch/case format. I eventually want to be able to add some other bits and pieces and I figure the code would be nice and organized if I formatted it into a nice row of cases, but it simply runs through the cases regardless of whether or not a button has been pushed at all. I added some serial print diagnostics so I can watch it hit case 1, sail right through to case 2, move on to case 3 or 4 and then just sit there. All without a shred of input at all. Meanwhile my kludgey if(this) {do that} code works fine. If I can figure out my error I would love to redo the code to switch cases, but for now the customer's getting this version so they can play with it at least and I can work a bit on some schematics for the hardware.

I don’t know what your code currently looks like, these comments are on the code in post #3.

On the first case of the switch, active is 1, you have two break statements that break out of the for loop but no statement to break out of the switch. This will send execution directly to the second condition, active is 2, even if it was not changed.

The break statements are in both the if and else clause of the button detection, which means that no matter what the loop will be broken on the first comparison. There is no need for the else clause which sets active to 1 since this switch case would not have been entered unless active was 1. I think you want to break out of the for loop if active becomes 2 or all buttons are scanned.

    switch (active)
  {
    //---------------------------------CASE1-----------------BUTTON SCANNER
    case 1:
      //diagnostic:
      Serial.println("1");
      //no buttons hit, no menus active, nothing to play- scan buttons and wait for a hit
      //scan buttons for a hit. LOW value = hit.
      for (scan = 0; (scan < 16) && (active == 1); scan++)
      {
        lasthit = thishit;
        lastbuttonstate[scan] = thisbuttonstate[scan];
        thisbuttonstate[scan] = digitalRead[scan];
        if (thisbuttonstate[scan] != lastbuttonstate[scan] && thisbuttonstate[scan] == LOW)
          //------------------BUTTON PUSH DETECTION
        {
          active = 2;
          lasthit = thishit;
          thishit = scan;
        }
      }

      break;

The next case, active is 2, has several break statements that sends execution out of the case statement too early. The code below is rearranged to achieve what (I think) you wanted to do.

    //-------------------------------CASE 2--------------BUTTON ANALYZER
    case 2:
      Serial.println("2");
      //a button has been hit. which one? and how many times? set cases for playback
      //analyse data

      if (thishit != lasthit)
        //unique button press, new checklist
      {
        hitcounter = 0;
        active = 3;

      } else {
        //the same button has been pushed as last time
        hitcounter++;

        if (hitcounter == menuoption[scan])
          //we've reached the last menu entry for that checklist
        {
          hitcounter = 0;
          active = 5;

        } else if (hitcounter > menuoption[scan])
        {
          //we've advanced past the end of a checklist, reset
          hitcounter = 0;
          active = 1;

        } else {

          active = 4;
        }

      }

      break;

baelthazoar:
Meanwhile this code works fine, no switch/case stuff, but I would like more control and for the code to be neatly defined functionwise in a manner that allows subroutines like blinkwithoutdelay to happen while waiting; and to handle different cases (blink all the LEDs when a checklist is complete, or pause if incoming audio is detected)...

you should study the enum resource for this kind of simplification