An Arduino blinking an LEDAn Arduino attached to a breadboard with multiple LEDs

Blink worked fine, no incantations required. Likewise for the first half of this week’s Digital Input and Output Lab. I used a tiny pushbutton switch that drops straight into the breadboard.

I ended up putting together a crude, digital (literally) combination lock system in response to the “Get Creative” suggestion at the end of the lab. Instead of turning a dial or punching keys, I wanted to make something that you could wave your hand over to interact with — the number of fingers held up during the process, could, in turn, enter the code.

For example, to enter the combination “3, 2, 1” (which is, incidentally, the combination to the prototype) you would first wave three fingers, then two fingers, and finally a single finger over the system’s sensor.

I wired up a light dependent resistor to track when a hand is waved over the system, and to count how many fingers you were holding up. This proved a bit tricky. Here are a few issues dealt with along the way:

In the eye of a photoresistor, what does a hand look like, what does a finger look like, and how to we separate them from background?

I experimented with some averaging and smoothing algorithms, but these proved too fussy. In the process of setting up serial communication back to my computer so I could keep an eye on variables, I added delay(60) to the main loop to prevent the serial situation from getting out of hand. (Update: Turns out this is not necessary.) This had the side-effect of smoothing out the input from the photosensor, while still hanging on to enough resolution to distinguish individual fingers flying overhead.

I set up a threshold value to decide whether the jumpy analog signal coming in from the photoresistor represented a hand. Finding the correct threshold value for different lighting conditions was a problem. Ideally, the threshold should be just below (e.g. darker) the number representing the ambient light hitting the photo sensor. Ambient light levels change, though, and the exact threshold has a big influence on the sensitivity of the system and how accurately it counts fingers. Again, I tried out some averaging algorithms to see if the code could learn about the ambient light levels in a particular room. Execution was problematic, though, so in the interest of time I ended up adding a potentiometer to the circuit so the threshold can be set manually as conditions change.

A visually annotated electronics project on a breadboard

Here’s a quick video demonstrating entering the “3, 2, 1” code. The red LED is illuminated when the system is “locked” — and a green LED flashes when unlocked. Yellow flashes as fingers are detected.


Update: Here’s the code. Bear with me, I’m still sussing out the best way to embed code in the blog.

// digital lock
// eric mika
// september 2009

// here's the combination!
int lockCombination[] = {3, 2, 1};

// store the user's combo for compairon
int enteredCombination[3];

boolean locked = true;

// analog pins
int photoResistor = 5;
int potentiometer = 4;

// digital pins
int yellowLedPin = 9;
int redLedPin = 11;
int greenLedPin = 13;
int lockSwitch = 5;

// store analog values
int photoValue = 0;
int potValue = 0;

// how much the photoresistor must change before we register a finger
// set by the potentiometer for simplicity's sake
// eventually should find a way to have this adjust intelligently to
// changing light conditions
int threshold = 0;

// keep track of fingers moving over
int fingerCount = 0;
boolean haveFinger = false; // currently have a finger
boolean waitingForNextFinger = false; // still waiting for the next finger
int fingerTimer = 0; // counts up between fingers
int fingerFail = 20; // how long before we give up on the next finger and declare the gesture complete

// how long to wait between attempts
boolean enteringCode = false;

int gestureCount = 0;
int codeTimer = 0;
int codeFail = 50;

void setup() {
pinMode(yellowLedPin, OUTPUT);
pinMode(redLedPin, OUTPUT);
pinMode(greenLedPin, OUTPUT);
pinMode(lockSwitch, INPUT);

Serial.begin(9600);
}

void loop() {
// listen for the lock switch
if (digitalRead(lockSwitch) == 1) {
locked = true;
digitalWrite(redLedPin, HIGH);
digitalWrite(greenLedPin, LOW);
enteredCombination[0] = 0;
enteredCombination[1] = 0;
enteredCombination[2] = 0;
fingerCount = 0;
gestureCount = 0;
Serial.println("Locked!");
}

if (locked) {
// read from the analog pins
photoValue = analogRead(photoResistor);
potValue = analogRead(potentiometer);

// potentiometer sets the finger threshold
threshold = potValue;

// Serial.print("Photo Value: ");
// Serial.print(photoValue);
// Serial.print("\tPot Value: ");
// Serial.println(potValue);

// flash the yellow when it picks up a finger
if (photoValue < threshold) {
digitalWrite(yellowLedPin, HIGH);
haveFinger = true;
}
else {
digitalWrite(yellowLedPin, LOW);

// if that's the end of a finger, then start waiting for the next
if (haveFinger) {
fingerCount++;
fingerTimer = 0;
haveFinger = false;
waitingForNextFinger = true;
}
}

if (waitingForNextFinger) {
// bump the timer
fingerTimer++;

// shine the green light to show we're grabbing the next one
digitalWrite(greenLedPin, HIGH);

if (fingerTimer > fingerFail) {
// register a gesture
enteredCombination[gestureCount] = fingerCount;
gestureCount++;

Serial.print("fingerCount: ");
Serial.print(fingerCount);
Serial.print("\tgestureCount: ");
Serial.println(gestureCount);

// check the combination if we're on 3
if (gestureCount == 3) {
// does it match?
// tried to write a function for this process, but compiler choked
if ((enteredCombination[0] == lockCombination[0]) &&
(enteredCombination[1] == lockCombination[1]) &&
(enteredCombination[2] == lockCombination[2])) {

Serial.println("Opened!");
locked = false;
}
else {
Serial.println("Wrong combo!");
}

// reset it since we're past three
gestureCount = 0;
}

waitingForNextFinger = false;
fingerCount = 0;
}
}
else {
digitalWrite(greenLedPin, LOW);
}

// seems like a bad thing, but helps smooth the analog input
// should rewrite the whole timing approach using millis()
delay(20);

digitalWrite(redLedPin, HIGH);
}
else {
// it's open!
digitalWrite(yellowLedPin, LOW);
digitalWrite(redLedPin, LOW);

// why not flash the green LED to celebrate
if (digitalWrite(greenLedPin, HIGH);
delay(100);
digitalWrite(greenLedPin, LOW);
delay(100);
}
}