Skip to content

Buzzer Christmas Ornament Tutorial


Background

In this tutorial, you'll learn how to make a Christmas tree ornament that is able to "sing" up to ten different Christmas carols. These carols are "sung" on the Buzzer Wireling and the song can be selected with the Rotary Switch Wireling. The songs can be changed at the time the device is programmed to any other set of songs too! An RGB LED Wireling can also be added if you'd like to make this project do even more.

This project doesn't necessarily have to be implemented as an ornament! As you'll see above, a small decoration can be made with the RGB LED shining through a mason jar to create a snowflake-like pattern.


Materials

In order to interface with Wirelings, you'll need the appropriate number of Wireling Cables and the Wireling.h Library (You can download this from GitHub as linked, or from the Library Manager in the Arduino IDE).

The table below details which Wirelings are used on each port. The tutorials and libraries for each of these Wirelings are also included.

Wireling Learn Library
Port 0 Buzzer Buzzer pitches.h and songs.h
Port 1 RGB LED RGB LED FastLED
Port 2 N/A N/A N/A
Port 3 Rotary Switch Rotary Switch SX1505

Hardware Assembly

All you need to do is plug in your Wirelings to the ports specified above using Wireling Cables. (You can change these ports in the included Arduino Sketch using the Wireling.selectPort() function and changing the definitions of the analog pins to their respective ports)

NOTE: Be mindful when inserting Wireling Cables - the connector pins inside the 5-pin connectors on Wirelings can be bent when cables are inserted at an angle.

Assembled Hardware


Software Setup

If you have not already done so, download the Buzzer Christmas Ornament Sketch and open it in the Arduino IDE.

You will need to download and install the libraries listed above for this project, excluding the pitches.h and songs.h header files which are included in the .zip file above with the sketch.

To install an Arduino library, check out our Library Installation Page.

Make the correct Tools selections for your development board. If unsure, you can double check the Help page that mentions the Tools selections needed for any TinyCircuits processor.


Upload Program

Plug in the micro USB cable form your computer to the WirelingZero. Make the appropriate tools selections to upload the program to the WirelingZero.

Code
/*
  Buzzer & RGB LED Christmas Ornament

  This project is assembled with the following Wirelings:
  PORT 0: Buzzer Wireling
  PORT 1: RGB LED Wireling
  PORT 2: 
  PORT 3: Rotary Switch Wireling

  By rotating the Rotary Switch Wireling, the
  song played by the Buzzer Wireling will change.
  An RGB LED will cycle through a few pre-defined
  colors every 1.5 seconds.

  Hardware by: TinyCircuits
  Written by: Hunter Hykes for TinyCircuits

  Initialized: 12/16/2020
  Last Updated: 12/18/2020
*/

#include <Wireling.h> // For interfacing with Wirelings
#include <SX1505.h>   // For interfacing with Rotary Switch Wireling
#include <FastLED.h>  // For RGB LED
#include "pitches.h"  // Definitions of notes as frequencies
#include "songs.h"    // Definitions of pitches and note durations for each song

// Universal Serial Monitor Config
#if defined(ARDUINO_ARCH_AVR)
#define SerialMonitorInterface Serial
#elif defined(ARDUINO_ARCH_SAMD)
#define SerialMonitorInterface SerialUSB
#endif

/* * * * * * * * * * Buzzer * * * * * * * * * */
#define buzzerPin A0 // Corresponds to Wireling Port 0 (A1 = Port 1, A2 = Port 2, A3 = Port 3)

/* * * * * * * * * * RGB LED * * * * * * * * * */
#define LED_PIN A1
#define NUM_LEDS 1 // This is the number of RGB LEDs connected to the pin
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
int numClrs = 4; // number of colors listed in the following colors[] array
CRGB colors[] = {
  CRGB(255,   0,   0), // RED
  CRGB(  0, 255,   0), // GREEN
  CRGB(127, 127,   0), // YELLOW
  CRGB(  0,   0, 255)  // BLUE
}; // colors to cycle through with the LED
int clr = 0; // counter to iterate through colors
int brightness = 64; // Brightness is on a scale of 0-255, 64 is 25% brightness
unsigned long LED_time = 1.5 * 1000; // LED will change every 1.5 seconds
unsigned long time_start;

/* * * * * * * * * * Rotary * * * * * * * * * */
#define ROT_PORT 3
#define ROT_INT A3
TinyRotary rotary = TinyRotary(); 
uint8_t rotaryValue = 0;
bool changeSong = 0;

void setup(void) {
  Wireling.begin();

  /* * * * * * Buzzer Init * * * * */
  pinMode(buzzerPin, OUTPUT); // Buzzer

  /* * * * * * RGB LED Init * * * * */
  FastLED.addLeds<WS2812, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(brightness);
  pinMode(LED_PIN, OUTPUT);

  /* * * * * * Rotary Init * * * * */
  Wireling.selectPort(ROT_PORT); 
  rotary.begin();
  rotary.setInterrupt(); // configure the interrupt settings for the onboard SX1505
  pinMode(ROT_INT, INPUT_PULLUP); // pullup since active low, keep pulled high if idle
  attachInterrupt(ROT_INT, ISR_Rotary, FALLING); // interrupt is active low, react on falling edge

  SerialMonitorInterface.begin(9600);
  time_start = millis(); // initialize the start time of the timer
}

void loop() {
  if(changeSong) { // if the rotary value changes
    // SerialMonitorInterface.println(rotaryValue); // print the value acquired by the ISR
    changeSong = 0;           // we are done changing songs, allow the new one to play
    delay(100);               // short delay so change in song is noticeable
    play_song(rotaryValue);   // sing the new song
  }

  updateLED(); // keep updating LED even if no song is playing
}

void play_song(int s) {
  // 0 = off
  switch(s) {
    case 1:
      SerialMonitorInterface.println("'Jingle Bells'");
      sing(jingle_melody, jingle_tempo, 104);
      break;
    case 2:
      SerialMonitorInterface.println("'We Wish You a Merry Christmas'");
      sing(wish_melody, wish_tempo, 52);
      break;
    case 3:
      SerialMonitorInterface.println("'Santa Claus is Coming to Town'");
      sing(santa_melody, santa_tempo, 112);
      break;
    case 4:
      SerialMonitorInterface.println("'Rudolph the Red-Nosed Reindeer'");
      sing(rudolph_melody, rudolph_tempo, 147);
      break;
    case 5:
      SerialMonitorInterface.println("'Carol Of The Bells'");
      sing(cotb_melody, cotb_tempo, 87);
      break;
    case 6:
      SerialMonitorInterface.println("'Deck the Halls'");
      sing(deck_melody, deck_tempo, 68);
      break;
    case 7:
      SerialMonitorInterface.println("'God Rest Ye Merry Gentlemen'");
      sing(rest_melody, rest_tempo, 68);
      break;
    case 8:
      SerialMonitorInterface.println("'O Christmas Tree'");
      sing(tree_melody, tree_tempo, 62);
      break;
    case 9:
      SerialMonitorInterface.println("'The First Noel'");
      sing(noel_melody, noel_tempo, 73);
      break;
    case 0:
      // off for now
      break;
    default:
      // do nothing
      break;
  }
}

// play the specified song on the Buzzer Wireling
// continues to update the LED every (LED_time) seconds
void sing(int* melody, int* tempo, int numNotes) {
  // Iterate over the notes of the melody:
  for (int thisNote = 0; (thisNote < numNotes) && (!changeSong); thisNote++) {
    // To calculate the note duration, take one second divided by the note type.
    // Ex. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000 / tempo[thisNote];
    tone(buzzerPin, melody[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);
    noTone(8); // Stop the tone playing

    updateLED(); // update LED while song is playing too
  }
}

// returns true when the specified interval (in ms) has passed
bool ms_timer(unsigned long interval) {
  unsigned long time_now = millis();
  if (millis() >= time_start + interval) { // if the timer is expired
    time_start = millis(); // set the start time for the next interval
    return 1;
  } else {
    return 0;
  }
}

// change the color of the RGB LED every (LED_time) seconds
// by iterating through the colors in the colors[] array
void updateLED() {
  if(ms_timer(LED_time)) {
    // uncomment for loop if using more than one RGB LED in the strand
    // for(int i = 0; i < NUM_LEDS; i++) {
      leds[0] = colors[clr];
      FastLED.show();
    // }
    clr++; // increment colors[] array iterator
    if(clr == numClrs) {
      // reset clr to 0 if clr exceeds the size of the color array
      clr = 0;
    }
  }
}

// interrupt handler for the Rotary Switch Wireling
void ISR_Rotary() {
  changeSong = 1;                     // let the program know to change the song
  rotaryValue = rotary.getPosition(); // get the new value
  rotary.clearInt();                  // clear the interrupt flag
}

The songs that are in the program can be changed in order and new songs can be added in their place. At the end of the songs.h file are a list of some other classic Christmas carols if you're looking to add different songs to your version of the project. Check out the Buzzer Wireling Tutorial to see how the pitch and duration for a note are determined. If you are familiar with sheet music, it isn't too difficult to transpose to these arrays and add your own songs to the project.

You can also change the number of colors the LED changes and even change those colors altogether. The interval at which the LEDs change can also be adjusted.

Once the software is operating, you can secure the electronics to your ornament of choice. Then your ornament will have new life with the addition of some lighting and sound effects!


Contact Us

If you have any questions or feedback, feel free to email us or make a post on our forum. Show us what you make by tagging @TinyCircuits on Instagram, Twitter, or Facebook so we can feature it.

Thanks for making with us!