Light-seeking robot on an Arduino Uno
A two-wheel robot with a photoresistor module on each front corner. It turns toward the brighter side, spins slowly in the dark to find a lamp, and stops when it reaches one. The whole build, an Arduino Uno and 4 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Beginner About 20 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $44. On Mokxi they're free.
What you'll learn
- Sensors
- Motors
- Using the Motor driver module
- Using the Robot car chassis
- Difficulty: Beginner
- Time: About 20 minutes
- Board: Arduino Uno R3
- Parts: 8
How it works
A two-wheel robot with a photoresistor module on each front corner. It turns toward the brighter side, spins slowly in the dark to find a lamp, and stops when it reaches one.
Arduino Uno R3 runs the sketch below. The circuit around it: Motor driver module, L298N, Robot car chassis, 2WD, Photoresistor sensor module. pinMode sets pins[i] as an output. digitalWrite drives pins[i] LOW. analogRead measures the voltage on pin as a number. analogWrite sends a PWM signal out on enable. delay waits 20 milliseconds.
Parts list
8 parts, plus the jumper wires. They retail for about $44; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Motor driver module, L298N
- 1 × Robot car chassis, 2WD
- 2 × Photoresistor sensor moduleIn Desk Buddy Kit, Escape Box Kit.
- 1 × Power, 7.4 V
- 2 × Power, 5 V
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Arduino Uno R3 pin GND | Ground |
| Motor driver module, L298N pin ENB | Arduino Uno R3 pin 10 |
| Motor driver module, L298N pin IN4 | Arduino Uno R3 pin 9 |
| Motor driver module, L298N pin IN3 | Arduino Uno R3 pin 8 |
| Motor driver module, L298N pin IN2 | Arduino Uno R3 pin 7 |
| Motor driver module, L298N pin IN1 | Arduino Uno R3 pin 6 |
| Motor driver module, L298N pin ENA | Arduino Uno R3 pin 5 |
| Photoresistor sensor module (1) pin AO | Arduino Uno R3 pin A0 |
| Photoresistor sensor module (2) pin AO | Arduino Uno R3 pin A1 |
| Robot car chassis, 2WD pin L+ | Motor driver module, L298N pin OUT1 |
| Robot car chassis, 2WD pin L- | Motor driver module, L298N pin OUT2 |
| Motor driver module, L298N pin 12V | 7.4 V |
| Motor driver module, L298N pin GND | Ground |
| Robot car chassis, 2WD pin R+ | Motor driver module, L298N pin OUT3 |
| Robot car chassis, 2WD pin R- | Motor driver module, L298N pin OUT4 |
| Photoresistor sensor module (1) pin VCC | 5 V |
| Photoresistor sensor module (1) pin GND | Ground |
| Photoresistor sensor module (2) pin VCC | 5 V |
| Photoresistor sensor module (2) pin GND | Ground |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Light-seeking robot: two photoresistors, one on each front corner, and a
// steering rule so simple it looks like instinct.
//
// Photoresistor modules: left AO on A0, right AO on A1 (VCC to 5V, GND to ground)
// L298N: ENA pin 5, IN1 pin 6, IN2 pin 7 (left motor) ENB pin 10, IN3 pin 8, IN4 pin 9 (right)
// 12V from the 7.4 V pack, GND to the Uno's GND
//
// # The sensors
//
// An LDR's resistance falls as the light rises. The module puts it under a
// 10k resistor, so its AO pin gets LOWER as the light gets brighter: about 1000
// in the dark and 100 in front of a lamp. The sketch flips that around, so
// brightness() is 0 in the dark and around 900 in front of a lamp.
//
// # The steering rule
//
// Whichever eye sees more light, the robot turns toward it, by a turn
// proportional to how much more. Both eyes equal and it drives straight. That
// is the whole idea behind many "behavior" robots: no map and no plan, only a
// reaction wired from the sensors to the wheels.
//
// # The three situations
//
// bright (both eyes above ARRIVED): it is at the lamp, so it stops
// lit steer toward the brighter eye and drive
// dark (both below DARK): nothing to steer by, so spin slowly until a
// lamp swings into view
//
// The readings are averaged over a few samples because an analogRead of a cheap
// sensor is never quite the same twice.
const int LEFT_EYE = A0;
const int RIGHT_EYE = A1;
const int ENA = 5;
const int IN1 = 6;
const int IN2 = 7;
const int ENB = 10;
const int IN3 = 8;
const int IN4 = 9;
const int DARK = 90;
const int ARRIVED = 780;
const int CRUISE = 190;
const int CREEP = 150;
const int GAIN = 2;
const char *mood = "";
void side(int enable, int a, int b, int speed) {
if (speed > 255) speed = 255;
if (speed < -255) speed = -255;
digitalWrite(a, speed > 0 ? HIGH : LOW);
digitalWrite(b, speed < 0 ? HIGH : LOW);
analogWrite(enable, speed < 0 ? -speed : speed);
}
void drive(int left, int right) {
side(ENA, IN1, IN2, left);
side(ENB, IN3, IN4, right);
}
int brightness(int pin) {
long sum = 0;
for (int i = 0; i < 8; i++) sum += analogRead(pin);
return 1023 - sum / 8;
}
void say(const char *next) {
if (next != mood) {
mood = next;
Serial.println(next);
}
}
void setup() {
const int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4};
for (int i = 0; i < 6; i++) {
pinMode(pins[i], OUTPUT);
digitalWrite(pins[i], LOW);
}
Serial.begin(115200);
Serial.println("Mokxi Arduino Uno: light-seeking robot");
Serial.println("ready");
}
void loop() {
int left = brightness(LEFT_EYE);
int right = brightness(RIGHT_EYE);
if (left > ARRIVED && right > ARRIVED) {
drive(0, 0);
say("reached the light: stopped");
} else if (left < DARK && right < DARK) {
// Too dark to steer by: turn slowly on the spot and look for a lamp.
drive(CREEP, -CREEP);
say("dark: searching");
} else {
// More light on the left means a left turn: slow the left wheel, speed up
// the right.
int turn = (left - right) * GAIN / 4;
drive(CRUISE - turn, CRUISE + turn);
say(turn > 15 ? "steering left" : turn < -15 ? "steering right" : "heading for the light");
}
delay(20);
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
Runs once when the board starts. It gets the pins and libraries ready.
void setup() {
const int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4};
for (int i = 0; i < 6; i++) {
pinMode(pins[i], OUTPUT);
digitalWrite(pins[i], LOW);
}
Serial.begin(115200);
Serial.println("Mokxi Arduino Uno: light-seeking robot");
Serial.println("ready");
}loop sketch.ino
Runs over and over for as long as the board has power. This is where the project does its work.
void loop() {
int left = brightness(LEFT_EYE);
int right = brightness(RIGHT_EYE);
if (left > ARRIVED && right > ARRIVED) {
drive(0, 0);
say("reached the light: stopped");
} else if (left < DARK && right < DARK) {
// Too dark to steer by: turn slowly on the spot and look for a lamp.
drive(CREEP, -CREEP);
say("dark: searching");
} else {
// More light on the left means a left turn: slow the left wheel, speed up
// the right.
int turn = (left - right) * GAIN / 4;
drive(CRUISE - turn, CRUISE + turn);
say(turn > 15 ? "steering left" : turn < -15 ? "steering right" : "heading for the light");
}
delay(20);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The motors do not run or run weakly. Motor driver module
Check the power jumpers and supply. Motor voltage goes on the +12V terminal, and ground must join the Arduino's ground. The driver drops about 2 V, so use a supply above the motor voltage. - Speed control with analogWrite does nothing. Motor driver module
Remove the ENA and ENB jumpers and connect those pins to PWM pins. With the jumpers on, the channel is always at full speed. - The car drives in circles or goes backwards. Robot car chassis
Motor polarity decides the direction. Swap the two wires on a motor that spins the wrong way. For a straight run, the motors are never identical, so trim the speed on one side. - The motors do not run, or the board resets when they start. Robot car chassis
Motors draw far more current than a pin can give. Use a motor driver such as an L298N or L293D. Power the motors from a separate battery pack and join its ground with the Arduino ground. - The digital output never changes, or is always on, no matter the light. Photoresistor sensor module
Turn the trimmer on the module until DO flips at the light level you want. The digital output is set by that threshold, and which direction means bright depends on the board. - Analog readings go down when it gets brighter, and the code expects the opposite. Photoresistor sensor module
The LDR sits in a divider, so the direction depends on how the module is built. Print the raw value in the light and dark, then flip it in code with 1023 minus the reading if needed. - Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected. - Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.