PComp: Stupid Pet Trick

For our first big physical computing project, we were required to come up with a “stupid pet trick”, demonstrating what we’ve learned on the Arduino board and about building circuits so far.

I immediately latched onto the idea of building a combination lock for security.  This made me think of one of my favorite movies of all time, Sneakers.  This film, I must add, is also up there in the man canon of “Best Hacking Movies Ever”.  By the way, lest you ever unfortunately remind yourself of the Sandra Bullock movie “The Net”, where she stars as a “hacker”, I did actually find a site (I was looking for help on installing etherpad on OS X) that had a Guy Fawkes Anonymous mask outline in the bottom right corner of the site.  So somehow that film penetrated our consciousness. :(

Anyway, Sneakers.  Here’s the trailer.

Keep in mind that this was a film made in 1992 about hacking (!), that somehow attracted Dan Akroyd, Robert Redford, Sidney Poitier, Ben Kingsley, and, get this (there must have been a lot of coke involved), River freaking Phoenix.  I don’t know how this film got greenlit but I thank Hollywood for it.

So the film is about a bunch of hackers who stumble upon the existence of a chip that can decode any encrypted data, including on infrastructure networks and for governments.  Naturally the NSA is interested.  So Redford and his crew have to go find it.  A lot of cool stuff happens.

I loved the scene when the crew is trying to figure out a scrambled message by using Scrabble tiles.

I wanted a puzzle theme for my project.  I love the look of the Arduino and breadboard and how it’s a puzzle waiting for you to figure it out.  So, for my limited knowledge in building things, I wanted to concentrate on the Arduino as a puzzle with a rudimentary interface, similar to a bomb to be defused or some Cloak and Dagger (remember that film?  with Dabney Coleman?/Sneakers -like device.

My device has no directions and uses potentiometers and switches for its inputs.  The feedback is through simple lights.  Much is intuitive, but it’ll require some morse skills to finish the puzzle.

My classmate Matt Richardson sent me this cool Nokia N900 unboxing video later, showing a hackable box that must be opened before you can get at the phone itself:

Here is the circuit diagram (thanks fritzing.org):

Here’s the code:

// Ben Turner
// NYU-ITP, PComp Fall 2011

// Stupid human pet trick code:  SETEC ASTRONOMY
// Requires watching the Serial Monitor.

// With much love to everyone who worked on Sneakers:
// http://www.imdb.com/title/tt0105435/

// lights showing which tests have been completed
const int test1LED = 2;
const int test2LED = 3;
const int test3LED = 4;

// booleans for each of 3 completed tests
boolean test1Complete = false;
boolean test2Complete = false;
boolean test3Complete = false;

// three red lights for displaying patterns/interface
const int pattern1LED = 5;
const int pattern2LED = 6;
const int pattern3LED = 7;

float analogNumVal = 0.0;

// test1 vars

// for randomized pattern used in test1, plus user's answers
int test1Num1 = 0;
int test1Num2 = 0;
int test1Num3 = 0;
int test1Answers[3];

// test2 vars

// morse alphabet and phrase converted to array for Arduino to understand
// gave up on the morse -.-. because of array/char problems. Used #s instead.
float ditOrDah = 0.0;
int morseCompletePhrase[26] = {2,1,1,1,0,1,1,0,1,1,1,0,1,1,1,1,0,2,2,2,0,1,2,2,1};
char morseCode[] = "";
char morseA[6] = ".-";
char morseB[6] = "-...";
char morseC[6] = "-.-.";
char morseD[6] = "-..";
char morseE[6] = ".";
char morseF[6] = "..-.";
char morseG[6] = "--.";
char morseH[6] = "....";
char morseI[6] = "..";
char morseJ[6] = ".---";
char morseK[6] = "-.-";
char morseL[6] = ".-..";
char morseM[6] = "--";
char morseN[6] = "-.";
char morseO[6] = "---";
char morseP[6] = ".--.";
char morseQ[6] = "--.-";
char morseR[6] = ".-.";
char morseS[6] = "...";
char morseT[6] = "-";
char morseU[6] = "..-";
char morseV[6] = "...-";
char morseW[6] = ".--";
char morseX[6] = "-..-";
char morseY[6] = "-.--";
char morseZ[6] = "--..";

int test2Answer = 2; // 2 is answer: (2) lights: "Martin?", (1) light: "Cosmo?"
int test2Guess = 0;
boolean awaitingAnswer = false;

// test3 vars

boolean fixedSwitch = false;
boolean awaitingAnswerTest3 = false;
char test3Answer[47] = "2022202220220120210212201110102121012101020111";
int test3Guess[47];

int n;

void setup() {
  Serial.begin(9600);
  pinMode(A0,INPUT);
  pinMode(13,INPUT);
  // test LEDs (2 yellow, 1 green) show which # test you're on
  pinMode(test1LED,OUTPUT);
  pinMode(test2LED,OUTPUT);
  pinMode(test3LED,OUTPUT);
  // 3 red LEDs for showing patterns
  pinMode(pattern1LED,OUTPUT);
  pinMode(pattern2LED,OUTPUT);
  pinMode(pattern3LED,OUTPUT);
}

void loop() {
  // need to randomize test 1 pattern
  randomSeed(analogRead(A5));
  test1Num1 = random(1,3);
  delay(50);
  randomSeed(analogRead(A5));
  test1Num2 = random(1,3);
  delay(50);
  randomSeed(analogRead(A5));
  test1Num3 = random(1,3);

  // test 1
  if (test1Complete == false && test2Complete == false && test3Complete == false) {

    // turns on 1st number in 3-part pattern
    switch (test1Num1) {
    case 1:
      digitalWrite(pattern1LED,HIGH);
      break;
    case 2:
      digitalWrite(pattern2LED,HIGH);
      break;
    case 3:
      digitalWrite(pattern3LED,HIGH);
      break;
    }
    // need this delay so LEDs are visible
    delay(1000);
    digitalWrite(pattern1LED,LOW);
    digitalWrite(pattern2LED,LOW);
    digitalWrite(pattern3LED,LOW);
    delay(50);

    // turns on 2nd number in 3-part pattern
    switch (test1Num2) {
    case 1:
      digitalWrite(pattern1LED,HIGH);
      break;
    case 2:
      digitalWrite(pattern2LED,HIGH);
      break;
    case 3:
      digitalWrite(pattern3LED,HIGH);
      break;
    }
    delay(1000);
    digitalWrite(pattern1LED,LOW);
    digitalWrite(pattern2LED,LOW);
    digitalWrite(pattern3LED,LOW);
    delay(50);

    // turns on 3rd number in 3-part pattern
    switch (test1Num3) {
    case 1:
      digitalWrite(pattern1LED,HIGH);
      break;
    case 2:
      digitalWrite(pattern2LED,HIGH);
      break;
    case 3:
      digitalWrite(pattern3LED,HIGH);
      break;
    }
    delay(1000);
    digitalWrite(pattern1LED,LOW);
    digitalWrite(pattern2LED,LOW);
    digitalWrite(pattern3LED,LOW);
    delay(3000);

    n = 0;

    // loop for allowing user to enter pattern
    while (n= 333 && analogNumVal < 666) {
        digitalWrite(pattern2LED,HIGH);
        digitalWrite(pattern1LED,LOW);
        digitalWrite(pattern3LED,LOW);
      }
      else {
        digitalWrite(pattern3LED,HIGH);
        digitalWrite(pattern1LED,LOW);
        digitalWrite(pattern2LED,LOW);
      }

      // user selects LED via the switch
      delay(100);
      if (digitalRead(13) == HIGH) {
        if (analogNumVal < 333) {
           test1Answers[n] = 1;
           delay(50);
           n++;
        }
        else if (analogNumVal >= 333 && analogNumVal < 666) {
          test1Answers[n] = 2;
          delay(50);
          n++;
        }
        else {
          test1Answers[n] = 3;
          delay(50);
          n++;
        } // end potentiometer scale
        delay(50);
      } // end user input

      // checks user's answers versus the random pattern
      if (test1Answers[0] == test1Num1 && test1Answers[1] == test1Num2 && test1Answers[2] == test1Num3) {
        test1Complete = true;
        digitalWrite(test1LED,HIGH);
        digitalWrite(pattern1LED,LOW);
        digitalWrite(pattern2LED,LOW);
        digitalWrite(pattern3LED,LOW);
      }
      else {
        digitalWrite(pattern1LED,LOW);
        digitalWrite(pattern2LED,LOW);
        digitalWrite(pattern3LED,LOW);
      }
    } // end while loop for allowing user to enter pattern
  } // end test1

  // test2: Read morse, understand context.
  if (test1Complete == true && test2Complete == false && test3Complete == false) {

    // displays morse pattern on LEDs
    Serial.println("This board is careful whom it associates with. Who does it favor?");
    Serial.println("1 light: Cosmo?");
    Serial.println("2 lights: Martin?");

    for (int i = 0; i<26; i++) {
      if (morseCompletePhrase[i] == 2) { // dah
        digitalWrite(pattern3LED,HIGH);
        digitalWrite(pattern2LED,HIGH);
        delay(1000);
      }
      else if (morseCompletePhrase[i] == 1) { // dit
        digitalWrite(pattern2LED,HIGH);
        delay(1000);
      }
      else if (morseCompletePhrase[i] == 0) { // space
        digitalWrite(pattern1LED,HIGH);
        delay(1000);
      }
      digitalWrite(pattern1LED,LOW);
      digitalWrite(pattern2LED,LOW);
      digitalWrite(pattern3LED,LOW);
      delay(1000);
    } // end for

    // keeps cycling internally to loop() until input received
    while (awaitingAnswer == false) {
      analogNumVal = analogRead(A0);
      // turns on respective LED when potentiometer value is in its range
      if (analogNumVal < 500) {
        digitalWrite(pattern2LED,HIGH);
        digitalWrite(pattern3LED,LOW);
      }
      else {
        digitalWrite(pattern2LED,HIGH);
        digitalWrite(pattern3LED,HIGH);
      }

      // user selects LED via the switch
      if (digitalRead(13) == HIGH) {
        delay(1000);
        if (analogNumVal < 500) {
          test2Guess = 1;
          delay(50);
        }
        else {
          test2Guess = 2;
          delay(50);
        } // end potentiometer scale
        delay(50);
      } // end user input

      // checks user's answers versus the random pattern
      if (test2Answer == test2Guess) {
        test2Complete = true;
        awaitingAnswer = true;
        digitalWrite(test2LED,HIGH);
        digitalWrite(pattern1LED,LOW);
        digitalWrite(pattern2LED,LOW);
        digitalWrite(pattern3LED,LOW);
      }
      else {
        digitalWrite(pattern1LED,LOW);
        digitalWrite(pattern2LED,LOW);
        digitalWrite(pattern3LED,LOW);
      }// end answer check
    } // end while

  } // end test2

  // test3: Respond in morse.  Is the breadboard wired correctly?
  if (test1Complete == true && test2Complete == true && test3Complete == false) {
    if (fixedSwitch == false) {
      while (fixedSwitch == false) {
        if (digitalRead(12) != HIGH) {
          Serial.println("ERR: NO INPUT DEVICE DETECTED.  FIX, THEN INPUT TO CONTINUE.");
        }
        else {
          fixedSwitch = true;
          delay(3000);
        }
      }
    }
    else { // if board is wired and input received on digitalRead(12)
      Serial.println("Scrabble? SETEC ASTRONOMY? Tell me what it means, Bishop.");
      int n = 0;

      while (awaitingAnswerTest3 == false) {
        analogNumVal = analogRead(A0);
        // turns on respective LED when potentiometer value is in its range
        if (analogNumVal > 333 && analogNumVal <= 666) {
          digitalWrite(pattern2LED,HIGH);
          digitalWrite(pattern3LED,LOW);
          digitalWrite(pattern1LED,LOW);
        }
        else if (analogNumVal <= 333) {
          digitalWrite(pattern1LED,HIGH);
          digitalWrite(pattern2LED,LOW);
          digitalWrite(pattern3LED,LOW);
        }
        else {
          digitalWrite(pattern2LED,HIGH);
          digitalWrite(pattern3LED,HIGH);
          digitalWrite(pattern1LED,LOW);
        }

        // user selects LED via the switch
        if (digitalRead(13) == HIGH) {
          delay(1000);
          if (analogNumVal <= 333) {
            test3Guess[n] = 0;
            Serial.println(test3Guess[n]);
            n++;
            delay(50);
          }
          else if (analogNumVal > 333 && analogNumVal <= 666) {
            test3Guess[n] = 1;
            Serial.println(test3Guess[n]);
            n++;
            delay(50);
          }
          else {
            test3Guess[n] = 2;
            Serial.println(test3Guess[n]);
            n++;
            delay(50);
          } // end potentiometer scale
          delay(50);
        }

        // finalizes response by pressing 2nd switch
        if (digitalRead(12) == HIGH) {
          delay(1000);
          // checks user's answers versus the random pattern
          int testIfArraysEqual = 0;
          for (int z=0;z<46;z++) {
            if (test3Answer[z] != test3Guess[z]) {
              testIfArraysEqual += 0;
            }
            else {
              testIfArraysEqual += 1;
            }
          } // end for
          if (testIfArraysEqual == 4) {
              test3Complete = true;
              awaitingAnswer = true;
              digitalWrite(test3LED,HIGH);
              digitalWrite(pattern1LED,LOW);
              digitalWrite(pattern2LED,LOW);
              digitalWrite(pattern3LED,LOW);
              Serial.println("Success!");
          }
          else {
            Serial.println("Nope. Try again.");
            n = 0;
          }
        } // end digitalRead if
      } // end while
    }
  } // end test3
} // end loop()

For the rest of how the puzzle works, go below the jump: SPOILERS.

The first puzzle flashes you a 3-light pattern.  You just have to duplicate the pattern by figuring out how the knob works with the lights.

The second puzzle prompts you with a question in the Serial Monitor, and then it flashes some morse at you.  How do you respond?

The third puzzle gives you an input error.  Is it wired correctly?  Prove it.  Then figure out the clue to enter in your morse message.

After this, you’ve solved the puzzle, indicated by the green LED turning on!

Finally, a demo of the stupid pet trick: