July 21, 2008
This example was inspired by JD Barnhart's "Ruby on Bells" RAD demo (see http://www.vimeo.com/1272402), but using standard Arduino (and not Ruby) code.
This Arduino ServoBells example by John Plocher is released into the public domain.
This example uses servos as "wine glass" bell clappers. The basic idea is to fill a bunch of crystal glasses with the right amount of liquid to "tune them" to a musical scale, mount a "hammer" on each servo so that it can ring two different glasses and mount the servos such that the hammers end up at the same level as the wine glasses. Then the program simply uses a sequence of "home, left n degrees, home" and "home, right n degrees, home" servo commands to ring either the glass on the left or the one on the right.
I stopped by TAP Plastics and purchased a 12"x16" sheet of black plastic for a dollar, cut it into 4x 4"x12" rectangles, drilled and cut out mounting holes for a servo and then clamped about 4" of the plastic into a vice, carefully heated it with a hot-air gun, and when the plastic slightly softened, bent each piece to form about a 30 degree angle. The only important "measurement" in all this is that the servo needs to fit in its mounting hole without bottoming out against the base.
Low temp hot melt glue is your friend! Drop the control wires and the servo into the mounting hole you cut in the plastic base and attach it to the base with a bead of hot melt glue. When it cools, flip the base over and squirt a small blob on each corner to act as no-slip feet. Let the "feet" cool/solidify BEFORE turning back over and setting it down - don't ask how I found this out :-)
Center the servos in the middle of their travel and mount the long straight "double" horn parallel with the long direction of the base.
I happened to have some 2"x3/16" mild steel cutoffs that I hot melt glued to the plastic bases to keep them from moving when in use. You can also tape them down, stack books on them, whatever...
I experimented with thick and thin blocks of wood, thick and thin dowels, felt, plastic and metal. Light weight was better than heavy, hard wood rather than soft. Springy shafts instead of rigid, and hard facing on the hammers. You want the shafts long enough to clear the plastic base by at least the diameter of a wine glass, with a low mass head.
Try the following suggestions, but play with things to get the best sound:
Hot glue to the rescue - again. Make sure the hammer head is parallel to the ground so that it strikes the sides of the wine glasses cleanly, and run a bead of hot melt glue down the double horn on the servo. Set the end of the shaft in the glue so that it is attached to as much of the servo horn surface as possible.
I used the Modern Devices Bare Board Arduino clone which has convenient pins for connecting servos and runs nicely off of USB power, as well as the Software Servo Library servo driver.
// Make music with servos and wine glases // by John Plocher // Application: 4 servos with wooden hammers #include <Servo.h> int servo1Pin = 14; // pwm outputs for servos int servo2Pin = 15; int servo3Pin = 16; int servo4Pin = 17; int home=93; // Somewhere between 85 and 95 // with a servo range of 0..180 Servo servo1, servo2, servo3, servo4; void setup() { servo1.attach(servo1Pin); servo1.write(home); servo2.attach(servo2Pin); servo2.write(home); servo3.attach(servo3Pin); servo3.write(home); servo4.attach(servo4Pin); servo4.write(home); Servo::refresh(); } void loop() { // Play thru once each reset... play("abcdefg gfedcba "); play("e2d2c2d2e2e2e4d2d2d4e2e2e6e2d2c2d2e2e2e4e2d2d2e2d2cDDD"); play("ededcddcbcaeaeaeabcbcddcdede "); play("fdfdcddcgbcafafafgabcbcddcdfdfg"); while (1) { d20(); } // stop after one loop to keep // from annoying neighbors :-) } void play(char *s) { int l = strlen(s); for (int x = 0; x < l; x++) { switch (s[x]) { case 'a': a(); break; case 'b': b(); break; case 'c': c(); break; case 'd': d(); break; case 'e': e(); break; case 'f': f(); break; case 'g': g(); break; case 'D': // Delays (cumulative) case ' ': case '8': d20(); case '6': d20(); case '4': d20(); case '2': d20(); break; } } } // Play a note. // // Tweak delays to make for crisp ringing void note(Servo *s, int n) { s->write(home + n); Servo::refresh(); d20(); d20(); s->write(home); Servo::refresh(); d20(); d20(); d20(); d20(); } // The "notes" // Tweak the 2nd parameter to give strong, authoritative // strikes without actually hitting the glass too hard. // This value is influenced by the physical behavior of // the materials you used to build the hammers. void a() { note(&servo1, -6); } void b() { note(&servo1, +6); } void c() { note(&servo2, -6); } void d() { note(&servo2, +5); } void e() { note(&servo3, -12); } void f() { note(&servo3, +12); } void g() { note(&servo4, -12); } // The servo library requires that the refresh // method be called at least every 20mS, so // we need to build up our own longer delays. void d20() { delay(20); Servo::refresh(); } void d100() { for (int x = 0; x < 5; x++) { d20(); }} void d1000() { for (int x = 0; x < 10; x++) { d100(); }}