<
>
back to all cubes

Recording Cube

One face of the cube starts the recording function - the opposite face plays it back

On the "Record" cube, we were lucky to have Leor on our side. A month after accepting the assignment of creating this cube's electronics and code, our super-hero came to visit us, bringing a soldered prefboard Arduino shield (an improvement he created for the "Voiceshield", also including a hi-tech microphone), a very small and neat DIY amplifier circuit, the best quality speaker element possible to fit inside our tiny cubes, and super-sophisticated Arduino code. After receiving this bundle (and spending 2 hours being waled through all the branches of the code), most of what left for us to do was to cover it all with plexiglass, and comment (disabling) 80% of the code... :)

Arduino code - download here

/*
  CUBES originally created by the interaction Lab, Holon Institute of Technology for the Design Museum Holon.
  http://interaction.shenkar.ac.il
  This work is licensed under a Creative Commons Attribution 3.0 Unported License

  Rec Cube by Leor Weinstein
*/


/*
****************************************************************************************************************************
 * Filename      : RecCube.C
 * Programmer(s) : L. Weinstein
 * Description   : Main module of project, contains:
 *                 loop() main function as the first entry point to this application (from startup).
 *                 Setup() function for initializing all global and all modules static variables.
 *                 !! Additional Description here !!
 ****************************************************************************************************************************
 */

/*
****************************************************************************************************************************
 *                                                    REVISION HISTORY
 ****************************************************************************************************************************
 *
 * Release 1.00 Dated ??/??/2010 - L. Weinstein
 *-----------------------------------------------
 * ??/??/2010 - First Release
 *
 ****************************************************************************************************************************
 */


/*
****************************************************************************************************************************
 *                                                     INCLUDE FILES
 ****************************************************************************************************************************
 */
#include <Wire.h>
#include <Adxl345.h>    // Using ADXL345 I2C accelerometer


/*
****************************************************************************************************************************
 *                                                LOCAL CONSTANTS AND MACROS
 ****************************************************************************************************************************
 */
#define DEBUG
#define DEBUG_BAUD   9600

#ifdef DEBUG
#define DEBUG_PRINT( msg )   Serial.print( msg )
#define DEBUG_PRINTF( msg, format )   Serial.print( msg, format )
#else
#define DEBUG_PRINT( msg )
#define DEBUG_PRINTF( msg, format )
#endif

/* Define the IO ports used */
#define TONE_IND_PIN      3
#define ISD_PLAY_PIN      4
#define ISD_REC_PIN        5
#define ISD_ERASE_PIN    6
#define ISD_FWD_PIN        7
#define ISD_VOL_PIN        8
#define ISD_FT_PIN           9
#define ISD_RDY_PIN        2

/* Delay time between accel access retry in mS */
#define ACL_RETRY_DELAY        200
/* Delay time between accel access reads in mS */
#define ACL_READ_DELAY        200
/* Define ACL G value reading and allowed tolerance */
#define ACL_G_VAL                250
#define ACL_G_TOL                50

/* Delay time for indicating Erase all operation in mS */
#define ISD_ERASE_ALL_DELAY    4000
/* Delay time for Rdy after Erase all operation in mS */
#define ISD_ERASE_ALL_RDY_DELAY    3000
/* Delay time for Rdy after Erase last msg operation in mS */
#define ISD_ERASE_MSG_RDY_DELAY    1500

/* Delay time for ISD button edge debounce operation in mS */
#define ISD_BUTTON_DBNC_DELAY    80

/* Delay time for FT (feed through) to take affect in mS */
#define ISD_FT_DELAY                          100

/* Powerup tone beep sequence (freq in Hz and duration in mS) */
#define PWRUP_TONE1                            1000
#define PWRUP_TONE1_DURATION       200
#define PWRUP_TONE2                            1500
#define PWRUP_TONE2_DURATION       200
#define PWRUP_TONE3                            2000
#define PWRUP_TONE3_DURATION       200
/* Error tone beep sequence (freq in Hz and duration in mS) */
#define ERR_TONE                            250
#define ERR_TONE_DURATION       300
/* Rec tone beep sequence (freq in Hz and duration in mS) */
#define REC_TONE                            1200
// Rec tone will be played until Erase last msg operation is complete (~1 Sec)
/* Play tone beep sequence (freq in Hz and duration in mS) */
#define PLAY_TONE1                            1000
#define PLAY_TONE1_DURATION       200
#define PLAY_TONE2                            0
#define PLAY_TONE2_DURATION       100
#define PLAY_TONE3                            1000
#define PLAY_TONE3_DURATION       300
/* Stop tone beep sequence (freq in Hz and duration in mS) */
#define STOP_TONE1                            2000
#define STOP_TONE1_DURATION       200
#define STOP_TONE2                            1500
#define STOP_TONE2_DURATION       200
#define STOP_TONE3                            1000
#define STOP_TONE3_DURATION       200
/* End of msg tone beep sequence (freq in Hz and duration in mS) */
#define END_TONE1                           500
#define END_TONE1_DURATION       440
//#define END_TONE2                            0
//#define END_TONE2_DURATION       100
//#define END_TONE3                           250
//#define END_TONE3_DURATION       300

/* Possible Rec Cube Orientations */
#define CUBE_ORIENTATION_N                 6      // No of valid orientations
#define CUBE_ORIENTATION_INIT          0
#define CUBE_ORIENTATION_NO_CHANGE  -1
#define CUBE_ORIENTATION_TOP             1
#define CUBE_ORIENTATION_BOTTOM      2
#define CUBE_ORIENTATION_RIGHT         3
#define CUBE_ORIENTATION_LEFT           4
#define CUBE_ORIENTATION_FRONT         5
#define CUBE_ORIENTATION_BACK            6
#define CUBE_ORIENTATION_ELSE           7

/* Define the required cube stable delay for valid pos detection (in mS) */
#define CUBE_ORIENTATION_DBNC_TIME    200

/* RecCube operations for different Cube orienattaions */
#define RECCUBE_ORIENTATION_STOP           CUBE_ORIENTATION_ELSE
#define RECCUBE_ORIENTATION_PLAY           CUBE_ORIENTATION_BOTTOM
#define RECCUBE_ORIENTATION_REC             CUBE_ORIENTATION_TOP


/*
****************************************************************************************************************************
 *                                                     LOCAL VARIABLES
 ****************************************************************************************************************************
 */
unsigned long     AclProcBaseTime;
unsigned long     AclProcCurTime;
int      AclXVal;
int      AclYVal;
int      AclZVal;

unsigned char  RecCubeState;
int  RecCubeOrientation;

/* Cube orientation table */
int CubeOrientationTab[ CUBE_ORIENTATION_N ][ 4 ] = \
{
  { 
    CUBE_ORIENTATION_TOP,                    0         ,            0         ,  ACL_G_VAL   }
  ,
  { 
    CUBE_ORIENTATION_BOTTOM,             0         ,            0         , -ACL_G_VAL   }
  ,
  { 
    CUBE_ORIENTATION_FRONT,                0         ,  ACL_G_VAL,            0            }
  ,
  { 
    CUBE_ORIENTATION_BACK,                  0         , -ACL_G_VAL,            0            }
  ,
  { 
    CUBE_ORIENTATION_LEFT,         ACL_G_VAL,           0         ,            0            }
  ,
  { 
    CUBE_ORIENTATION_RIGHT,      -ACL_G_VAL,           0         ,            0            }
};


/*
****************************************************************************************************************************
 *                                                             LOCAL FUNCTIONS
 ****************************************************************************************************************************
 */
int CubeAnalyzeOrientation( int acl_x, int acl_y, int acl_z );


/* Create used library objects */
Accelerometer acl( LOW );       // Create a instance of the ADXL345 accelerometer - AltAdrs = Low


/*
****************************************************************************************************************************
 *                                                 GLOBAL INTERACE FUNCTIONS
 ****************************************************************************************************************************
 */
/*
****************************************************************************************************************************
 *                                                         setup()
 *
 * Description : Initialize all global variables, and each module static variables by calling each ModuleInit function.
 * Arguments   : None
 * Returns     : None
 * Notes       : Order of initialzation may be application dependent to ensure proper hardware operation.
 ****************************************************************************************************************************
 */
void setup(  )
{
  Serial.begin( DEBUG_BAUD );

  /* Hold start base time of module polling processing */
  AclProcBaseTime = millis();

  /* Setup IO ports */
  pinMode( ISD_RDY_PIN, INPUT );
  pinMode( ISD_VOL_PIN, OUTPUT );
  pinMode( TONE_IND_PIN, OUTPUT );
  pinMode( ISD_PLAY_PIN, OUTPUT );
  pinMode( ISD_REC_PIN, OUTPUT );
  pinMode( ISD_ERASE_PIN, OUTPUT );
  pinMode( ISD_FWD_PIN, OUTPUT );
  pinMode( ISD_FT_PIN, OUTPUT );
  digitalWrite( ISD_PLAY_PIN, HIGH );
  digitalWrite( ISD_REC_PIN, HIGH );
  digitalWrite( ISD_ERASE_PIN, HIGH );
  digitalWrite( ISD_FWD_PIN, HIGH );
  digitalWrite( ISD_FT_PIN, HIGH );
  digitalWrite( ISD_VOL_PIN, HIGH );

  /* Initialize glabal vars */
  RecCubeState = 0;

}  /* setup() */


/*
****************************************************************************************************************************
 *                                                      Loop()
 *
 * Description : Main loop routine
 *               This is executing the actual rec cube application process
 * Arguments   : None
 * Returns     : None
 * Notes       : None
 ****************************************************************************************************************************
 */
void loop(  )
{
  byte cnt;
  int   tmp;

  /* Switch current state */
  switch ( RecCubeState )
  {
    /* Acl initialization */
  case 0:
    DEBUG_PRINT( "ACL Initialization\n" );
    /* Ensure accelerometer is On - repeat until successful with no err */
    do
    {
      acl.clrErr();
      acl.powerOn();
      delay( ACL_RETRY_DELAY );    // Delay between retries
    } 
    while (  acl.isErr() );
    RecCubeState++;    // Next state
    break;

    /* ISD17xxx initialization - Erase all msgs */
  case 1:
    DEBUG_PRINT( "ISD Erase all msgs\n" );
    /* Trigger Erase chip operatiopn  */
    digitalWrite( ISD_ERASE_PIN, LOW );
    delay( ISD_ERASE_ALL_DELAY );
    digitalWrite( ISD_ERASE_PIN, HIGH );
    /* Wait until erase is complete */
    cnt = 0;
    while ( ( digitalRead( ISD_RDY_PIN ) == LOW ) && ( cnt < (ISD_ERASE_ALL_RDY_DELAY / 10) ) )
    {
      delay( 10 );
      cnt++;
    };
    if ( digitalRead( ISD_RDY_PIN ) == LOW )
      RecCubeState = 0;    // Re-initialize process
    else
      //      {
      //        RecCubeOrientation = CUBE_ORIENTATION_INIT;
      //        DEBUG_PRINT( "Wait Cube orientation stable on Stop face\n" );
      RecCubeState++;    // Next state
    //      }
    break;

    /* Wait cube is located and debounced in idle stop orientation */
  case 2:
    //      /* Read current accelerometer */
    //      acl.readAccel( &AclXVal, &AclYVal, &AclZVal );
    //      if ( acl.isErr() )
    //      {
    //        acl.clrErr();
    //        DEBUG_PRINT( " ACL Read Err\n" );
    //      }
    //      else
    //      {
    //        /* Only if successful read from accelerometer */
    //        tmp = CubeAnalyzeOrientation( AclXVal, AclYVal, AclZVal );
    //        if ( tmp == RECCUBE_ORIENTATION_STOP )
    //        {
    //          /* Stop orientation of cube debounced */
    //          RecCubeOrientation = tmp;
    //          RecCubeState++;    // Next state
    //        }
    //        else
    //        {
    //          /* Error beep if found another new orientation other then stop */
    //          if ( tmp != RecCubeOrientation )
    //          {
    //            digitalWrite( ISD_FT_PIN, LOW );
    //            delay( ISD_FT_DELAY );
    //            tone( TONE_IND_PIN, ERR_TONE );
    //            delay( ERR_TONE_DURATION );
    //            noTone( TONE_IND_PIN );
    //            digitalWrite( ISD_FT_PIN, HIGH );
    //            delay( ISD_FT_DELAY );
    //            RecCubeOrientation = tmp;          // Hold new orientation
    //          }
    //          else
    //          {
    //            /* Wait read delay before repeating accelrometer readout */
    //            delay( ACL_READ_DELAY );
    //          }
    //        }
    //      }
    //    break;

    /* Power up ready indication */
  case 3:
    //      DEBUG_PRINT( "Pwrup Tone beep\n" );
    //      digitalWrite( ISD_FT_PIN, LOW );
    //      delay( ISD_FT_DELAY );
    //      tone( TONE_IND_PIN, PWRUP_TONE1 );
    //      delay( PWRUP_TONE1_DURATION );
    //      tone( TONE_IND_PIN, PWRUP_TONE2 );
    //      delay( PWRUP_TONE2_DURATION );
    //      tone( TONE_IND_PIN, PWRUP_TONE3 );
    //      delay( PWRUP_TONE3_DURATION );
    //      noTone( TONE_IND_PIN );
    //      digitalWrite( ISD_FT_PIN, HIGH );
    //      delay( ISD_FT_DELAY );
    //        RecCubeState++;    // Next state
    //    break;

    /* Wait cube new orientation command (Other then Stop) */
  case 4:
    /* Read current accelerometer */
    acl.readAccel( &AclXVal, &AclYVal, &AclZVal );
    if ( acl.isErr() )
    {
      acl.clrErr();
      DEBUG_PRINT( " ACL Read Err\n" );
    }
    else
    {
      /* Only if successful read from accelerometer */
      tmp = CubeAnalyzeOrientation( AclXVal, AclYVal, AclZVal );
      if ( tmp != RecCubeOrientation )
      {
        /* New orientation of cube debounced */
        RecCubeOrientation = tmp;          // Hold new orientation
        if ( tmp == RECCUBE_ORIENTATION_PLAY )
        {
          /* Play orientation of cube debounced */
          RecCubeState = 10;    // Play state
        }
        else if ( tmp == RECCUBE_ORIENTATION_REC )
        {
          /* Rec orientation of cube debounced */
          RecCubeState = 20;    // Record state
        }
        else if ( tmp == RECCUBE_ORIENTATION_STOP )
        {
          /* Stop orientation of cube debounced */
          RecCubeState = 5;    // Re-beep Stop state
        }
        //          else
        //          {
        //            /* Error beep if found another new orientation other then Play/Rec */
        //            digitalWrite( ISD_FT_PIN, LOW );
        //            delay( ISD_FT_DELAY );
        //            tone( TONE_IND_PIN, ERR_TONE );
        //            delay( ERR_TONE_DURATION );
        //            noTone( TONE_IND_PIN );
        //            digitalWrite( ISD_FT_PIN, HIGH );
        //            delay( ISD_FT_DELAY );
        //          }
      }
      else
      {
        /* Wait read delay before repeating accelrometer readout */
        delay( ACL_READ_DELAY );
      }
    }
    break;

    /* Stop orientation indication */
  case 5:
    DEBUG_PRINT( "RecCube Stop\n" );
    //      digitalWrite( ISD_FT_PIN, LOW );
    //      delay( ISD_FT_DELAY );
    //      tone( TONE_IND_PIN, STOP_TONE1 );
    //      delay( STOP_TONE1_DURATION );
    //      tone( TONE_IND_PIN, STOP_TONE2 );
    //      delay( STOP_TONE2_DURATION );
    //      tone( TONE_IND_PIN, STOP_TONE3 );
    //      delay( STOP_TONE3_DURATION );
    //      noTone( TONE_IND_PIN );
    //      digitalWrite( ISD_FT_PIN, HIGH );
    //      delay( ISD_FT_DELAY );
    RecCubeState = 4;    // Wait for new orientation
    break;


  case 10:
    DEBUG_PRINT( "ISD Play\n" );
    //      digitalWrite( ISD_FT_PIN, LOW );
    //      delay( ISD_FT_DELAY );
    //      tone( TONE_IND_PIN, PLAY_TONE1 );
    //      delay( PLAY_TONE1_DURATION );
    //#if ( PLAY_TONE2 == 0)
    //      noTone( TONE_IND_PIN );
    //#else
    //      tone( TONE_IND_PIN, PLAY_TONE2 );
    //#endif
    //      delay( PLAY_TONE2_DURATION );
    //      tone( TONE_IND_PIN, PLAY_TONE3 );
    //      delay( PLAY_TONE3_DURATION );
    //      noTone( TONE_IND_PIN );
    //      digitalWrite( ISD_FT_PIN, HIGH );
    //      delay( ISD_FT_DELAY );
    RecCubeState++;    // Next state
    break;

  case 11:
    /* Play last msg */
    digitalWrite( ISD_PLAY_PIN, LOW );
    delay( ISD_BUTTON_DBNC_DELAY );
    digitalWrite( ISD_PLAY_PIN, HIGH );
    RecCubeState++;    // Next state
    break;

  case 12:
  case 13:
    /* Wait completion and/or new cube orientation */
    /* Check msg completion and beep end msg only once */
    if ( ( RecCubeState == 12 ) && ( digitalRead( ISD_RDY_PIN ) == HIGH ) )
    {
      //        digitalWrite( ISD_FT_PIN, LOW );
      //        delay( ISD_FT_DELAY );
      //        tone( TONE_IND_PIN, END_TONE1 );
      //        delay( END_TONE1_DURATION );
      //#if ( END_TONE2 == 0)
      //        noTone( TONE_IND_PIN );
      //#else
      //        tone( TONE_IND_PIN, END_TONE2 );
      //#endif
      //        delay( END_TONE2_DURATION );
      //        tone( TONE_IND_PIN, END_TONE3 );
      //        delay( END_TONE3_DURATION );
      //        noTone( TONE_IND_PIN );
      //        digitalWrite( ISD_FT_PIN, HIGH );
      RecCubeState++;      // So will only beep end once and then wait for cube stop orientation
    }
    /* Read current accelerometer */
    acl.readAccel( &AclXVal, &AclYVal, &AclZVal );
    if ( acl.isErr() )
    {
      acl.clrErr();
      DEBUG_PRINT( " ACL Read Err\n" );
    }
    else
    {
      /* Only if successful read from accelerometer */
      tmp = CubeAnalyzeOrientation( AclXVal, AclYVal, AclZVal );
      if ( tmp != RecCubeOrientation )
      {
        /* New orientation of cube debounced */
        RecCubeOrientation = tmp;          // Hold new orientation
        if ( ( tmp == RECCUBE_ORIENTATION_STOP ) || ( tmp == RECCUBE_ORIENTATION_REC ) )
        {
          /* Stop/Rec orientation of cube debounced */
          /* Stop the current msg playback */
          if ( digitalRead( ISD_RDY_PIN ) == LOW )
          {
            /* Only if already in middle of playback */
            digitalWrite( ISD_PLAY_PIN, LOW );
            delay( ISD_BUTTON_DBNC_DELAY );
            digitalWrite( ISD_PLAY_PIN, HIGH );
          }
          if ( tmp == RECCUBE_ORIENTATION_REC )
            RecCubeState = 20;    // Record state
          else
            RecCubeState = 5;    // Stop state
        }
        else    // Any other cube orientation is ignored and does not affect playback
        delay( ACL_READ_DELAY );
      }
      else
      {
        /* Wait read delay before repeating accelrometer readout */
        delay( ACL_READ_DELAY );
      }
    }
    break;


  case 20:
    /* Erase last msg */
    DEBUG_PRINT( "ISD Erase last msgs\n" );
    /* Trigger Erase last msg operatiopn  */
    digitalWrite( ISD_FT_PIN, LOW );
    digitalWrite( ISD_ERASE_PIN, LOW );
    tone( TONE_IND_PIN, REC_TONE );              // Record tone indication during Erase last msg process
    delay( ISD_BUTTON_DBNC_DELAY );
    digitalWrite( ISD_ERASE_PIN, HIGH );
    /* Wait until erase is complete */
    cnt = 0;
    while ( ( digitalRead( ISD_RDY_PIN ) == LOW ) && ( cnt < (ISD_ERASE_MSG_RDY_DELAY / 10) ) )
    {
      delay( 10 );
      cnt++;
    };
    noTone( TONE_IND_PIN );    // Record tone until Erase last msg is complete
    digitalWrite( ISD_FT_PIN, HIGH );
    if ( digitalRead( ISD_RDY_PIN ) == LOW )
      RecCubeState = 0;    // Re-initialize process
    else
      RecCubeState++;    // Next state
    break;

  case 21:
    /* Record new msg */
    DEBUG_PRINT( "ISD Record\n" );
    digitalWrite( ISD_REC_PIN, LOW );
    delay( ISD_BUTTON_DBNC_DELAY );    // To Ensure Rdy pin set
    RecCubeState++;    // Next state
    break;

  case 22:
  case 23:
    /* Wait completion and/or new cube orientation */
    /* Check end of msg recording (end of memory) and beep end msg only once */
    if ( ( RecCubeState == 22 ) && ( digitalRead( ISD_RDY_PIN ) == HIGH ) )
    {
      digitalWrite( ISD_REC_PIN, HIGH );      // End of record in any case
//      digitalWrite( ISD_FT_PIN, LOW );
//      delay( ISD_FT_DELAY );
//      tone( TONE_IND_PIN, END_TONE1 );
//      delay( END_TONE1_DURATION );
      //#if ( END_TONE2 == 0)
      //        noTone( TONE_IND_PIN );
      //#else
      //        tone( TONE_IND_PIN, END_TONE2 );
      //#endif
      //        delay( END_TONE2_DURATION );
      //        tone( TONE_IND_PIN, END_TONE3 );
      //        delay( END_TONE3_DURATION );
//      noTone( TONE_IND_PIN );
//      digitalWrite( ISD_FT_PIN, HIGH );
      RecCubeState++;      // So will only beep end once and then wait for cube stop orientation
    }
    /* Read current accelerometer */
    acl.readAccel( &AclXVal, &AclYVal, &AclZVal );
    if ( acl.isErr() )
    {
      acl.clrErr();
      DEBUG_PRINT( " ACL Read Err\n" );
    }
    else
    {
      /* Only if successful read from accelerometer */
      tmp = CubeAnalyzeOrientation( AclXVal, AclYVal, AclZVal );
      if ( tmp != RecCubeOrientation )
      {
        /* New orientation of cube debounced */
        RecCubeOrientation = tmp;          // Hold new orientation
        if ( ( tmp == RECCUBE_ORIENTATION_STOP ) || ( tmp == RECCUBE_ORIENTATION_PLAY ) )
        {
          /* Stop/Play orientation of cube debounced */
          /* Stop the current msg recording */
          digitalWrite( ISD_REC_PIN, HIGH );
          delay( ISD_FT_DELAY );
          if ( tmp == RECCUBE_ORIENTATION_PLAY ) {
            RecCubeState = 10;    // Play state
          } else {
//            digitalWrite( ISD_FT_PIN, LOW );
//            delay( ISD_FT_DELAY );
//            tone( TONE_IND_PIN, END_TONE1 );
//            delay( END_TONE1_DURATION );
            //    #if ( END_TONE2 == 0)
            //            noTone( TONE_IND_PIN );
            //    #else
            //            tone( TONE_IND_PIN, END_TONE2 );
            //    #endif
            //            delay( END_TONE2_DURATION );
            //            tone( TONE_IND_PIN, END_TONE3 );
            //            delay( END_TONE3_DURATION );
//            noTone( TONE_IND_PIN );
//            digitalWrite( ISD_FT_PIN, HIGH );
            RecCubeState = 5;    // Stop state
          }
        }
        else    // Any other cube orientation is ignored and does not affect recording
        delay( ACL_READ_DELAY );
      }
      else
      {
        /* Wait read delay before repeating accelrometer readout */
        delay( ACL_READ_DELAY );
      }
    }
    break;


  default:
    DEBUG_PRINT( "Error unknown state!\n" );
    RecCubeState = 0;  // Re-start
    break;
  }

}  /* loop() */


/*
****************************************************************************************************************************
 *                                                      CubeAnalyzeOrientation()
 *
 * Description : Analyze from accelerometer readout the current cube orientation
 *               This is based on provided table of orientations and defined tolerance for each orienatation
 * Arguments   : acl_x, acl_y, acl_x - accelerometer readout (int)
 * Returns     : New cube orientation
 * Notes       : Will always return last reproted orientation until a new orientation is detected
 *                       Init orienatation will be returned on startup until debounced for first time
 ****************************************************************************************************************************
 */
int CubeAnalyzeOrientation( int acl_x, int acl_y, int acl_z )
{
  static int cube_cur_orientation = CUBE_ORIENTATION_INIT;
  static unsigned long cube_dbnc_time;
  static int cube_cur_orientation_idx = -1;
  unsigned char i;

  /* Loop all defined orinetations in table */
  i = 0;
  while ( i < CUBE_ORIENTATION_N )
  {
    if ( ( acl_x > (CubeOrientationTab[ i ] [ 1 ] - ACL_G_TOL) ) &&( acl_x < (CubeOrientationTab[ i ] [ 1 ] + ACL_G_TOL) ) &&
      ( acl_y > (CubeOrientationTab[ i ] [ 2 ] - ACL_G_TOL) ) &&( acl_y < (CubeOrientationTab[ i ] [ 2 ] + ACL_G_TOL) ) &&
      ( acl_z > (CubeOrientationTab[ i ] [ 3 ] - ACL_G_TOL) ) &&( acl_z < (CubeOrientationTab[ i ] [ 3 ] + ACL_G_TOL) ) )
    {
      /* Match accelerometer value with current idx value */
      if ( cube_cur_orientation_idx != i )
      {
        /* New idx orientation match - restart debounce */
        cube_dbnc_time = millis();      // record current time for debounce
        cube_cur_orientation_idx = i;
      }
      else
      {
        /* Check that not simply in an already debounced and reported orientation */
        if ( cube_cur_orientation != CubeOrientationTab[ i ] [ 0 ]  )
        {
          /* Continue debounce count */
          /* Note! below may glitch approx every 50 days but considered neglectable */
          if ( (millis() - cube_dbnc_time) > CUBE_ORIENTATION_DBNC_TIME )
          {
            /* Debounce time elapssed - report as new valid cube pos */
            cube_cur_orientation = CubeOrientationTab[ i ] [ 0 ] ;
            DEBUG_PRINT( "Cube Orientation - " );
            DEBUG_PRINTF( i, DEC );
            DEBUG_PRINT( "\n" );
            if (cube_cur_orientation != CUBE_ORIENTATION_TOP && cube_cur_orientation != CUBE_ORIENTATION_BOTTOM)
              cube_cur_orientation = CUBE_ORIENTATION_ELSE;
          }
        }
      }
      i = CUBE_ORIENTATION_N;      // Force loop exit
    }
    else
      i++;        // Next table entry
  }

  return cube_cur_orientation;

}  /* RecCubeGetOrientation() */

Parts:

(1x) Arduino Uno

(1x) Improved VoiceSheild with Microphone

(1x) ADXL345 Accelerometer

(1x) 2 Watt Amplifier Kit

(1x) Speaker

(1x) 10.8V Li-Po Battery

(1x) Audio Jack Socket