Dream Walk
An interactive snowflake installation responds to movement and gesture with color and light.
These suspended snowflakes use sensors and a microcontroller to analyse your motion in real time to render animated pixels of light.
Event

Vanier Snow Party, February 15, 7pm at 84-88 Beechwood Avenue. This neighborhood is one of my old haunts.
Walk up to a station to make your own snowflakes from paper to take with you. Ever hear of a throwie? It’s a little light. Make a snowflake with a lanyard, add the throwie and presto! A Snowie!
Artist
I’m a multidisciplinary artist with a background in software and physics. I explore interactive and emersive art and systems highlighting features of our own social network and being.
Credits: Maryam AmiriNezhad is a computer scientist. Shirley Lee, snow flake builder. Andrew B snow flake installer. Max from FrozenElectronics. Eric Field, wiring technician. Sarah Simpkin is a librarian and advocate of the Maker Movement.
Snowflake Party
Fabrication
Here are shots from the very un-glamorous electronics fabrication.
A few shots from the snow flake production.
Related
Instructables snowflakes.
This project uses an Arduino microcontroller.
The light system is Red, Green and Blue LEDs. It uses “additive color mixing“. This project uses type 2811 LED controllers.
Code
/*
Dream Walk
Darcy@inventorArtist.com
Quick and dirty code for one-time run.
WS2811 LED Strip
================
// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel. Avoid connecting
// on a live circuit...if you must, connect GND first.
IR Receiver
SR02 Range Finder
16752735
16720095
16736415
16769055
16748655
16716015
16732335
16764975
16756815
16724175
16740495
16773135
16754775
16722135
16738455
16771095
16750695
16718055
16734375
16767015
16746615
16713975
16730295
16762935
repeat 4294967295
*/
#include <Adafruit_NeoPixel.h> //Light Strip
#include <IRremote.h> // Infra Red Receiver
#define stripPIN 6
#include <Wire.h> // Range Finder
#define Debug
// Discrete Hartley transform
//#define LOG_OUT 1 // use the log output function
//#define FHT_N 256 // set to 256 point fht
//#include <FHT.h>
int motionN = 20;
int motion[21];
unsigned long motionLast;
unsigned int motionDelay= 400;
int motionIndex=0;
unsigned long calcLast;
unsigned int calcDelay = 1200;
unsigned int statAverage;
unsigned int statTotal;
unsigned int statSlope;
unsigned int statMax;
unsigned int statMin;
//person
unsigned int personFlag;
unsigned int person;
unsigned int lastHuman;
// initialize light strip
Adafruit_NeoPixel strip = Adafruit_NeoPixel(40, stripPIN, NEO_RGB + NEO_KHZ800);
//Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, stripPIN, NEO_GRB + NEO_KHZ800);
// range finder
#define SDM_IO_TIMEOUT 10000
int TrigPin = 13;
int EchoPin = 12;
unsigned long ultrasoundDuration;
int timeout;
unsigned long tStartPing = 0;
int d = 0; //range finder distance
int dLast = 0;
int dDir = 1;
//inbound ir
IRrecv irrecv(8);
decode_results results;
//frames
unsigned long lastFrame;
unsigned int FrameDelay = 900;
unsigned int frame = 0;
unsigned int frameN = 39;
//ultrasonic range
unsigned int rangeLow = 3;
unsigned int rangeHi = 200;
unsigned int rangeError = 999;
//strip size
unsigned int stripN = 38;
float stripR[39];
float stripG[39];
float stripB[39];
//heart
unsigned int heart = 0;
unsigned int heartDir = 1;
unsigned int metabolism=500;
//sprite
float sprite = 0;
float spriteDir = .1;
//random event
unsigned long randomLast;
unsigned int randomDelay = 10000;
unsigned int effectRandom=0;
void setup() {
//debug
Serial.begin(9600); //115200
// Discrete Hartley transform
//TIMSK0 = 0; // turn off timer0 for lower jitter
//ADCSRA = 0xe5; // set the adc to free running mode
//ADMUX = 0x40; // use adc0
//DIDR0 = 0x01; // turn off the digital input for adc0
//light strip
strip.begin();
strip.show(); // Initialize all pixels to 'off'
for (int i = 0;i<=stripN;i++) {
stripR[i] = 0;
stripG[i] = 0;
stripB[i] = 0;
}
rainbow(1);
// for (int i = 0;i<=stripN;i++){
// strip.setPixelColor(i,strip.Color(30 ,0,100));
// strip.setPixelColor(max(i-1, 0), strip.Color(0,0,0));
// strip.show();
// delay(200);
// }
//theaterChaseRainbow(1);
for (int i = 0;i<=stripN;i++) {
strip.setPixelColor(i,strip.Color(0,0,0));
}strip.show();
//range finder
Wire.begin(); //setup i2c
//pinMode(TrigPin, OUTPUT); //pin is output
//pinMode(EchoPin, INPUT); // pin is now input
// Infrarad Reciept
pinMode(7, INPUT);
digitalWrite(7, HIGH);
irrecv.enableIRIn(); // Start receiver
//randomSeed(12);
}
void loop() {
// range finder
if (millis() >= motionLast + motionDelay) {
motionLast = millis();
d = takereading(); //d = read_sdm_io_range();
//Serial.print(d); Serial.println("");
dLast = d;
motion[motionIndex] = d;
if (d>dLast){
dDir = 1;
}else{
dDir = -1;
}
//fht_input[motionIndex] = d; // Discrete Hartley transform
motionIndex++;if (motionIndex >=motionN) {motionIndex = 0;}
//Serial.print("m=");Serial.println(motionIndex);
// last human
if (d < rangeHi) {
lastHuman = millis();
}
//for (int i=0;i<=motionN;i++){Serial.print(motion[i]);Serial.print(" ");} Serial.println("");
}
// Discrete Hartley transform
if (millis() >= calcLast + calcDelay) {
calcLast = millis();
statTotal=0;
statMax=0;
statMin=99999;
for (int i=0;i<=motionN;i++){
statTotal = statTotal + motion[i];
statMax = max(motion[i], statMax);
statMin = min(motion[i], statMin);
}
statAverage = statTotal/(motionN-1);
Serial.println("");
Serial.print("total "); Serial.print(statTotal);
Serial.print(" average "); Serial.print(statAverage);
Serial.print(" max "); Serial.print(statMax);
Serial.print(" min "); Serial.println(statMin);
for (int i=0;i<=motionN;i++){Serial.print(motion[i]);Serial.print(" ");} Serial.println("");
}
// Discrete Hartley transform
//while(1) { // reduces jitter
// cli(); // UDRE interrupt slows this way down on arduino1.0
// for (int i = 0 ; i < FHT_N ; i++) { // save 256 samples
// while(!(ADCSRA & 0x10)); // wait for adc to be ready
// ADCSRA = 0xf5; // restart adc
// byte m = ADCL; // fetch adc data
// byte j = ADCH;
// int k = (j << 8) | m; // form into an int
// k -= 0x0200; // form into a signed int
// k <<= 6; // form into a 16b signed int
// fht_input[i] = k; // put real data into bins
// }
// fht_window(); // window the data for better frequency response
// fht_reorder(); // reorder the data before doing the fht
// fht_run(); // process the data in the fht
// fht_mag_log(); // take the output of the fht
// sei();
// Serial.write(255); // send a start byte
// Serial.write(fht_log_out, FHT_N/2); // send out the data
// }
//random events
if (millis() >= randomLast + randomDelay) {
randomLast = millis();
randomDelay = random(20000);
effectRandom++; if (effectRandom>=6){effectRandom=0;}
Serial.print("random/////////////////////// " );Serial.println(effectRandom);
switch (effectRandom) {
case 0: colorWipe(strip.Color(255, 255, 0), 0); break; //
case 1: colorWipe(strip.Color(0, 255, 255), 0); break; //
case 2: colorWipe(strip.Color(255, 0, 255), 0); break; //
case 3: rainbow(0);break;
case 4: rainbowCycle(0);rainbowCycle(0);rainbowCycle(0);break;
case 5: theaterChaseRainbow(5);break;
//default: {renderNumber(0, 255,255,255, 255,255,255); break;}
}
}
// Look for IR data
unsigned long remoteData;
if (irrecv.decode(&results)) {
remoteData = results.value;
Serial.print("IR in " );Serial.println(remoteData);
irrecv.resume();
//we got data, cook a random function
int effect = random(5);
switch (effect) {
case 0: theaterChaseRainbow(5); break; //R
case 1: colorWipe(strip.Color(0, 255, 0), 10); break; //G
case 2: colorWipe(strip.Color(0, 0, 255), 10); break; //B
case 3: rainbow(0);break;
case 4: rainbowCycle(0);break;
case 5: theaterChaseRainbow(5);break;
//default: {renderNumber(0, 255,255,255, 255,255,255); break;}
}
}
// switch (remoteData) {
// case 16748655: colorWipe(strip.Color(255, 0, 0), 10); break; //R
// case 16716015: colorWipe(strip.Color(0, 255, 0), 10); break; //G
// case 16732335: colorWipe(strip.Color(0, 0, 255), 10); break; //B
//
// //default: {renderNumber(0, 255,255,255, 255,255,255); break;}
// }
// advance frames
if (millis() >= lastFrame + FrameDelay ) {//advance frame
lastFrame = millis(); frame++; //probably not needed by this vignette
if (frame > frameN) {
frame = 0;
}
//Serial.print("frame ");Serial.println(frame);
//react to range finder
for (int i = 0;i<=stripN;i++){strip.setPixelColor(i,strip.Color(0,0,0));} //clear strip
Serial.print(" statAverage ");Serial.print(statAverage);
Serial.print(" rangeLow ");Serial.print(rangeLow);
Serial.print(" rangeHi ");Serial.print(rangeHi);
Serial.print(" stripN ");Serial.print(stripN);
person = min(map(d, rangeLow, rangeHi, 0, stripN), stripN);
//person = map(statAverage, 0, rangeHi, 0, stripN);
Serial.print("d/person ");Serial.print(d);Serial.print(" ");Serial.println(person);
//Boost the pixel that the person maps to
stripR[person] = stripR[person] + 10; if (stripR[person] >=255) {stripR[person] = 255;}
stripG[person] = stripG[person] + 0 ; if (stripG[person] >=255) {stripG[person] = 255;}
stripB[person] = stripB[person] + 10; if (stripB[person] >=255) {stripB[person] = 255;}
for (int j=1;j<=5;j++){
int brightness; brightness = map(j, 0, 5, 20, 0);
stripR[person+j] = stripR[person+j] + brightness; if (stripR[person+j] >=255) {stripR[person+j] = 0;}
stripG[person+j] = stripG[person+j] + brightness; if (stripG[person+j] >=255) {stripG[person+j] = 0;}
stripB[person+j] = stripB[person+j] + brightness; if (stripB[person+j] >=255) {stripB[person+j] = 0;}
stripR[person-j] = stripR[person-j] + brightness; if (stripR[person-j] >=255) {stripR[person-j] = 0;}
stripG[person-j] = stripG[person-j] + brightness; if (stripG[person-j] >=255) {stripG[person-j] = 0;}
stripB[person-j] = stripB[person-j] + brightness; if (stripB[person-j] >=255) {stripB[person-j] = 0;}
}
//fade
//stripR[person] = stripR[person] - 5; if (stripR[person] <=0) {stripR[person] = 0;}
//stripG[person]=stripG[person] -5; if (stripG[person] <=0) {stripG[person] = 0;}
//stripB[person]=stripG[person] - 5; if (stripG[person] <=0) {stripG[person] = 0;}
//strip.setPixelColor(person,strip.Color(random(255),random(255),random(255)));
//if (d >= hi) {
// heart=heart + heartDir;
// if(heart>=stripN || heart<=0) {heartDir = -1 * heartDir;}
// for (int i = 0;i<=stripN;i++){strip.setPixelColor(i,strip.Color(0,0,0));}
// strip.setPixelColor(heart,strip.Color(0,0,random(8)));
// //strip.show();
//}
}
//fade
for (int i = 0;i<=stripN;i++){
stripR[i] = stripR[i] - .05; if (stripR[i] <=0) {stripR[i] = 0;}
stripG[i] = stripG[i] - .05; if (stripG[i] <=0) {stripG[i] = 0;}
stripB[i] = stripB[i] - .05; if (stripB[i] <=0) {stripB[i] = 0;}
//strip.setPixelColor(i,strip.Color(stripR[i] ,stripG[i],stripB[i]));
}
float val = (exp(sin(millis()/(float)metabolism*PI)) - 0.36787944)*13;
//setColor (6,1023, val);
stripR[0] = val; stripG[0]= 0; stripB[0] =0;
for (int i = 0;i<=stripN;i++){
strip.setPixelColor(i,strip.Color(stripR[i] ,stripG[i],stripB[i]));
}
strip.show();
//sprite
// sprite = sprite + spriteDir;
// //Serial.print(sprite);
// //Serial.println("");
// if (sprite> stripN) { sprite = 0;}
// if (sprite<0) {sprite = stripN;}
// strip.setPixelColor(sprite,strip.Color(200,0,200));
// for (int j=1;j<=10;j++){
// int brightness;
// brightness = map(j, 1, 10, 10, 1);
// strip.setPixelColor(min(sprite+j, stripN),strip.Color(brightness,0,brightness));
// strip.setPixelColor(max(sprite-j, 0),strip.Color(brightness,0,brightness));
// }
//strip.setPixelColor(min(sprite+1, stripN),strip.Color(20,0,20));
//strip.setPixelColor(max(sprite-1, 0),strip.Color(20,0,20));
// for (int i = 0;i<=stripN;i++){
// stripR[i] = stripR[i] - .05; if (stripR[i] <=0) {stripR[i] = 0;}
// stripG[i] = stripG[i] - .05; if (stripG[i] <=0) {stripG[i] = 0;}
// stripB[i] = stripB[i] - .05; if (stripB[i] <=0) {stripB[i] = 0;}
// strip.setPixelColor(i,strip.Color(stripR[i] ,stripG[i],stripB[i]));
// }
//colorWipe(strip.Color(255, 0, 0), 10); // Green
// strip.setPixelColor(5,strip.Color(255,255,255));
//strip.show();
//
// theaterChase(strip.Color(127, 127, 127), 50); // White
// theaterChase(strip.Color(127, 0, 0), 50); // Red
// theaterChase(strip.Color( 0, 0, 127), 50); // Blue
//
// rainbow(20);
// delay(3000);
//rainbowCycle(0);
// theaterChaseRainbow(5);
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
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);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (int i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
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);
}
}
// range finder
// Make sure you add the above line to your code -- #include <wire.h>
//
//The line in the setup() routine needs to be added to the setup() in your program. So add Wire.begin(); into your setup();
// Then copy and paste the whole takereading() function into your code\
// to get a reading, simply take the variable you want the reading to be assigned to, and write something like
// sensorReading = takereading();
// this will make sensorReading equal to whatever the sensor returns. Then this variable can be used
// in any way you want to calculate the light patterns, etc.
// #include <Wire.h>
int takereading() {
//Serial.println("readin1");
int reading = 0;
Wire.beginTransmission(112);//Serial.println("readin2");
Wire.send(0x00);//Serial.println("readin3");
Wire.send(0x50);//Serial.println("readin4");
Wire.endTransmission();//Serial.println("readin5");
//Serial.println("readin6");
delay(70);
Wire.beginTransmission(112);
Wire.send(0x02);
Wire.endTransmission();
Wire.requestFrom(112, 2);
if (2 <= Wire.available())
{
reading = Wire.receive();
reading = reading << 8;
reading |= Wire.receive();
}
return reading;
}
//legacy range finder
float read_sdm_io_range()
{
unsigned char pin = 0;
unsigned int time_flag = 0;
digitalWrite(TrigPin, HIGH);
delayMicroseconds(2);
digitalWrite(TrigPin, LOW);
delayMicroseconds(50);
digitalWrite(TrigPin, HIGH);
tStartPing = micros();
timeout = 0;
pin = digitalRead(EchoPin);
while(pin)
{
pin = digitalRead(EchoPin);
time_flag++;
if(time_flag > SDM_IO_TIMEOUT)
{
timeout = 1;
break;
}
}
ultrasoundDuration = micros() - tStartPing;
//Serial.print(ultrasoundDuration);
//Serial.print(" us, ");
//Serial.print(ultrasoundDuration*0.017, DEC); // result in cm
//Serial.print(" cm");
//Serial.println();
if (timeout)
return 999;
else
return ultrasoundDuration * 0.017; // result in cm
}




























































Check out the XV-11 Lidar scanner. Its from a robot vacuum cleaner and is spectacular. It gives you a full 360 scan (360 readings) 5 times a second and works out to 6 meters. The data comes out in a 115200 baud serial stream at 3.3v and is easy to interpret. Draws around 30mA in use.
I’ve got 2 now. The latest one was $70 on eBay which is a steal compared to any other Lidar. That type of sensor typically runs into the thousands for low end versions.
http://www.theamphour.com/179-greg-charvat-returns-with-a-book-laboratory-literature-laureate/
TSOP4838 IR receiver
http://wiki.openmusiclabs.com/wiki/FHTExample
http://en.wikipedia.org/wiki/Discrete_Hartley_transform
http://dangerousprototypes.com/docs/Bus_Pirate_I/O_Pin_Descriptions
http://forums.adafruit.com/viewtopic.php?f=22&t=46064
IR an Pixel: http://learn.adafruit.com/wearable-flora-powered-tardis-costume-dogs/code
http://www.robotshop.com/ca/en/ultrasonic-range-finder-15cm-6000cm.html
http://www.hobbytronics.co.uk/srf02-ultrasonic-rangefinder
http://www.vanier-association.com/index.php/en/component/k2/item/247-apres-snow
http://apt613.ca/events/apres-snow-party/
Current bill of materials in progress (o = complete):
o-LEDs
o-Controller
o-Wire
o-Heat shrink
o-Ultrasonic Range Finder
o-Infrared Receiver
o-Remote Control
o-Paper
o-headers for range finder (female)
o-lanyard material
-extension cord
o-hanging wire
-hooks
-controller cabinet materials (hardboard, acrylic, fasteners)
Work in progress:
o-designing snowflakes
o-cutting out parts
o-Assembly of flakes
o-soldering wire harness extensions
o-wiring controller box
o-wiring sensor extensions
Snowie
o-100 batteries
o-100 LEDs
o-lanyard
Other
-extension cord
o-solder, iron
o-extra wire
o-meter
o-fishing line
-hooks
o-snippers
o-drywall screws /driver
o-tie wraps
o-tape
fft
Way awesome!
🙂
wire
http://thekavanaugh.ca/
http://paperpapier.com/