/* ///////////////////////// // INTERACTIVE PLUSH // ///////////////////////// A plush that makes noises in response to accelerometer readings from an imbedded MMA7361L accelerometer. Created by Michelle Del Rosario, May 31 2011 Datasheet: http://cdn.shopify.com/s/files/1/0038/9582/files/MMA7361L.pdf?1282525052 Modified from the following code source examples: http://arduino.cc/en/Tutorial/Tone http://www.arduino.cc/en/Tutorial/ADXL3xx http://bildr.org/2011/04/sensing-orientation-with-the-adxl335-arduino/ Further reading: http://www.instructables.com/id/Accelerometer-Gyro-Tutorial/ http://cache.freescale.com/files/sensors/doc/app_note/AN3461.pdf http://www.varesano.net/blog/fabio/exploring-advanced-features-adxl345-accelerometer-single-double-tap-activity-inactivity-f http://gotoandplay.freeblog.hu/archives/2010/06/15/Get_pitch_and_roll_angles_from_the_iPhones_accelerometer_vector/ ------- The circuit: analog 0: accelerometer self test analog 1: z-axis analog 2: y-axis analog 3: x-axis analog 4: ground analog 5: vcc -- digital 8: 8-ohm speaker ------- */ /////////////////////// //--- LIBRARIES ---// ///////////////////// #include #include "pitches.h" ////////////////// //--- DEBUG ---// ///////////////// #define DEBUG // Comment this line to remove debugging features boolean printAll = true; //true prints both curr and prev XYZ360Deg data, false for only curr data const int delayDuration = 100; ///////////////////////////////////// //--- ACCELEROMETER VARIABLES ---// /////////////////////////////////// //Pin constants const int groundPin = 18; // analog input pin 4 -- ground const int powerPin = 19; // analog input pin 5 -- voltage const int xPin = A0; // x-axis of the accelerometer const int yPin = A1; // y-axis const int zPin = A2; // z-axis (only on 3-axis models) //The minimum and maximum values that came from the accelerometer's z axis while standing still //You may need to change this to match with your own accelerometer. const int minVal = 150; const int maxVal = 470; //xyz orientation angles in 360 degrees double currXYZ360Deg[3]; double prevXYZ360Deg[3]; /////////////////////////// //--- SOUNDS ARRAYS ---// ///////////////////////// //If plush makes a step on its left foot, play this sound array int stepLeft[] = {NOTE_G3}; //If plush makes a step on its right foot, play this sound array int stepRight[] ={NOTE_A3}; //If plush is lying on side int onSide[] = {NOTE_B0}; int leanForward[] = {NOTE_D8, NOTE_DS8}; int leanBackward[] = {NOTE_F4, NOTE_E4}; int onHead[] = {NOTE_D6, NOTE_DS6, NOTE_CS7}; int onFace[] = {NOTE_B0, NOTE_B0, NOTE_B0}; int onBack[] = { NOTE_DS1,NOTE_C1,NOTE_DS1, NOTE_C1}; int test[] ={ NOTE_B0,NOTE_C1,NOTE_CS1,NOTE_D1,NOTE_DS1,NOTE_E1,NOTE_F1,NOTE_FS1,NOTE_G1,NOTE_GS1/*, NOTE_A1,NOTE_AS1,NOTE_B1,NOTE_C2,NOTE_CS2,NOTE_D2,NOTE_DS2,NOTE_E2,NOTE_F2,NOTE_FS2, NOTE_G2,NOTE_GS2,NOTE_A2,NOTE_AS2,NOTE_B2,NOTE_C3,NOTE_CS3,NOTE_D3,NOTE_DS3,NOTE_E3, NOTE_F3,NOTE_FS3,NOTE_G3,NOTE_GS3,NOTE_A3,NOTE_AS3,NOTE_B3,NOTE_C4,NOTE_CS4,NOTE_D4, NOTE_DS4,NOTE_E4,NOTE_F4,NOTE_FS4,NOTE_G4,NOTE_GS4,NOTE_A4,NOTE_AS4,NOTE_B4,NOTE_C5, NOTE_CS5,NOTE_D5,NOTE_DS5,NOTE_E5,NOTE_F5,NOTE_FS5,NOTE_G5,NOTE_GS5,NOTE_A5,NOTE_AS5, NOTE_B5,NOTE_C6,NOTE_CS6,NOTE_D6,NOTE_DS6,NOTE_E6,NOTE_F6,NOTE_FS6,NOTE_G6,NOTE_GS6, NOTE_A6,NOTE_AS6,NOTE_B6,NOTE_C7,NOTE_CS7,NOTE_D7,NOTE_DS7,NOTE_E7,NOTE_F7,NOTE_FS7, NOTE_G7,NOTE_GS7,NOTE_A7,NOTE_AS7,NOTE_B7,NOTE_C8,NOTE_CS8,NOTE_D8,NOTE_DS8*/ }; ///////////////////////////// //--- NOTE DURATIONS: ---// /////////////////////////// //Note durations for left step, 4 = quarter note, 8 = eighth note, etc. int noteDurationsLeft[] = {4}; //Note durations for right step int noteDurationsRight[] = {4}; int durationsOnSide[] = {8}; int durationsLeanForward[] = {8,4}; int durationsLeanBackward[] = {4,8}; int durationsOnHead[] = {8,8,8}; int durationsOnFace[] = {4,4,4}; int durationsOnBack[] = {4, 4, 4, 4}; int testDurations[] ={ 4,4,4,4,4,4,4,4,4,4/*, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4*/ }; /////////////////////////////////// //--- numNotes in Duration: ---// ///////////////////////////////// const int NUM_STEPLEFT = 1; const int NUM_STEPRIGHT = 1; const int NUM_ONSIDE = 1; const int NUM_LEANFORWARD = 2; const int NUM_LEANBACKWARD = 2; const int NUM_ONHEAD = 3; const int NUM_ONFACE = 3; const int NUM_ONBACK = 4; const int NUM_TEST = 10; //89 total ///////////////////////////////////// //--- SETUP and INITIALIZATION ---// //////////////////////////////////// void setup() { // initialize the serial communications: Serial.begin(9600); // initAccelerometer(); } void initAccelerometer(){ // Provide ground and power by using the analog inputs as normal // digital pins. This makes it possible to directly connect the // breakout board to the Arduino. If you use the normal 5V and // GND pins on the Arduino, you do not have to call this function. pinMode(groundPin, OUTPUT); pinMode(powerPin, OUTPUT); digitalWrite(groundPin, LOW); digitalWrite(powerPin, HIGH); } /////////////////// //--- LOOP ---// ///////////////// void loop() { setPrevXYZ360Deg(); calculateXYZ360Deg(); #ifdef DEBUG printXYZOrientation(printAll); printOrientationState(); #endif //monitorOrientation(); delay(delayDuration); } ////////////////////////////// //--- HELPER FUNCTIONS ---// //////////////////////////// /* Monitors the orientation and sets variable flags as needed to determine what actions should be performed based on the plush's tilt. */ void monitorOrientation(){ //s if(getPitch < int orientation = getOrientation(); //switch(orientation) } /* Returns current orientation state in the form of an int. Used for monitorOrientation cases. 0 = on feet 1 = tilt left 2 = on left 3 = on head 4 = on right 5 = tilt right 6 = tilt backward 7 = on back 8 = on face 9 = tilt forward */ int getOrientation(){ double pitch = getPitch(); double roll = getRoll(); if((0 <= pitch && pitch <= 34 && 0 <= roll && roll <= 34) || (324 <= pitch && pitch <= 360) || (324 <= roll && roll <= 360)){ //on feet return 0; } else if((91 <= roll && roll <= 249) || (91 <= pitch && pitch <= 249)){ //on head return 3; } else if(35 <= roll && roll <= 55){ //tilt left return 1; } else if(56 <= roll && roll <=90){ //on left return 2; } else if(250 <= roll && roll <= 304){ //on right return 4; } else if(305 <= roll && roll <=325){ //tilt right return 5; } else if(35 <= pitch && pitch <= 55){ //tilt backwards return 6; } else if(36 <= pitch && pitch <= 90){ //on back return 7; } else if(250 <= pitch && pitch <= 304){ //on face return 8; } else if (304 <= pitch && pitch <= 325){ return 9; } return 10; //do nothing because probably in between on side and on head states } /* Prints the current orientation state. */ void printOrientationState(){ int state = getOrientation(); Serial.print("case "); Serial.print(state); Serial.print( " : \t"); switch(state){ case 0: Serial.println("On Feet"); //play I'm ready! //playSoundArray(test,testDurations, NUM_TEST); break; case 1: Serial.println("Tilt Left"); playSoundArray(stepLeft,noteDurationsLeft, NUM_STEPLEFT); break; case 2: Serial.println("On Left"); //play bump playSoundArray(onSide, durationsOnSide, NUM_ONSIDE); break; case 3: Serial.println("On Head"); //play confused playSoundArray(onHead, durationsOnHead, NUM_ONHEAD); break; case 4: Serial.println("On Right"); //play bump playSoundArray(onSide, durationsOnSide, NUM_ONSIDE); break; case 5: Serial.println("Tilt Right"); playSoundArray(stepRight,noteDurationsRight, NUM_STEPRIGHT); break; case 6: Serial.println("Tilt Backward"); //play ohNo playSoundArray(leanBackward, durationsLeanBackward, NUM_LEANBACKWARD); break; case 7: Serial.println("On Back"); //play snore playSoundArray(onBack, durationsOnBack, NUM_ONBACK); break; case 8: Serial.println("On Face"); //play dotDotDot playSoundArray(onFace, durationsOnFace, NUM_ONFACE); break; case 9: Serial.println("Tilt Forward"); //play ohNo playSoundArray(leanForward, durationsLeanForward, NUM_LEANFORWARD); break; } } /* Returns the current Pitch value in degrees, ranging from 0-360. Pitch is measured as the counter-clockwise angle around the X-axis. */ double getPitch(){ return currXYZ360Deg[0]; } /* Returns the current Roll value in degrees, ranging from 0-360. Roll is measured as the counter-clockwise angle around the Y-axis. */ double getRoll(){ return currXYZ360Deg[1]; } /* Sets prevoius XYZ360Deg reading for determining the past tilt state. */ void setPrevXYZ360Deg(){ for(int i = 0; i <3; i++){ if(currXYZ360Deg[i] != NULL){ prevXYZ360Deg[i] = currXYZ360Deg[i]; } else{ prevXYZ360Deg[i] = 0; //set values to 0 if currXYZ360Deg does not have a value yet } } } /* Calculates orientation in 360 degrees. Modified from Bildr code. Assumes accelerometer's orientation (from top-down view) has: X @ 0g pointing right Y @ 0g pointing up Z @ +1g pointing to the ground */ void calculateXYZ360Deg(){ //read the analog values from the accelerometer int xRead = analogRead(xPin); int yRead = analogRead(yPin); int zRead = analogRead(zPin); //convert read values to degrees -90 to 90 - Needed for atan2 int xAng = map(xRead, minVal, maxVal, -90, 90); int yAng = map(yRead, minVal, maxVal, -90, 90); int zAng = map(zRead, minVal, maxVal, -90, 90); //Caculate 360deg values like so: atan2(-yAng, -zAng) //atan2 outputs the value of -π to π (radians) //We are then converting the radians to degrees currXYZ360Deg[0] = RAD_TO_DEG * (atan2(-yAng, -zAng) + PI); currXYZ360Deg[1] = RAD_TO_DEG * (atan2(-xAng, -zAng) + PI); currXYZ360Deg[2] = RAD_TO_DEG * (atan2(-yAng, -xAng) + PI); } /* Prints the XYZ orientation boolean printAll will print both current and previous XYZ360Deg data when set to true. If false, only prints current XYZ360Deg data. */ void printXYZOrientation(boolean printAll){ // print the sensor values: Serial.print("currPitch: "); Serial.print(currXYZ360Deg[0]); // print a tab between values: Serial.print("\t currRoll: "); Serial.print(currXYZ360Deg[1]); // print a tab between values: Serial.print("\t currTheta: "); Serial.print(currXYZ360Deg[2]); Serial.println(); if(printAll){ Serial.print("prevPitch: "); Serial.print(prevXYZ360Deg[0]); // print a tab between values: Serial.print("\t prevRoll: "); Serial.print(prevXYZ360Deg[1]); // print a tab between values: Serial.print("\t prevTheta: "); Serial.print(prevXYZ360Deg[2]); Serial.println(); Serial.println(); } } /* Plays a specified soundArray[]. */ void playSoundArray(int soundArray[], int noteDurations[], int numNotes) { Serial.println(numNotes); // iterate over the notes of the melody: for (int thisNote = 0; thisNote < (numNotes); thisNote++) { // to calculate the note duration, take one second // divided by the note type. //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. int noteDuration = 1000/noteDurations[thisNote]; tone(8, soundArray[thisNote],noteDuration); // to distinguish the notes, set a minimum time between them. // the note's duration + 30% seems to work well: int pauseBetweenNotes = noteDuration * 1.30; delay(pauseBetweenNotes); // stop the tone playing: noTone(8); } }