Light-Seeking Robot using the Arduino
Monday, May 25th, 2009I’ve always been interested in physical computing. Even though I love creating software, I’ve always felt that creating websites and software doesn’t go far enough to satiate my creative desire. Enter the Arduino, a great open-source micro processor and prototyping board for around $30. This post marks the first, in what I hope will be a long line of tutorials and projects using the Arduino to develop robots and sensor driven applications that I hope to release into the wild. This post details a light-seeking robot with an Arduino brain, kind of the “Hello World!” application for autonomous robotics.
The Parts
I got a number of parts from several different sources. I had some parts lying around, but I’ll also provide links where you too can buy yourself all the light-seeking robot parts your heart desires.
- Arduino (1 @ $30.00 from Adafruit.com) – I actually got the Arduino Starter kit from Adafruit.com which also came with the 9V battery holder you see in the pictures and videos below.
- Arduino Motor Shield (1 @ $19.50 from Adafruit.com) – You don’t actually need the motor shield, but it provides a great base for prototyping and I recommend it if you’re doing repeated prototyping with motors and servers.
- RP5 Tracked Chassis (1 @ 49.95 from Pololu.com) – I picked this chassis because it has all the necessary parts: geared motor assembly, tracks (who doesn’t like tracks), battery pack, etc. You can make your own chassis, of course, but for those interested, this is a nice one that’s virtually plug and play.
- CDS Photoresistors (2 @ $1.50 from Adafruit.com) – For the eyes. You can get these from anywhere, but if you order from Adafruit.com, you might as well get some of these too.
- 10 Kohms Resistors (4) – You can get these from anywhere.
- Momentary Switch – (1 @ $3.95 for a Pack of 4 from Radio Shack) – It’s a tiny little push button switch, and you can use any you would like…this is just the one I used.
Prototype I – Proof of Concept
While I was waiting for my chassis to arrive, I put together an initial prototype to test the circuit and code. The first prototype was rough, to say the least, and it didn’t move, but the code worked. I used the prototype board you see in the picture (available at Radio Shack) as a base to mount (tape) the motors to. I put tape on the axles to see if the code was actually making the motors turn the right way. It was.

And here’s a video, showing it work in all it’s glory.
As it says in the video, I initially gave the robot seeking and avoiding behavior (you can see it in the code below in the function foundSource()), but I realized that there are times when the robot will never find it’s source (especially if that source is the sun) and that it was needless behavior. I left the runAway variable in the code through so you could easily turn the robot into a photophobe. You could also mount a switch on the robot that changes the behavior from photovore to photophobe as well.
Prototype II
Once I got the chassis from Pololu, I put together a better prototype, one that moves and turns around when it runs into something…you know, typical robot behavior. In the following pictures, you can see the very crude method of assembly, but it works…and that was really the point.

I literally moved the prototype board in the first picture above to the top of the chassis and hooked up the motors to the motor shield. I added a small switch for the motor power so I could stop screwing and unscrewing the motor’s power supply. Everything is just taped down. This is, again, more of a proof of concept than a finished robot. I plan on making a custom motor driver for the chassis and better mount for the Arduino and other sensors.

To give the push button more surface area, I added a plow to the front, with an “ultra-strong” electrical tape hinge. Obviously, when the robot runs into something, it pushes the button and the turnAround() function executes.

There’s not a lot of room in the chassis, but you could replace the AA battery pack with a 9V to give you some more space. But there are mounting holes on the corners which is probably a preferable location for your robot’s brains.
Here it is in action:
The Circuit
The circuit is fairly simple, especially because I’m using the Motor Shield. This circuit diagram illustrates the sensor circuitry and status led you see on the white bread board above. The motors are attached to the M1 and M2 points on the motor shield (positive leads toward the inside of the board). I used OmniGraffle to draw up the circuit, but I’m still looking for better templates so my circuits are prettier, more accurate, etc, but this drawing should illustrate the connections just fine.

The Code
You program the Arduino in Wiring, which is basically a library in C++. Forgive me any trespasses below. C++ is not a language I’ve worked in a lot, and the code below could most likely use some optimization. I originally had many delays written into the loop because that’s what I saw a lot of other code doing. That resulted in erratic behavior and a lot of running into stuff even though the object’s shadow should have caused the robot to avoid it. When I removed the delay and averaged the light sensor readings, I got a very responsive robot. I’ve also left a lot of code in the source because I’m working on building a robot base that I can use over and over again as I install more sensors and create more interesting behavior.
#include #define NUMREADINGS 5 #define LEFTSENSOR 3 #define RIGHTSENSOR 5 #define BUMPSENSOR 2 #define TOPSPEED 200 int i; int valLefat; int valRight; int valCenter; int oldLeft = 0; int oldRight = 0; int sensitivity = 40; int threshold = 50; int topThreshold = 650; boolean runAway = false; int leftReadings[NUMREADINGS]; int rightReadings[NUMREADINGS]; int index = 0; // the index of the current reading int leftTotal = 0; // the running total int leftAverage = 0; // the average int rightTotal = 0; // the running total int rightAverage = 0; // the average AF_DCMotor rightMotor(2, MOTOR12_8KHZ); // create motor #2, 64KHz pwm AF_DCMotor leftMotor(1, MOTOR12_8KHZ); // create motor #1, 64KHz pwm void setup() { Serial.begin(9600); rightMotor.setSpeed(TOPSPEED); leftMotor.setSpeed(TOPSPEED); pinMode(BUMPSENSOR, INPUT); moveForward(); delay(500); for (int i = 0; i < NUMREADINGS; i++){ leftReadings[i] = 0; // initialize all the readings to 0 rightReadings[i] = 0; } } void loop() { checkForBump(); averageReadings(); checkLightandMove(); } void checkLightandMove(){ if(valLeft > threshold && valRight > threshold){ if((valLeft > valRight) && (valLeft - valRight > sensitivity)){ if(runAway == false){ turnRight(); }else{ turnLeft(); } }else if((valLeft < valRight) && (valRight - valLeft > sensitivity)){ if(runAway == false){ turnLeft(); }else{ turnRight(); } }else{ moveForward(); } }else{ //turnAround(); stop(); } if(valLeft > topThreshold && valRight > topThreshold){ runAway = false; } //delay(500); } void checkForBump(){ int bumped = digitalRead(BUMPSENSOR); Serial.println(digitalRead(BUMPSENSOR)); if(bumped == HIGH){ Serial.println(); turnAround(); } } boolean foundSource(){ return oldLeft > valLeft && oldRight > valRight; //the robot has reached the source of the light, or the point of maximum brightness } void moveForward(){ Serial.println("Move Forward"); rightMotor.run(FORWARD); leftMotor.run(FORWARD); } void speedUp(){ for (i=0; i==TOPSPEED; i++) { rightMotor.setSpeed(i); leftMotor.setSpeed(i); } } void slowToStop(){ for (i=TOPSPEED; i==0; i--) { rightMotor.setSpeed(i); leftMotor.setSpeed(i); } rightMotor.run(RELEASE); leftMotor.run(RELEASE); } void turnLeft(){ Serial.println("Turn Left"); rightMotor.run(BACKWARD); leftMotor.run(FORWARD); } void turnRight(){ Serial.println("Turn Right"); rightMotor.run(FORWARD); leftMotor.run(BACKWARD); } void stop(){ Serial.println("Stop"); rightMotor.run(RELEASE); leftMotor.run(RELEASE); delay(500); } void turnAround(){ slowToStop(); delay(500); moveBackward(); delay(2000); turnLeft(); delay(2000); stop(); //runAway = true; } void moveBackward(){ Serial.println("Move Backward"); rightMotor.run(RELEASE); leftMotor.run(RELEASE); rightMotor.run(BACKWARD); leftMotor.run(BACKWARD); } void averageReadings(){ leftTotal -= leftReadings[index]; // subtract the last reading leftReadings[index] = analogRead(LEFTSENSOR); // read from the sensor leftTotal += leftReadings[index]; // add the reading to the total rightTotal -= rightReadings[index]; // subtract the last reading rightReadings[index] = analogRead(RIGHTSENSOR); // read from the sensor rightTotal += rightReadings[index]; // add the reading to the total index = (index + 1); // advance to the next index if (index >= NUMREADINGS) // if we're at the end of the array... index = 0; // ...wrap around to the beginning valLeft = leftTotal / NUMREADINGS; valRight = rightTotal / NUMREADINGS; Serial.print(valLeft, DEC); // prints the left sensor value Serial.print(" | "); Serial.println(valRight, DEC); // prints the right sensor value }
