<
>
back to all cubes

Storm Cube

Blowing into the weathervane makes a storm

The Storm Cube was a real fun to create, from head to toe. Guy Izoun spent hours on hours creating the perfect sounds of a gradually strengthening storm - from soft wind to heavy rain with a bit of ocean feel. We used an Arduino VoiceShield for playing these sounds, which required a special setup for recording them into the shield's memory.

For sensing the user's blow, we used a turbine from a broken wind speed sensor used for hang-gliding. After careful calibration of a trim potentiometer, we got clear "ticks" from it even from the faintest blow.

Designing the cube was real fun - we inserted a "wind tunnel" inside the cube.

Arduino code - download here

/*
  CUBES originally created by the interaction Lab, Holon Institute of Technology for the Design Museum Holon.
  http://interaction.shenkar.ac.il
  This work is licensed under a Creative Commons Attribution 3.0 Unported License

  Storm Cube by Guy Izoun
*/

#include "src/VoiceShield/VoiceShield.h"             // USES VoiceShield Library.

#define PLAY_SPEED 5                 // min sensor speed to account as blow
#define WIND_MIN_TIME 300            // min blow time to start playing storm sounds
#define WIND_MIN_STOP_TIME 1000
#define MIN_TIME_IN_SOFT_WIND 2500
#define MIN_TIME_IN_WIND_RAIN 3500
#define MIN_TIME_IN_THUNDER 3500
#define Low 35
#define High 75
VoiceShield vs(1200);                  // Create VoiceShield class with 80 sound bytes

int data[2];

char COMMAND = 0;                     // Programming Command 1 = STOP, 2 = Play,  3 = Record, 4 = Report bytes
byte ADDRESS = 0;                     // Sound slot to record into or play from

boolean playing = false;

// variables for sensor sampling inside an interrupt:
volatile uint16_t sensorCounter = 0;
volatile uint16_t sensorSpeed = 0;

byte state = 0;

#define IDLE      0
#define FEEL_WIND 1
#define SOFT_WIND 2
#define SOFT_WIND2 3
#define WIND_RAIN 4
#define THUNDER 5
#define SHIP 6
#define FADING 7
#define WIND_STOP 8
#define FADE_OUT_SOFT_WIND 9
#define FADE_OUT_WIND_RAIN 10
#define FADE_OUT_THUNDER 11
#define WIND_STOP2 12

int  currentSpeed;
long MIN_TIME_IN_FADE_OUT;
long statetimer;
long windStartTime;
long windStopTime;
long TIMEinSOFT_WIND;
long TIMEinWIND_RAIN;
long TIMEinTHUNDER;
long  TIMEinSHIP;

void setup()
{
  pinMode (13, OUTPUT);
  Serial.begin (115200);
  
  // Sensor attatched to PCINT1 pin (digital 9)
  PCMSK0 = 1 << PCINT1; // set mask to turn on PCINT1 pin
  PCICR = 1 << PCIE0; // enable pin change interrupt 0

  // Timer1 used for sampling turbine counter and calculating speed
  // Turn timer 1 mode to normal, prescaler clk/1024:
  TCCR1A = 0;
  TCCR1B = (1 << CS10) | (1 << CS12);
  TCCR1C = 0;
  OCR1A = 1562;       // Set output compare 1 to sample sensor 10 times a second
  TCNT1 = 0;           // Set timer 1 counter to 0 before enabling interrupt
  TIMSK1 = 1 << OCIE1A; // Enable output compare interrupt
}

// interrupt occurs when sensor pin changes
ISR (PCINT0_vect) {
  PINB |= 1 << 5;
  sensorCounter++;
}

// update sensor speed 10 times per second
ISR (TIMER1_COMPA_vect) {
  sensorSpeed = sensorCounter;
  sensorCounter = 0;
  TCNT1 = 0;
}

void loop()
{
  switch (state) {
    case IDLE:
      if (sensorSpeed >= PLAY_SPEED) {
        state = FEEL_WIND;
        Serial.println ( "FEEL_WIND");
        windStartTime = millis();
      }
      break;

    case FEEL_WIND:
      if (sensorSpeed < PLAY_SPEED) {
        state = IDLE;
        Serial.println ( "IDLE");
      }
      else if (millis() - windStartTime > WIND_MIN_TIME) { // blown enough time - start storm sound
        if ( sensorSpeed < Low) { // weak blow
          vs.ISDPLAY (0);
          state = SOFT_WIND;
          Serial.println ( "SOFT_WIND");
          statetimer = millis();
          TIMEinSOFT_WIND = millis();
        }
        else if (sensorSpeed < High) {  // medium blow
          vs.ISDPLAY (14);
          state = WIND_RAIN;
          Serial.println ( "WIND_RAIN");
          statetimer = millis();
          TIMEinWIND_RAIN = millis();
        }
        else {
          vs.ISDPLAY (28.7);  // strong blow
          state = THUNDER;
          Serial.println ( "THUNDER");
          statetimer = millis();
          TIMEinTHUNDER = millis();
        }
      }
      break;

    case SOFT_WIND:  // weak storm
      if (millis() - statetimer > MIN_TIME_IN_SOFT_WIND) { // stay at lease 2.5 seconds with this sound
        if (sensorSpeed < PLAY_SPEED) {  // if blow stopped - fade out
          Serial.println (" FADE_OUT_SOFT_WIND");
          state =  FADE_OUT_SOFT_WIND;
          MIN_TIME_IN_FADE_OUT = millis();
          vs.ISDPLAY(11);  // jump to a premade fade out in the sound file
          windStopTime = millis();
        }
        else if  (millis() - TIMEinSOFT_WIND > 9000) {  // loop through weak storm sound
          vs.ISDPLAY(3);
          //        state = SOFT_WIND2;  // this is not used currently, but an option to skip to different storm strengths according to current blow speed
          TIMEinSOFT_WIND = millis();
        }
      }
      break;

    case  FADE_OUT_SOFT_WIND:
      if (millis() - MIN_TIME_IN_FADE_OUT >= 2000) {
        vs.ISDSTOP();
        statetimer = millis();
        state = IDLE;
        Serial.println ( "IDLE");
      }
      break;

    //  case SOFT_WIND2:
    //    if (millis()-statetimer>MIN_TIME_IN_SOFT_WIND) {
    //      if (sensorSpeed < PLAY_SPEED) {
    //        state = WIND_STOP;
    //        Serial.println ( "WIND_STOP");
    //        windStopTime = millis();
    //      }
    //      else if (sensorSpeed >High ) {
    //        vs.ISDPLAY (9);
    //        state = THUNDER;
    //        Serial.println ( "THUNDER");
    //        statetimer=millis();
    //
    //      }
    //      else if  (sensorSpeed > Low) {
    //        vs.ISDPLAY (17);
    //        state = WIND_RAIN;
    //        Serial.println ( "WIND_RAIN");
    //        statetimer=millis();
    //
    //      }
    //      else if  (millis()-TIMEinSOFT_WIND >5000) {
    //        vs.ISDPLAY(4);
    //        TIMEinSOFT_WIND=millis();
    //      }
    //    }
    //    break;

    case WIND_RAIN: // medium storm
      if (millis() - statetimer > MIN_TIME_IN_WIND_RAIN) {
        if (sensorSpeed < PLAY_SPEED) {
          state = FADE_OUT_WIND_RAIN;
          vs.ISDPLAY (26);
          MIN_TIME_IN_FADE_OUT = millis();
          Serial.println ( " FADE_OUT_WIND_RAIN");
          statetimer = millis();
        }
        else if ( millis() - TIMEinWIND_RAIN > 10000) {
          vs.ISDPLAY (17);
          TIMEinWIND_RAIN = millis();
        }
      }
      break;

    case  FADE_OUT_WIND_RAIN:
      if ( millis() - MIN_TIME_IN_FADE_OUT >= 3000) {
        vs.ISDSTOP();
        statetimer = millis();
        state = IDLE;
        Serial.println ( "IDLE");
      }
      break;

    case THUNDER:  // strong storm
      if (millis() - statetimer > MIN_TIME_IN_THUNDER) {
        if (sensorSpeed < PLAY_SPEED ) {
          //        currentSpeed = sensorSpeed;
          state = FADE_OUT_THUNDER;
          vs.ISDPLAY (40);
          MIN_TIME_IN_FADE_OUT = millis();
          Serial.println (" FADE_OUT_THUNDER");
          statetimer = millis();
        }
        else if ( millis() - TIMEinTHUNDER > 7000 ) {
          vs.ISDPLAY (29.5);
          TIMEinTHUNDER = millis();
          TIMEinSHIP = millis();
          if (millis() - TIMEinSHIP > 3000)
            state = THUNDER;
        }
      }
      break;

    case  FADE_OUT_THUNDER:
      if ( millis() - MIN_TIME_IN_FADE_OUT >= 2500) {
        vs.ISDSTOP();
        statetimer = millis();
        state = IDLE;
        Serial.println ( "IDLE");
      }
      break;

/*    
 *    This is a previous method we don't use in this version 

 case WIND_STOP:
      if (millis() - windStopTime > WIND_MIN_STOP_TIME) {
        vs.ISDSTOP();
        state = IDLE;
        Serial.println ( "IDLE");
      }
      else if (sensorSpeed >= PLAY_SPEED && sensorSpeed < Low) {
        vs.ISDPLAY (0);
        state = SOFT_WIND;
        Serial.println ( "SOFT_WIND");
        statetimer = millis();
      }
      else if (sensorSpeed >= High) {
        vs.ISDPLAY (9);
        state = THUNDER;
        Serial.println ( "THUNDER");
        statetimer = millis();
      }
      else if (sensorSpeed >= 40 && sensorSpeed < High) {
        vs.ISDPLAY (17);
        state = WIND_RAIN;
        Serial.println ( "WIND_RAIN");
        statetimer = millis();
      }
      break;

    case WIND_STOP2:
      if   (sensorSpeed < PLAY_SPEED )
        state = WIND_STOP;
      break;
*/
  }
}

Parts:

(1x) Arduino Uno

(1x) Arduino VoiceShield

(1x) Wind turbine taken from a broken wind sensor

(1x) Amplifier taken from portable speakers

(2x) Small speakers

(1x) LM358 Operational Amplifier

(1x) 10k Trimmer Potentiometer

(2x) 10k Resistors

(1x) 7.2V Li-Po Battery

(1x) 5.5/2.1mm Jack Socket