/* Snowman by Chris Ritchie - 05/06/2017 Based off of https://github.com/rmadhuram/ArduinoChristmasTunes Sleep code from https://bigdanzblog.wordpress.com/2014/08/10/attiny85-wake-from-sleep-on-pin-state-change-code-example/ 14/06/2017 - Added sleep code */ #include "pitches.h" #include <avr/sleep.h> #include <avr/interrupt.h> //#include <avr/pgmspace.h> typedef struct { unsigned int *melody; byte *durations; byte numCount; byte tempo; byte low, high; } tune; tune tunes[3]; // Amount of songs unsigned int frostySnowman[] = { N_G4, N_E4, N_F4, N_G4, N_C5, N_B4, N_C5, N_D5, N_C5, N_B4, N_A4, N_G4, N_B4, N_C5, N_D5, N_C5, N_B4, N_A4, N_A4, N_G4, N_C5, N_E4, N_G4, N_A4, N_G4, N_F4, N_E4, N_F4, N_G4}; byte frostySnowmanDurations[] = { 8, 6, 3, 5, 8, 3, 3, 5, 5, 5, 5, 11, 3, 3, 3, 4, 4, 5, 5, 6, 5, 5, 3, 3, 5, 5, 5, 5, 14, 14}; unsigned int jingleBells[] = { N_E5, N_E5, N_E5, N_E5, N_E5, N_E5, N_E5, N_G5, N_C5, N_D5, N_E5, N_F5, N_F5, N_F5, N_F5, N_F5, N_E5, N_E5, N_E5, N_E5, N_D5, N_D5, N_E5, N_D5, N_G5, N_E5, N_E5, N_E5, N_E5, N_E5, N_E5, N_E5, N_G5, N_C5, N_D5, N_E5, N_F5, N_F5, N_F5, N_F5, N_F5, N_E5, N_E5, N_E5, N_G5, N_G5, N_F5, N_D5, N_C5 }; byte jingleBellsDurations[] = { 4, 4, 8, 4, 4, 8, 4, 4, 4, 4, 16, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 4, 4, 8, 4, 4, 8, 4, 4, 4, 4, 16, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 16 }; unsigned int rudolph[] = { N_G4, N_A4, N_G4, N_E4, N_C5, N_A4, N_G4, N_G4, N_A4, N_G4, N_A4, N_G4, N_C5, N_B4, N_F4, N_G4, N_F4, N_D4, N_B4, N_A4, N_G4, N_G4, N_A4, N_G4, N_A4, N_G4, N_A4, N_E4}; byte rudolphDurations[] = { 2, 2, 4, 4, 4, 4, 8, 2, 2, 2, 2, 3, 3, 11, 2, 2, 3, 3, 3, 3, 8, 2, 2, 2, 2, 3, 3, 11, 11}; const byte nosePin = 0; // Pin for nose LED const byte buzzerPin = 1; // Pin for piezo buzzer // Button pins and states const int frostyBttn = 2; boolean frostyState = 0; const int jingleBttn = 4; boolean jingleState = 0; const int rudolphBttn = 3; boolean rudolphState = 0; void play(int tuneNum) { int numNotes = tunes[tuneNum].numCount; int tempo = tunes[tuneNum].tempo; for (int thisNote = 0; thisNote < numNotes; thisNote++) { int freq = tunes[tuneNum].melody[thisNote] * 2; int noteDuration = tempo * tunes[tuneNum].durations[thisNote]; if (freq > 0) { digitalWrite(nosePin, HIGH); // Turn on nose when tone plays tone(buzzerPin, freq, noteDuration); } else { // REST delay(noteDuration); } delay(noteDuration); digitalWrite(nosePin, LOW); // Turn off nose when tone stops // to distinguish the notes, set a minimum time between them. // the note's duration + 30% seems to work well: int pauseBetweenNotes = noteDuration * 0.30; delay(pauseBetweenNotes); noTone(buzzerPin); } } void setup() { int numTunes = 3; analogReference(INTERNAL); tunes[0].melody = jingleBells; tunes[0].durations = jingleBellsDurations; tunes[0].numCount = sizeof(jingleBells) / sizeof(int); tunes[0].tempo = 60; tunes[1].melody = rudolph; tunes[1].durations = rudolphDurations; tunes[1].numCount = sizeof(rudolph) / sizeof(int); tunes[1].tempo = 60; tunes[2].melody = frostySnowman; tunes[2].durations = frostySnowmanDurations; tunes[2].numCount = sizeof(frostySnowman) / sizeof(int); tunes[2].tempo = 60; for (int i = 0; i < numTunes; i++) { int low = N_DS8; int high = 0; for (int j = 0; j < tunes[i].numCount; j++) { int freq = tunes[i].melody[j]; if (freq != 0 && freq < low) low = freq; if (freq > high) high = freq; } tunes[i].high = high; tunes[i].low = low; } // Initialize the button pins as an input with internal pullup resistors enabled pinMode(jingleBttn, INPUT_PULLUP); pinMode(frostyBttn, INPUT_PULLUP); pinMode(rudolphBttn, INPUT_PULLUP); pinMode(nosePin, OUTPUT); } void sleep() { GIMSK |= _BV(PCIE); // Enable Pin Change Interrupts PCMSK |= _BV(PCINT2); // Use PB2 as interrupt pin PCMSK |= _BV(PCINT3); // Use PB3 as interrupt pin PCMSK |= _BV(PCINT4); // Use PB4 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(PCINT2); // Turn off PB2 as interrupt pin PCMSK &= ~_BV(PCINT3); // Turn off PB3 as interrupt pin PCMSK &= ~_BV(PCINT4); // Turn off PB4 as interrupt pin sleep_disable(); // Clear SE bit ADCSRA |= _BV(ADEN); // ADC on sei(); // Enable interrupts } // sleep ISR(PCINT0_vect) { // This is called when the interrupt occurs, but I don't need to do anything in it } void loop() { sleep(); // Read the state of the buttons jingleState = digitalRead(jingleBttn); frostyState = digitalRead(frostyBttn); rudolphState = digitalRead(rudolphBttn); // Check if button has been pressed, and play corresponding song if (jingleState == LOW) play(0); else if (rudolphState == LOW) play(1); else if (frostyState == LOW) play(2); }