Maker.io main logo

Weather Display Matrix

2022-05-24 | By Adafruit Industries

License: See Original Project

Courtesy of Adafruit

Guide by John Park

Overview

Always know the current weather without even sticking you head out ‎the window with the Weather Display Matrix.‎

display_1

Using CircuitPython, this project queries the Open Weather Maps site ‎API to find out the current weather for your location. It displays the ‎temperature and weather condition icon, while scrolling additional ‎details such as humidity and conditions.‎

Want to change between Imperial and metric units? We've got you ‎covered! Use the Matrix Portal buttons or the added jumper on the ‎Metro M4 Airlift's pin D12 to do so.‎

Parts

or

Optional

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).‎

download_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.‎

drag_3

drag_4

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! :)‎

done_5

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!‎

assembly_6

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.‎

plug_7

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.‎

connections_8

connections_9

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_10

cable_11

cable_12

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_13

adapter_14

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

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.

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.‎

prep_15

prep_16

Power Terminals

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

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

terminal_17

terminal_18

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_19

panel_20

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.‎‎

‎Check nothing is impeding the board from plugging in firmly. If there's ‎a plastic nub on the matrix that's keeping the Portal from sitting flat, ‎cut it off with diagonal cutters.‎

plugin_21

plugin_22

board_23

board_24

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

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).‎

save_25

Plug your MatrixPortal 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 (indicated by the green arrow) on your ‎board, and you will see the NeoPixel RGB LED (indicated by the ‎magenta 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!‎

reset_26

You will see a new disk drive appear called MATRIXBOOT.‎‎ ‎

Drag the adafruit_circuitpython_etc.uf2 file to MATRIXBOOT.‎

drive_27

drive_28

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! :)‎

flash_29

Code the Weather Display Matrix

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

libraries_30

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 weather_display_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 three bitmap fonts
  • loading.bmp
  • openweather_graphics.py
  • weather-icons.bmp
  • secrets.py (after you edit to put your Wi-Fi and weather ‎credentials in the file, noted below)‎

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT

# Matrix Weather display
# For Metro M4 Airlift with RGB Matrix shield, 64 x 32 RGB LED Matrix display

"""
This example queries the Open Weather Maps site API to find out the current
weather for your location... and display it on a screen!
if you can find something that spits out JSON data, we can display it
"""
import time
import board
import microcontroller
from digitalio import DigitalInOut, Direction, Pull
from adafruit_matrixportal.network import Network
from adafruit_matrixportal.matrix import Matrix
import openweather_graphics # pylint: disable=wrong-import-position

# Get wifi details and more from a secrets.py file
try:
from secrets import secrets
except ImportError:
print("WiFi secrets are kept in secrets.py, please add them there!")
raise

if hasattr(board, "D12"):
jumper = DigitalInOut(board.D12)
jumper.direction = Direction.INPUT
jumper.pull = Pull.UP
is_metric = jumper.value
elif hasattr(board, "BUTTON_DOWN") and hasattr(board, "BUTTON_UP"):
button_down = DigitalInOut(board.BUTTON_DOWN)
button_down.switch_to_input(pull=Pull.UP)

button_up = DigitalInOut(board.BUTTON_UP)
button_up.switch_to_input(pull=Pull.UP)
if not button_down.value:
print("Down Button Pressed")
microcontroller.nvm[0] = 1
elif not button_up.value:
print("Up Button Pressed")
microcontroller.nvm[0] = 0
print(microcontroller.nvm[0])
is_metric = microcontroller.nvm[0]
else:
is_metric = False

if is_metric:
UNITS = "metric" # can pick 'imperial' or 'metric' as part of URL query
print("Jumper set to metric")
else:
UNITS = "imperial"
print("Jumper set to imperial")

# Use cityname, country code where countrycode is ISO3166 format.
# E.g. "New York, US" or "London, GB"
LOCATION = "Los Angeles, US"
print("Getting weather for {}".format(LOCATION))
# Set up from where we'll be fetching data
DATA_SOURCE = (
"http://api.openweathermap.org/data/2.5/weather?q=" + LOCATION + "&units=" + UNITS
)
DATA_SOURCE += "&appid=" + secrets["openweather_token"]
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
# it goes in your secrets.py file on a line such as:
# 'openweather_token' : 'your_big_humongous_gigantor_token',
DATA_LOCATION = []
SCROLL_HOLD_TIME = 0 # set this to hold each line before finishing scroll

# --- Display setup ---
matrix = Matrix()
network = Network(status_neopixel=board.NEOPIXEL, debug=True)
if UNITS in ("imperial", "metric"):
gfx = openweather_graphics.OpenWeather_Graphics(
matrix.display, am_pm=True, units=UNITS
)

print("gfx loaded")
localtime_refresh = None
weather_refresh = None
while True:
# only query the online time once per hour (and on first run)
if (not localtime_refresh) or (time.monotonic() - localtime_refresh) > 3600:
try:
print("Getting time from internet!")
network.get_local_time()
localtime_refresh = time.monotonic()
except RuntimeError as e:
print("Some error occured, retrying! -", e)
continue

# only query the weather every 10 minutes (and on first run)
if (not weather_refresh) or (time.monotonic() - weather_refresh) > 600:
try:
value = network.fetch_data(DATA_SOURCE, json_path=(DATA_LOCATION,))
print("Response is", value)
gfx.display_weather(value)
weather_refresh = time.monotonic()
except RuntimeError as e:
print("Some error occured, retrying! -", e)
continue

gfx.scroll_next_label()
# Pause between labels
time.sleep(SCROLL_HOLD_TIME)

‎View on GitHub

Open Weather Maps API Key

We'll be using OpenWeatherMaps.org to retrieve the weather info ‎through its API. In order to do so, you'll need to register for an account ‎and get your API key.‎

Go to this link and register for a free account. Once registered, you'll ‎get an email containing your API key, also known as the "openweather ‎token".‎

Copy and paste this key into your secrets.py file that is on the root ‎level of your CIRCUITPY drive, so it looks something like this:‎

Download File

Copy Code
secrets = {
'ssid' : 'your_wifi_ssid',
'password' : 'your_wifi_password',
'openweather_token' : 'xxxxxxxxxxxxxxxxxxxxxxxx'
}

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',
'openweather_token' : 'xxxxxxxxxxxxxxxxxxxxxxxx',
'aio_username' : '_your_aio_username_',
'aio_key' : '_your_big_huge_super_long_aio_key_'
}

Problems Getting Data

If you have any problems getting the data to display correctly, check ‎that the secrets.py file has the information noted above.‎

How it Works

Libraries

First, we import libraries to help us behind the scenes. time will allow ‎us to pause between weather queries. board gives us pin definitions. ‎And we'll use the digitalio library to query a DigitalInOut pin in the ‎input Direction with Pull down resistor for mode selection.‎

The adafruit_matrixportal library will be used both for Network queries ‎and for controlling the Matrix display.‎

We'll also import the openweather_graphics class to take care of the ‎graphics, text display, scrolling and more.‎

Secrets

Next, we import the secrets needed for Wi-Fi access point connection, ‎Open Weather Maps key, and more.‎

Units

Want to change between Imperial and metric units? We've got you ‎covered! Use the Matrix Portal buttons or the added jumper on the ‎Metro M4 Airlift's pin D12 to do so.‎

Jumper

For the Metro M4 Airlift version, next we set up a digital input pin and ‎a variable called jumper in order to switch the device from imperial to ‎metric units.‎

Buttons

For the Matrix Portal version, we can use the on-board buttons instead. ‎By holding down one of the buttons while you power on the Matrix ‎Portal, you can set unit mode of the display. The value is stored in ‎non-volatile memory and is automatically used the next time the ‎Matrix Portal is powered on.‎

  • Hold Down for metric mode
  • Hold Up for imperial mode

Download File

Copy Code
if hasattr(board, "D12"):
jumper = DigitalInOut(board.D12)
jumper.direction = Direction.INPUT
jumper.pull = Pull.UP
is_metric = jumper.value
elif hasattr(board, "BUTTON_DOWN") and hasattr(board, "BUTTON_UP"):
button_down = DigitalInOut(board.BUTTON_DOWN)
button_down.switch_to_input(pull=Pull.UP)

button_up = DigitalInOut(board.BUTTON_UP)
button_up.switch_to_input(pull=Pull.UP)
if not button_down.value:
print("Down Button Pressed")
microcontroller.nvm[0] = 1
elif not button_up.value:
print("Up Button Pressed")
microcontroller.nvm[0] = 0
print(microcontroller.nvm[0])
is_metric = microcontroller.nvm[0]
else:
is_metric = False

if is_metric:
UNITS = "metric" # can pick 'imperial' or 'metric' as part of URL query
print("Jumper set to metric")
else:
UNITS = "imperial"
print("Jumper set to imperial")

Data

We'll set a variable for the DATA_SOURCE which will be used to query ‎Open Weather Maps API next. This query will use ‎the LOCATION and UNITS variables to form the request.‎

Download File

Copy Code
LOCATION = "Los Angeles, US"
print("Getting weather for {}".format(LOCATION))
DATA_SOURCE = (
"http://api.openweathermap.org/data/2.5/weather?q=" + LOCATION + "&units=" + UNITS
)
DATA_SOURCE += "&appid=" + secrets["openweather_token"]

API Query and JSON

Using this information, the code can then send a query to Open ‎Weather Maps's API that looks something like this:‎

http://api.openweathermap.org/data/2.5/weather?q=Los Angeles, ‎US&appid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

‎(where all of those 'x's are your token).‎

When this query is complete, it returns a JSON file that looks like this:‎

Download File

Copy Code
{
"coord": {
"lon": -118.24,
"lat": 34.05
},
"weather": [
{
"id": 501,
"main": "Rain",
"description": "moderate rain",
"icon": "10d"
}
],
"base": "stations",
"main": {
"temp": 287.42,
"pressure": 1016,
"humidity": 50,
"temp_min": 285.15,
"temp_max": 289.15
},
"visibility": 16093,
"wind": {
"speed": 3.6,
"deg": 300
},
"rain": {
"1h": 1.52
},
"clouds": {
"all": 75
},
"dt": 1552073935,
"sys": {
"type": 1,
"id": 3514,
"message": 0.0087,
"country": "US",
"sunrise": 1552054308,
"sunset": 1552096542
},
"id": 5368361,
"name": "Los Angeles",
"cod": 200
}

JSON Traversal

The JSON file is formatted in a way that makes it easy to traverse the ‎hierarchy and parse the data. In it, you'll see keys, such ‎as main, description, icon, and temp, and their respective values. So, ‎here are some key: value pairs we care about for the weather station:‎

  • "main" : "Rain"
  • "description" : "moderate rain"‎
  • "icon" : "10d"‎
  • "temp" : "287.42"‎

In order to fetch this data from the file, we need to be able to describe ‎their locations in the file hierarchically. This is helpful, for example, in ‎differentiating between the 'main' weather condition and ‎the 'main' section containing temperature and other data. To avoid ‎name clashing we rely on JSON traversal.‎

In the openweather_graphics.py file, you'll see how this is done. For ‎example, the main key is found in this hierarchy of the JSON ‎file: ['weather'], [0], ['main']‎

This means there is a key at the top level of the JSON file ‎called 'weather', which has a sub-tree indexed [0], and then below ‎that is the 'main' key.‎

This process is used to cast the values of the temperature, weather, ‎description, and which icon to display from the directory of bitmap ‎icons.‎

Scroll Time

You can customize your scroll hold time here:‎

SCROLL_HOLD_TIME = 0 # set this to hold each line before finishing ‎scroll

Setting this value to 0 means there will not be a hold when a line of ‎text reaches the edge of the display.‎

Display and Network Setup

The display and network setup are next, along with setting the call to ‎the openweather_graphics class:‎

Download File

Copy Code
matrix = Matrix()
network = Network(status_neopixel=board.NEOPIXEL, debug=True)
if UNITS == "imperial" or UNITS == "metric":
gfx = openweather_graphics.OpenWeather_Graphics(
matrix.display, am_pm=True, units=UNITS
)

Main Loop

In the main loop we check the online time server once an hour to stay ‎in sync with internet time, and we check the weather data every ten ‎minutes, using the gfx call to the openweather_graphics class.‎

Download File

Copy Code
if (not localtime_refresh) or (time.monotonic() - localtime_refresh) > 3600:
try:
print("Getting time from internet!")
network.get_local_time()
localtime_refresh = time.monotonic()
except RuntimeError as e:
print("Some error occured, retrying! -", e)
continue

# only query the weather every 10 minutes (and on first run)
if (not weather_refresh) or (time.monotonic() - weather_refresh) > 600:
try:
value = network.fetch_data(DATA_SOURCE, json_path=(DATA_LOCATION,))
print("Response is", value)
gfx.display_weather(value)
weather_refresh = time.monotonic()
except RuntimeError as e:
print("Some error occured, retrying! -", e)
continue

With the json data from Open Weather Maps parsed, we can then scroll ‎through the text labels for weather description, humidity, wind ‎speed, and location.‎

LED Matrix Diffuser

LED Diffusion Acrylic

You can add an LED diffusion acrylic faceplate to 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 tricky 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_38

dashes_39

dashes_40

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

sign_41

sign_43

sign_42

Stand

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

stand_44

stand_45

stand_46

stand_47

stand_48

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.

制造商零件编号 4000
METRO M4 EXPRESS AIRLIFT WIFI
Adafruit Industries LLC
¥284.49
Details
制造商零件编号 4745
ADAFRUIT MATRIX PORTAL - CIRCUIT
Adafruit Industries LLC
¥203.09
Details
制造商零件编号 2278
64X32 RGB LED MATRIX - 4MM PITCH
Adafruit Industries LLC
¥336.58
Details
制造商零件编号 2601
RGB MATRIX SHIELD FOR ARDUINO
Adafruit Industries LLC
¥48.43
Details
制造商零件编号 4594
BLACK LED DIFFUSION ACRYLIC PANE
Adafruit Industries LLC
¥80.99
Details
制造商零件编号 1995
AC/DC WALL MOUNT ADAPTER 5.25V
Adafruit Industries LLC
¥67.15
Details
制造商零件编号 592
CABLE A PLUG TO MCR B PLUG 3'
Adafruit Industries LLC
¥24.01
Details
制造商零件编号 598
36-PIN 0.1 FEMALE HEADER- 5PK
Adafruit Industries LLC
¥24.01
Details
制造商零件编号 288
HOOK-UP SOLID 22AWG 300V RED 25'
Adafruit Industries LLC
¥24.75
Details
制造商零件编号 D275-5
CUTTER SIDE TAPERED FLUSH 5"
Klein Tools, Inc.
¥150.77
Details
制造商零件编号 4474
CABLE A PLUG TO C PLUG 3'
Adafruit Industries LLC
¥40.29
Details
制造商零件编号 4631
MINI-MAGNET FEET FOR RGB LED MAT
Adafruit Industries LLC
¥21.06
Details
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum