Alex Wulff
Published © GPL3+

Motion-Activated Musical Trophy

It looks like a normal trophy from the outside, but the slightest touch will cause this trophy to break out into song.

BeginnerFull instructions provided3 hours2,714
Motion-Activated Musical Trophy

Things used in this project

Hardware components

Mini Breadboard
Only one is necessary for this project.
×1
Microchip ATtiny85 DIP
These can be also be obtained at a much lower price from DigiKey. Only one is necessary for this project.
×1
Jumper Wires
×1
Buzzer
Buzzer
×1
Fast Vibration Switch
×1
Some Kind of Trophy
×1
AA Batteries
AA Batteries
×1
2xAA Battery Holder
×1
Arduino UNO
Arduino UNO
Used only for programming the ATtiny85
×1
Capacitor 10 µF
Capacitor 10 µF
Used only for programming the ATtiny85
×1

Software apps and online services

Autodesk Circuits.io

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Schematics

Breadboard Schematic

Code

SoundTrophy.ino

Arduino
This is the sketch you will upload to your ATtiny
//Sketch produced by Alex Wulff (www.AlexWulff.com)
//RTTL Code written by Brett Hagman http://www.roguerobotics.com/
//Pin change interrupt code obtained from https://thewanderingengineer.com/2014/08/11/pin-change-interrupts-on-attiny85/
//Sleep code obtained from https://bigdanzblog.wordpress.com/2014/08/10/attiny85-wake-from-sleep-on-pin-state-change-code-example/

#include "avr/interrupt.h"
#include "avr/sleep.h"

const int speakerPin = 1;
const int vibPin = 3;

#define OCTAVE_OFFSET 0

int notes[] = { 0,
262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951
};

const char *song = "TakeOnMe:d=4,o=4,b=160:8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5,8f#5,8e5,8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5";
void setup(void)
{
  pinMode(vibPin, INPUT_PULLUP);
  pinMode(speakerPin, OUTPUT);
}

void loop(void)
{
  sleep();
  play_rtttl(song);
}

ISR(PCINT0_vect)
{
    //This is not needed for the current sketch
}

void sleep() {

    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT3);                   // Use PB3 as interrupt pin
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep

    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT3);                  // Turn off PB3 as interrupt pin
    sleep_disable();                        // Clear SE bit
    ADCSRA |= _BV(ADEN);                    // ADC on

    sei();                                  // Enable interrupts
} 

#define isdigit(n) (n >= '0' && n <= '9')

void play_rtttl(char *p)
{
  // Absolutely no error checking in here

  byte default_dur = 4;
  byte default_oct = 6;
  int bpm = 63;
  int num;
  long wholenote;
  long duration;
  byte note;
  byte scale;

  // format: d=N,o=N,b=NNN:
  // find the start (skip name, etc)

  while(*p != ':') p++;    // ignore name
  p++;                     // skip ':'

  // get default duration
  if(*p == 'd')
  {
    p++; p++;              // skip "d="
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    if(num > 0) default_dur = num;
    p++;                   // skip comma
  }

  // get default octave
  if(*p == 'o')
  {
    p++; p++;              // skip "o="
    num = *p++ - '0';
    if(num >= 3 && num <=7) default_oct = num;
    p++;                   // skip comma
  }

  // get BPM
  if(*p == 'b')
  {
    p++; p++;              // skip "b="
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    bpm = num;
    p++;                   // skip colon
  }

  // BPM usually expresses the number of quarter notes per minute
  wholenote = (60 * 1000L / bpm) * 4;  // this is the time for whole note (in milliseconds)

  // now begin note loop
  while(*p)
  {
    // first, get note duration, if available
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    
    if(num) duration = wholenote / num;
    else duration = wholenote / default_dur;  // we will need to check if we are a dotted note after

    // now get the note
    note = 0;

    switch(*p)
    {
      case 'c':
        note = 1;
        break;
      case 'd':
        note = 3;
        break;
      case 'e':
        note = 5;
        break;
      case 'f':
        note = 6;
        break;
      case 'g':
        note = 8;
        break;
      case 'a':
        note = 10;
        break;
      case 'b':
        note = 12;
        break;
      case 'p':
      default:
        note = 0;
    }
    p++;

    // now, get optional '#' sharp
    if(*p == '#')
    {
      note++;
      p++;
    }

    // now, get optional '.' dotted note
    if(*p == '.')
    {
      duration += duration/2;
      p++;
    }
  
    // now, get scale
    if(isdigit(*p))
    {
      scale = *p - '0';
      p++;
    }
    else
    {
      scale = default_oct;
    }

    scale += OCTAVE_OFFSET;

    if(*p == ',')
      p++;       // skip comma for next note (or we may be at the end)

    // now play the note
    if(note)
    {
      freqout(notes[(scale - 4) * 12 + note], duration);      
    }
    else
    {
      delay(duration);
    }
  }
}

// freqout from http://www.arduino.cc/playground/Main/Freqout
void freqout(int freq, int t)  // freq in hz, t in ms
{
  int hperiod;                               //calculate 1/2 period in us
  long cycles, i;
  pinMode(speakerPin, OUTPUT);                   // turn on output pin

  hperiod = (500000 / freq) - 7;             // subtract 7 us to make up for digitalWrite overhead

  cycles = ((long)freq * (long)t) / 1000;    // calculate cycles

  for (i=0; i<= cycles; i++){              // play note for t ms 
    digitalWrite(speakerPin, HIGH); 
    delayMicroseconds(hperiod);
    digitalWrite(speakerPin, LOW); 
    delayMicroseconds(hperiod - 1);     // - 1 to make up for digitaWrite overhead
  }
pinMode(speakerPin, INPUT);                // shut off pin to avoid noise from other operations

}

Credits

Alex Wulff

Alex Wulff

12 projects • 226 followers
I'm a maker and student at Harvard. I love Arduino, embedded systems, radio, 3D printing, and iOS development. www.AlexWulff.com

Comments