Christmas is here again, and whether it's your favorite holiday of the year or it brings you out in a cold sweat, the decorations are starting to go up. This year, why not incorporate some DIY technology into your decorations to make them stand out?

In this project we will be building a weatherproof, motion activated 8 x 8 LED matrix from scratch... for under $20. It's designed to fit in the center of a standard Christmas door wreath, though it could be used anywhere around the house. And since it's battery powered, anywhere away from the house too!

Parts List

For this project you will need:

  • Arduino.
  • 64 x red LEDs.
  • 8 x 220 Ohm resistors.
  • PIR motion sensor.
  • 1 piece of prototyping board.
    • The one used here was 9 x 7 cm, though you can use any size you want.
  • 7-12v battery.
    • A simple battery pack is used here for budget reasons, but a mobile bank charger may last even longer.
  • Assorted short pieces of wire.
  • Tupperware box or similar weatherproof enclosure.
    • Make sure it will be big enough to fit all your components inside!
  • Christmas wreath.
    • Any will do, just make sure the enclosure box will fit inside it.
  • Soldering iron and solder.

While not strictly necessary as you could solder the components directly to the Nano, I also found a small breadboard very useful while testing. A hot glue gun also helps in putting all the parts together.

christmas wreath led matrix parts

This project requires quite a lot of soldering, and as a beginner it may seem daunting. I personally am still very much a beginner in soldering and found it to be not as challenging or time consuming as it seems. If you are also new to soldering here are some some good tips to help.

If you really aren't keen on the idea of soldering, this project is also possible with LED strips, or a ready-made LED matrix that you might have in your starter kit. Some code adjustments will be necessary if you decide to go that route.

Setting Up the Arduino

We'll start with the circuit diagram for the Arduino and the wires we will be attaching to our PIR sensor and LED matrix.

wreath fritzing

Inside the Matrix

Now to make our 8 x 8 LED matrix. It's a good idea to begin with to create one row and one column of the matrix, to make sure it is exactly where you want it on the prototyping board.

testing where to put leds

In the photo above, all of the LEDs are placed so that the anodes (the longer, positive leg) are towards the top of the protoboard. This is important, as we will be creating columns of common anodes by joining them together, and rows of common cathodes (the shorter, negative leg). Getting this right now will save headaches later!

We are going to build a common row cathode matrix, this diagram shows how it is all connected.

doub diag

It may look a little daunting at first, but it is quite a simple configuration. In each row, all of the cathodes are joined from the right to the left, and then attach to one of our Arduino pins. After this, we do the same for each column of anodes. This way, depending on which column we apply power to, and which row we join to ground, we can turn on any individual LED in the array.

Let the Soldering Begin

Start by placing your first row of LEDs. Make sure all of the anodes are facing the top, and flip it over. I found that adding another LED in each corner, and attaching another piece of protoboard on top using an elastic cord helped hold everything in place.

proto elastic

Now one by one bend the cathode (short) leg of each LED to the left so that they all overlap with one another. It's easiest to start from the left side and work right. If you are using a larger piece of protoboard, you can solder them to the board first and connect them together using the pads. Be careful not to join any of the cathodes to any other lines on the board or any of the anodes!

cathode fold solder

Repeat this process for all eight rows, and when you are finished you should have something that looks something like this:

picture of finished rows

Jumping Anodes!

The columns of anodes are a little more fiddly. In the diagram above, the anodes curve each time they cross a row of cathodes. This is because they cannot touch the rows at all. We must bend the anodes over the cathode rows and attach them to one another. You may find that using a pen to bend the legs helps a lot.

ben solder anodes

Do this for every row of anodes, and attach a resistor to each top anode. You will probably find it easier to put the resistor in the next hole in the protoboard and join the pads using solder. You should now have something like this:

finished soldering

Congratulations! The LED matrix is complete. Check your soldering thoroughly at this stage to make sure there are no breaks and that none of the columns are touching the rows. Don't worry if it doesn't look pretty, we just need it to work! You can check each LED individually now by attaching 5v to any of the column ends, and ground to any of the row ends.

testing matrix

Provided all is well, attach hook up wires to each column and each row, and attach them to your Arduino as shown in the diagram above.

Let's Get Coding

Open the Arduino IDE and choose your board and port. If you are new to Arduino, check out this getting started guide.

Enter this code into the editor. It's quite dense code if you are unfamiliar with it, but it is available here fully annotated to help understanding how it works.

        const int row[8] = {
  2,3,4,5,6,7,8,9
};
const int col[8] ={
    10,11,12,14,15,16,17,18
};

int pirPin = 19;
int pirState = LOW;
int val = 0;
bool pirTrigger = false;
const int pirLockTime = 12000;
int pirCountdown = pirLockTime;
int pixels[8][8];
const int refreshSpeed = 500;
int countDown = refreshSpeed;
int currentCharIndex = 0;
typedef bool CHAR_MAP_NAME[8][8];

const CHAR_MAP_NAME blank = {
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
};
              
const CHAR_MAP_NAME threedownthreein = {
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 1 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
{0 , 0 , 0 , 0 , 0 , 0 , 0 , 0},
};

const int noOfFrames = 5;

const CHAR_MAP_NAME *charMap[noOfFrames] ={
&blank,
&threedownthreein,
&blank,
&blank,
&threedownthreein
};

void setup(){
 
  for (int i=0;i<8;i++){
    pinMode(row[i], OUTPUT);
    pinMode(col[i],OUTPUT);

   //motion sensor
   pinMode(pirPin, INPUT);

    digitalWrite(col[i], LOW);
  }
}

void screenSetup(){
  const CHAR_MAP_NAME *thisMap = charMap[currentCharIndex];

  for (int x = 0; x < 8; x++)
  {
    for (int y = 0; y < 8; y++) { bool on = (*thisMap)[x][y]; if(on) { pixels[x][y] = HIGH; } else { pixels[x][y] = LOW; } } } currentCharIndex++; if(currentCharIndex>=noOfFrames){
    currentCharIndex = 0;
  }
  
}

void refreshScreen(){
  
  for (int currentRow = 0; currentRow < 8; currentRow++){
  digitalWrite(row[currentRow], LOW);

    for (int currentCol = 0; currentCol < 8; currentCol++){
      int thisPixel = pixels[currentRow][currentCol];

      digitalWrite(col[currentCol], thisPixel);


        if (thisPixel == HIGH) {
          digitalWrite(col[currentCol], LOW);
        }
      }

    digitalWrite(row[currentRow], HIGH);
    }
  
}

void loop(){

  
  val = digitalRead(pirPin);
  if (val == HIGH){
    pirTrigger = true;
  }
 
  else if (val == LOW && pirCountdown <=0) { pirTrigger=false; pirCountdown = pirLockTime; } if(pirTrigger==true && pirCountdown > 0)
  {
  refreshScreen();
  countDown--;
  pirCountdown--;
  
   if(countDown <= 0)
    {
      countDown = refreshSpeed;
      screenSetup();
      
    }
   
  }
}

The important parts to understand are:

The refreshSpeed variable. This variable determines how the time between each screen refresh. A bigger number means a longer wait.

The const CHAR_MAP_NAMEs. This is where you put each character map (or frame if it's easier to think of them that way) you want to display.

The noOfFrames variable. This determines how many frames get displayed in one full play through. Note that it can be different to the number of character maps. For example if you wanted to display "A CAT" you would only need to define four distinct frames: blank, an A, a C and a T.

Now, when the motion sensor detects movement the LED screen should blink the LED three down and three in from the top left. If it doesn't display correctly, check over your wiring again to make sure everything is in the right place! When you add your own image or message, it may get cut off early, or play for too long. Try changing the pirLockTime variable until it plays for the amount of time you want.

The process of adding each frame to the LED display can be a little tedious, so we have created this spreadsheet to make it a little easier to create text and images for your LED matrix (make a copy of the Google Sheet so you can edit it).

Using the spreadsheet, you can copy your creations directly into the code.

Make It Brave the Elements

Now that we have a working LED matrix, we need a way for it to survive the winter weather. While this method may not stand up to a tropical storm or being dunked in the pool, it should be enough to keep all of the electronics safe from the elements.

I used a round Tupperware box that's 15 cm in diameter and 6 cm deep as it fit my components perfectly. Cut a window in the lid slightly larger than your LED matrix, and attach a clear plastic film to it, making sure to leave no spaces for liquid to get in. Sturdy plastic from some packaging would work best, but this was all I had. You could also attach some mounts for the protoboard, though both jobs could easily be done with strong waterproof tape.

weather proof

Next, make a small hole underneath the window, then carefully and slowly widen it until your PIR sensor can only just fit through. You want it to fit as snugly as possible.

cut hole

Attach your PIR sensor, and fill in any gaps you can see with tape or hot glue.

glue pir

Clean up any tape or glue that might stop the box from closing properly, and add all of your components to the box along with your battery. Here, a simple AA battery pack was used, plugged directly into the VCC pin of the Nano. A few small pieces of cork were added to the outside of the enclosure to help with hanging the build in the center of the wreath.

And We Are Done

Once the box is sealed, hang it with your Christmas wreath, and wait for your visitors reactions to your high tech sub $20 personal welcome! You could even go one step further and create awesome DIY decorations for elsewhere around the house too!

finished led matrix christmas wreath diy

In this project we have built a self contained LED matrix system from scratch, that is motion activated and can survive being outside in all but the most inclement of weather. This build will come in useful long after the holiday season is over in other projects, and the same technique could be used to create cheap weatherproof enclosures for other projects too.

Have you build anything to give your Christmas a DIY twist? Are you planning any DIY themed Christmas gifts this year? Let us know in the comments below!