Dream Walk

 

DIY_Snowflake

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

Apres-snow

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
}

 

19 Responses to “Dream Walk

Leave a Reply

Your email address will not be published. Required fields are marked *