Halloween Countdown Display Matrix

By Adafruit Industries

Courtesy of Adafruit

Guide by John Park

Overview

Don't lose track of the days between now and your favorite holiday -- Halloween!

This internet-connected LED matrix display running CircuitPython will tell you how many days, hours, and minutes left until Halloween! Uses Adafruit IO time server to keep in sync. And, it runs on either the Matrix Portal or a Metro M4 Airlift with RGB Matrix Shield.

Custom bitmap images make it spooky and festive!

BUT THAT'S NOT ALL! You can adapt the code in this project to create a countdown display for ANY EVENT! Use any graphics and colors you like, too!

 

Halloween Countdown Display Matrix

Parts

Using MatrixPortal

You can build this project with an all-in-one Matrix Portal board, it’s definitely the easiest and least-expensive way to go about it.

You will need a matrix portal, matrix, and USB C power/data cable.

Install CircuitPython

CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.

Set up CircuitPython Quick Start!

Follow this quick step-by-step for super-fast Python power :)

Download the latest version of CircuitPython for this board via circuitpython.org

CircuitPython 6.0.0.alpha.1 had a serious bug with the RGBMatrix display. Skip it and use 6.0.0.alpha.2 or newer!

Further Information

For more detailed info on installing CircuitPython, check out Installing CircuitPython.

Click the link above and download the latest UF2 file.

Download and save it to your desktop (or wherever is handy).

UF2_2

Plug your Metro M4 into your computer using a known-good USB cable.

A lot of people end up using charge-only USB cables and it is very frustrating! So, make sure you have a USB cable you know is good for data sync.

Double-click the Reset button next to the USB connector on your board, and you will see the NeoPixel RGB LED (indicated by the arrow) turn green. If it turns red, check the USB cable, try another USB port, etc.

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

Metro2_3

You will see a new disk drive appear called MATRIXBOOT.

Drag the adafruit_circuitpython_etc.uf2 file to MATRIXBOOT.

disk_4

boot_5

The LED will flash. Then, the MATRIXBOOT drive will disappear and a new disk drive called CIRCUITPY will appear.

That's it, you're done! :)

drive_6

Prep the MatrixPortal

Power Prep

The MatrixPortal supplies power to the matrix display panel via two standoffs. These come with protective tape applied (part of our manufacturing process) which MUST BE REMOVED!

Use some tweezers or a fingernail to remove the two amber circles.

Power_7 

Power_8

Power Terminals

Next, screw in the spade connectors to the corresponding standoff.

  • red wire goes to +5V
  • black wire goes to GND

terminals_24

terminals_23

Panel Power

Plug either one of the four-conductor power plugs into the power connector pins on the panel. The plug can only go in one way, and that way is marked on the board's silkscreen.

panel_26

panel_25

Board Connection

Now, plug the board into the left side shrouded 8x2 connector as shown. The orientation matters, so take a moment to confirm that the white indicator arrow on the matrix panel is oriented pointing up and right as seen here and the MatrixPortal overhangs the edge of the panel when connected. This allows you to use the edge buttons from the front side.

board_28

board_27

board_29

board_30

For info on adding LED diffusion acrylic, see the page LED Matrix Diffuser.

Using M4 Airlift

If you have a Metro M4 AirLift, you can build this project easily - you just need an RGB Matrix shield to help connect!

You will need a Metro M4 Airlift, matrix shield and matrix.

Install CircuitPython

CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.

Set up CircuitPython Quick Start!

Follow this quick step-by-step for super-fast Python power :)

Download the latest version of CircuitPython for this board via circuitpython.org

Further Information

For more detailed info on installing CircuitPython, check out Installing CircuitPython.

Click the link above and download the latest UF2 file.

Download and save it to your desktop (or wherever is handy).

UF2_2_2

Plug your Metro M4 into your computer using a known-good USB cable.

A lot of people end up using charge-only USB cables and it is very frustrating! So, make sure you have a USB cable you know is good for data sync.

Double-click the Reset button next to the USB connector on your board, and you will see the NeoPixel RGB LED (indicated by the arrow) turn green. If it turns red, check the USB cable, try another USB port, etc.

If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!

You will see a new disk drive appear called METROM4BOOT.

Drag the adafruit_circuitpython_etc.uf2 file to METROM4BOOT.

Boot_5_2

drive_6_2

 The LED will flash. Then, the METROM4BOOT drive will disappear and a new disk drive called CIRCUITPY will appear.

That's it, you're done! :)

drive_6_3

Build Metro M4 Airlift Matrix Display

Assembly

Talking to an LED matrix display can be tricky! The 64 x 32 LED used here has a whopping 2,048 pixels, and each can display RGB colors, which makes for a whole lot of data to sling around. Thankfully, our RGB Matrix shield paired with the Metro M4 Airlift does most of the heavy lifting.

Let's assemble the boards and the display so we can get things running!

matrix_12

Shields Up

First, add the male headers, screw terminal block, and the 8x2-pin socket to the Matrix shield, by following this guide. Be careful to match the socket polarity to the silkscreen image on the board.

Be sure to also perform the clock pin mod as shown here.

Then plug the shield into the Metro M4 Airlift.

shields_13

Power Connections

To provide power, we'll screw the wiring harness connectors to the screw terminal blocks of the shield. Be sure to match the black wire to GND and the red wire to +5Vout.

Now, simply plug the other end into the panel's power header. It can only go in one way.

power_14

power_15

Data Cable

Plug in the two ends of the ribbon cable, note that the connectors are keyed to only fit in the correct orientation.

cable_16

cable_17

cable_18

Wall Adapter

We'll power the Metro M4 from the 5V 2.5 (or a 4A) DC wall adapter plugged into the barrel jack. Even though USB can provide power to the board, the current isn't adequate for lighting up hundreds and thousands of LEDs!

adapter_20

adapter_19

For info on adding LED diffusion acrylic, see the page LED Matrix Diffuser.

Code the Halloween Countdown

Halloween_9

Libraries

We'll need to make sure we have these libraries installed. (Check out this link on installing libraries if needed.)

  • adafruit_bitmap_font
  • adafruit_bus_device
  • adafruit_display_shapes
  • adafruit_display_text
  • adafruit_esp32spi
  • adafruit_io
  • adafruit_matrixportal
  • adafruit_requests.mpy
  • neopixel.mpy

Connect to the Internet

Once you have CircuitPython setup and libraries installed we can get your board connected to the Internet. The process for connecting can be found here.

Text Editor

Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.

Alternatively, you can use any text editor that saves simple text files.

Code

Click the Download: Zip File link below in the code window to get a zip file with all the files needed for the project. Copy halloween_countdown_matrix.py from the zip file and place on the CIRCUITPY drive, then rename it to code.py.

You'll also need to copy the following files to the CIRCUITPY drive. See the graphic at the top of the page as to filenames and where they go):

  • fonts directory, which contains a .bdf font
  • bmp directory, which contains the graphics
  • secrets.py (after you edit to put your Wi-Fi and AIO credentials in the file)

Download: Project Zip or halloween_countdown_matrix.py | View on Github 

Copy Code
import time
import board
from adafruit_matrixportal.matrixportal import MatrixPortal

EVENT_YEAR = 2020
EVENT_MONTH = 10
EVENT_DAY = 31
EVENT_HOUR = 17
EVENT_MINUTE = 0

FRAME_DURATION = 3
FRAMES = (
"bmps/jack.bmp",
"DAYS",
"bmps/ghost.bmp",
"HOURS",
"bmps/bats.bmp",
"MINUTES",
"bmps/skull.bmp",
"bmps/halloween.bmp",
)

EVENT_DAY_IMAGE = "bmps/happy_halloween.bmp"
SYNCHRONIZE_CLOCK = True

# --- Display setup ---
matrixportal = MatrixPortal(status_neopixel=board.NEOPIXEL, debug=True)

current_frame = None

# Create a new label with the color and text selected
matrixportal.add_text(
text_font="fonts/Arial-12.bdf",
text_position=(4, (matrixportal.graphics.display.height // 2) - 1),
text_color=0xEF7F31,
)


def set_time_until(unit=None):
event_time = time.struct_time(
(
EVENT_YEAR,
EVENT_MONTH,
EVENT_DAY,
EVENT_HOUR,
EVENT_MINUTE,
0, # we don't track seconds
-1,
-1,
False,
)
)
remaining = time.mktime(event_time) - time.mktime(time.localtime())
if remaining <= 0:
# oh, its event time!
matrixportal.set_background(EVENT_DAY_IMAGE)
return
remaining //= 60
mins_remaining = remaining % 60
remaining //= 60
hours_remaining = remaining % 24
remaining //= 24
days_remaining = remaining

if unit == "DAYS":
text = "{} day".format(days_remaining)
if days_remaining != 1:
text += "s"
if unit == "HOURS":
text = "{} hour".format(hours_remaining)
if hours_remaining != 1:
text += "s"
if unit == "MINUTES":
text = "{} min".format(mins_remaining)
if mins_remaining != 1:
text += "s"
matrixportal.set_text(text)
matrixportal.set_background(0)


def set_next_frame():
# pylint: disable=global-statement
global current_frame

# Advance to next frame if we already have one
if current_frame is not None:
current_frame += 1

# Loop back or set initial frame
if current_frame is None or current_frame >= len(FRAMES):
current_frame = 0

# Check if Picture or Text
print(FRAMES[current_frame])
if FRAMES[current_frame][-4:] == ".bmp":
matrixportal.set_background(FRAMES[current_frame])
matrixportal.set_text("")
else:
set_time_until(FRAMES[current_frame])


# Simulate the delay in case fetching time is fast
set_next_frame()
start_time = time.monotonic()
if SYNCHRONIZE_CLOCK:
matrixportal.get_local_time()
while time.monotonic() < start_time + FRAME_DURATION:
pass

while True:
set_next_frame()
time.sleep(FRAME_DURATION) 

Adafruit IO Time Server

In order to get the precise time, our project will query the Adafruit IO Internet of Things service for the time. Adafruit IO is absolutely free to use, but you'll need to log in with your Adafruit account to use it. If you don't already have an Adafruit login, create one here.

If you haven't used Adafruit IO before, check out this guide for more info.

Once you have logged into your account, there are two pieces of information you'll need to place in your secrets.py file: Adafruit IO username, and Adafruit IO key. Head to io.adafruit.com and simply click the View AIO Key link on the left-hand side of the Adafruit IO page to get this information.

Then, add them to the secrets.py file like this:

Download: file 

Copy Code
secrets = {
'ssid' : 'your_wifi_ssid',
'password' : 'your_wifi_password',
'aio_username' : '_your_aio_username_',
'aio_key' : '_your_big_huge_super_long_aio_key_'
}

How it Works

Libraries

Here's how the code works. First, we import the time, board, and adafruit_matrixportal libraries.

Event Time

Then, we'll set the variables for the event year, month, day, hour, and minute. These will be used to calculate the countdown remaining based on the current time.

Download: file 

Copy Code
import time
import board
from adafruit_matrixportal.matrixportal import MatrixPortal

EVENT_YEAR = 2020
EVENT_MONTH = 10
EVENT_DAY = 31
EVENT_HOUR = 17
EVENT_MINUTE = 0

Frames

Next, we set the FRAME_DURATION = 3 which means each image or text screen will hold for three seconds. You can adjust this as you like.

Then, we'll define the order in which the images and text will be displayed. This is pretty cool, as it is essentially making a list of which items you want to see appear sequentially. You can mix and match this as you see fit!

Download: file 

Copy Code
FRAME_DURATION = 3
FRAMES = (
"bmps/jack.bmp",
"DAYS",
"bmps/ghost.bmp",
"HOURS",
"bmps/bats.bmp",
"MINUTES",
"bmps/skull.bmp",
"bmps/halloween.bmp",

We'll also set one image aside as the EVENT_DAY_IMAGE = "bmps/happy_halloween.bmp".

set_11

MatrixPortal Setup

Next, we'll set up the matrixportal object for the display, and then create a text label.

Download: file 

Copy Code
matrixportal = MatrixPortal(status_neopixel=board.NEOPIXEL, debug=True)

current_frame = None

# Create a new label with the color and text selected
matrixportal.add_text(
text_font="fonts/Arial-12.bdf",
text_position=(4, (matrixportal.graphics.display.height // 2) - 1),
text_color=0xEF7F31,
)

Set Time Until Function

This function is used to derive the time until the event based up one the start variables and the current time.

Download: file 

Copy Code
def set_time_until(unit=None):
event_time = time.struct_time(
(
EVENT_YEAR,
EVENT_MONTH,
EVENT_DAY,
EVENT_HOUR,
EVENT_MINUTE,
0, # we don't track seconds
-1,
-1,
False,
)
)
remaining = time.mktime(event_time) - time.mktime(time.localtime())
if remaining <= 0:
# oh, its event time!
matrixportal.set_background(EVENT_DAY_IMAGE)
return
# secs_remaining = remaining % 60
remaining //= 60
mins_remaining = remaining % 60
remaining //= 60
hours_remaining = remaining % 24
remaining //= 24
days_remaining = remaining

if unit == "DAYS":
text = "{} day".format(days_remaining)
if days_remaining != 1:
text += "s"
if unit == "HOURS":
text = "{} hour".format(hours_remaining)
if hours_remaining != 1:
text += "s"
if unit == "MINUTES":
text = "{} min".format(mins_remaining)
if mins_remaining != 1:
text += "s"
matrixportal.set_text(text)
matrixportal.set_background(0)

Set Next Frame Function

We'll use this function to set the next frame to either a bitmap graphic or a text label depending on where we are in the list order.

Download: file 

Copy Code
ef set_next_frame():
global current_frame

# Advance to next frame if we already have one
if current_frame is not None:
current_frame += 1

# Loop back or set initial frame
if current_frame is None or current_frame >= len(FRAMES):
current_frame = 0

# Check if Picture or Text
print(FRAMES[current_frame])
if FRAMES[current_frame][-4:] == ".bmp":
matrixportal.set_background(FRAMES[current_frame])
matrixportal.set_text("")
else:
set_time_until(FRAMES[current_frame])

Main Loop

The main loop of the program is simple -- it calls the set_next_frame() function and then pauses for the frame duration!

Download: file 

Copy Code
while True:
set_next_frame()
time.sleep(FRAME_DURATION)

LED Matrix Diffuser

LED Diffusion Acrylic

You can add an LED diffusion acrylic faceplate to the your LED matrix display. (Pictured here with the ON AIR project)

This can help protect the LEDs as well as enhance the look of the sign both indoors and out by reducing glare and specular highlights of the plastic matrix grid.

LED_31

Measure and Cut the Plastic

You can use the sign to measure and mark cut lines on the paper backing of the acrylic sheet.

Then, use a tablesaw or bandsaw with a fine-toothed blade and a guide or sled to make the cuts.

Note: It is possible to score and snap acrylic, but it can be very trick to get an even snap without proper clamping.

measure_32

measure_33

measure_34

measure_35

measure_36

Peel away the paper backing from both sides and set the acrylic onto your matrix display.

Peel_37

Uglu Dashes

The best method we've found for adhering acrylic to the matrix display is to use Uglu Dashes clear adhesive rectangles from Pro Tapes. They are incredibly strong (although can be removed if necessary), easy to apply, and are invisible once attached.

Use one at each corner and one each at the halfway point of the long edges, then press the acrylic and matrix panel together for about 20 seconds.

dashes_39

dashes_38

dashes_40

Here you can see the impact of using the diffusion acrylic. (Pictured here with the ON AIR sign project)

sign_41

Stand

A very simple and attractive way to display your matrix is with the adjustable bent-wire stand.

stand_43

stand_44

stand_45

stand_42

OnAir

Alternately, you can use a frame, 3D printed brackets, tape, glue, or even large binder clips to secure the acrylic to the sign and then mount it on a wall, shelf, or display cabinet.

These mini-magnet feet can be used to stick the sign to a ferrous surface.

Key Parts and Components

Add all Digi-Key Parts to Cart
  • 1528-2920-ND
  • 1528-2505-ND
  • 1528-2629-ND
  • TL510-ND
  • 1528-4631-ND
  • 1528-4474-ND