Everybody knows the movie Wall-E (and if you don't, go and watch it now!) and the yellow hero who is trying to clean up the earth. In this project, I used a Lego version of our little friend and taught him how to avoid obstacles. This was my first project and a great learning experience to figure out the basics of electronics.
Step 1 - The CodeAs a software developer by trade, I thought about what I wanted him to do and started with the code.
// This program is to control the Wall-E Lego robot.
// Wall-E is driving around. When he sees an obstacle, he stops to look around and chooses another path.
// Arduino Nano has 21 pins that can be used for digitalRead and digitalWrite
// PWM pins 3, 5, 6, 9, 10, 11 can be used for analogWrite
// pins 0 and 1 can be used for TTL
// pins 2 and 3 can be used for external interrupts
// pins 10, 11, 12, 13 support SPI communication
// pin 13 can be internal LED
// pins 14 to 21 are also analog pins A0 to A7 and can be used for analogRead
#define INFRA_RED 9 // can be any pin
#define GREEN_LED 7 // can be any pin but needs resistor, maybe 220 Ohm - or the ground pin gets 1 kOhm
#define RED_LED 8 // can be any pin but needs resistor, maybe 220 Ohm - or the ground pin gets 1 kOhm
#define BUZZER 10 // needs to be PWM pin to set frequency, needs resistor, maybe 1 kOhm
// MR is the right motor, ML is the left motor
#define MR_1 A1 // can be any pin, so let's make them correspond to the pin numbers on the L289N shield
#define MR_2 A2 // can be any pin, so let's make them correspond to the pin numbers on the L289N shield
#define MR_ENABLE 5 // needs to be PWM pin for speed control
#define ML_1 A3 // can be any pin, so let's make them correspond to the pin numbers on the L289N shield
#define ML_2 A4 // can be any pin, so let's make them correspond to the pin numbers on the L289N shield
#define ML_ENABLE 6 // needs to be PWM pin for speed control
// set his normal speed to maximum
const int NORMAL_SPEED = 255;
void setup() {
// right after pressing the reset button, wait for a bit so we can turn him off without damaging any components through voltage spikes
delay(2000);
// initialise LEDs and buzzer
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
// pinMode(BUZZER, OUTPUT); // not necessary
// reset LED to green
digitalWrite(RED_LED, LOW);
digitalWrite(GREEN_LED, HIGH);
// set DC motor pins
pinMode(MR_ENABLE, OUTPUT); // motor right
pinMode(MR_1, OUTPUT);
pinMode(MR_2, OUTPUT);
pinMode(ML_ENABLE, OUTPUT); // motor left
pinMode(ML_1, OUTPUT);
pinMode(ML_2, OUTPUT);
// initialise infra red
pinMode(INFRA_RED, INPUT);
// initialise random number generator for random turns
randomSeed(analogRead(0));
// say hello
playHello();
}
void loop() {
// normal operations
driveForwards(NORMAL_SPEED);
// set LED to green
digitalWrite(RED_LED, LOW);
digitalWrite(GREEN_LED, HIGH);
// check for obstacles
if (digitalRead(INFRA_RED) == LOW) { //LOW means obstacle detected
// change LED to red
digitalWrite(GREEN_LED, LOW);
digitalWrite(RED_LED, HIGH);
// stop motors
stopDriving();
// play uh-oh sound
playUhOh();
// check left
turnLeft(500);
boolean obstacleLeft = false;
if (digitalRead(INFRA_RED) == LOW) {
obstacleLeft = true;
}
// turn back to centre
delay(100);
turnRight(500);
// wait a little, we don't want to seem rushed
delay(500);
// check right
turnRight(500);
boolean obstacleRight = false;
if (digitalRead(INFRA_RED) == LOW) {
obstacleRight = true;
}
// turn back to centre
delay(100);
turnLeft(500);
// now check how to get out of here
if (obstacleLeft && obstacleRight) {
driveBackwards(NORMAL_SPEED / 3);
// beep while going backwards for 5 seconds
for (int i = 0; i < 5; i++) {
tone(BUZZER, 1000, 500);
delay(1000);
}
// to avoid getting stuck somewhere, randomly turn into a direction before continuing journey
randomTurn(800, 1600);
} else if (obstacleLeft) {
turnRight(1000);
} else if (obstacleRight) {
turnLeft(1000);
} else {
randomTurn(1000, 1800);
}
}
// do random stuff for more interaction
int number = random(100); // creates a random number between 0 and 99
if (number == 0) {
randomTurn(200,2000);
}
}
void driveForwards(int speed) {
// set the motors to go in the same direction
digitalWrite(MR_1, LOW);
digitalWrite(MR_2, HIGH);
digitalWrite(ML_1, HIGH);
digitalWrite(ML_2, LOW);
setSpeed(speed);
}
void driveBackwards(int speed) {
// set the motors to go in the opposite direction
digitalWrite(MR_1, HIGH);
digitalWrite(MR_2, LOW);
digitalWrite(ML_1, LOW);
digitalWrite(ML_2, HIGH);
setSpeed(speed);
}
void turnLeft(int duration) {
// turn left by going forwards with the right wheel and backwards with the left wheel
digitalWrite(MR_1, HIGH);
digitalWrite(MR_2, LOW);
digitalWrite(ML_1, HIGH);
digitalWrite(ML_2, LOW);
// slow down to turn
setSpeed(NORMAL_SPEED / 2);
delay(duration);
stopDriving();
}
void turnRight(int duration) {
// turn right by going backwards with the right wheel and forwards with the left wheel
digitalWrite(MR_1, LOW);
digitalWrite(MR_2, HIGH);
digitalWrite(ML_1, LOW);
digitalWrite(ML_2, HIGH);
// slow down to turn
setSpeed(NORMAL_SPEED / 2);
delay(duration);
stopDriving();
}
void stopDriving() {
// turn off all the motor pins
digitalWrite(MR_1, LOW);
digitalWrite(MR_2, LOW);
digitalWrite(ML_1, LOW);
digitalWrite(ML_2, LOW);
// not sure what to do with the ENABLE pins, but doesn't hurt to turn them off as well I guess
digitalWrite(MR_ENABLE, LOW);
digitalWrite(ML_ENABLE, LOW);
}
void setSpeed(int speed) {
// speed must be between 0 and 255
speed = constrain(speed, 0, 255);
// set the speed to turn the motors on
analogWrite(MR_ENABLE, speed);
analogWrite(ML_ENABLE, speed);
}
void randomTurn(int minimum, int maximum) {
unsigned long time = millis();
int duration = random(minimum, maximum);
if (time % 2) {
turnRight(duration);
} else {
turnLeft(duration);
}
}
void playHello() {
tone(BUZZER, 262, 250); // plays C4
delay(300);
tone(BUZZER, 330, 250); // plays E4
delay(300);
tone(BUZZER, 392, 250); // plays G4
delay(300);
tone(BUZZER, 523, 500); // plays C5
delay(550);
}
void playUhOh() {
tone(BUZZER, 523, 250); // plays C5
delay(300);
tone(BUZZER, 415, 500); // plays Gis4
delay(600);
}
When you disconnect a running motor, voltage spikes can occur and damage your electronics. Therefore, I make Wall-E wait for two seconds before he does anything. That means I can just press the Reset button on the Arduino and quickly disconnect the battery without damaging anything.
He plays a little melody when he wakes up and then starts driving. When he sees an obstacle, he stops, plays an "Uh-oh" sound, and looks around to determine the best way. The type of infra-red sensor that I used has a little screw on its back that lets you determine the distance at which it creates a signal. That's why there are no distance calculations in the code. (I wanted to use an ultrasonic sensor first, but it doesn't fit with his eyes.)
Wall-E first checks if the left side is clear, and then if the right side is clear. If both sides are blocked, he goes backwards while beeping like heavy machinery on a construction site, and then turns into a random direction and continues. If only one side is blocked, he continues to the other side. If both sides are free, he picks one randomly and continues his way.
I tried to make him do random turns, but that part is not quite completed yet. I am trying to use the in-built Arduino timer. Let me know in the comments if you have an idea how to optimise it!
Step 2 - Wiring him upThe first thing was to get the infra-red sensor built into the eye so it doesn't look too obvious. I took it apart, super-glued a Lego pin to the sensor (so his eye can move up and down) and then used blue-tack to put the Lego pieces back around the sensor:
The most important thing about the motors was that they have enough torque, because the setup of Wall-E's wheels needs quite a bit of force to move. I had to solder wires to the motors and attach them in a way that they connect securely to the Lego. So I took his wheels apart, ordered a whole bag full of Lego Technic pins, wrapped plaster tape (is that how you call it? It's like soft tape that can go onto your skin) around the motor shafts, and stuck them into two pins that became the main axle for each wheel. This worked for a few minutes, but then the friction was too high for the glue in the tape to hold. Luckily, the Technic pins have little grooves on the side, so the tape did have a place to hold on to, which they happily did after being soaked in super glue.
I also took a bit of his chest off to stick the LED through. Then I connected all the parts and the Arduino.
The motor shield just fit into his tummy:
The diagram isn't very clear either, but I tried to colour-code the wires for a better overview:
As per convention, all red wires are positive ("deliver" electricity) and all black wires are negative ("receive" electricity). The yellow wire on the top right carries the signal for the infra-red sensor; the orange and green wires are for the three-pin bi-colour LED, the purple wires are for telling the motor shield into which direction to turn the motors, and the blue cables tell the motor shield how fast to turn them.
The motor shield had a very good tutorial that made it easy to connect and use. Unfortunately, the Fritzing part did not have the two pins for setting the speed, so the blue cables just randomly end up on the motor shield in the diagram.
One more problem that I faced while sticking it all together was a lack of Voltage and Ground pins. I wanted to feed the motors directly through the motor shield so they can get as much power as possible, but I somehow had to feed 5V to the Arduino and the infra-red sensor as well. So I did what pretty much everybody on the web said I shouldn't do: I connected the 5V output of the motor shield to the 5V pin of the Arduino as an input. Now with the shield that I'm using, I can be absolutely sure that it outputs a regulated 5V without any nasty spikes that could damage my Arduino. If you attach an unregulated power source to that pin, you will probably fry something. I wanted to use the Vin pin at first, but that one has an in-built mechanism that regulates everything down, so my 5V would have turned into 3.8V or so, which is not enough for the Arduino to function properly. Instead, I used the free Vin to power(!) the infra-red sensor with 5V, because I didn't have any cable splitters, and I knew there would be 5V coming out of there as well. Yeah, it started to feel a bit like Frankenstein at this point. But it worked!
Step 3 - Wall-E in ActionHere are a couple of videos showing him in action:
And here I tested what he would do if he was stuck in a corner:
So this was my first little project. I am now planning to optimise the cable connections and maybe add a Servo motor so he can turn his head. I might even buy splitters and a smaller motor shield and board so I can fit everything into his tummy.
And so Wall-E lived happily ever after. The end.
Comments