Slackline for KulturSydhavn

Interactive balance installation

I did a small interactive slackline project for KulturSYDHavn. A festival on the quay of Teglholmen and around the Illutron barge in conjunction with the annual KulturHavn festival on Islands Brygge on the 6, 7 and 8th of August. We created an interactive playground around the barge and together with Illutron I put up the slackline with RGB color feedback on the quay. I owe a great thanks to the Illutron crew who put a lot of energy into this festival, especially Troels Just Christoffersen who helped put the last pieces of the slackline installation up while I was gone in the week preceeding the festival. And I owe a thanks to the sponsors who made it possible, Slackline.dk and Rullegræsset.dk who sponsored a free feet slacklight15 kit and 1 metric ton of roll out grass respectively.

Documentation

The slackline is hooked up to a strain gauge that measures the weight on the slackline. An Op-Amp amplifies the signal for the arduino and two pieces of flexible high intensity RGB led strip are controlled with PWM via a ULN2003 Darlington transistor array.

Schematics

Code

// KulturSYDHavn Illutron Interactive slackline code by Johan Bichel Lindegaard

float h;
int h_int;
int r=0, g=0, b=0, intensity=100;
void h2rgb(float h, int &R, int &G, int &B);

// Select which analog input the strain gauge is connected to.
const int mPin = 0;

int m = 0;
int state = 0; // 0: inactive, 1: active, 3: pending inactive
int deactivationThreshold = 8;
int deactivationTime = 1000;
unsigned long stateTimeStamp;
unsigned long stateTime = 0;

// Select which PWM-capable pins are to be used.
const int redPin = 10;
const int greenPin = 11;
const int bluePin = 9;

long brbgPreviousMillis = 0;
long fadePreviousMillis = 0;
boolean blinkState = 0;
boolean fadeUp = 0;

// range calibration variables.
int lowPoint;
int highPoint;

void setup()          
{
  // start serial port at 9600 bps:
  // Serial.begin(9600);

  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);

  // calibrate
  lowPoint = analogRead(mPin);
  highPoint = lowPoint + 124;
  if(highPoint > 1024) {
    highPoint = 1024;
  }

  // test pins
  rgb(255,0  ,0  ); delay(500);
  rgb(0  ,255,0  ); delay(500);
  rgb(0  ,0  ,255); delay(500);
}

void loop()
{  
    int m = analogRead(mPin);

    // Check calibration, and expand range if needed.
    if(m < lowPoint) {
      lowPoint = m;
    } else if(m > highPoint) {
      highPoint = m;
    }

    // Serial.println(m);

    // keep track of how long the line has been active and inactive
    if(m > lowPoint + deactivationThreshold) {
      if(state == 0) {
        state = 1;
        stateTimeStamp = millis();
      }
      stateTime = millis() - stateTimeStamp;     
    } else if(state == 1){
      state = 3;
      stateTimeStamp = millis();
    } else {
      stateTime = millis() - stateTimeStamp;
      if(stateTime > deactivationTime) {
        state = 0;
      }     
    }

    if(state != 0){

      // Convert input to RGB through hue then output.
      h = ((float)map(m, lowPoint, highPoint, 1024, 0))/1024;
      h_int = (int) 360*h;
      h2rgb(h,r,g,b);

      // if active for a short while enter fancy blink mode then return to normal
      if(stateTime > 15000 && stateTime < 60000) {
        int btime = map(stateTime,15000,60000,1000,50);
        brgb(r,g,b,btime);       
      } else {
        rgb(r,g,b);
      }

    } else {      
      unsigned long currentMillis = millis();
      if(currentMillis - fadePreviousMillis > 100) {
        fadePreviousMillis = currentMillis;

        if(intensity > 99) { fadeUp = false;
        } else if (intensity < 1) {
          fadeUp = true; }

        if(fadeUp) {  ++intensity;
        } else {      --intensity; }

        rgb(255,0,0,intensity);   
      }

   }   
}

void rgb(int r, int g, int b) {
  analogWrite(redPin,r);
  analogWrite(greenPin,g);
  analogWrite(bluePin,b); 
}

void rgb(int r, int g, int b, float intensity) {
  if(intensity > 100) {
    intensity = 100;
  } else if(intensity < 0){
    intensity = 0; 
  }

  rgb(r/100*intensity, g/100*intensity, b/100*intensity);  
}

void brgb(int r, int g, int b, int interval) {
  unsigned long currentMillis = millis();
  if(currentMillis - brbgPreviousMillis > interval) {
    brbgPreviousMillis = currentMillis;
    if (!blinkState) {
      blinkState = 1;
      rgb(r,g,b);
    } else {
      blinkState = 0;
      rgb(0,0,0);
    }
  }
}

void h2rgb(float H, int& R, int& G, int& B) {

  int var_i;
  float S=1, V=1, var_1, var_2, var_3, var_h, var_r, var_g, var_b;

  if ( S == 0 )                       //HSV values = 0 √∑ 1
  {
    R = V * 255;
    G = V * 255;
    B = V * 255;
  }
  else
  {
    var_h = H * 6;
    if ( var_h == 6 ) var_h = 0;      //H must be < 1
    var_i = int( var_h ) ;            //Or ... var_i = floor( var_h )
    var_1 = V * ( 1 - S );
    var_2 = V * ( 1 - S * ( var_h - var_i ) );
    var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) );

    if      ( var_i == 0 ) {
      var_r = V     ;
      var_g = var_3 ;
      var_b = var_1 ;
    }
    else if ( var_i == 1 ) {
      var_r = var_2 ;
      var_g = V     ;
      var_b = var_1 ;
    }
    else if ( var_i == 2 ) {
      var_r = var_1 ;
      var_g = V     ;
      var_b = var_3 ;
    }
    else if ( var_i == 3 ) {
      var_r = var_1 ;
      var_g = var_2 ;
      var_b = V     ;
    }
    else if ( var_i == 4 ) {
      var_r = var_3 ;
      var_g = var_1 ;
      var_b = V     ;
    }
    else                   {
      var_r = V     ;
      var_g = var_1 ;
      var_b = var_2 ;
    }

    R = (1-var_r) * 255;                  //RGB results = 0 √∑ 255
    G = (1-var_g) * 255;
    B = (1-var_b) * 255;
  }
}