ZenPin PoC
<ul><li>Property "ProjectImage" (as page type) with input value "Image:ZenPin1.jpg|300px" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.</li> <!--br--><li>"{{{StartDate}}}" contains an extrinsic dash or other characters that are invalid for a date interpretation.</li></ul>
Hive13 Project |
---|
ZenPin PoC |
[[![]() |
Status: Completed |
Overview
Contest submission by: Craig
The concept of the ZenPin is to make it so when a person enters a PIN code that if somebody is watching they can not simply re-enter the pin. This is similar to a one-time password system but doesn't not require the user to memorize or carry any pincodes. Instead of memorizing a pin the user memorizes an algorithm.
For this Proof-of-Concept (PoC) the algorithm is very simple. The layout is:
(R) (Y) (G) 7 | 8 | 9 --------------------- 4 | 5 | 6 --------------------- 1 | 2 | 3 --------------------- C | 0 | E
Where (R) = Red LED, (Y) = Yellow/Orange LED, (G) = Green LED
PIN codes are 5 digits long.
Goal
Get the green light to stay solid and the "good" tone to play.
Failure if the red light goes solid and the "bad" tone plays :)
How it works
The user enters any number they want to start. The system then randomly generates a 4 digit pin. Then the system uses the lights to "hint" as to what the next pin is. It will find the shortest distance to the next number via adding or subtracting and then blink either the Red LED or the Yellow LED for the amount of times needed. The Red LED represents subtraction while the Yellow represents addition. If the red blinks 3 times then you subtract 3 from the last number you just pressed. The number will roll over. For instance, if you press a 9 and the Yellow light blinks twice, then the next number is a 1.
If both the Red and the Yellow light blink at the same time then the next number is the same.
You can press 'C' to start over. This is verified by a quick blink of all lights.
You can press 'E' to repeat the last hint.
If you get any number wrong in the sequence then there will not be any indication. Instead the system will give you "hints" to the wrong numbers. Only after 5 digits will you hear the "bad" tone and the red light will stay on.
Source Code
Here is the Arduino Sourecode.
/* Keypad concept for Passcode Challenge #1. The idea is that instead of memorizing a passcode you can memorize a simple algorithm. this prevents ppl from shoulder surfing to see your PIN. In this example there are two LEDs, addLed and subLed. To enter a passcode you can press any button. After you press the first button the chip picks a random 4 digit PIN. It then determines if it is quicker to add or subtract to reach the next number in the pin. It will then send short blinks to the appropriate LED to tell you what the next pin is. Example: User Press #5. Board picks a random 4 digit pin, 3912 Board blinks subLed 2 time User Presses 3 Board blinks subLed 4 times User Presses 9 Board blinks addLed 2 times User Presses 1 Board blinks addLed 1 time User Presses 2 Board "unlocks" If at any time the user enters the wrong number then the board will pick random Leds and blink patterns for each press after. Once 5 keys have been pressed a "bad" state will be displayed. If the next key is the same then both Leds blink once at the same time. Pressing 'C' will reset the counter. Pressing 'E' will redisplay the hint. -- Craig */ // Set PIN constants const int addLed = 9; const int subLed = 10; const int winLed = 11; const int speakerPin = 12; const int grid1 = 8; const int grid2 = 7; const int grid3 = 6; const int grid4 = 5; const int grid5 = 4; const int grid6 = 3; const int grid7 = 2; // Set other constants const int DEBUG = 1; const int MAXPINS = 4; const int BLINKMS = 150; // Set globals int pinCount = 0; int badPins = 0; int pin[MAXPINS]; int buttonState = 0; int higher = false; int blinkCnt = 0; /* Given row-col return button in ASCII */ int whichKey(int row, int col) { /* Guess: Each keypress will turn 2 pins HIGH */ /* C | 0 | C-E = 4-5 | 4-6 | 4-7 1 | 2 | 3 = 3-5 | 3-6 | 3-7 4 | 5 | 6 = 2-5 | 2-6 | 2-7 7 | 8 | 9 = 1-5 | 1-5 | 1-7 */ switch(col) { case 5: switch(row) { case 4: return 'C'; break; case 3: return '1'; break; case 2: return '4'; break; case 1: return '7'; break; default: return 0; } case 6: switch(row) { case 4: return '0'; case 3: return '2'; case 2: return '5'; case 1: return '8'; default: return 0; } case 7: switch(row) { case 4: return 'E'; case 3: return '3'; case 2: return '6'; case 1: return '9'; default: return 0; } default: Serial.println("ERROR: Invalid Column"); return 0; } return 0; } /* Check to see if a keywas pressed */ int getKeyPress() { int pinData = 0; int column = 0; /* We'll check for the Y cords first (smaller combos) */ pinData = digitalRead(grid5); if(pinData) { pinData = digitalRead(grid6); if(pinData) { pinData = digitalRead(grid7); if(pinData) { return 0; /* Exit early, no reason to waste more cycles */ } else { column = 7; } } else { column = 6; } } else { /* We know it's column 5 */ column = 5; } /* If we make it here we know some button has been pressed */ pinData = digitalRead(grid1); if(pinData) { pinData = digitalRead(grid2); if(pinData) { pinData = digitalRead(grid3); if(pinData) { pinData = digitalRead(grid4); if(pinData) { return 0; } else { return whichKey(4, column); } } else { return whichKey(3, column); } } else { return whichKey(2, column); } } else { return whichKey(1, column); } return 0; } // Taken from the arduino example 'melody' void playTone(int tone, int duration) { for (long i = 0; i < duration * 1000L; i += tone * 2) { digitalWrite(speakerPin, HIGH); delayMicroseconds(tone); digitalWrite(speakerPin, LOW); delayMicroseconds(tone); } } // Play good tone void playGood() { playTone(1519, 150); delay(150); playTone(1519, 150); delay(150); playTone(1275, 400); delay(150); } // Play bad tone void playBad() { playTone(1275, 300); delay(150); playTone(1700, 600); delay(150); } // Blinks all lights once, setup / clearing void blinkAll() { digitalWrite(addLed, HIGH); digitalWrite(subLed, HIGH); digitalWrite(winLed, HIGH); delay(BLINKMS); digitalWrite(addLed, LOW); digitalWrite(subLed, LOW); digitalWrite(winLed, LOW); } // Blinks the hint based on the globals: higher, blinkCnt void blinkHint() { int cnt; if(!blinkCnt && pinCount) { digitalWrite(addLed, HIGH); digitalWrite(subLed, HIGH); delay(BLINKMS); digitalWrite(addLed, LOW); digitalWrite(subLed, LOW); delay(BLINKMS); return; } // No game in progress if(!(blinkCnt && pinCount)) { blinkAll(); return; } if(DEBUG) { if(higher) { Serial.print("Add "); } else { Serial.print("Sub "); } Serial.print(blinkCnt, DEC); Serial.println(); } for(cnt = 0; cnt < blinkCnt; cnt++) { if(higher) { digitalWrite(addLed, HIGH); delay(BLINKMS); digitalWrite(addLed, LOW); delay(BLINKMS); } else { digitalWrite(subLed, HIGH); delay(BLINKMS); digitalWrite(subLed, LOW); delay(BLINKMS); } } } // Blinks an LED based on the last pin pressed void blinkLed(int pin, int guess) { int diff = pin - guess; higher = false; blinkCnt = 0; // If the next number is the same the blink both LEDs once if(!diff) { blinkHint(); return; } if(diff > 5) { blinkCnt = 10 - diff; } else if(diff < -5) { blinkCnt = diff + 10; higher = 1; } else { blinkCnt = diff; if(diff > 0) { higher = true; } } blinkCnt = abs(blinkCnt); blinkHint(); } void setup() { Serial.begin(9600); // Analog Pin0 must be unconnected so we can read random noise randomSeed(analogRead(0)); // Ensure lights are off pinMode(addLed, OUTPUT); pinMode(subLed, OUTPUT); pinMode(winLed, OUTPUT); // Speaker pinMode(speakerPin, OUTPUT); // This is the default state for digital pins, but meh... pinMode(grid1, INPUT); pinMode(grid2, INPUT); pinMode(grid3, INPUT); pinMode(grid4, INPUT); pinMode(grid5, INPUT); pinMode(grid6, INPUT); pinMode(grid7, INPUT); // Enable pull up resistors digitalWrite(grid1, HIGH); digitalWrite(grid2, HIGH); digitalWrite(grid3, HIGH); digitalWrite(grid4, HIGH); digitalWrite(grid5, HIGH); digitalWrite(grid6, HIGH); digitalWrite(grid7, HIGH); //Blink all lights to show we are done with setup blinkAll(); } void loop() { int cnt; int key = getKeyPress(); delay(40); int key2 = getKeyPress(); // Attempt to debounce switch if(key == key2) { if(key && !buttonState) { if(DEBUG) { Serial.print("Key pressed: "); Serial.print(key, BYTE); Serial.println(); } buttonState = 1; // Reset PIN attempt if(key == 'C') { pinCount = 0; badPins = 0; blinkAll(); return; } // Repeat last hint if(key == 'E') { blinkHint(); return; } // Check to see if we are already processing a PIN if(pinCount) { pinCount++; // User PIN == Random Pin and no bad Pins yet if(key == pin[pinCount-2] && !badPins) { if(pinCount < MAXPINS+1) { blinkLed(pin[pinCount-1], key); } } else { // Bad Pin badPins++; // Blink code to a random pin if(pinCount < MAXPINS+1) { if(DEBUG) { Serial.println("BadPins, Faking Guess"); } blinkLed('0'+random(10), key); } } } else { // This is the first key pressed, initialize new random pin pinCount++; for(cnt = 0; cnt < MAXPINS; cnt++) { pin[cnt] = '0' + random(10); } if(DEBUG) { Serial.print("Random PIN: "); for(cnt = 0; cnt < MAXPINS; cnt++) { Serial.print(pin[cnt], BYTE); } Serial.println(); } blinkLed(pin[0], key); } } } // End if key == key2 if(!key && buttonState) { buttonState = false; } // 5-Digit PIN w/ the first one not counting if(pinCount > MAXPINS && !buttonState) { if(!badPins) { Serial.println("ACCESS GRANTED"); digitalWrite(winLed, HIGH); playGood(); //delay(2000); digitalWrite(winLed, LOW); } else { Serial.println("FAIL"); digitalWrite(subLed, HIGH); playBad(); //delay(2000); digitalWrite(subLed, LOW); } pinCount = 0; badPins = 0; } }
TODO List
- Nada, It's done!
Parts involved with this entry
- Arduino
- minikey 12 button keypad ($1.50 at mendlesons)
- 3 LEDs
- 9v Battery
- Box from the clearance rack of TJ Max ;)