Project 2: Art Sabers

As our final project for this class, Sonia and I decided to make a set of light painting tools that we could use for our own personal projects. We were inspired to create a light painting device that felt highly interactive in a way designed to provoke spontaneity and introduce unplanned moments of beauty. Our plan became to create two lightsaber-esque rods that would glow different colors depending on their physical tilt, and would interact with each other in some way via radio communication.

We started by selecting our components and designing our enclosure. It became clear to us that it would work well to use Adafruit NeoPixel strips as lights, and we believed that it would be simplest to use a BBC micro:bit microcontroller to control the device, as they have radio and accelerometer functionality built into them. We decided to place two strands of NeoPixels on the rod within a PVC pipe (to diffuse the light) and mount the microcontroller and power source within an external hilt.

Early sketch:

img_9047.jpg

Schematics:

schematic1schematic2

Rough interaction diagram:

diagramcloserfurther

To create the body of the device, we bought a small wooden dowel and sanded down the top portion of the stick down to create room for the NeoPixels. We placed a PVC pipe around this top half and secured it to the dowel. At the bottom of the pipe, we glued a laser cut box around the main body to hold our other components. One side of this box was cut to hold a switch. Another side of the box was not glued in, to provide easy access of the various components (uploading code to the micro:bit and changing out the batteries.)

We had a very difficult time connecting everything within the hilt. Soldering to the micro:bits was problematic, and our battery connectors were poorly made and would break on being unplugged from the device. However, we were finally able to get everything connected and the device turned on!

dsc0005.jpg

_DSC0012

_DSC0015

_DSC0027

_DSC0025

_DSC0019_DSC0021

_DSC0031

Our next setback came upon implementing the code. We quickly discovered that the apparent simplicity of block-style coding was also very restrictive. It took us a long time to get used to the Microsoft makecode interface, which provided very limited debugging and documentation for more advanced features. We spent a few days trying to move our code onto Arduino and Python micro:bit libraries, but found that those provided even less documentation. The learning curve of this microcontroller was far steeper than expected.

Eventually, we were able to write our entire program in block-style code / Javascript.

/*
* ART SABER (JANKYSTICK) version 1.0
* Date: 5/30/2018
* Authors: Lillie Bahrami && Sonia Szeton
* Purpose: This code is intended for a set of light painting rods,
* controlled by Microsoft micro:bits and illuminated by Adafruit NeoPixels.
* The colors and patterns of the NeoPixels are affected by accelerometer data,
* as well as proximity of the rods to one another as measured by strength of a
* radio signal.
* Created using Microsoft MakeCode as a final project for ATLS Object.
*/
/*
* prevR, prevG, prevB represent the current values (RGB) of the rod's
* "dominant" color.
*/
let prevR = 0
let prevG = 0
let prevB = 0
/*
* targetR, targetG, targetB represent the values which prevR,
* prevG, and prevB must approach (determined by the physical tilt
* of the rod, roll.) Necessary to create smooth transitions between colors as
* the rod moves through space.
*/
let targetR = 0
let targetG = 0
let targetB = 0
let roll = 0
let colorsEqual = true
/*
* otherR, otherG, and otherB represent the dominant values of the
* other rod. As the proximity of the rods increases, this color will integrate
* itself into the display (interweave represents the frequency of this)
* prox roughly represents proximity of the rods to one another as measured by
* absolute value of radio signal strength (42 = strongest, 128 = weakest)
*/
let otherR = 0
let otherG = 0
let otherB = 0
let prox = 0
let interweave = 123
let signal = 0
let transmitVal = 0
/*
* Determines whether color transition is complete by comparing current,
* target RGB values
*/
function colorCompare() {
if (prevR != targetR || (prevG != targetG || prevB != targetB)) {
colorsEqual = false
} else {
colorsEqual = true
}
}
/*
* Receives incoming radio signals from other rod: stores incoming color info,
* signal strength
*/
radio.onDataPacketReceived(({ receivedString: name, receivedNumber: transmitVal, signal }) => {
if (name == "incomingR") {
otherR = transmitVal
basic.showNumber(otherR)
}
if (name == "incomingG") {
otherG = transmitVal
}
if (name == "incomingB") {
otherB = transmitVal
}
prox = Math.abs(signal)
})
/*
* Initialize NeoPixel strands
*/
let strand1: neopixel.Strip = null
let strand2: neopixel.Strip = null
strand1 = neopixel.create(DigitalPin.P0, 123, NeoPixelMode.RGB)
strand2 = neopixel.create(DigitalPin.P1, 123, NeoPixelMode.RGB)
/*
* Initialize radio communication. Power is set low so that significant
* differences in signal strength can be seen at short range.
*/
radio.setGroup(1)
radio.setTransmitPower(1)
/*
* loop
*/
basic.forever(() => {
// send current color to other rod
radio.sendValue("incomingR", prevR)
radio.sendValue("incomingG", prevG)
radio.sendValue("incomingB", prevB)
// get accel data
roll = input.rotation(Rotation.Roll)
// based on angle of roll from -90 to 90 degrees, assign target color
if (roll >= 90 && roll <= 70) {
// red
targetR = 240
targetG = 0
targetB = 0
} else {
if (roll >= 69 && roll <= 50) {
// orange
targetR = 240
targetG = 128
targetB = 0
} else {
if (roll >= 49 && roll <= 30) {
// yello
targetR = 240
targetG = 240
targetB = 0
} else {
if (roll >= 29 && roll <= 10) {
// greeeeen
targetR = 0
targetG = 240
targetB = 0
} else {
if (roll >= 9 && roll <= 10) {
// cyan
targetR = 0
targetG = 240
targetB = 240
} else {
if (roll >= 11 && roll <= 30) {
// blue
targetR = 0
targetG = 0
targetB = 240
} else {
if (roll >= 31 && roll <= 50) {
// magenta
targetR = 240
targetG = 0
targetB = 240
} else {
if (roll >= 51 && roll <= 70) {
// purple
targetR = 160
targetG = 80
targetB = 208
} else {
if (roll >= 71 && roll <= 90) {
// pink
targetR = 224
targetG = 112
targetB = 192
}
}
}
}
}
}
}
}
}
//has significant movement occurred?
arrayCompare()
/*
* if so, need to transition between colors (rapidly adjust current values
* by increments of 16)
*/
while (colorsEqual == false) {
if (prevR != targetR) {
if (prevR < targetR) {
prevR += 16
} else {
prevR += 0 16
}
}
if (prevG != targetG) {
if (prevG < targetG) {
prevG += 16
} else {
prevG += 0 16
}
}
if (prevB != targetB) {
if (prevB < targetB) {
prevB += 16
} else {
prevB += 0 16
}
}
/*
* set frequency at which color of other rod should appear (number of
* NeoPixels)
*/
if (prox > 120) {
interweave = 120
} else {
if (prox > 110) {
interweave = 60
} else {
if (prox > 100) {
interweave = 50
} else {
if (prox > 90) {
interweave = 40
} else {
if (prox > 80) {
interweave = 30
} else {
if (prox > 70) {
interweave = 20
} else {
if (prox > 60) {
interweave = 10
} else {
if (prox > 50) {
interweave = 5
} else {
if (prox > 40) {
interweave = 2
}
}
}
}
}
}
}
}
}
/*
* Set color pattern on rod (front and back strands): dominant color for
* all pixels not at interweave frequency
*/
for (let index = 0; index <= 122; index++) {
if (index % interweave == 0) {
strand1.setPixelColor(index, neopixel.rgb(otherR, otherG, otherB))
} else {
strand1.setPixelColor(index, neopixel.rgb(prevR, prevG, prevB))
}
}
for (let index2 = 0; index2 <= 122; index2++) {
if (index2 % interweave == 0) {
strand2.setPixelColor(index2, neopixel.rgb(otherR, otherG, otherB))
} else {
strand2.setPixelColor(index2, neopixel.rgb(prevR, prevG, prevB))
}
}
// set brightness of strands based on proximity
strand1.setBrightness(255 prox)
strand2.setBrightness(255 prox)
//show color changes
strand1.show()
strand2.show()
arrayCompare()
}
})

It worked!… until it didn’t. Although we were able to write various demos that worked perfectly with color changes based on accelerometer data and with radio communication, we found ourselves unable to combine the two in a way that worked. There was a massive delay somewhere in our code which meant that the color changes and proximity changes were glacially slow. We spent a lot of time trying to locate this problem and fix it (testing whether radio communication was causing delays, attempting to increase multithreading, etc.) but, unfortunately, ran out of time. Our code was completely insufficient for light painting purposes. Hopefully, we’ll come back to it from another angle and make it work in the future.

After our presentation, we went to Chautauqua Park and made some light painting photographs and demonstration videos using only the accelerometer functionality of the sticks. Although it was disappointing to not have the radio implemented, we had so much fun playing with the devices. It was so satisfying and cool to swing them around and watch them change, try to hit certain colors, and see how our final images were beautiful in ways that we could never expected while we were making them. We definitely succeeded in creating an interactive light painting device that introduced more moments of serendipity into the artistic process and was highly enjoyable to use, and we learned so much in the process. For a first iteration, the Art Sabers are pretty fantastic.

DSC_0018DSC_0046DSC_0049DSC_0051DSC_0056

Video demo (sorry for poor quality:)

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s