Introduction: Arduino IR Robot Display Platform

About: Arduino and Robotics Nerd, and alumni of FIRST Robotics Team 5683 (Go R.A.V.E.!). Blinking LEDs with Arduino is still one of my favorite pastimes even after years of doing it.

This is a visual display system based on an Arduino Mega 2560. I built it as a multi-functional expansion board to mount on top of a small robot, and it features a 16x2 LCD display, a buzzer, a light sensor, a beacon light, a police-ish light bar, a small stepper motor and control board, an IR receiver, and tons of places to attach a never-ending assortment of random things that scan, blink, beep, and generally add to the complexity of the robot.

In this Instructable, I will guide you through the process I used to design, build, and code this board to run in a variety of situations. The system is currently controlled with a dinky little IR remote (which I hope to upgrade), and runs a looped code based on the input from the onboard IR receiver. I built this about 4 months ago, have made ~10 revisions since then, and have decided to share it with all of you as well.

Demo Video:

Step 1: Gathering Materials:

For this project, you will need a wide variety of parts. These can vary greatly in size and function depending on your particular needs, however, these are the parts I used:

1x Arduino Mega 2560

3x blue LEDs

3x red LEDs

6x orange LEDs

2x white LEDs

1x photoresistor

1x 200 ohms resistor

1x 10k potentiometer

1x small buzzer (doesn't matter what particular kind, but I would recommend something smaller than an 8 ohm speaker)

1x 183B 3-pin IR receiver

1x IR remote (any will do, you can even use a TV remote)

1x 16 pin backlit 16x2 LCD (mine is blue)

1x 28BYJ-48 5v mini stepper motor

1x ULN2003 stepper motor driver

Insulated Wire

Assorted male and female pin headers

a breadboard with lots (and I mean lots) of jumper cables

paper

modeling wire

tape

Tools:

Soldering iron

Hot glue gun

Scissors

Step 2: Prototype Assembly: LCD

In any project, it is a good idea to lay everything out and set it up to make sure it all works the way you intend it to. When I went about designing this, I started with a breadboard, and I suggest you do the same, because later on, when you want to improve on part of this project, you will then understand the basics of each component and why it works, not just that it magically happens to work. Bearing that in mind, let's start with the LCD display.

1. Bread the LCD, and place jumpers in the rows corresponding to pins 1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, and 16, from left to right, skipping pins 7-10, as we will not need those.

2. Connect the jumpers from pins 1, 5, and 16 to the ground row of the breadboard, and pins 2 and 15 to the positive row. These are the power and ground for the LCD and the backlight.

3. Connect the power and ground pins on the Mega to the power and ground rows of the breadboard, respectively.

4. Connect the jumpers from pins 11 (lcd) to 49 (mega), 12 to 47, 13 to 45, and 14 to 43. These are what allow the Mega to tell the LCD what characters to display where.

5. Connect pin 4 of the LCD to pin 53 on the Mega, and pin 6 to pin 51. This is the LCD enable, allowing it to display characters, and the read/write pin, telling the LCD when to display characters.

6. Bread the potentiometer, connecting the side pins to power and ground, and the middle to pin 3 on the LCD. This allows you to manually tune the contrast of the screen.

7. Power on the Mega. Little white blocks should appear on the LCD. If not, turn the dial on the potentiometer until they do.

Congratulations, you just made a working display. Now all you have to do is code it! (don't worry, I'll explain how to do that later)

Step 3: Prototype Assembly: LED Light Bar

The light bar in this project, although not in the typical 'light bar' format, consists of 3 red and 3 blue LEDs. It can be used as running lights and simulated police lights, and can be repurposed for many other blinking, flashing, strobing, or signaling applications.

1. Bread the LEDs in a row, with one side red and the other side blue

2. Connect the cathode (short side) of each LED to ground

3. Connect the anodes (long side) of the blue LEDs to pins 23, 25, and 27 of the Mega

4. Connect the anodes of the red LEDs to pins 22, 24, and 26 of the Mega

Your light bar is now ready to use!

Step 4: Prototype Assembly: Photo-reactive Headlights

The photoresistor changes resistance based on the amount of light in a room. Here, I use it as a sensor, and run a code on the Mega that turns on and off a pair of "headlights" based on the amount of light in a room.

1. Bread the photoresistor

2. Bread the 200 ohms resistor so that one leg shares a row with a leg of the photoresistor

3. Connect the other leg of the resistor to ground

4. Connect the unoccupied leg of the photoresistor to the positive row of the breadboard

5. Connect the row that contains a lead from both the resistor and the photoresistor to analog pin A8 on the Mega

6. Bread the two white LEDs

7. Connect the cathodes to ground and the anodes to pins 41 and 42 on the Mega

Your photo-reactive headlights are ready to be programmed!

Step 5: Prototype Assembly: Beacon Light

No robot worth it's bolts doesn't have a beacon light of some sort, and that includes this one. I built my beacon with six orange LEDs, wire, paper, hot glue, and a soldering iron. It can simulate a rotary beacon as well as pulse, blink and fade.

1. Bend the legs on each LED to a 90 degree angle, making sure that the cathode is always on the left when the back of the LED is facing towards you and the leads are pointing down.

2. Cut out a small circle of paper, about half an inch in diameter.

3. Glue 2 LEDs directly across from each other on the circle.

4. Glue the remaining four LEDs on either side, each directly across from another, and all evenly spaced.

4.5 Fill in the gaps with hot glue (not required, but it makes everything else easier)

5. Bend the cathodes towards the center, and twist them together.

6. Cut 7 lengths of insulated wire, each about an inch and a half long, and strip each end (solid core works best)

7. Solder the cathodes together, then solder on a wire from step 6

8. Solder the remaining wires to the anodes of the LEDs

9. Arrange the unoccupied ends of the wires in a row, cathode first, then each LED in order around the circle.

10. Insert the wire from the cathode into the ground pin of the Mega

11. Insert the rest of the wires into analog pins A0 through A5 (we will be re-purposing these analog pins as digital PWM pins)

Your beacon is ready to go!

Step 6: Prototype Assembly: Stepper Motor/Radar Dish

In this project, I used the stepper motor as a simulated mini radar dish, mostly because it's the only motor I have that spins slowly enough to be realistic.

1. Insert the motor leads into the motor driver.

2. Connect the motor driver board to the power and ground rows of the breadboard.

3. Connect pins 1 (stepper driver) to 3 (Mega), 2 to 4, 3 to 5, and 4 to 6.

The stepper motor is ready to run, and now we'll build a model radar dish.

1. Cut a length of modeling wire about six inches long, and straighten it out.

2. Bend it into the shape shown in Figure 1.

3. Cut a piece of paper about 2 inches by 6 inches

4. Fold the paper lengthwise, then fold it crosswise (it should now measure 1" by 3")

5. Turn the paper so that the longest sides are now top and bottom. Fold the top and bottom into the center, then crease the paper along the lines created.

6. Unfold the sides from the center, leaving them at an angle.

7. Tape the paper to the top of the wire, and bend the wire to match the creases (see Figure 4)

8. Add a layer of tape over the paper (front and back, covering the wire), see Figures 2 and 3.

9. Form a coil at the bottom end of the wire so that it will fit snugly around the shaft of the stepper motor.

You now have a model radar dish! To make it look more realistic, cover it in masking tape and paint it.

Step 7: Prototype Assembly: IR Receiver and Buzzer

The IR receiver allows your display platform to receive commands in the form of infra-red light signals. The buzzer allows it to create sirens, bleep when you tell it to, and otherwise make random 8-bit sounds.

IR Receiver:
1. Bread the IR receiver
2. Connect the cathode to ground, and the anode to the power rail of the breadboard
3. Connect the signal pin to analog pin A8 of the Mega
Your receiver is now ready.

Buzzer:
1. Connect the cathode to ground
2. Connect the anode to analog pin A9 on the Mega
Your buzzer is now functional.

Step 8: The Code

The code for this display is rather long and complex, so instead of explaining it all right here, I embedded the explanation in the code so that you can view it side-by-side with the actual program. You can also download the code.

Note: You will need to find and download the IRremote and AccelStepper libraries to use this code (if you don't already have them).

/*
This code is for the Arduino Robot Display Platform. It consists of 
several 'while' loops that depend on variable values, which are changed 
in accordance to signals received by the IR sensor. It has the capacity
to retain as many functions as there are buttons on your remote. 

This program first saw use on a robot called "R.O.M.A.N.", standing for 
"Remotely Operated Mobile Arduino Nuisance". As the name implies, this 
robot drove around the house blinking and beeping and generally annoying 
everyone. Everyone, that is, except for the creator and operator (me).
Although it no longer functions in it's original format, I have decided 
to share the design and this later revision of the code with you on 
Instructables. I fully believe that each of you will find that there are 
many improvements to be made, and that this little bugger has many more uses
than I ever hoped to imagine. Good Luck, and Happy Creating.

- D.E.

- Please note that you will have to change the hex values in the IR receive 
loop in order to account for your remote, as all remotes have a slightly 
different output signal. This can be done using the demo codes included in the 
IR remote library. 

You also have to change the timer pin used in the IRremote library so that it 
does not interfere with the tone library used for the buzzer. Instructions for
doing this can be found online, or you can follow mine:
1. Open the IRremote library folder.
2. Find and open the file "boarddefs.h" with a script editor
3. Scroll down and find the section for the Mega 2560 (should be the first one in 
the timer section)
4. Comment out Timer 2
5. Un-comment Timer 1
You are ready to go!

This code was conglomerated from several other codes of varying 
origin. Feel free to edit and use this code as you wish.

Code written, edited, and conglomerated by Dangerously Explosive

Final version created August 2017

*/

//Initiate libraries

#include <IRremote.h>
#include <LiquidCrystal.h> 
#include <AccelStepper.h>
#define HALFSTEP 8

// Define the stepper pins

#define motorPin1  3  // IN1 of ULN2003 stepper driver
#define motorPin2  4  // IN2 
#define motorPin3  5  // IN3 
#define motorPin4  6  // IN4 


//define the stepper and LCD driver pins

AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);
LiquidCrystal LCD(53, 51, 49, 47, 45, 43);  

// Define variables

int t = 0;

// pins for the light bar

int r1 = 27;
int r2 = 25;
int r3 = 23;
int b1 = 26;
int b2 = 24;
int b3 = 22;

// pin for the buzzer

int buzzerPin = A6;

//timing and display values

int x = 500;
int y = 60;
int z = 500;
int d = 50;
int bright = 0;
int bright1 = 0;
int fadeVal = 5;
int fadeVal1 = 5;
int negFadeVal = 5;
int upVal = 5;
int light = 0;
int frequency1 = 500;
int p = 400;
int p1 = 600;
int distance = 100000;

//Program variables:

//Light bar functions

int red = 0;
int green = 0;
int blue = 0;
int space = 0;
int police = 0;

//random functions

int sound = 0;
int chase = 0;
int triGreen = 0;
int power = 0;

//Beacon functions

int beaconBlink = 0;
int pulse1 = 0;
int pulse2 = 0;
int pulse3 = 0;

//randomized variables

int pinNumber;
int pinNumber2;
int pinNumber3;
int frequency;

// IR input pin and value:

IRrecv irrecv(A7);
decode_results results;

//Standard Beacon Code:

void beacon(){
  digitalWrite(A0, HIGH); //light 1 on
  delay(d);
  digitalWrite(A0, LOW); //light 1 off, light 2 on
  digitalWrite(A1, HIGH);
  delay(d);
  digitalWrite(A1, LOW); //light 2 off, light 3 on
  digitalWrite(A2, HIGH);
  delay(d);
  digitalWrite(A2, LOW); //light 3 off, light 4 on
  digitalWrite(A3, HIGH); //So on and so forth
  delay(d);
  digitalWrite(A3, LOW);
  digitalWrite(A4, HIGH);
  delay(d);
  digitalWrite(A4, LOW);
  digitalWrite(A5, HIGH);
  delay(d);
  digitalWrite(A5, LOW);
}

//Beacon single pulse

void pulse(){
  analogWrite(A0, 255); //All lights on
  analogWrite(A1, 255);
  analogWrite(A2, 255);
  analogWrite(A3, 255);
  analogWrite(A4, 255);
  analogWrite(A5, 255);
  delay(40);
  analogWrite(A0, 0); //all lights off
  analogWrite(A1, 0);
  analogWrite(A2, 0);
  analogWrite(A3, 0);
  analogWrite(A4, 0);
  analogWrite(A5, 0);
  delay(1000);
}

//beacon triple pulse

void triPulse(){
  analogWrite(A0, 255); //all on
  analogWrite(A1, 255);
  analogWrite(A2, 255);
  analogWrite(A3, 255);
  analogWrite(A4, 255);
  analogWrite(A5, 255);
  delay(d);
  analogWrite(A0, 0); //all off
  analogWrite(A1, 0);
  analogWrite(A2, 0);
  analogWrite(A3, 0);
  analogWrite(A4, 0);
  analogWrite(A5, 0);
  delay(d);
  analogWrite(A0, 255); //all on
  analogWrite(A1, 255);
  analogWrite(A2, 255);
  analogWrite(A3, 255);
  analogWrite(A4, 255);
  analogWrite(A5, 255);
  delay(d);
  analogWrite(A0, 0); //all off, etc.
  analogWrite(A1, 0);
  analogWrite(A2, 0);
  analogWrite(A3, 0);
  analogWrite(A4, 0);
  analogWrite(A5, 0);
  delay(d);
  analogWrite(A0, 255);
  analogWrite(A1, 255);
  analogWrite(A2, 255);
  analogWrite(A3, 255);
  analogWrite(A4, 255);
  analogWrite(A5, 255);
  delay(d);
  analogWrite(A0, 0);
  analogWrite(A1, 0);
  analogWrite(A2, 0);
  analogWrite(A3, 0);
  analogWrite(A4, 0);
  analogWrite(A5, 0);
  delay(1000);
}

//Beacon double pulse

void dualPulse(){
  analogWrite(A0, 255); //all on
  analogWrite(A1, 255);
  analogWrite(A2, 255);
  analogWrite(A3, 255);
  analogWrite(A4, 255);
  analogWrite(A5, 255);
  delay(d);
  analogWrite(A0, 0); //all off
  analogWrite(A1, 0); //Seeing a pattern yet?
  analogWrite(A2, 0);
  analogWrite(A3, 0);
  analogWrite(A4, 0);
  analogWrite(A5, 0);
  delay(d);
  analogWrite(A0, 255);
  analogWrite(A1, 255);
  analogWrite(A2, 255);
  analogWrite(A3, 255);
  analogWrite(A4, 255);
  analogWrite(A5, 255);
  delay(d);
  analogWrite(A0, 0);
  analogWrite(A1, 0);
  analogWrite(A2, 0);
  analogWrite(A3, 0);
  analogWrite(A4, 0);
  analogWrite(A5, 0);
  delay(1000);
}

// Code for a scrolling message along the bottom of the LCD
//there are more efficient ways of doing this, I just don't know how to use them.
//Therefore, I went analog and did it the hard way. It ain't purdy, but it works.

void nameDisplay() {
  digitalWrite(6, LOW); //turn off the Stepper Driver because it interferes with
  digitalWrite(5, LOW); //the timing of the loops and gets hot while holding the
  digitalWrite(4, LOW); //motor in place
  digitalWrite(3, LOW);
  LCD.setCursor(0, 1);

  if(t == 0){ // use the variable "t" to determine timing
    LCD.print("Remotely Operate"); // there can only be 16 characters displayed
  };                               // at any one time.
                                   
  t ++;

  if (t == 2){
    LCD.print("emotely Operated");
  };

  if (t == 3){
    LCD.print("motely Operated ");
  };

  if (t == 4){
    LCD.print("otely Operated M");
  };

  if (t == 5){
    LCD.print("tely Operated Mo");
  };

  if (t == 6){
    LCD.print("ely Operated Mob");
  };

  if (t == 7){
    LCD.print("ly Operated Mobi");
  };

  if (t == 8){
    LCD.print("y Operated Mobil");
  };

  if (t == 9){
    LCD.print(" Operated Mobile");
  };

  if (t == 10){
    LCD.print("Operated Mobile ");
  };

  if (t == 11){
    LCD.print("perated Mobile A");
  };

  if (t == 12){
    LCD.print("erated Mobile Ar");
  };

  if (t == 13){
    LCD.print("rated Mobile Ard");
  };

  if (t == 14){
    LCD.print("ated Mobile Ardu");
  };

  if (t == 15){
    LCD.print("ted Mobile Ardui");
  };

  if (t == 16){
    LCD.print("ed Mobile Arduin");
  };

  if (t == 17){
    LCD.print("d Mobile Arduino");
  };

  if (t == 18){
    LCD.print(" Mobile Arnuino ");
  };

  if (t == 19){
    LCD.print("Mobile Arnuino N");
  };

  if (t == 20){
    LCD.print("obile Arduino Nu");
  };

  if (t == 21){
    LCD.print("bile Arduino Nui");
  };

  if (t == 22){
    LCD.print("ile Arduino Nuis");
  };

  if (t == 23){
    LCD.print("le Arduino Nuisa");
  };

  if (t == 24){
    LCD.print("e Arduino Nuisan");
  };

  if (t == 25){
    LCD.print(" Arduino Nuisanc");
  };

  if (t == 26){
    LCD.print("Arduino Nuisance");
  };

  if (t == 27){
    LCD.print("rduino Nuisance ");
  };

  if (t == 28){
    LCD.print("duino Nuisance  ");
  };

  if (t == 29){
    LCD.print("uino Nuisance   ");
  };

  if (t == 30){
    LCD.print("ino Nuisance    ");
  };

  if (t == 31){
    LCD.print("no Nuisance     ");
  };

  if (t == 32){
    LCD.print("o Nuisance      ");
  };

  if (t == 33){
    LCD.print(" Nuisance       ");
  };

  if (t == 34){
    LCD.print("Nuisance        ");
  };

  if (t == 35){
    LCD.print("uisance         ");
  };

  if (t == 36){
    LCD.print("isance          ");
  };

  if (t == 37){
    LCD.print("sance           ");
  };

  if (t == 38){
    LCD.print("ance            ");
  };

  if (t == 39){
    LCD.print("nce             ");
  };

  if (t == 40){
    LCD.print("ce              ");
  };

  if (t == 41){
    LCD.print("e               ");
  };

  if (t == 42){
    LCD.print("                ");
  };

  if (t == 43){
    LCD.print("               R");
  };

  if (t == 44){
    LCD.print("              Re");
  };

  if (t == 45){
    LCD.print("             Rem");
  };

  if (t == 46){
    LCD.print("            Remo");
  };

  if (t == 47){
    LCD.print("           Remot");
  };

  if (t == 48){
    LCD.print("          Remote");
  };

  if (t == 49){
    LCD.print("         Remotel");
  };

  if (t == 50){
    LCD.print("        Remotely");
  };

  if (t == 51){
    LCD.print("       Remotely ");
  };

  if (t == 52){
    LCD.print("      Remotely O");
  };

  if (t == 53){
    LCD.print("     Remotley Op");
  };

  if (t == 54){
    LCD.print("    Remotely Ope");
  };

  if (t == 55){
    LCD.print("   Remotely Oper");
  };

  if (t == 56){
    LCD.print("  Remotely Opera");
  };

  if (t == 57){
    LCD.print(" Remotely Operat");
  };

  if (t == 58){
    LCD.print("Remotely Operate");
    t = 0;
    delay(z); // extra delay to denote the end of the looped message
  };
}

// police light simulator

void policeLights(){
  tone(buzzerPin, p); //tone frequency 1
  digitalWrite(r1, HIGH); // three flashes from red
  digitalWrite(r2, HIGH);
  digitalWrite(r3, HIGH);
  delay(y);
  digitalWrite(r1, LOW);
  digitalWrite(r2, LOW);
  digitalWrite(r3, LOW);
  delay(y);
  digitalWrite(r1, HIGH);
  digitalWrite(r2, HIGH);
  digitalWrite(r3, HIGH);
  delay(y);
  digitalWrite(r1, LOW);
  digitalWrite(r2, LOW);
  digitalWrite(r3, LOW);
  delay(100);
  tone(buzzerPin, p1); //tone frequency 2
  digitalWrite(b1, HIGH); //three flashes from blue
  digitalWrite(b2, HIGH);
  digitalWrite(b3, HIGH);
  delay(y);
  digitalWrite(b1, LOW);
  digitalWrite(b2, LOW);
  digitalWrite(b3, LOW);
  delay(y);
  digitalWrite(b1, HIGH);
  digitalWrite(b2, HIGH);
  digitalWrite(b3, HIGH);
  delay(y);
  digitalWrite(b1, LOW);
  digitalWrite(b2, LOW);
  digitalWrite(b3, LOW);
  delay(100);
  noTone(buzzerPin); // turn off buzzer
}

// A simple horn to persuade the cat to get out of my way

void honk(){
  tone(buzzerPin, 600);  //Beep!
  delay(800);
  noTone(buzzerPin);
  delay(250);
  tone(buzzerPin, 600);  //Beep!
  delay(800);
  noTone(buzzerPin);
  delay(250);
}

//determine the amount of light in a room 

void readLight(){
  int lightVal = analogRead(A8); //read the photoresistor value
  int power1 = 0;
  if (lightVal <= 25){
    power = 1;          //too little light, turn headlights on,
  }
  else {
    power = 0;          //too much light, turn headlights off
  };
} 

// turning on and off the lights 

void headlights(){
  if (power == 1){          // ^ use same varibles as above ^
    digitalWrite(42, HIGH);
    digitalWrite(41, HIGH);
  }
  else {
    digitalWrite(42, LOW);
    digitalWrite(41, LOW);
  };
}

//red running lights 

void blinkRed(){
  digitalWrite(r1, HIGH);
  delay(x);
  digitalWrite(r1, LOW);
  digitalWrite(r2, HIGH);
  delay(x);
  digitalWrite(r2, LOW);
  digitalWrite(r3, HIGH);
  delay(x);
  digitalWrite(r3, LOW);
}

//blue running lights

void blinkBlue(){
  digitalWrite(b1, HIGH);
  delay(x);
  digitalWrite(b1, LOW);
  digitalWrite(b2, HIGH);
  delay(x);
  digitalWrite(b2, LOW);
  digitalWrite(b3, HIGH);
  delay(x);
  digitalWrite(b3, LOW);

}

//randomized blinking of red and blue lights, 
//simulating a '60s space display, hence the 
//name of the loop

void spaceDisplay(){
  pinNumber = random(b3, r1);  //assign 3 random pins 
  pinNumber2 = random(b3, r1);
  pinNumber3 = random(b3, r1);

  digitalWrite(pinNumber, HIGH); //turn the three pins on
  digitalWrite(pinNumber2, HIGH);
  digitalWrite(pinNumber3, HIGH);
  delay(x);
  digitalWrite(pinNumber, LOW);  //turn the 3 pins off
  digitalWrite(pinNumber2, LOW);
  digitalWrite(pinNumber3, LOW); //loop and assign 3 new pins
}

// same as above, but with a random tone as well 

void spaceDisplaySound(){
  pinNumber = random(b3, r1);
  pinNumber2 = random(b3, r1);
  pinNumber3 = random(b3, r1);

  frequency = random(300, 700); //pick a random frequency within the range
  tone(buzzerPin, frequency);   // and then play it through the buzzer
  digitalWrite(pinNumber, HIGH);
  digitalWrite(pinNumber2, HIGH);
  digitalWrite(pinNumber3, HIGH);
  delay(x);
  digitalWrite(pinNumber, LOW);
  digitalWrite(pinNumber2, LOW);
  digitalWrite(pinNumber3, LOW);
  noTone(buzzerPin);
}

//running lights with the whole light bar, similar to a scanner display

void runningLights(){
  digitalWrite(r1, HIGH);
  delay(d+50);
  digitalWrite(r1, LOW);
  digitalWrite(r2, HIGH);
  delay(d+50);
  digitalWrite(r2, LOW);
  digitalWrite(r3, HIGH);
  delay(d+50);
  digitalWrite(r3, LOW);
  digitalWrite(b3, HIGH);
  delay(d+50);
  digitalWrite(b3, LOW);
  digitalWrite(b2, HIGH);
  delay(d+50);
  digitalWrite(b2, LOW);
  digitalWrite(b1, HIGH);
  delay(d+50);
  digitalWrite(b1, LOW);
  digitalWrite(b2, HIGH);
  delay(d+50);
  digitalWrite(b2, LOW);
  digitalWrite(b3, HIGH);
  delay(d+50);
  digitalWrite(b3, LOW);
  digitalWrite(r3, HIGH);
  delay(d+50);
  digitalWrite(r3, LOW);
  digitalWrite(r2, HIGH);
  delay(d+50);
  digitalWrite(r2, LOW);
}


/* translating IR values
For those interested, the reason I use variables and while loops instead of
just assigning a function to each button is so that the loop will run until 
I tell it to stop. If I just assigned each case with a function, it would do 
what I have the honk(); function do: Run once and then stop. This happens 
because there is nothing telling it to run the loop again, whereas the while 
loops run a code continuously until a variable changes or a condition is no 
longer met. If I used that with the honk(); function, I would end up with a 
car alarm rather than a horn.

Note: you will need to use the IRrecvDemo sketch from the IRremote library
to determine the .hex codes for each button on your remote. To do this, you
open the serial monitor while running the sketch on your board (make sure to 
change the pin for the IR receiver) and record the values received when you 
press each button on your remote. If multiple values are received, record the 
longest one that appears most often when the button is pressed. Then insert 
the values in the place of mine to assign that button to a function.
*/

void translateIR(){
  switch(results.value){      

  case 0x9716BE3F: //1  
    red = 1;       //make the variable "red" 1 and make all others 0.
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0x3D9AE3F7:  //2
    blue = 1;   //same as above, just a different button and a different varible
    red = 0;
    green = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0x6182021B:  //3
    green = 1;
    red = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0xC101E57B:  //0
    space = 1;
    red = 0;
    green = 0;
    blue = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0x488F3CBB:  //5   This button turns everything off
    red = 0;
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0x1BC0157B: //8  Beep! Beep!
    honk();
    break;

  case 0x8C22657B: //4
    police = 1;
    red = 0;
    blue = 0;
    green = 0;
    space = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0x449E79F: //6
    sound = 1;
    red = 0;
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0x3EC3FC1B: //9
    red = 0;
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 1;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0x32C6FDF7: //7
    triGreen = 1;
    red = 0;
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0xE5CFBD7F: //EQ
    red = 0;
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 1;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0xEE886D7F: //CH+
    red = 0;
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 1;
    pulse2 = 0;
    pulse3 = 0;
    break;

  case 0x511DBB: //CH
    red = 0;
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 1;
    pulse3 = 0;
    break;

  case 0xE318261B: //CH-
    red = 0;
    green = 0;
    blue = 0;
    space = 0;
    police = 0;
    sound = 0;
    chase = 0;
    triGreen = 0;
    beaconBlink = 0;
    pulse1 = 0;
    pulse2 = 0;
    pulse3 = 1;
    break;

  case 0xA3C8EDDB: // +  these two increase and decrease the speed of the beacon simulation
    d = d - 10;
    break;

  case 0xF076C13B: // - 
    d = d + 10;
    break;

  };
}
void setup (){         // initiate all of the pins we will be using
  pinMode(A0, OUTPUT);
  pinMode(A1, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT);
  pinMode(A4, OUTPUT);
  pinMode(A5, OUTPUT);
  pinMode(A8, INPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(22, OUTPUT);
  pinMode(23, OUTPUT);
  pinMode(24, OUTPUT);
  pinMode(25, OUTPUT);
  pinMode(26, OUTPUT);
  pinMode(27, OUTPUT);
  pinMode(45, OUTPUT);
  pinMode(46, OUTPUT);
  pinMode(47, OUTPUT);
  pinMode(48, OUTPUT);
  pinMode(49, OUTPUT);
  pinMode(50, OUTPUT);
  pinMode(51, OUTPUT);
  pinMode(52, OUTPUT);
  pinMode(53, OUTPUT);
  pinMode(A6, INPUT);
  pinMode(A7, INPUT);
  irrecv.enableIRIn();  //start receiving
  pinMode(41, OUTPUT);
  pinMode(42, OUTPUT);
  Serial.begin(9600);  //initiate the serial monitor (I forget why)
  LCD.begin(16,2);     //turn on the LCD
  LCD.setCursor(0,0);  //tell the LCD where to write
  LCD.print("R.O.M.A.N.ver7.0"); //Display the name of the platform and the version (you can change this if you want)
  stepper1.setMaxSpeed(1000.0);  //set the stepper motor speed and acceleration protocols
  stepper1.setSpeed(200); 
  stepper1.setAcceleration(200.0);
}
void loop(){                       //and now the code begins
  while (irrecv.decode(&results)){ //start looking for incoming commands
    translateIR();                 //translate them into .hex format
    irrecv.resume();               //then continue the code
  };
  z = 500;     //set the LCD delay (the one denoting the end of the message loop)
  readLight();   //sense the light conditions
  headlights();  //activate the headlights accordingly
  nameDisplay(); // run the scrolling message banner
  
  while (red == 1){     // while the condition is met, only this code will run
    analogWrite(13, 0); //turn off the loop indicator (I have this near the bottom)
    blinkRed();         // run the red running lights
    readLight();        //use the headlights if needed
    headlights();
    z = 0;              // turn off the LCD delay (it interferes with timing)
    nameDisplay();
    while (irrecv.decode(&results)){ //look for new instructions
      translateIR(); 
      irrecv.resume();
    };
  };   //end the while loop

  while (green == 1){  //same deal, different function
    readLight();
    headlights(); 
    stepper1.moveTo(distance);           //run the stepper
    if (stepper1.distanceToGo() == 0) {  //reverse occasionally (more visually entertaining)
      stepper1.setCurrentPosition(0);
      stepper1.setSpeed(200);
      distance = -distance;
    };
    stepper1.run();
    while (irrecv.decode(&results)){   //look for new input
      translateIR(); 
      irrecv.resume();
    };
  };   //end the loop

  while (blue == 1){     //I think you should have the idea by now, and most of this is just 
    analogWrite(13, 0);  //repeated with different varibles and responding functions
    blinkBlue();
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      irrecv.resume();
    };
  };
  
  while (space == 1){
    analogWrite(13, 0);
    spaceDisplay();
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      irrecv.resume();
    };
  };
  
  while (police == 1){
    analogWrite(13, 0);
    policeLights();
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      irrecv.resume();
    };
  };

  while (sound == 1){  
    nameDisplay;
    readLight();
    headlights();
    spaceDisplaySound();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      irrecv.resume();
    };
  };

  while (chase == 1){  //this one never worked as a function, so I had to write it out here
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    analogWrite(13, 0);
    frequency1 = frequency1 + upVal;   // Basically it uses the old "Fade" code, except with sound,
    tone(A6, frequency1);              // and for whatever reason, the fade code never worked as a 
    if (frequency1 >= 1000 || frequency1 <= 500){ //function, only as a part of a loop
      upVal = -upVal;
    };
    while (irrecv.decode(&results)){ 
      translateIR(); 
      noTone(A6);
      irrecv.resume();
    };
    delay(30);
  };

  while (beaconBlink == 1){
    beacon();
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      irrecv.resume();
    };
  };

  while (pulse1 == 1){
    pulse();
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      irrecv.resume();
    };
  };

  while (pulse2 == 1){
    dualPulse();
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      irrecv.resume();
    };
  };

  while (pulse3 == 1){
    triPulse();
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      irrecv.resume();
    };
  };

  while (triGreen == 1){
    runningLights();
    readLight();
    headlights();
    z = 0;
    nameDisplay();
    while (irrecv.decode(&results)){ 
      translateIR(); 
      noTone(A7);
      irrecv.resume();
    };
  };
 
 // this here just turns the built in led on and off every other 
 // loop (so you know that it's running code)
 
  if (bright == 0){
    digitalWrite(13, HIGH);
    bright = 1;
  }
  else {
    digitalWrite(13, LOW);
    bright = 0;
  };
  delay(600); //let the processor rest
}

Step 9: Final Assembly: Printed Circuit Board (Optional)

To finalize this project, I created a PCB to mount all of the components on. I used Fritzing to design it, and then printed it myself. You can design your own fairly easily, or you can use and modify mine to your liking. My design has a hole in the middle to allow access to the reset button on the Mega, is designed so that it can use a separate power source for the stepper motor driver, and can accommodate two other devices, such as CPU fans or a robot drive system, also powered separately. It can even power the Mega if you so choose. Although it is not originally designed to be stackable, it is possible to use stackable pin headers to accommodate other shields (you could use a WiFi shield, for instance, and use app control instead of IR).

Step 10: The Results

You now have an open source display platform with a plethora of possible functions and uses. You can continue to improve the design, or build a robot to mount it on. You can add more sensors, or incorporate more blinky lights. You can do anything and everything within reason, should you put your mind to it. All you need is a touch of creativity and the will to explore new possibilities, "to boldly build what you want to build, and more".

I hope you liked this Instructable, and if you have any questions, just let me know.

Happy building,
Dangerously Explosive

(And feel free to steal my adapted Star Trek quote.) :-)