new years eve-erbotwatch

11 views
Skip to first unread message

Mark Tabry

unread,
Dec 29, 2009, 3:50:29 AM12/29/09
to makerb...@googlegroups.com
Here's some code I threw together to make your watch snazzy for NYE.

If you're looking at the watch w/ the pinheaders on top:
Top left button: NYE countdown + "fireworks" + auld lang syne
Bottom left button: Party mode! (still keeps time)
Bottom right button: Set time (hold this while pressing top left for hour and bottom left for minute)

#include <Tone.h>
 
#define OCTAVE_OFFSET 1

#define OUTSIDE_RING 0
#define INSIDE_RING 1
#define CENTER_COL 2

#define countdownSeconds 10

#define countdownPin 4
#define partyPin 5
#define setPin 3
 
int notes[] = { 0,
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
};
char *song = "AuldLangSyne:d=4,o=6,b=120:g5,c.,8b5,c,e,d.,8c#,d,8e,8d,c.,8c,e,g,2a.,a,g.,8e,e,c,d.,8c#,d,8e,8d,c.,8a5,a5,g5,2c.";//,a,g.,8e,e,c,d.,8c#,d,a,g.,8e,e,g,2a.,a,g.,8e,e,c,d.,8c#,d,8e,8d,c.,8a5,a5,g5,2c.";
Tone tone1;
 
void setup()   {
 // initialize the digital pin as an output:
 for(int k=2; k<14; k++)
 {
   pinMode(k, OUTPUT);
 }
 tone1.begin(2);
// Serial.begin(9600);
 clearLeds();
 spinLeds();
 clearLeds();
}
 
// the loop() method runs over and over again,
// as long as the Arduino has power

int hour=0,minute=0,second=0;

boolean startCountdown=false;
int countdown=-1;

boolean partyMode=false;

void loop()
{
  static unsigned long lastTick = 0;
  
  unsigned long currTime = millis();
  if (currTime - lastTick >= 1000 ) {
    lastTick = currTime - currTime%1000;
    if ( startCountdown ) {
      countdown = countdown < 0 ? countdownSeconds : -1;
      startCountdown = false;
    }
    if ( countdown >= 0 ) countdown --;
    ++second %= 60;
    if ( second == 0 ) {
      ++minute %= 60;
      if ( minute == 0 ) {
        ++hour %= 12;        
      }
    }        
    
  }
  if ( countdown >= 0 ) {
    showCountdown(currTime);
  }
  else if ( partyMode ) {
    showPartyMode(currTime);
  }
  else {
    showHours();
    showMinutes();
  }
  checkButtons();
}

void showHours() {
  ledBlink(OUTSIDE_RING, hour);
}

void showMinutes() {
  ledBlink(INSIDE_RING, minute/5);
  binaryBlink(CENTER_COL, minute%5);
}

void showPartyMode(unsigned long currTime) {
  static unsigned long nextTime = 0;
  static int count = 0;
  if ( nextTime < currTime ) {
    ++count %= 12*12;
    nextTime = currTime + 100;
  }
  patternBlink(count%12,count/12);
}

void checkButtons() {
  static boolean countdownPressed=false,partyPressed=false,setPressed=false;
  if (!analogRead (countdownPin) && countdownPressed == false) { // if a normally closed switch is pressed
    countdownPressed = true; // note the pressed state
    if ( setPressed ) {
      ++hour %= 12;
    }
    else {
      startCountdown = true;
    }
  }
  if (analogRead (countdownPin)) countdownPressed = false; // reset the state when the button is released
  
  if (!analogRead (partyPin) && partyPressed == false) { // if a normally closed switch is pressed
    partyPressed = true; // note the pressed state
    if ( setPressed ) { 
      ++minute %= 60;
    }
    else {
      partyMode = !partyMode;
    }
  }
  if (analogRead (partyPin)) partyPressed = false; // reset the state when the button is released
  
  if (!analogRead (setPin) && setPressed == false) { // if a normally closed switch is pressed
   setPressed = true; // note the pressed state
  }
  if (analogRead (setPin)) setPressed = false; // reset the state when the button is released
}

void showCountdown(unsigned long currTime) {
  countdownTone(currTime);
  if ( countdown > 0 ) {
    altLedBlink(OUTSIDE_RING, countdown%2);
    altLedBlink(INSIDE_RING, (countdown+1)%2);
    binaryBlink(CENTER_COL, countdown);
  }
  else {
    clearLeds();
    fireworks();
    play_rtttl(song);
    clearLeds();
  }
}

void countdownTone(unsigned long currTime) {
  static boolean tonePlaying = false;
  static unsigned long toneStart = 0;
  
  if ( !tonePlaying && toneStart + 1000 < currTime ) {
    tonePlaying = true;
    toneStart = currTime;
    tone1.play(NOTE_C7);
  }
  
  if ( tonePlaying && toneStart + 25 < currTime ) {
    tonePlaying=false;
    tone1.stop();
  }
}

void fireworks() {
  for (int y=0; y < 2; y++) {
    for (int x=0; x < 12; x++) {
      clearLeds();
      altLed(x%2, x%y, true);
      delay(100);
    }
  }
}

void ledBlink(int rgy, int pos) {
  ledOn(rgy, pos);
  ledOff(rgy, pos);
}

void binaryBlink(int rgy, int n) {
  binaryToggle(rgy, n, true);
  binaryToggle(rgy, n, false);
}

void patternBlink(int x, int y) {
  altLed(x%2, x%y, true);
  altLed(x%2, x%y, false);
}

void spinLeds() {
  for (int x=0; x < 12; x++) {
    ledOn(x%2, x);
    ledOn((x+1)%2, (x+6)%12);
    ledOn(x%2, (x+3)%12);
    ledOn((x+1)%2, (x+9)%12);
    delay(100);
    ledOff(x%2, x);
    ledOff((x+1)%2, (x+6)%12);
    ledOff(x%2, (x+3)%12);
    ledOff((x+1)%2, (x+9)%12);
  }
}

void binaryToggle(int rgy, int n, boolean on) {
  for (int x=0; x < 4; x++) {
    if (n & 0x1) {
      if ( on ) ledOn(rgy, x);
      else ledOff(rgy, x);
    }
    n = n >> 1;
  } 
}

void clearLeds() {
  for(int i = 3; i < 8; i++) {
    digitalWrite(i, HIGH);
  }
  for(int j = 8; j < 14; j++) {
    digitalWrite(j, LOW);
  }
}

void altLedBlink(int rgy, int start) {
  altLed(rgy, start, true);
  altLed(rgy, start, false);
}

void altLed(int rgy, int start, boolean on) {
  for (int x=start; x < start+12; x+=2) {
    if ( on ) ledOn(rgy, x%12);
    else ledOff(rgy, x%12);
  }
}

void ledOn(int rgy, int pos) {
  digitalWrite(getRow(rgy, pos), LOW);
  digitalWrite(getCol(rgy, pos), HIGH);
}

void ledOff(int rgy, int pos) {
  digitalWrite(getRow(rgy, pos), HIGH);
  digitalWrite(getCol(rgy, pos), LOW);
}

int getRow(int rgy, int pos) {
  return rgy*2 + pos/6 + 3;
}

int getCol(int rgy, int pos) {
  return pos%6 + 8;
}
  
#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
 }
 
 Serial.print("ddur: "); Serial.println(default_dur, 10);
 
 // get default octave
 if(*p == 'o')
 {
   p++; p++;              // skip "o="
   num = *p++ - '0';
   if(num >= 3 && num <=7) default_oct = num;
   p++;                   // skip comma
 }
 
 Serial.print("doct: "); Serial.println(default_oct, 10);
 
 // get BPM
 if(*p == 'b')
 {
   p++; p++;              // skip "b="
   num = 0;
   while(isdigit(*p))
   {
     num = (num * 10) + (*p++ - '0');
   }
   bpm = num;
   p++;                   // skip colon
 }
 
 Serial.print("bpm: "); Serial.println(bpm, 10);
 
 // BPM usually expresses the number of quarter notes per minute
 wholenote = (60 * 1000L / bpm) * 4;  // this is the time for whole note (in milliseconds)
 
 Serial.print("wn: "); Serial.println(wholenote, 10);
 
 // 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)
   {
     Serial.print("Playing: ");
     Serial.print(scale, 10); Serial.print(' ');
     Serial.print(note, 10); Serial.print(" (");
     Serial.print(notes[(scale - 4) * 12 + note], 10);
     Serial.print(") ");
     Serial.println(duration, 10);
     tone1.play(notes[(scale - 4) * 12 + note]);
     delay(duration);
     tone1.stop();
   }
   else
   {
     Serial.print("Pausing: ");
     Serial.println(duration, 10);
     delay(duration);
   }
 }
}

--
Mark Tabry
AdaptiveBlue - Senior Software Engineer
http://getglue.com/
http://twitter.com/marktabry

Mark Tabry

unread,
Dec 29, 2009, 3:55:38 AM12/29/09
to makerb...@googlegroups.com
Whoops, found a typo. Use this code instead:

Charles Edward Pax

unread,
Dec 30, 2009, 12:23:32 AM12/30/09
to makerb...@googlegroups.com
I burned this onto my watch. Works great!. Thanks, Mark.

Charles Edward Pax
blog: http://charlespax.wordpress.com/
twitter: http://twitter.com/charlespax

Charles Edward Pax

unread,
Jan 2, 2010, 5:03:40 PM1/2/10
to makerb...@googlegroups.com
I posted a short video on youtube: http://www.youtube.com/watch?v=-mGMe6PF6KI
Reply all
Reply to author
Forward
0 new messages