The existing code works pretty well – the LED eyes come on, wink, and fade back out. The only shortcoming is that it is not at all interactive – they just keep following the same pattern on a loop. It would be more realistic, and spookier, if the LED routine somehow interacted with the viewer.
This can be addressed with an ultrasonic proximity sensor that detects the distance to a viewer, and responds based on that distance. In this case the LED eyes would come on when someone came within distance, and fade out as they moved away.
We chose the HC-SR04 Ultrasonic Module, purchased on Amazon from Virtuabotix (about $6). It was connected to the Arduino Uno board like this – close to but not exactly the setup in the Virtuabotix documentation because some of the pins they specified were already in use. It is ultrasonic range finder that detects the distance of the closest object in front of the sensor.
HC-SR04 Ultrasonic Module |
Arduino Uno |
Gnd | GND |
Echo | 13 |
Trig | 12 |
Vcc | 5V |
It's important to note that for this sensor to work the Ultrasonic.h library, which is not included in the native Arduino IDE, has to be installed.
First you need to download the library, from here or elsewhere. Then, in the Mac version of the IDE go to the navigation bar and choose:
Sketch > Include Library > Add .ZIP Library
...then locate the library that you downloaded.
As a starting point we looked to this HC-SR04 demo project and melded it with the existing code so it looked like this. The changes are described below.
// BRIGHTNESS // minimum brightness level for the led int brightness_minimum = 1; // maximum brightness level for the led int brightness_maximum = 75; // PAUSES // pause between each increase in brightness int pause_between_increase = 12; // pause between each decrease in brightness int pause_between_decrease = 64; // pause at max brightness, before the wink effect int pause_at_max_before_wink = 2000; // pause for the wink effect int pause_for_wink = 100; // pause at max brightness, after the wink effect int pause_at_max_after_wink = 200; // the pause at min brightness int pause_at_min_brightness = 600; #define trigPin 12 #define echoPin 13 const int ledPin1 = 9; // the pin that the LED is attached to const int ledPin2 = 10; // the pin that the LED is attached to void setup () { Serial.begin (9600); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); pinMode(ledPin1, OUTPUT); // declare pin 9 to be an output: pinMode(ledPin2, OUTPUT); // declare pin 10 to be an output: } void loop() { long duration, distance; digitalWrite(trigPin, LOW); // Added this line delayMicroseconds(2); // Added this line digitalWrite(trigPin, HIGH); delayMicroseconds(10); // Added this line digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance = (duration / 2) / 29.1; if (distance < 8) { // within proximity limit; start routine analogWrite(ledPin1, 255); // When the Red condition is met, the Green LED should turn off analogWrite(ledPin2, 255); } else { // outside proximity limit; do not start routine analogWrite(ledPin1, 1); analogWrite(ledPin2, 1); } delay(5); } /* void loop() { // ramp up led brightness for (int brightness_current=brightness_minimum; brightness_current<=brightness_maximum;brightness_current++) //loop from brightness_minimum to brightness_maximum { analogWrite(ledPin1, brightness_current); // set the brightness of pin 9: analogWrite(ledPin2, brightness_current); // set the brightness of pin 10: delay(pause_between_increase); } // hold at max brightness before wink delay(pause_at_max_before_wink); // wink (optional) if (pause_for_wink > 0) { analogWrite(ledPin1, 0); // turn first led off, creating the wink effect: delay(pause_for_wink); analogWrite(ledPin1, brightness_maximum); // restore first led to max brightness, ending the wink effect: } // hold at max brightness after wink delay(pause_at_max_after_wink); // ramp down led brightness for (int brightness_current=brightness_maximum; brightness_current>=brightness_minimum;brightness_current--) //loop from brightness_maximum down to brightness_minimum { analogWrite(ledPin1, brightness_current); // set the brightness of pin 9: analogWrite(ledPin2, brightness_current); // set the brightness of pin 10: delay(pause_between_decrease); } // hold at minimum brightness delay(pause_at_min_brightness); } */
First we commented out the big section of existing code that deals with ramping the LED brightness up and down. It gets added back in later but for now the focus is on getting the proximity sensor to work, and it's just easier to keep that part lean.
The new lines all relate to setting up the HC-SR04 unit and are quite straightforward.
Another section, also new, is where we use the sensor to detect distance. This works by sending out a burst of ultrasound (the ping) and then listening for the echo that bounces back off of an object and, most importantly, keeping track of the time between the ping leaving the sensor and the echo coming back. This, the time taken by the sound to travel to the object and back to the sensor can be converted to distance using the speed of sound.
At the end of this section we have a variable (distance) that represents the distance to the object in cm.
The final new section decides what to do based on that distance. In this test we say that if the object is more than 8 cm away the LEDs should stay at low power (1) , and if the object is closer than 8 cm they should switch to full power (255).
This worked so the next step was to uncomment and reintroduce the original LED ramp up/down routine:
// BRIGHTNESS // minimum brightness level for the led int brightness_minimum = 1; // maximum brightness level for the led int brightness_maximum = 75; // PAUSES // pause between each increase in brightness int pause_between_increase = 12; // pause between each decrease in brightness int pause_between_decrease = 64; // pause at max brightness, before the wink effect int pause_at_max_before_wink = 2000; // pause for the wink effect int pause_for_wink = 100; // pause at max brightness, after the wink effect int pause_at_max_after_wink = 200; // the pause at min brightness int pause_at_min_brightness = 600; // DISTANCE // minimum distance before starting routine (cm) int min_distance = 30; #define trigPin 12 #define echoPin 13 const int ledPin1 = 9; // the pin that the LED is attached to const int ledPin2 = 10; // the pin that the LED is attached to void setup () { pinMode(ledPin1, OUTPUT); // declare pin 9 to be an output: pinMode(ledPin2, OUTPUT); // declare pin 10 to be an output: Serial.begin (9600); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); } void loop() { // set leds to minimum brightness analogWrite(ledPin1, brightness_minimum); analogWrite(ledPin2, brightness_minimum); long duration, distance; digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance = (duration / 2) / 29.1; if (distance < min_distance ) { // within proximity limit; start routine // ramp up led brightness for (int brightness_current = brightness_minimum; brightness_current <= brightness_maximum; brightness_current++) //loop from brightness_minimum to brightness_maximum { analogWrite(ledPin1, brightness_current); // set the brightness of pin 9: analogWrite(ledPin2, brightness_current); // set the brightness of pin 10: delay(pause_between_increase); } // hold at max brightness before wink delay(pause_at_max_before_wink); // wink (optional) if (pause_for_wink > 0) { analogWrite(ledPin1, 0); // turn first led off, creating the wink effect: delay(pause_for_wink); analogWrite(ledPin1, brightness_maximum); // restore first led to max brightness, ending the wink effect: } // hold at max brightness after wink delay(pause_at_max_after_wink); // ramp down led brightness for (int brightness_current = brightness_maximum; brightness_current >= brightness_minimum; brightness_current--) //loop from brightness_maximum down to brightness_minimum { analogWrite(ledPin1, brightness_current); // set the brightness of pin 9: analogWrite(ledPin2, brightness_current); // set the brightness of pin 10: delay(pause_between_decrease); } // hold at minimum brightness delay(pause_at_min_brightness); } else { // outside proximity limit; do not start routine // we're just going to loop back and test proximity again, but we pause briefly to conserve power // there is no need to use power checking proxinity continuously delay(100); } }
We've also moved the distance value up to the top, just like the brightness and timing values, to make fine-tuning easier.
This works pretty well. When an object moves within the distance then the LEDs ramp up, wink and fade out.
But after that... not so great. As long as a target stays within the distance the LED routine just keeps repeating – ramp up, wink, fade out.
It would be better if the LEDs stayed at full brightness as long as the object was in range, so we made these changes:
// BRIGHTNESS // minimum brightness level for the led int brightness_minimum = 1; // maximum brightness level for the led int brightness_maximum = 75; // current brightness level for the led // changes throughout the script but we set the default here int brightness_current = brightness_minimum; // PAUSES // pause between each increase in brightness int pause_between_increase = 12; // pause between each decrease in brightness int pause_between_decrease = 64; // pause at max brightness, before the wink effect int pause_at_max_before_wink = 2000; // pause for the wink effect int pause_for_wink = 100; // pause at max brightness, after the wink effect int pause_at_max_after_wink = 200; // the pause at min brightness int pause_at_min_brightness = 600; // DISTANCES // minimum distance before starting led routine (cm) int led_distance = 30; #define trigPin 12 #define echoPin 13 const int ledPin1 = 9; // the pin that the LED is attached to const int ledPin2 = 10; // the pin that the LED is attached to void setup () { // LEDS pinMode(ledPin1, OUTPUT); // declare pin 9 to be an output: pinMode(ledPin2, OUTPUT); // declare pin 10 to be an output: // set leds to minimum brightness analogWrite(ledPin1, brightness_current); analogWrite(ledPin2, brightness_current); // PROXIMITY SENSOR Serial.begin (9600); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); } void loop() { long duration, distance; digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance = (duration / 2) / 29.1; if (distance < led_distance ) { // within led proximity limit; start routine // ramp up led brightness for (int brightness_temp = brightness_current; brightness_temp <= brightness_maximum; brightness_temp++) //loop from brightness_minimum to brightness_maximum { analogWrite(ledPin1, brightness_temp); // set the brightness of pin 9: analogWrite(ledPin2, brightness_temp); // set the brightness of pin 10: delay(pause_between_increase); } // safely assume current brightness is now maximum brightness brightness_current = brightness_maximum; // hold at max brightness before wink delay(pause_at_max_before_wink); // wink (optional) if (pause_for_wink > 0) { analogWrite(ledPin1, 0); // turn first led off, creating the wink effect: delay(pause_for_wink); analogWrite(ledPin1, brightness_maximum); // restore first led to max brightness, ending the wink effect: } long duration, distance; digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance = (duration / 2) / 29.1; if (distance > led_distance ) { // ramp down led brightness for (int brightness_current = brightness_maximum; brightness_current >= brightness_minimum; brightness_current--) //loop from brightness_maximum down to brightness_minimum { analogWrite(ledPin1, brightness_current); // set the brightness of pin 9: analogWrite(ledPin2, brightness_current); // set the brightness of pin 10: delay(pause_between_decrease); } // safely assume current brightness is now minimum brightness brightness_current = brightness_minimum; } } else { // outside proximity limit; do not start routine // we're just going to loop back and test proximity again, but we pause briefly to conserve power // there is no need to use power checking proxinity continuously delay(100); } }
These changes to just that, and give us the most lifelike/spookiest effect yet.