It was some time last year, back in good ol' 2013, when my wife bought herself a lava lamp from Amazon (what don't we buy from Amazon these days?). I said, "What's this? No, I know it's a lava lamp. Why do we have it?" She said, "'Cause it's cool." "Whatever, dude." I had a lava lamp as a kid, and I didn't recall anything particularly interesting about it.
But a few days later she'd had it on for a while when I came home from work, and it looked kinda, hm, funny. You know how lava lamps are normally depicted: globs of whatever (wax) bubbling up and down languidly. At that point it looked more like a cave formation, some jagged sculpture randomly built up from the ground from aeons of mineral collection. "Huh," I said, "interesting."
After chancing upon her lava lamp in a similar state a few times, I mused idly aloud that I bet I could make a program to catch the lava lamp "in the act" of looking more interesting than they generally do. That got the wheels in my brain turning. A few months later, LavaMite is born (update: source code, download, and instructions now available). The name is a dubious portmanteau of "lava lamp" and "stalagmite", which should surprise absolutely nobody who knows me.
Round 4. Prior cooldown time: 4:35. Warm-up time: 1:40. pic.twitter.com/RNMmuQXAHu
— LavaMite (@lava_mite) May 27, 2014
I like this project because it is basically my very first endeavor that you could venture to call artistic. Not that there's some grand or deep message I'm trying to convey; I just saw something unusual that people probably overlook, and I wanted to share it. Best of all, I think it has turned out successfully--the images it captures fit my original vision. My technology has realized my intentions in a way that most of my other projects don't (usually from abandonment).
So now you know why it is. Read on to find out, in probably progressively gorier detail, what comprises it and how it works.
Several moving parts
With a goal in mind, I needed to come up with a system to capture the images of my (or my program's) choosing. First of all, I'm going to need a lava lamp. No problem, I'll just steal my wife's aforementioned lamp (hearing angry yells about stealing her stuff right about now). Secondly, I'll need something to turn it on and off. Hmm, some kind of computer-controlled power switch. We'll get to that shortly. Thirdly, I need to snap a photo of the lamp. That will require a webcam (already got one) and software to take pictures. Fourthly, I need software to share the picture online. Let's tackle these one at a time.
Oh, one last restriction. I sometimes use new projects to learn a new progamming language, since I can't really learn a new language well without applying it to a real problem. I used this project as a vehicle to learn D, a language which my friend @neworb couldn't shut up about. I have a bunch of thoughts on this language, which I will probably save for a different post.
The power to power and un-power
I did some research about what I might use to turn the power to the lamp on and off. I've seen those timed power switches before, with the clock-like dial where you set on and off periods, but I want to dynamically control when it goes on and off, so it needs to have a computer interface. I looked up network-controlled power switches. Holy cow, these are expensive. I didn't want to spend upwards of $50 (and generally much more) to make this thing.
Well, I've got the old Arduino Duemilanove sitting in the closet, collecting dust after my microcontrollers course a few years back. Maybe I can put that to good use. I remembered that it has a USB/serial interface, so it would be easy for me to control from a PC. It has several pins that I can turn on and off programmatically. I just need a power switch that activates or deactivates based on something like a 5-volt / 0-volt signal.
Quick preface to the next part: I know practically nothing about circuits. I'm a CS guy, not an electrical engineer, and when you show me a circuit diagram, my eyes glaze over. All the circuits related technology I ended up using in this project--admittedly, a laughably small amount--was nonetheless stretching my skills. BUT! I didn't kill myself with a live wire, so that's good, right?
I did some searches for Arduino-controlled power switches. I found a bunch of circuit diagrams and warnings about "live rails" and killing yourself with amps. I got a bit worried, so stepped back a bit. Maybe I could find something a bit safer? I came upon a nice looking device called the PowerSwitch Tail II. It looked perfect: for $25 I could get a power switch specifically designed to be controlled by Arduino. I nearly bought the thing. I don't know why I didn't, honestly.
Something made me go back and search for "relays" on Amazon, and I turned up this little SainSmart 2-channel relay. From looking at the comments and reviews I was able to glean that this $10 gadget might be exactly what I was looking for. Even better, it's a fairly popular part, so I found a wealth of information on YouTube about it--great tutorials and so on. This is what I ended up buying, and I'll say more about the actual exercise of putting the solution together in a bit.
My very own personal photographer
I already had webcams lying around, so I just grabbed any old one, in this case a Microsoft LifeCam VX-5000, I think. That's the easy part. The harder part was figuring out how to interface with the Windows camera APIs to take a picture using it. Heck, I don't even know which API to use for this. Apparently in the olden days you would use WIA, but it has since been deprecated in favor of MediaFoundation.
I found this very good CaptureEngine sample that does more than I needed, such as record videos from the webcam to file (cool!). To be honest, I didn't really follow most of this code--it's all MF gobbledygook about frames and sinks and media types, but I got enough to figure out how to snap a single picture.
It wasn't so simple, though, as it rarely is. Have you ever used a webcam and noticed that the video stream adjusts to the lighting conditions after turning it on for a few seconds? The colors in the picture seem much better after it's stabilized, right? The code I hacked in to take a photo didn't do any of this stabilization at first, so I had to pull in the code to start a preview, leave it on for a few seconds, and only then snap the picture. It wasn't a big deal, but it's worth mentioning.
I implemented this functionality in a C++ executable, separate from the main program, because MediaFoundation is a Windows COM interface, which I could not get to interoperate with D easily. I think it might be possible if I create a ton of wrappers, but that's basically the least appealing thing, so I just took a simpler path. I could have made it as a C++ library and wrapped a simple interface for the main D program, but it just didn't seem worth it. Plus, command line executables are really easy for testing with along the way.
A little birdie to share photos
The final sub-component is something to upload a photo to Twitter, the sharing medium I decided on. Twitter is a good choice, because there are a lot of libraries already out there for integrating with it. In actuality, a huge part of the decision to use Twitter is probably the works of Darius Kazemi, whom I went to highschool with, and who is now an Internet bot-maker phenomenon of some kind. I guarantee that, before seeing his stuff, I did not think of Twitter bots as a medium of expression.
I found a Twitter library for D called graphite. Grabbing this library required me to learn about dub, the package manager for D. There was some learning curve involved, because the documentation for it is kind of spotty. When I got compiler or linker errors with a dub build but not with a regular dmd build (using the D compiler directly), it was more of a pain to figure out the cause.
Actually, I had a lot of problems getting graphite to work. The very first time I tried to load it, I got syntax errors, like something was fundamentally busted in it. I looked at the source and could not for the life of me figure out how it could ever work. I kept stripping out more and more pieces of it until the barest essentials kind of worked.
As soon as I added back HMAC functionality, a crucial part of Twitter authentication, I started getting real crashes, as in postmortem debugger, not just D exceptions. This ended up being a bug in the D runtime, and having to recompile D from scratch was odious enough to put me off of this project for literally months. Battling my tools is one thing that tries my patience more than almost anything else in software development, let me tell you.
When I came back after my couple months away, I reinstalled some stuff, re-downloaded graphite, used the recompiled D toolset and runtime, and everything magically worked. I have no idea what to thank, but I don't care about this aspect enough to question a battle won.
The best part about graphite is it really does take care of Twitter functionality for me. Doing OAuth by hand is kind of a pain, for some of the same reasons as other security-related stuff: by design, it is hard to debug, lest an attacker gain any purchase.
Check out the entirety of my code that interfaces with Twitter. This is kind of awesome.
void tweetTextAndPhoto(string textToTweet, string photoPath)
{
string[string] parms;
parms["status"] = textToTweet;
log(format("Tweeting \"%s\" with image %s", textToTweet, photoPath));
Twitter.statuses.updateWithMedia(g_twitterInfo.accessToken, [photoPath], parms);
}
Programming the microcontroller
It's pretty cool how popular Arduino microcontrollers have become in the last several years, because it means there is a wealth of information out there on how to use them. Even better, the toolset is supported pretty well on Windows (where I do all my development), a sort of rarity among open source projects.
The code is about a hundred lines long, and half of that is comments. The microcontroller reads the serial input line (which is transported over USB) until it receives the string "switch_1" or "switch_0", upon which it will turn on or off the voltage on one of its digital I/O pins. I just happen to connect this pin to the relay, so I can provide or cut wall power to the lamp.
void loop()
{
int foundString = -1;
const char* strings[] = { "switch_1\0", "switch_0\0", NULL };
foundString = waitForString(strings);
switch (foundString)
{
case 0:
digitalWrite(switchingPin, POWER_ON);
break;
case 1:
digitalWrite(switchingPin, POWER_OFF);
break;
}
}
Wiring it up
Probably the hardest part of the project was doing the actual circuit wiring, even if there was an embarrassingly small amount of it needed. Luckily, the parts I chose were popular enough that I could find really good YouTube videos of similar projects, from which I could extrapolate what to do in mine. Constantly lurking in the back of my mind was the threat of shocking myself really pretty badly, but I'll forestall your worry right now: I got through unscathed.
I wanted to see the relay itself in action first, without any live power involved. I ordered a little pack of adorable multicolored wires and set to work dutifully connecting up the pins I cared about to the relay, leaving the actual power cable out of the picture for now. The relay has a handy LED on it that lights up when the circuit is closed, so I could know if I'm successfully controlling it.
Having hooked everything up and loaded my simple program on the microcontroller, I used the serial input window of the Arduino tools and (programmatically) threw the switch! ... and nothing happened. Suddenly, like the weight of a freight train, I was hit with the realization that I was reduced to the same status of most of the people whose computers I help troubleshoot: "I flipped the switch, and it didn't work." "What do you mean, it didn't work?" "I dunno. It didn't turn on." I had not even the vocabulary to express the thing that wasn't working. It was refreshing, eye-opening, and, frankly, pretty annoying.
I didn't realize this at the time, but I think the digital pin I had chosen happened to be reserved for additional serial communications, so it was never going to work. I switched to using pin 13, which is also wired to an LED on the Arduino board. I could see this LED turning on and off correctly, so something was working.
I broke out the multimeter and checked both ends of the cable (painstakingly, with a head-mounted lamp strapped to me, and in a cramped sitting position). Hey, if the voltage is high at this end of the cable, shouldn't it also be high at the other end? I swapped the adorable, apparently cheap cable, and things sprung into action. I heard that click from the relay I was waiting for.
Speaking of click, I didn't know how a relay worked before this, but it's super cool. Did you know there is a physical switch inside? A small piece of metal is pushed away from or pulled towards a metal contact by an electromagnet. How cool is that? (if you ignore the inevitable mechanical failure)
I wasn't about to do my first live test using my beloved lamp. I took an old phone charger and cut the cable. The charger had its own power adapter, which I think reduces the current and voltage on the actual wire, for safety. As instructed by the tutorial video, I spliced the live ends together and put the two parts of the neutral line into the terminals of the relay. With every click of the relay, I could see the phone start and stop charging. Nice!
The test having succeeded, I chose a standard extension cable as the real accomplice for my relay. One nice thing with the extension cable is that the positive and neutral lines are individually shielded in plastic, so I never even exposed the positive wire, further minimizing the chance of shorting.
OK, but what about the actual program?
So far all I've done is describe the individual parts. The actual program, written in D, has to put them together in a meaningful way. I knew what the main loop of the program should be right from the start: turn on the lamp for a while; snap a picture and tweet it; keep the lamp on long enough to start bubbling normally (I don't want it to cool in formation); turn off the lamp for long enough to "reset" it.
Each of these parts has some nuances to them. The biggest question is how long each of these phases should go for. Warm up for too little time, and all you see is a blob on the bottom. Warm up for too much time and you see only a normal, bubbling lava lamp. Leaving the lamp on too long in general (like, 10 hours) is not good for it, I think. Cool down for too little time, and the next time you warm up, it will skip over the "weird stalagmite" phase and straight to bubbling normally.
From observing the lamp out of the corner of my eye for weeks or months before finishing the project, I knew all these things would need adjusting, so I built in features to allow me to tune the timing right away. In addition to the "money shot" that gets uploaded to Twitter, the program keeps a running thread in the background that takes a photo every 1 minute and just keeps it on disk. Later on, I can look at the times chosen in an unsatisfactory round (one in which I don't like the picture that was posted) and see with my eyes the time range in which the interesting structures presented.
Scrolling through the images real fast also makes for a pretty cool time lapse video.
I used a spreadsheet to keep track of the prior cooldown time, warmup time, and visually-inspected "interesting range" of times for each round. From this I was able to extrapolate a range of warm-up durations and cooldown times that would produce interesting formations. The program simply picks a random time to wait within that range, before taking the picture. In case you're curious, if the lamp cools for 4 hours or more, it is usually looking interesting in the 90 - 120 minute range after turning it on again.
Round 9. Prior cooldowntime: 4:22. Warm-up time: 1:38. pic.twitter.com/0IhqymC56e
— LavaMite(@lava_mite) May 29, 2014
That's actually it for the simple stuff. I still need to publish the source code. I also have a lot more to write about, but it's in other weird details, programming decisions, and hurdles I overcame while creating this. I will certainly write about these in further blog posts, but for now, enjoy the weird lava lamp pictures that come from the machinations of LavaMite!
1 comments:
even though you stole my lava lamp after calling it stupid and useless, i'm not mad! everything about this is awesome and the finished product is super cool. now you need to buy me a new lava lamp since you stole mine and tuned your program to it. same colors, please!
Post a Comment