For my final project, I have created the…. Piano Lamp. (I’m still working on the title.) Essentially, it’s an odd little device connected to the internet, specifically to a Firebase server, and it’s listening for user music input from a website. I’ve utilized a Javascript piano I found off of the internet and modified it to give me the data of what keys a user is pressing, and once a user chooses to submit their sound, it gets uploaded to the Firebase server. From there, the Piano Lamp catches the updated database and automatically plays the new song. If a user wants to hear it again, they can press the button on top of the lamp. But once a new song is uploaded, the old one goes away. It’s almost like Snapchat… but also absolutely nothing like it!
Supplies: Breadboard (preferably of the tiny, tiny variety), buzzer, button, LED Neopixel light strip (30 pixels), Huzzah ESP32 Board, and many wires. For the casing, simply make due with an amalgam of random things you buy at the hardware store.
Although my project is a little bit different than what I had proposed, I am much happier with what this one does. I think it’s offbeat and whimsical, and something I think people would find both really fun and funny. I’m imagining having this plugged in at night as my nightlight, and one of my friends inputting a song at 3 in the morning to wake me up. The buzzer itself is pretty small but certainly mighty when it comes to volume.
There were 3 stages to my project: building the hardware and Arduino code, building the casing, and building the website for user-input. The first stage was probably one of the hardest things I’ve ever done, and it’s all because of Firebase, C++, and how they both interact with arrays/JSON. There is simply no easy way for arrays to be accessed and modified in Firebase functions for the ESP32, and it took countless hours of meddling until I gave up on using arrays or JSON as my data storage method. Python would have made everything much easier, but I wanted to learn more about C++ and gain experience in the language. I actually ended up creating strings instead, of each note/frequency separated by a space. The hardware itself was fairly simple, as its essentially a buzzer, a button, and an LED strip connected on a circuit. Originally, I was going to use four separate stiff LED strips, each positioned on the corner of the lamp, and I had purchased soldering equipment to connect them to each other and the breadboard. Little did I know, I purchased a soldering iron that goes up to 750 degrees, and solder that has a melting point of 1070 degrees. Safe to say, it did not work out.
Once I finished with the hardware, it was time for my favorite part: making it look like a lamp. I went to the hardware store with no concept of what I wanted it to look like, but I roamed the aisles searching for things that caught my eye. Eventually, I purchased two rubber cups (that go under furniture legs), shelf liner, and yawn. When I got back, I cut holes into the rubber cups, allowing the breadboard to fit into one, and the buzzer and button to fit into the other, which then created the top and bottom of my lamp. With an excess of LED strip, I at first wasn’t sure how to incorporate it, but I eventually landed on getting a toilet paper roll and wrapping the strip around it. I then attached the roll to the top of the lamp, and then cut pieces of cardboard to make the stands, connecting the top and bottom. I cut the shelf liner and wrapped it around the lamp, creating a semi-transparent veil to soften the light. As a small, final touch, I wrapped some yarn around the bottom of the lamp because I felt like it’d be wasteful if I didn’t use it.
For the website, I scoured the internet for a decent Javascript piano, and I ended up finding a great one. After attaching it to my website, I started editing it to make it better fit my project. I took away a few keys for simplicity, leaving only one octave, and I made a few small aesthetic changes. I coded a new input field, creating a function that appends any pressed key to it. There, a user can choose to submit the input to the database. Here, I have a few functions I wrote that take the input and convert it into a format that the lamp (and specifically buzzer) can read, since it plays frequencies.
Piano SiteOverall, I’m so happy with this project. There are, of course, a few limitations to this project. Currently, there is no way to differentiate duration of notes, so when the buzzer plays the tune, all of the notes last the same amount of time. Additionally, I really wanted the lamp to shift through the rainbow colors as its playing the song, but I couldn’t figure out how to do both at the same time. Nevertheless, I think this project is a huge personal achievement for me, as I could never have imagined making something like this even a few months ago.
#include <Adafruit_NeoPixel.h> #include <WiFi.h> // esp32 library #include <FirebaseESP32.h> // firebase library #include <Tone32.h> #include <stdio.h> #include <string.h> #include <iostream> using namespace std; #define FIREBASE_HOST "https://physci-70.firebaseio.com" // the project name address from firebase id #define FIREBASE_AUTH "firebaseauth" // the secret key generated from firebase #define WIFI_SSID "wifiname" // input your home or public wifi name #define WIFI_PASSWORD "wifipass" // password of wifi ssid // Music setup String music_str_arr = "261 293 329 349 391 440 493 587 659 "; // Buzzer setup int freq = 2000; int channel = 0; int resolution = 8; // Button setup int buttonPin = A0; int buttonState = 0; // variable for reading the pushbutton status // LED Strip setup #define LIGHTPIN 14 #define NUMPIXELS 29 Adafruit_NeoPixel strip(NUMPIXELS, LIGHTPIN, NEO_GRB + NEO_KHZ800); //Define FirebaseESP32 data object FirebaseData firebaseData; void setup() { Serial.begin(9600); delay(1000); // Buzzer setup ledcSetup(channel, freq, resolution); ledcAttachPin(12, channel); // Button setup pinMode(buttonPin, INPUT); // Light setup strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) strip.show(); // Turn OFF all pixels ASAP strip.setBrightness(10); // Set BRIGHTNESS low to reduce draw (max = 255) strip.fill(strip.Color(255, 255, 255), 0, 29); strip.show(); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // try to connect with wifi Serial.print("Connecting to "); Serial.print(WIFI_SSID); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } Serial.println(); Serial.print("Connected to "); Serial.println(WIFI_SSID); Serial.print("IP Address is : "); Serial.println(WiFi.localIP()); // print local IP address Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH); // connect to firebase Firebase.reconnectWiFi(true); Firebase.set(firebaseData, "/MUSIC_ARR", music_str_arr); } void loop() { while (digitalRead(buttonPin) == LOW) { // Find new music array Firebase.get(firebaseData, "/MUSIC_ARR"); String new_music_str_arr = firebaseData.stringData(); if (new_music_str_arr != music_str_arr) { strip.fill(strip.Color(255, 0, 255), 0, 29); strip.show(); playMusic(new_music_str_arr); rainbow(20); music_str_arr = new_music_str_arr; }; } strip.fill(strip.Color(255, 0, 255), 0, 29); strip.show(); playMusic(music_str_arr); rainbow(20); }; void playMusic(String music_str_array) { for (int i = 0; i < music_str_array.length() / 4; i++) { String note = getValue(music_str_array, ' ', i); playNote(note.toInt()); } } void playNote(int note) { ledcWrite(channel, 125); ledcWriteTone(channel, note); delay(400); ledcWrite(channel, 0); delay(50); } String getValue(String data, char separator, int index) { int found = 0; int strIndex[] = { 0, -1 }; int maxIndex = data.length(); for (int i = 0; i <= maxIndex && found <= index; i++) { if (data.charAt(i) == separator || i == maxIndex) { found++; strIndex[0] = strIndex[1] + 1; strIndex[1] = (i == maxIndex) ? i+1 : i; } } return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; } // END void rainbow(uint8_t wait) { uint16_t i, j; for(j=0; j<256; j++) { for(i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, Wheel((i+j) & 255)); } strip.show(); delay(wait); } strip.fill(strip.Color(255, 255, 255), 0, 29); strip.show(); } uint32_t Wheel(byte WheelPos) { if(WheelPos < 85) { return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); } else if(WheelPos < 170) { WheelPos -= 85; return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); } else { WheelPos -= 170; return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); } }