<
>
back to all cubes

Flipping Cube

Originally, the cube was supposed to flip when softly blown on (thus creating an effect of over-response to the power of the blowing), but someone remarked that bringing one's head close to such a violent cube could end wrong. So we switched the idea to one inspired by "the useless machine" (original credit unknown): a box with a switch and an internal mechanism whose only objective when someone flips the switch is to flip the switch back to its original state.

Developing the flipping mechanism took quite a few sketches, and not fewer concepts for the mechanism. From pulling a spring and letting go at once, to quickly extracting a pole from the cubes bottom - all concepts were either rejected for aesthetic reasons, for safety reasons, for feasibility reasons - or they just failed.

The solution came from "bending the rules" of the cubes a bit: We created the heaviest weight we could fit in the mechanism from brass, attached it to the longest arm we could fit in the cube, and joined them both to the strongest servo motor we could find (at reasonable size - not necessarily reasonable price :). Then we took this "bang", and helped it by rounding the edge of the cube, and adding it small legs positioned such that the force is enough to start the cube rolling. The real fun part was iterating with the Arduino code and creating an "ease" at the end of the roll (by flipping the weight back at a precise speed and timing) thus giving it a soft touch when hitting the table.

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

  Flipping Cube by Shachar Geiger
  Based on the idea of useless machines
*/

// the servo stays on while the cube is moved. This is done to prevent the weight from moving freely inside, which mught harm the cube.
// after leaving the cube static for some time (see below), the servo is turned off, for saving battery and stopping the annoying "hummm".

#include <Servo.h> 
#include "src/SparkFun_ADXL345_Arduino_Library/SparkFun_ADXL345.h"

// sensor definitions:
#define FILTER_LENGTH 50
#define SENSOR_TOLERANCE 10    // acceleration tolerance (arbitrary units) for sensing if cube orientation is up
#define X_UP 0                 // emperical setting for udentifiyng up-standing of the cube
#define Y_UP 268
#define Z_UP 0
#define TIME_TO_FLIP 500       // after staying this long at upwards position - flip
#define MOVE_CHECK_TIME 100    // period between checks for user moving the cube
#define USER_RELEASE_TIME 600  // time from user release to servo detaching

// servo definitions:
#define SERVO_MAX_POS 1780
#define UP_POS 860
#define MID_POS 600
#define DOWN_POS 0

#define SERVO_PIN 10

ADXL345 adxl;

int filter[FILTER_LENGTH][3];  // filtering sensor readings. always calculate average of last 50 reads.
int filterPointer = 0;

int x,y,z;                     // sensor output
boolean upwards = false;       // cube is facing upwards
boolean moving = false;        // cube is moved by a user
boolean released = false;      // cube is release from user
boolean problematicY = false;  // servo might move on its own
long turnedUpTime;
long userCheckTime;
long stoppedMovingTime;

Servo servo;
int alpha;  // servo angle

void setup(){
  Serial.begin(115200);
  adxl.powerOn();

  //register interupt actions - 1 == on; 0 == off  
  adxl.setInterrupt( ADXL345_INT_SINGLE_TAP_BIT, 0);
  adxl.setInterrupt( ADXL345_INT_DOUBLE_TAP_BIT, 0);
  adxl.setInterrupt( ADXL345_INT_FREE_FALL_BIT,  0);
  adxl.setInterrupt( ADXL345_INT_ACTIVITY_BIT,   0);
  adxl.setInterrupt( ADXL345_INT_INACTIVITY_BIT, 0);

  adxl.readAccel(&x, &y, &z); //read the accelerometer values and store them in variables  x,y,z

  for (int i=0; i<FILTER_LENGTH; i++) {  // reset all filter buffer to current readings
    filter[i][0] = x;
    filter[i][1] = y;
    filter[i][2] = z;
  }
}

void loop(){
  
  readADXL();

  // logic for activating flipping on the right time
  if (cubeFacingUpwards()) {
    if (upwards) {
      if (millis() - turnedUpTime > TIME_TO_FLIP) {
        upwards = false;
        flip();
      }
    } else {
      upwards = true;
      turnedUpTime = millis();
    }
  } else upwards = false;

  // logic for enabling or disabling the servo if users move / leave the cube for some time
  if (millis() - userCheckTime > MOVE_CHECK_TIME) {
    userCheckTime = millis();
    if (cubeMoved()) {
      if (!moving) {
        enableServo();
        moving = true;
      }
    } else {
      if (moving) {
        moving = false;
        released = false;
        stoppedMovingTime = millis();
      } else {
        if (millis() - stoppedMovingTime >= USER_RELEASE_TIME) {
          if (!released) {
            disableServo();
            released = true;
          }
        }
      }
    }
  }
  
  // Output x,y,z values - Commented out
  Serial.print(x); Serial.print('\t');
  Serial.print(y); Serial.print('\t');
  Serial.println(z);
} 

boolean cubeFacingUpwards() {
  if ((abs(x-X_UP)<=SENSOR_TOLERANCE) && (abs(y-Y_UP)<=SENSOR_TOLERANCE) && (abs(z-Z_UP)<=SENSOR_TOLERANCE))
   return true;
  else
   return false; 
}

void readADXL() {
  long X=0, Y=0, Z=0;
  
  adxl.readAccel(&filter[filterPointer][0], &filter[filterPointer][1], &filter[filterPointer][2]);
    
  filterPointer++;
  filterPointer%=FILTER_LENGTH;
  for (int i=0; i<FILTER_LENGTH; i++) {
    X+=filter[i][0];
    Y+=filter[i][1];
    Z+=filter[i][2];
  }
  x = X/FILTER_LENGTH;
  y = Y/FILTER_LENGTH;
  z = Z/FILTER_LENGTH;  
}

boolean cubeMoved() {
  static long px, py, pz;
  boolean answer = (abs(px-x)>SENSOR_TOLERANCE) || (abs(py-y)>SENSOR_TOLERANCE) || (abs(pz-z)>SENSOR_TOLERANCE);
  px = x;
  py = y;
  pz = z;
  if (answer) Serial.print('m');
  return (answer);
}

void enableServo() {
  servo.writeMicroseconds (SERVO_MAX_POS-DOWN_POS);
  servo.attach(10);
}

void disableServo() {
  servo.detach();
}

void flip() {  // this was written specificaly for the mechanics of the cube, using trial and error, to get nice and smooth motion.
  alpha = DOWN_POS;
  enableServo();
  while (alpha++ < MID_POS) {
    servo.writeMicroseconds (SERVO_MAX_POS - alpha);
    delay((MID_POS-alpha+5)*3/(MID_POS-DOWN_POS));
  }
  alpha = UP_POS;
  servo.writeMicroseconds (SERVO_MAX_POS - alpha);
  delay(350);
  while (alpha-- > DOWN_POS) {
    servo.writeMicroseconds (SERVO_MAX_POS - alpha);
    delayMicroseconds(230);
  }
  delay(100);
  disableServo();
}

Parts:

(1x) Arduino Mini Pro 5V

(1x) ADXL345 - Accelerometer Module

(1x) HSR-5990TG Servo

(1x) Brass Weights

(1x) 7.2V Li-Po Battery

(1x) 5.5/2.1mm Jack Socket