In this fun project, we'll learn how to make a buzz wire game with the Arduino. Many of the parts needed can be found in a starter kit (what's in a starter kit?) and around the house. This project uses an Arduino, although you could use nearly any microcontroller you have around (have a look at this comparison between $5 microcontrollers for some inspiration).

Check out the end result -- it even plays music:

What You Need

Here are the core parts you'll need to complete this project:

  • 1 x Arduino UNO or similar.
  • 1 x Metal coat hanger.
  • 2 x 220 ohm resistors.
  • 1 x Breadboard.
  • 1 x Piezo buzzer.
  • 2 x Crocodile clips.
  • Assorted heat shrink tubing.
  • Male-to-male hookup wires.

Here are some optional parts to enhance the build:

  • 1 x Additional piezo buzzer.
  • 1 x Dowel rod.
  • 1 x Quad seven segment display.
  • 1 x 220 ohm resistor.
  • 1 x Momentary button.
  • Male-to-female hookup wires.
  • Wooden plank (for case).
  • Assorted wood screws.

Almost any Arduino will work, providing it has enough pins. Take a look at this buying guide if you are not sure what you need.

The Build Plan

Arduino Buzzwire Completed

Whilst this may look complex, it's actually quite a simple project. I'll start with the basic game, and then add additional components to increase complexity. You can "pick and choose" as you like depending on the components you have available.

The core mechanic consists of a wire shape and a loop on a handle. The player has to guide the loop around the course without the two touching. If the two touch, the circuit is completed and the buzzer sounds. It is of course possible to build this circuit using no microcontroller, but where's the fun in that (and how else would you get to listen to Monty Python's "Flying Circus" theme song)?

The Course

Arduino Buzzwire Course

This is the shape the player will have to guide their loop round. It's the basis of the whole game, so make it good! I chose to have a small drop, followed by a large climb. Bend a metal coat hanger into the shape you need. Brass wire or copper tube will work equally as well, although a coat hanger may be the cheapest.

You may need to wear gloves and use pliers or a hammer to get things perfect. Cut off any excess with bolt cutters. Leave two vertical uprights to push through the base. You way want to file the cut ends for safety. Finally, cut two pieces of heat shrink tubing and place over the ends as follows:

Arduino Buzzwire Insulation

This will insulate the loop from the course, providing a start/end or safety zone. Alternatively, tape or even a straw will do if you do not have any heat shrink tubing.

Now attach a cable to one end of the course. You have two options here: you can either solder, or use a crocodile clip. A crocodile clip is the easier option, but soldering is a more reliable and long-term option. Make sure to "rough up" the surface of the coat hanger first with sandpaper, and use plenty of flux. (Never soldered before? Learn how to here.)

Depending on the size of the hole you drill in the base on the next step, you may need to feed the cable through the mounting hole first. Using two wires twisted together will increase durability:

Arduino Twisted Wire

Using a drill to do this helps a lot:

Drill Chuck Wire Twisting

The Base

Arduino Buzzwire Case

It's time to create the base. This serves to hold the course in an upright position, as well as providing a place to anchor the electronics to. I used some pine offcuts, although you could use whatever you have around the house -- even a cardboard box.

Cut three pieces to form an "n" shape. Simply screw (or glue) these three pieces together. Remember to drill a pilot hole in the side pieces first to prevent them splitting. You may want to countersink the screws (especially if you will be filling and then painting), and I highly recommend a countersink drill bit. If you do not have a countersinking tool or drill bore, a larger diameter drill bit will do the trick.

Drill two holes far enough apart for the ends of the course to sit into. Countersink the underside ready for glueing.

The Handle

Arduino Buzzwire Handle

Now it's time to make the loop/controller. Twist a small piece of the coat hanger at one end to create a loop with a small metal handle. Make sure you file the cut edge, and then cover it with tape/foam if necessary.

Arduino Buzzwire Loop

This will form the other half of the circuit -- when this loop touches the course it will complete the circuit (exactly like a switch). Solder (or use a crocodile clip) another wire to the bottom of this, exactly the same as you did previously for the course.

Cut a small length of dowel for the actual handle. This metal loop will slot into this handle. If you do not have any dowel, you can round off a piece of square softwood using a belt or disc sander (you could also use sandpaper, but it would take a long time).

Drill a hole through this handle. This needs to be large enough to fit the metal loop and the wire through:

Arduino Buzzwire Handle Hole

This is possible to do on a pillar drill, although it is tricky. A lathe will do the job perfectly:

Dowel Lathe Buzzwire Handle

Yes, I'm quite aware this is a metal lathe (for anybody interested, it's a Boley watchmaking lathe from the 1930s. I think it's a 3C, but I'd love to hear from you if you know any more about it).

You could also use a ballpoint pen with the center removed.

Finally, use hot glue to secure the cable and the loop into the handle. Hot glue will provide a strong (but not permanent) fixture, so it's perfect for this.

Finishing Off

Insert the wire course into the holes in the base. Don't forget to add the loop/controller first. Use hot glue again to secure the course to the base by filling the countersunk holes on the underside of the base as follows:

Arduino Buzzwire Soldering

The Circuit

Here's the full circuit. You do not have to make yours as complex as this -- read on as we break down each part.

Arduino Buzzwire Full Circuit

First, connect the two piezo elements to digital pins 10 and 11. The polarity doesn't matter:

Arduino Piezo Circuit

You do not have to use two piezos -- the only reason I've done so is to have a much louder buzz sound when the wires touch. Connect one side to the digital pin, and the other to ground.

Now plug in the metal course and handle:

Arduino Buzzwire Circuit

Again, it does not matter which way round these two are wired. This part of the circuit is exactly like a button or switch -- the player completes the circuit when the loop touches the course. Make sure you include both resistors.

One resistor ties the circuit to ground (called a pull-down resistor), ensuring it is not "floating" (this allows the Arduino to detect the circuit changing). The other resistor protects the Arduino. When the two parts touch, +5V goes into the digital pin. If this resistor was not present there would be a dead short -- your computer would disconnect the USB socket for drawing too much current if you are lucky.

Connect the signal lead (purple, on the diagram) to digital pin 9.

Next, connect a push button to digital pin 2:

Arduino Piezo Button Circuit

Finally, connect the seven-segment LED display:

Arduino Seven Segment Circuit

This particular model is from Seeed. This uses a TM1637 to drive four displays -- this means only two digital pins are needed. Connect GND to Arduino ground and VCC to Arduino +5V. Connect D10 to Arduino digital pin 13 and CLK to digital pin 12.

The Code

To make this project work you will need two additional files. The first is called

        pitches.h
    

. This file simply maps note names to their piezo value. This makes it much easier to write a tune, as you can simply say "NOTE_C3" rather than "31", for example. This is in the public domain, and is available on the Arduino website here. Follow the instructions to create a new file called

        pitches.h
    

(alternatively, paste the code into your existing script).

Next, you need a method to actually play notes/melodies on the piezo. This gist by Anthony DiGirolamo on Github contains the code you need. Copy everything between "void buzz" and "}}" and paste it into your main file. For reference, here it is:

        void buzz(int targetPin, long frequency, long length) {
 /* Buzzer example function by Rob Faludi
 http://www.faludi.com
 https://gist.github.com/AnthonyDiGirolamo/1405180
 */
 long delayValue = 1000000/frequency/2; // calculate the delay value between transitions
 //// 1 second's worth of microseconds, divided by the frequency, then split in half since
 //// there are two phases to each cycle
 long numCycles = frequency * length/ 1000; // calculate the number of cycles for proper timing
 //// multiply frequency, which is really cycles per second, by the number of seconds to
 //// get the total number of cycles to produce
 for (long i=0; i < numCycles; i++){ // for the calculated length of time...
 digitalWrite(targetPin,HIGH); // write the buzzer pin high to push out the diaphragm
 delayMicroseconds(delayValue); // wait for the calculated delay value
 digitalWrite(targetPin,LOW); // write the buzzer pin low to pull back the diaphragm
 delayMicroseconds(delayValue); // wait again for the calculated delay value
 }
}

The last library you need is to control the seven segment display -- you can skip this step if you are not using one. This library is called TM1637 and was created by Seeed, the same company that created the driver board.

In the Arduino IDE, go to "Manage Libraries" (Sketch > Include Library > Manage Libraries). This will bring up the library manager. Allow it a few seconds to update and then search in the top right search box "TM1637". Two libraries will be found -- you want "TM1637" and not "TM1637Display". Select and then click "install".

One last task with this library -- it's not complete! As it stands, the library can only display numbers 0--9 and letters A--F. If this covers everything you would like to display, then you can skip this step. If not, you will need to modify the code. Relax! This is not as hard as it sounds, and if you can write code using the Arduino IDE, you can do this.

First, open your library folder. This will be in your Arduino folder. On Mac OS X, this is in

        /Users/Joe/Documents/Arduino/Libraries
    

. Open the folder called TM1637. You will need to edit the file called

        TM1637.cpp
    

-- you can safely ignore the other file with the extension 

        .h
    

. Open this file in your favorite text editor (for me, that's Sublime Text 3), Notepad, or the Arduino IDE.

Modify the third line of code from this:

        static int8_t TubeTab[] = {0x3f,0x06,0x5b,0x4f,

 0x66,0x6d,0x7d,0x07,

 0x7f,0x6f,0x77,0x7c,

 0x39,0x5e,0x79,0x71};//0~9,A,b,C,d,E,F

To this:

        static int8_t TubeTab[] = {
/* defaults */
0x3f, // 0
0x06, // 1
0x5b, // 2
0x4f, // 3
 0x66, // 4
 0x6d, // 5
 0x7d, // 6
 0x07, // 7
0x7f, // 8
0x6f, // 9
0x77, // A -- 10
0x7c, // b -- 11
0x39, // C -- 12
0x5e, // d -- 13
0x79, // E -- 14
0x71, // F -- 15

/* additional */
0x174, // h -- 16
 0x176, // H -- 17
 0x138, // L -- 18
 0x15, // M -- 19
 0x137, // n -- 20
 0x73, // P -- 21
 0x67, // q -- 22
 0x131, // r -- 23
 0x78, // t -- 24

 0x240 // - 25
};

You can now save and close this file. After each element, the comment describes what character this is. The next part of the comment is the index of the element.

It's time for the actual code. First, include the two libraries mentioned previously:

        #include <TM1637.h>
#include <pitches.h>

Now create the display object:

        TM1637 *_display = new TM1637(12, 13);
    

Don't worry if you don't understand the syntax -- this line tells the Arduino that pins 12 and 13 are attached to a seven segment display, and to configure it appropriately.

The song is stored in

        melody
    

and

        tempo
    

. These contain all the notes and the note duration for the music. If you would like to change the music, modify these arrays (although, it's not as simple as pasting in the note values, timing is a very important part of music). The

        songState
    

variable simply stores the position of the last played note. This ensures the melody is played from start to finish, rather than jumping around inconsistently:

        int songState = 0; 
int melody[] = {
 NOTE_F4,...}
int tempo[] = {
 8,...}

Note that I have removed the contents of the arrays, see below for the full code.

This code is non blocking -- this means the Arduino can perform multiple tasks simultaneously. Take a look at this explanation for more information. This is how the timers are setup:

        unsigned long previousMillis1 = 0;
const long interval1 = 1500;

The variable

        previousMillis1
    

will be updated at a later stage to store the current time. The

        interval1
    

variable stores how long to wait between code execution -- in this case, 1.5 seconds. It is defined as

        const
    

, which means it is constant and will never change -- this allows the Arduino to further optimize the code.

Inside the

        setup()
    

function there are a few things going on. First, the inputs and outputs are setup. This has to be done, so the Arduino knows what is connected to each of it's pins:

        pinMode(9, INPUT); // setup circuit
pinMode(10, OUTPUT); // setup buzzer 1
pinMode(11, OUTPUT); // setup buzzer 2
pinMode(2, INPUT); // setup button

Now the display needs configuring:

        _display->set(5); // set brightness
_display->point(false); // remove colon
_display->init(); // start display

The methods

        set
    

,

        point
    

, and

        init
    

are all contained within the

        _display
    

object. Instead of a dot, a pointer ("->") is used to access these. Again, do not worry about the syntax (although, if you would like to learn more, look up C++ Pointers).

The main loop has two game modes: challenge and free play. Free play allows the player to play an unlimited amount of times. Challenge mode sets a timer for 20 seconds using the

        showCountdown
    

 method. It uses the button to start and stop the timer. currently, the only way to change game modes is to manually edit the variable called

        mode
    

. See if you can add another button to do this and modify the code appropriately.

The

        buzz
    

method plays the notes given to it. This is used in conjunction with

        sing
    

. The sing method goes through every note and plays it. This method is called regularly, although it will only play the next note when enough time has elapsed since last playing. Once the song has reached the end, it resets the song to verse 1 (songState = 14). You could set this to zero to start the song at the beginning, however the reason for doing this is to skip the introduction. The introduction is played once after the Arduino has powered up, and then it is not played again.

The 

        showFree
    

and

        showPlay
    

methods simply write the words "FrEE" and "PLAY" to the display. Notice how the "r" in free is lowercase, when all the other characters are uppercase. This is one of the limitations of seven segment displays. They cannot show every letter of the alphabet, and some of the characters they can display have to be in mixed case.

The

        toggleFreePlay
    

method flashes the display between "FREE" and "PLAY". Again, it does this in a non-blocking way.

Another useful method is

        showNumber
    

. This writes a number to the middle two characters of the display like this:

Arduino Buzzwire Display

The display is not smart enough to know how to show large numbers, it has to be explicitly told what to do. This method uses some simple logic to show the appropriate number on each character.

The final method used is called

        showCountdown
    

. This starts a counter at 20, and decreases it by one every second. If this reaches zero, it buzzes three times, to indicate that the time has run out.

Here's all that code put together:

        #include <TM1637.h> // include display library
#include <pitches.h> // include pitches

TM1637 *_display = new TM1637(12, 13); // create display object, 12 = CLK (clock), 13 = D10 (data)

// music
int songState = 0;

int melody[] = {
 NOTE_F4, NOTE_E4, NOTE_D4, NOTE_CS4,
 NOTE_C4, NOTE_B3, NOTE_AS3, NOTE_A3,
 NOTE_G3, NOTE_A3, NOTE_AS3, NOTE_A3,
 NOTE_G3, NOTE_C4, 0,
 
 NOTE_C4, NOTE_A3, NOTE_A3, NOTE_A3,
 NOTE_GS3, NOTE_A3, NOTE_F4, NOTE_C4,
 NOTE_C4, NOTE_A3, NOTE_AS3, NOTE_AS3,
 NOTE_AS3, NOTE_C4, NOTE_D4, 0,
 
 NOTE_AS3, NOTE_G3, NOTE_G3, NOTE_G3,
 NOTE_FS3, NOTE_G3, NOTE_E4, NOTE_D4,
 NOTE_D4, NOTE_AS3, NOTE_A3, NOTE_A3,
 NOTE_A3, NOTE_AS3, NOTE_C4, 0,
 
 NOTE_C4, NOTE_A3, NOTE_A3, NOTE_A3,
 NOTE_GS3, NOTE_A3, NOTE_A4, NOTE_F4,
 NOTE_F4, NOTE_C4, NOTE_B3, NOTE_G4,
 NOTE_G4, NOTE_G4, NOTE_G4, 0,
 
 NOTE_G4, NOTE_E4, NOTE_G4, NOTE_G4,
 NOTE_FS4, NOTE_G4, NOTE_D4, NOTE_G4,
 NOTE_G4, NOTE_FS4, NOTE_G4, NOTE_C4,
 NOTE_B3, NOTE_C4, NOTE_B3, NOTE_C4, 0
};

int tempo[] = {
 8, 16, 8, 16,
 8, 16, 8, 16,
 16, 16, 16, 8,
 16, 8, 3,
 
 12, 16, 16, 16,
 8, 16, 8, 16,
 8, 16, 8, 16,
 8, 16, 4, 12,
 
 12, 16, 16, 16,
 8, 16, 8, 16,
 8, 16, 8, 16,
 8, 16, 4, 12,
 
 12, 16, 16, 16,
 8, 16, 8, 16,
 8, 16, 8, 16,
 8, 16, 4, 16,
 
 12, 17, 17, 17,
 8, 12, 17, 17,
 17, 8, 16, 8,
 16, 8, 16, 8, 1
};

// non blocking setup
// free play
unsigned long previousMillis1 = 0; // time words last changed
const long interval1 = 1500; // interval between changing

// music
unsigned long previousMillis2 = 0; // time last changed
const long interval2 = 100; // interval between notes

int displayStatus = 0; // keep track of what's displayed
int mode = 0; // keep track of game mode -- change to 0 or 1 for different modes

bool countdown = false;

unsigned long previousMillis3 = 0; // time last changed
const long interval3 = 1000; // interval between countdown
int count = 20; // challenge mode timer

void setup() {
 // put your setup code here, to run once:
 pinMode(9, INPUT); // setup circuit
 pinMode(10, OUTPUT); // setup buzzer 1
 pinMode(11, OUTPUT); // setup buzzer 2
 pinMode(2, INPUT); // setup button
 
 _display->set(5); // set brightness
 _display->point(false); // remove colon
 _display->init(); // start display
}

void loop() {
 // put your main code here, to run repeatedly:
 if(mode == 0) {
 // challenge mode
 if(digitalRead(2) == HIGH) {
 delay(25);
 if(digitalRead(2) == HIGH) {
 countdown = true; // stop the countdown
 }
 else {
 countdown = false; // stop the countdown
 }
 }
 if(countdown) {
 showCountdown(); // advance countdown
 }
 }
 else {
 // free play
 toggleFreePlay();
 }
 if(digitalRead(10) == HIGH) {
 delay(25);
 if(digitalRead(10) == HIGH) {
 while(digitalRead(10) == HIGH) {
 buzz(11, NOTE_B0, 1000/24);
 }
 }
 }
 else
 sing();
}

void showCountdown() {
 // countdown the time remaining
 unsigned long currentMillis = millis(); // current time
 if (currentMillis - previousMillis3 >= interval3) {
 previousMillis3 = currentMillis;
 --count;
 showNumber(count);
 if(count == 0) {
 // game over
 countdown = false;
 count = 20;
 // reset countdown
 // buzz 3 times
 buzz(11, NOTE_B0, 1000/24);
 delay(100);
 buzz(11, NOTE_B0, 1000/24);
 delay(100);
 buzz(11, NOTE_B0, 1000/24);
 }
 }
}

void showNumber(int number) {
 // show numbers (maximum 99) on display
 _display->display(0, 25); // write - to segment 1
 _display->display(3, 25); // write - to segment 4
 
 // write number to middle of display
if(number == 10)
{
_display->display(1,1);
_display->display(2,0);
}
else if(number > 9)
{
_display->display(1,1);
int newVal = number - 10;
_display->display(2, newVal);
}
else
{
_display->display(1,0);
_display->display(2,number);
}
}

void toggleFreePlay() {
 // scroll between words without blocking
 unsigned long currentMillis = millis(); // current time
 if (currentMillis - previousMillis1 >= interval1) {
 previousMillis1 = currentMillis;
 if(displayStatus == 1)
 showPlay();
 else
 showFree();
 }
}

void showPlay() {
 // write "PLAY" to the display
 _display->display(0, 21); // write P to segment 1
 _display->display(1, 18); // write L to segment 2
 _display->display(2, 10); // write A to segment 3
 _display->display(3, 4); // write Y to segment 4
 displayStatus = 2;
}

void showFree() {
 // write "Free" to the display
 _display->display(0, 15); // write F to segment 1
 _display->display(1, 23); // write r to segment 2
 _display->display(2, 14); // write E to segment 3
 _display->display(3, 14); // write E to segment 4
 displayStatus = 1;
}

void buzz(int targetPin, long frequency, long length) {
 /* Buzzer example function by Rob Faludi
 http://www.faludi.com
 https://gist.github.com/AnthonyDiGirolamo/1405180
 */
 long delayValue = 1000000/frequency/2; // calculate the delay value between transitions
 //// 1 second's worth of microseconds, divided by the frequency, then split in half since
 //// there are two phases to each cycle
 long numCycles = frequency * length/ 1000; // calculate the number of cycles for proper timing
 //// multiply frequency, which is really cycles per second, by the number of seconds to
 //// get the total number of cycles to produce
 for (long i=0; i < numCycles; i++){ // for the calculated length of time...
 digitalWrite(targetPin,HIGH); // write the buzzer pin high to push out the diaphragm
 delayMicroseconds(delayValue); // wait for the calculated delay value
 digitalWrite(targetPin,LOW); // write the buzzer pin low to pull back the diaphragm
 delayMicroseconds(delayValue); // wait again for the calculated delay value
 }
}

void sing() {
 // play the song in a non blocking way
 unsigned long currentMillis = millis();

 if (currentMillis - previousMillis2 >= interval2) {
 previousMillis2 = currentMillis;
 int noteDuration = 1000 / tempo[songState];
 buzz(10, melody[songState], noteDuration);
 int pauseBetweenNotes = noteDuration;
 delay(pauseBetweenNotes);

 // stop the tone playing:
 buzz(10, 0, noteDuration);
 
 ++songState;
 // start song again if finished
 if(songState > 79) {
 songState = 14; // skip intro
 }
 }
}

Save this file as "buzzwire" (File > Save As) and then upload it to your board (File > Upload). If you are not sure how to upload the the Arduino, or that code looks a bit scary, take a look at our Arduino Beginners Guide. All being well, you should now have your own buzz wire game -- cool!

If you made something cool after reading this, I'd love to see -- let me know in the comments below!