In part 1 of this series I demonstrated how I created a clock using my 64×32 LED panel. In this blog I will expand upon this experiment and add current weather observations as a scrolling ticker across the bottom of the panel.
The first step to add weather observations to the clock is to register with Open Weather Map (openweathermap.org) and obtain an API key. Registration is free, and the free access API is sufficient for the needs of the clock.
You will also need to download and install the pyOWM module from pypi.org/project/pyowm, so the clock can query Open Weather Map for weather observations.
I created a config.py file to contain the OWM API key, as well as the ID for the city for which I want the weather observations. I used the command import config to merge the contents of the config.py file with the clock code. The contents of the config.py file looks like this.
# list of city Ids # http://bulk.openweathermap.org/sample/ weather_city = 4138106 weather_api_key = '1234567890abcdef' # number of minutes between weather updates weather_update_rate = 15
Because the free OWM API only allows a limited number of API calls (60/min), I have limited the update of the weather observations to every 15 minutes. You can adjust this number as you wish, but I found that the observations don’t change that quickly and this value won’t exceed your allotted calls.
The next step was to create a get_weather() function to obtain the weather observations and return them as a formatted string to the run() loop. The function is straightforward and is listed below.
I also created a helper function, get_wind_direction(), to translate the degree data returned from OWM, into meaningful compass directions.
def get_weather():
"""
This function reads the weather API key and city of interest from the config.py file.
Specific weather info to display is extracted from the weather observations object.
"""
weather_string = ''
weather_api_key = config.weather_api_key
weather_city = config.weather_city
weather_client = pyowm.OWM(weather_api_key)
observations = weather_client.weather_at_id(config.weather_city)
weather = observations.get_weather()
loc = observations.get_location().get_name()
# get weather
temp_f = weather.get_temperature('fahrenheit')['temp']
temp_c = weather.get_temperature('celsius')['temp']
wind = weather.get_wind(unit='miles_hour')['speed']
wind_dir = get_wind_direction(weather.get_wind()['deg'])
humidity = weather.get_humidity()
status = weather.get_detailed_status()
# get forecast
forecaster = weather_client.three_hours_forecast(loc).get_forecast()
forecast = forecaster.get_weathers()[0].get_detailed_status()
# build weather scrolling text
weather_str = '{} weather: {}f {}c {}% hum {}@{}mph {} Forecast: {}'.format(loc, int(temp_f), int(temp_c),
int(humidity), wind_dir, int(wind),
status, forecast)
print('weather updated @ {}'.format(datetime.datetime.now()))
return weather_str
def get_wind_direction(deg):
"""
Return the cardinal directions for the wind
"""
if 0 < int(deg) <= 23 or 338 >= int(deg):
return 'N'
elif 23 < int(deg) <= 68:
return 'NE'
elif 68 < int(deg) <= 113:
return 'E'
elif 113 < int(deg) <= 158:
return 'SE'
elif 158 < int(deg) <= 203:
return 'S'
elif 203 < int(deg) <= 248:
return 'SE'
elif 248 < int(deg) <= 293:
return 'W'
else:
return 'NW'
The final step to was to call get_weather() during the main clock loop, but only according to the config.weather_update_rate.
def run(matrix):
"""
Run the clock.
"""
# setup canvas
canvas = matrix.CreateFrameCanvas()
# fill it with black
canvas.Fill(0, 0, 0)
# setup the fonts for the clock
font = graphics.Font()
font.LoadFont("../rpi-rgb-led-matrix/fonts/5x7.bdf")
time_font = graphics.Font()
time_font.LoadFont("../rpi-rgb-led-matrix/fonts/7x13.bdf")
# text will be yellow
textColor = graphics.Color(255, 235, 59)
# set initial values
now = datetime.datetime.now()
weather_string = 'No weather data.'
date_string = now.strftime('%b %d, %Y')
time_string = now.strftime('%H:%M:%S')
big_x = 64
last_switch = now
last_fetch = now
show_dow = False
# setup for first run
init = True
while True:
# get the current time
now = datetime.datetime.now()
# fetch weather every X minutes
if init or (now - last_fetch).seconds > config.weather_update_rate * 60:
weather_string = get_weather()
last_fetch = datetime.datetime.now()
# switch dow and date every X sec
if (now - last_switch).seconds > config.face_flip_rate:
last_switch = datetime.datetime.now()
if show_dow == True:
show_dow = False
else:
show_dow = True
# display clock
if show_dow == False:
now = datetime.datetime.now() # so seconds tick
date_string = now.strftime('%A')
time_string = now.strftime('%-I:%M %p')
else:
date_string = now.strftime('%b %d, %Y')
time_string = now.strftime('%H:%M:%S')
# switch off init mode after through above code once
init = False
# fill convas with black
canvas.Fill(0, 0, 0)
# calculate element positions
time_pos = int((64 - len(time_string) * 7) / 2)
date_pos = int((64 - len(date_string) * 5) / 2)
# put elements on canvas
graphics.DrawText(canvas, time_font, time_pos, 11, textColor, time_string)
graphics.DrawText(canvas, font, date_pos, 20, textColor, date_string)
graphics.DrawText(canvas, font, big_x, 30, textColor, weather_string)
# calculate scroll horizontal position
big_x = big_x - 1
if big_x < len(weather_string) * -5:
big_x = 64
# display the clock
canvas = matrix.SwapOnVSync(canvas)
# so you can read the scrolling message
time.sleep(.07)
Notice there is now an additional graphics.DrawText() call to place the weather_string on the canvas. Also notice the y coordinates of each graphics.DarwText() command have been tweaked slightly to make room for the scrolling text. Each time the loop runs, the scrolling text is placed at the big_x coordinate on the canvas. big_x is reduced by the width of one character (5) each time the loop runs. The loop runs so fast that without a short sleep() it would scroll by too fast to read.
Here’s what the clock looks like with the scrolling weather.

The code for this blog series can be downloaded from GitHub.
