Introduction: Motion Controlled Ultrasonic Lamp
Recently I've been learning about Ultrasonic waves and how to harness their power. It's simple enough to make some type of sonar device, but I wanted to take at a step cooler and see if I could use Ultrasonic waves to not only detect movement, but also the direction of that movement. So let's take it to the ultimate level of awesomeness and use ultrasonic waves to make a motion controlled desk lamp. There's a lot to do, so let's get tinkering!
Step 1: What You Will Need
If you want to follow along with this project here's a list of parts you will probably need to order:
- Ultrasonic Sensors (x 3) = $7.20
- Arduino Uno = $25.00
- High torque servos (x2) = $12.56
- AA Battery holder = $1.91
- 6x4x2 Project Box = $6.49
- Toggle Switch = $3.49
- Desk Lamp = $22.69
Here's a list of other parts and tools that you you will probably find around the house (as I did):
- Dremel and/or Drill
- Soldering equipment
- Breadboard and wires
Step 2: Connecting the Components
Alright, let's start connecting the hardware first. We're going to be using three ultrasonic sensors. They'll be used to detect swipe left, swipe right, and forward and back movement. Depending on what type of utrasonic sensor you have, you may have a different ammount of pins. Mine has 5, but we'll only be using four. You want to connect a ground port on the Arduino to the negative row on the breadboard and the 5v port to the positive row on the breadboard.
Now you can now connect the ground pins of the sensors to the ground row, and the VCC pins to the positive row. On the left sensor connect the Trig pin to port 8, and the echo to port 9. For the center sensor, trig goes to 10 and echo goes to 11. And for the right sensor, trig goes to 12 and echo goes to 13.
Using a separate breadboard, let's connect the servos. Start by connecting both ground wires together and both power wires together. Then connect a power supply to the motors. But you have to make sure you also connect the ground to a ground on the Arduino. Eventually this power supply will power both of the motors as well as the Arduino. Now connect one servo to port 6 on the Arduino and the other one to port 7. Then connect the Arduino up to your computer and let's start writing code!
Step 3: Writing the Arduino Code
When you have everything connected up the next step is to plug the Arduino into your computer and start writing code for it. You can download the code or copy and paste from below, but before you can run it, you will need to download the NewPing Arduino library and unzip it to your Arduino Libraries folder.
/*BEGIN ARDUINO CODE*/
/*IMPORT NECESSARY LIBRARIES*/<br>#include //Import the "NewPing" library for the Ultrasonic Sensors #include //Import the server library
/*DECLARE ALL VARIABLES*/
int slide = 0; //Slide detector variable
boolean left=false; //Left true/false variable boolean center=false; //Center true/false variable boolean right=true; //Right true/false varialbe
#define leftTrig 8 //left sensor output Arduino pin #define leftEcho 9 //left sensor input Arduino pin #define centerTrig 10 //center sensor output Arduino pin #define centerEcho 11 // center sensor input Arduino pin #define rightTrig 12 //right sensor output Arduino pin #define rightEcho 13 //right sensor input Arduino pin
Servo servoRotate; //Servo that will rotate the lamp Servo servoUpDown; //Servo that will move the lamp up/down
int servoRotatePin = 4; //Rotational servo Arduino pin int servoUpDownPin = 5;//Vertical servo Arduino pin
const int maxD = 20; //cm; maximum distance
long int lastTouch = -1; //ms int resetAfter = 2000; //ms int afterSlideDelay = 500; //ms; all slides ignored after successful slide int afterSlideOppositeDelay = 1500; //left slides ignored after successful right slide
int SLIDELEFT_BEGIN = -1; //Motion was detected from right int SLIDELEFT_TO_CENTER = -2; //motion was detected from right to center
int SLIDENONE = 0; //No motion was detected
int SLIDERIGHT_BEGIN = 1; // motion was detected from left int SLIDERIGHT_T0_CENTER = 2; // motion was detected from left to center
int centerDistance;
/*SETUP SERIAL MONITOR AND SERVOS*/ void setup() { Serial.begin(9600); servoRotate.attach(servoRotatePin, 0, 180); servoUpDown.attach(servoUpDownPin, 0, 180); servoRotate.write(0); }
/*PING ULTRASONIC SENSORS, CALCULATE SLIDE STATUS*/ void loop() { left = ping(leftTrig, leftEcho); //send ping to left sensor center = ping(centerTrig, centerEcho); //send ping to center sensor right = ping(rightTrig, rightEcho); //send ping to right censor centerDistance = getDistance(centerTrig, centerEcho); //get object distance from center sensor //If there is anything detected by the snensors, store the time to "last touch" if (left || center || right){ lastTouch=millis(); } //If the time last sensed is greater than the reset time, reset the slide variable if (millis()-lastTouch>resetAfter) { slide = 0; Serial.println("Reset slide and timer. "); } //Detect a slide to the right if (slide >= SLIDENONE) { if ( (left) && (!right) ) slide = SLIDERIGHT_BEGIN; if (center && (slide == SLIDERIGHT_BEGIN)) slide = SLIDERIGHT_T0_CENTER; if (right && (slide == SLIDERIGHT_T0_CENTER)){ slideNow('R'); } } //Detect a slide to the left if (slide <= SLIDENONE) { if ( right && (!left) ) slide = SLIDELEFT_BEGIN; if (center && slide == SLIDELEFT_BEGIN) slide = SLIDELEFT_TO_CENTER; if (left && slide == SLIDELEFT_TO_CENTER){ slideNow('L'); } } //detect center distance if(slide){ if(center && (!left) && (!right)){ if (centerDistance){ verticalMove(centerDistance); } } } delay(50); //ms }
/*FUNCTION TO SEND OUT A PING*/ boolean ping(int trigPin, int echoPin) //, int ledPin) { int d = getDistance(trigPin, echoPin); //cm boolean pinActivated = false; if (d < maxD){ pinActivated = true; } else { pinActivated = false; } return pinActivated; }
/*FUNCTION TO GET AND CACLULATE DISTANCE*/ int getDistance(int trigPin, int echoPin) { long duration, inches, cm; pinMode(trigPin, OUTPUT); digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); pinMode(echoPin, INPUT); duration = pulseIn(echoPin, HIGH); //inches = microsecondsToInches(duration); cm = microsecondsToCentimeters(duration); //return(inches); return(cm); }
/*CONVERT MICROSECONDS TO INCHES long microsecondsToInches(long microseconds) { return microseconds / 74 / 2; } *
*CONVERT MICROSECONDS TO CENTIMETERS*/ long microsecondsToCentimeters(long microseconds) { return microseconds / 29 / 2; }
/*SLIDE LEFT AND SLIDE RIGHT MOVEMENT FUNCTIONS*/ void slideNow(char direction){ if ('R' == direction){ servoRotation('R'); Serial.println("Rotate Right"); } if ('L' == direction){ servoRotation('L'); Serial.println("rotate Left"); } delay(afterSlideDelay); slide = SLIDENONE; }
/*UP AND DOWN MOVEMENT FUNCTIONS*/ int verticalMove(int originalDistance){ if (originalDistance > 0 && originalDistance <= 10){ servoVertical('D'); Serial.print("Move Down: "); Serial.println(originalDistance); } else if (originalDistance <= 30){ servoVertical('U'); Serial.print("Move Up: "); Serial.println(originalDistance); } delay(afterSlideDelay); slide = SLIDENONE; }
/*SERVO ROTATION FUNCTION*/ void servoRotation(char whichway){ int currentPos = servoRotate.read(); if ('R' == whichway){ servoRotate.write(currentPos + 15); }else if('L' == whichway){ servoRotate.write(currentPos - 15); } }
/*SERVO UP/DOWN FUNCTION*/ void servoVertical(char upOrDown){ int currentPos = servoUpDown.read(); if ('U' == upOrDown){ servoUpDown.write(currentPos - 25); }else if('D' == upOrDown){ servoUpDown.write(currentPos + 25); } }
Step 4: Testing the Code
Here is a test of motor functionality using the code above. When you swipe left and right, one motor should move, and when you move forward and back, the second motor should move.
Step 5: Creating an Enclosure
To hide all of the electronics and to make the lamp look a lot more functional and less nerdy, it's best to put everything into a project enclosure box. The 6x4x2.5 project enclosure from Radio Shack is the perfect size for the lamp base and can easily hold all of our electronics. But it will require some modifications in order to make it functional.
- Using a dremel (or other rotary tool), you will need to cut a square on the top of the box to house our rotational servo motor. Make sure it's not too large, but that the motor fits snuggly inside of it.
- On one of the long sides of the box, you will need to drill 8 holes that are about 3/4 inches in diameter. These will be fore the ultrasonic sensors to peep out of. MAKE SURE YOUR MEASUREMENTS ARE ACCURATE. When in doubt, measure again.
- On the reverse side of the box from the ultrasonic sensor holes, you will need to drill one final hole for the power switch. Mine was about 1/2 inch in diameter.
Step 6: Simplifying the Circuitry
In order to save on space, I swapped out the Arduino Uno for a Arduino Pro Mini compatible board. And using the smaller Arduinio, I was able to solder everything onto one circuit board. I soldered two 3 pronged connector pins so that the servo's can plug right into the board, and I also soldered all power and ground sources to the battery pack (and added a switch). Please refer to the Fritzing sketch above to see how the circuitry connects.
Step 7: Attaching the Lamp
With the Project Box modded and the circuitry simplified, let's attach the motors to the lamp. Here's the steps I took to get everything attached
- Mount the rotational motor into the project box and fasten it into place.
- Screw the base of the lamp on the rotational motor head
- Notice in the first picture that I already have the vertical motor cable already threaded through the base. This is important because it will be a pain to run this through later.
- Attach the rest of the lamp to the base.
- Mount the vertical motor to the middle of the bottom arm.
- Using strong wire (I was able to use a coat hanger), attach the motor head to the upper arm so that it pulls it up and down.
Step 8: Final Steps
Now that the lamp and motors are mounted to the project box, you can now connect the servos to the circuit board and mount all of the circuitry into the box, making sure that the switch is properly attached and the Ultrasonic sensors are peeping through the front holes.
You'll notice that the lamp is extremely wobbly from balancing on the rotating servo head, so I attached some thumbtacks to the base of the lamp to help prevent tilting.
Step 9: Testing Out the Lamp / Final Comments
Here is a final test of the functionality. Swiping left and right makes the lamp rotate left and right. Moving up/down makes it move up and down.
There are a couple of improvements I'd like to make to this project, and I'd love suggestions on how to fix these issues.
- I'd love to find someway (besides tacks) to stabilize the movement of the servos with the lamp.
- The code works, but is very glitchy. If anyone has anyway to make it more smooth/stable, I'd love the suggestions!