That is what my final project looks like at the December showcase.
Overview
The project is called Lights and Constellation, a very straightforward representation of what the thing does: Mapping a png image of constellations on the computer screen to the physical board of lights. The whole thing consists of four units: 1) Computer screen, key and mouse, 2) Physically acrylic board and LEDs, 3) the speaker, and 4) Scott’s powerful battery.
Each part looks like this respectively:
In combination, they work like this:
A short video at the show: All lights on at the showcase
A lengthy one on vimeo:
Here’s my code.
Arduino:
int ledPin[] = {3, 5, 6, 9, 10, 11}; byte brightness[6]; int blinkInterval[6]; void setup() { pinMode(ledPin[0], OUTPUT); pinMode(ledPin[1], OUTPUT); pinMode(ledPin[2], OUTPUT); pinMode(ledPin[3], OUTPUT); pinMode(ledPin[4], OUTPUT); pinMode(ledPin[5], OUTPUT); Serial.begin(9600); } void loop() { if (Serial.available() == 12) { brightness[0] = Serial.read(); blinkInterval[0] = Serial.read(); brightness[1] = Serial.read(); blinkInterval[1] = Serial.read(); brightness[2] = Serial.read(); blinkInterval[2] = Serial.read(); brightness[3] = Serial.read(); blinkInterval[3] = Serial.read(); brightness[4] = Serial.read(); blinkInterval[4] = Serial.read(); brightness[5] = Serial.read(); blinkInterval[5] = Serial.read(); } for (int i = 0; i <= 5; i++) { analogWrite(ledPin[i], brightness[i]); delay(blinkInterval[i]); analogWrite(ledPin[i], 0); delay(blinkInterval[i]); analogWrite(ledPin[i], brightness[i]); } }
Processing:
import processing.serial.*; import ddf.minim.*; Serial myPort; Minim minim; AudioPlayer player; AudioPlayer blink; Slider blSlider, brSlider; PImage consTa; PFont AmericanType; int whatConst; float[] brightness = new float[6]; float [] blinkRate = new float[6]; void setup() { fullScreen(); //size(1200, 1200); //myPort = new Serial(this, "/dev/cu.RNBT-AC28-RNI-SPP", 38400); myPort = new Serial(this, "/dev/cu.usbserial-DA01LJIV", 9600); minim = new Minim(this); player = minim.loadFile("SpaceSound.mp3"); blink = minim.loadFile("Ding.mp3"); blSlider = new Slider(1200, 180, 200, 0, 1000); brSlider = new Slider(1200, 280, 200, 0, 255); } void draw() { background(100); consTa = loadImage("constellation1.png"); imageMode(CENTER); image(consTa, width/2, height/2); AmericanType = loadFont("AmericanTypewriter-30.vlw"); textFont(AmericanType); text("Blinky", 1200, 150); text("Brightness", 1200, 250); textSize(20); fill(255); text("Click on the underlined constellation(s) first!", 30, 50); text("Click after you drag!", 1200, 50); strokeWeight(3); // draw the lines that indicate the lighted constellations stroke(255); line(150, 55, 255, 55); line(815, 231, 858, 231); line(693, 250, 725, 250); line(340, 355, 362, 355); line(820, 553, 867, 553); line(943, 765, 990, 765); line(596, 575, 657, 576); line(696, 560, 728, 560); line(492, 745, 517, 745); line(570, 785, 606, 785); brSlider.render(); blSlider.render(); brSlider.update(); blSlider.update(); brSlider.reStrain(); blSlider.reStrain(); if (whatConst == 6) { brightness[0] = brSlider.getVal(); blinkRate[0] = blSlider.getVal(); println("current br:" + brightness[0]); println("current bl:" + blinkRate[0]); } if (whatConst == 5) { brightness[1] = brSlider.getVal(); blinkRate[1] = blSlider.getVal(); println("current br:" + brightness[1]); println("current bl:" + blinkRate[1]); } if (whatConst == 4) { brightness[2] = brSlider.getVal(); blinkRate[2] = blSlider.getVal(); println("current br:" + brightness[2]); println("current bl:" + blinkRate[2]); } if (whatConst == 3) { brightness[3] = brSlider.getVal(); blinkRate[3] = blSlider.getVal(); println("current br:" + brightness[3]); println("current bl:" + blinkRate[3]); } if (whatConst == 2) { brightness[4] = brSlider.getVal(); blinkRate[4] = blSlider.getVal(); println("current br:" + brightness[4]); println("current bl:" + blinkRate[4]); } if (whatConst == 1) { brightness[5] = brSlider.getVal(); blinkRate[5] = blSlider.getVal(); println("current br:" + brightness[5]); println("current bl:" + blinkRate[5]); } } void mousePressed() { if (((mouseX > 680) && (mouseX < 765) && (mouseY > 200) && (mouseY < 385)) || ((mouseX > 695) && (mouseX < 885) && (mouseY > 300) && (mouseY < 420))) { whatConst = 6; blink.play(); } if ((mouseX > 805) && (mouseX < 880) && (mouseY > 163) && (mouseY < 297)) { whatConst = 5; blink.rewind(); blink.play(); } if ((mouseX > 295) && (mouseX < 433) && (mouseY > 280) && (mouseY < 390)) { whatConst = 4; blink.rewind(); blink.play(); } if (((mouseX > 742) && (mouseX < 925) && (mouseY > 458) && (mouseY < 685)) || ((mouseX > 868) && (mouseX < 1020) && (mouseY > 670) && (mouseY < 805))) { whatConst = 3; blink.rewind(); blink.play(); } if ((mouseX > 575) && (mouseX < 760) && (mouseY > 510) && (mouseY < 620)) { whatConst = 2; blink.rewind(); blink.play(); } if ((mouseX > 433) && (mouseX < 687) && (mouseY > 685) && (mouseY < 870)) { whatConst = 1; blink.rewind(); blink.play(); } myPort.write(int(brightness[0])); myPort.write(int(blinkRate[0])); myPort.write(int(brightness[1])); myPort.write(int(blinkRate[1])); myPort.write(int(brightness[2])); myPort.write(int(blinkRate[2])); myPort.write(int(brightness[3])); myPort.write(int(blinkRate[3])); myPort.write(int(brightness[4])); myPort.write(int(blinkRate[4])); myPort.write(int(brightness[5])); myPort.write(int(blinkRate[5])); } void keyPressed() { if ( player.isPlaying()) { player.pause(); } else if ( player.position() == player.length() ) { player.rewind(); player.play(); } else { player.play(); } } //The Slider Class class Slider { float initX, setY; float xPos; float xLength; float dotSize = 12; float highVal, lowVal, curVal; Slider(float iX, float y, float range, float lVal, float hVal) { initX = iX; setY = y; xLength = range; highVal = hVal; lowVal = lVal; } void render() { fill(150); stroke(150); strokeWeight(8); line(initX, setY, initX+xLength, setY); ellipse(xPos, setY, dotSize, dotSize); } void update() { if (mousePressed == true) { if ((mouseX >= initX) && (mouseX <= initX+xLength) && (mouseY >= setY-dotSize) && (mouseY <= setY+dotSize)) { xPos = mouseX; } } } void reStrain() { if (xPos < initX) { xPos = initX; } else if (xPos > initX + xLength) { xPos = initX + xLength; } } float getVal() { curVal = map(xPos, initX, initX+xLength, lowVal, highVal); return curVal; } }
Some thoughts on the project:
Before starting to make this light thing, I had a “grand vision” for it. I wanted it to look magical, and function magical just as the actual constellations. The idea was to use the click of the mouse to light the “stars” up and shut them down. I achieved half the original goal in the end, with the additional function of adjusting the brightness and blink rate of the constellations. The hardest part for me are two things. First, making serial communication work between processing and arduino is unexpectedly hard for me. I tested several ways of making them talk to each other, only the last one works. Second, laser cutting the physical board and attaching LEDs to their proper positions and wiring them all up takes the largest bulk of time and workload for this project. The extremely bright LEDs that Scott recommended work really well —- They shine like stars without any extra shields/caps on them. The extremely toxic-smelled chemical that makes the two halves of the board stick together works well too (and I got to wear the professional-looking mask during the process yah!). I think I learned a bit more about both the software and hardware part during this final project, and it feels great to introduce others at the show to your own work. That makes the time and effort all worth it.
If I have time (and battery) to again finalise the whole board I will hang it on the wall of my dorm room. The heritage of this course!
Again thanks Scott and Diana for helping me with coding and creating the physical board.