Project tutorial

LEGO Wall-E with Arduino © GPL3+

My first project combines everyone's favorite robot with everyone's favorite tinkering platform. Hope you like it!

  • 15,368 views
  • 14 comments
  • 84 respects

Components and supplies

LEGO Wall-E
Had to get one 2nd hand because they don't make them anymore
×1
Ard nano
Arduino Nano R3
×1
DC motor (generic)
×2
L298N Dual Motor Controller
×1
Bi-colour LED
with 3 pins
×1
Infra-red sensor
×1
Adafruit industries ada1536 image
Buzzer
One that can play different tones
×1
9V battery
×1
08377 02 l
Resistor 330 ohm
×3
11026 02
Jumper wires (generic)
×20

Necessary tools and machines

09507 01
Soldering iron (generic)

Apps and online services

About this project

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 Code

As 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 up

The 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 Action

Here are a couple of videos showing him in action:

Wall-E's first test!

And here I tested what he would do if he was stuck in a corner:

Wall-E is trying to get out of the 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.

Code

Wall_e_controlArduino
This is the central control file for Wall-E. It is basic, but all he needs at the moment.
// 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);
}

Schematics

Wall_e_wiring
Meaning of the cable colours:
red = voltage (positive)
black = ground (negative)
yellow = signal for infra-red sensor
orange and green = connections for red and green LED input
purple = motor direction control
blue = motor speed control (unfortunately, the Fritzing part did not have the two pins that my motor bridge had for these connections, so it looks like loose wires at the moment)
wall-e2_3P6X71BCnP.fzz

Comments

Similar projects you might like

Arduino Bluetooth Basic Tutorial

by Mayoogh Girish

  • 454,681 views
  • 42 comments
  • 236 respects

Home Automation Using Raspberry Pi 2 And Windows 10 IoT

Project tutorial by Anurag S. Vasanwala

  • 285,749 views
  • 95 comments
  • 672 respects

Security Access Using RFID Reader

by Aritro Mukherjee

  • 233,498 views
  • 40 comments
  • 245 respects

OpenCat

Project in progress by Team Petoi

  • 196,226 views
  • 154 comments
  • 1,364 respects
Add projectSign up / Login