Last active
December 29, 2020 08:09
-
-
Save idriszmy/21241005044636957b4c9fb49b30825d to your computer and use it in GitHub Desktop.
Weather Display With e-Paper and Raspberry Pi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This code was based on code taken from a tutorial on the Adafruit website. | |
# https://learn.adafruit.com/raspberry-pi-e-ink-weather-station-using-python | |
# | |
# e-Paper: | |
# - https://my.cytron.io/p-universal-e-paper-raw-panel-driver-hat?tracking=idris | |
# - https://my.cytron.io/p-2.9-inch-e-ink-raw-display-panel-tri-color?tracking=idris | |
# | |
# Register at https://openweathermap.org/ and get the token (API key) | |
# | |
# Download meteocons font and unzip | |
# curl --remote-name https://www.alessioatzeni.com/meteocons/res/download/meteocons-font.zip && unzip meteocons-font.zip | |
import time | |
import urllib.request | |
import urllib.parse | |
import digitalio | |
import busio | |
import board | |
from adafruit_epd.il0373 import Adafruit_IL0373 | |
from weather_graphics import Weather_Graphics | |
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) | |
ecs = digitalio.DigitalInOut(board.CE0) | |
dc = digitalio.DigitalInOut(board.D25) | |
rst = digitalio.DigitalInOut(board.D17) | |
busy = digitalio.DigitalInOut(board.D24) | |
srcs = None | |
# You'll need to get a token from openweathermap.org, looks like: | |
# 'b6907d289e10d714a6e88b30761fae22' | |
OPEN_WEATHER_TOKEN = "" | |
# Use cityname, country code where countrycode is ISO3166 format. | |
# E.g. "New York, US" or "London, GB" | |
LOCATION = "Kuala Lumpur, MY" | |
DATA_SOURCE_URL = "http://api.openweathermap.org/data/2.5/weather" | |
if len(OPEN_WEATHER_TOKEN) == 0: | |
raise RuntimeError( | |
"You need to set your token first. If you don't already have one, you can register for a free account at https://home.openweathermap.org/users/sign_up" | |
) | |
# Set up where we'll be fetching data from | |
params = {"q": LOCATION, "appid": OPEN_WEATHER_TOKEN} | |
data_source = DATA_SOURCE_URL + "?" + urllib.parse.urlencode(params) | |
# Initialize the Display | |
display = Adafruit_IL0373( | |
128, 296, spi, | |
cs_pin=ecs, | |
dc_pin=dc, | |
sramcs_pin=srcs, | |
rst_pin=rst, | |
busy_pin=busy | |
) | |
display.rotation = 3 | |
gfx = Weather_Graphics(display, am_pm=True, celsius=True) | |
weather_refresh = None | |
while True: | |
# only query the weather every 10 minutes (and on first run) | |
if (not weather_refresh) or (time.monotonic() - weather_refresh) > 600: | |
response = urllib.request.urlopen(data_source) | |
if response.getcode() == 200: | |
value = response.read() | |
print("Response is", value) | |
gfx.display_weather(value) | |
weather_refresh = time.monotonic() | |
else: | |
print("Unable to retrieve data at {}".format(url)) | |
gfx.update_time() | |
time.sleep(300) # wait 5 minutes before updating anything again |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from datetime import datetime | |
import json | |
from PIL import Image, ImageDraw, ImageFont | |
from adafruit_epd.epd import Adafruit_EPD | |
small_font = ImageFont.truetype( | |
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 16 | |
) | |
medium_font = ImageFont.truetype( | |
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20 | |
) | |
large_font = ImageFont.truetype( | |
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 24 | |
) | |
icon_font = ImageFont.truetype( | |
"./meteocons.ttf", 48 | |
) | |
# Map the OpenWeatherMap icon code to the appropriate font character | |
# See http://www.alessioatzeni.com/meteocons/ for icons | |
ICON_MAP = { | |
"01d": "B", | |
"01n": "C", | |
"02d": "H", | |
"02n": "I", | |
"03d": "N", | |
"03n": "N", | |
"04d": "Y", | |
"04n": "Y", | |
"09d": "Q", | |
"09n": "Q", | |
"10d": "R", | |
"10n": "R", | |
"11d": "Z", | |
"11n": "Z", | |
"13d": "W", | |
"13n": "W", | |
"50d": "J", | |
"50n": "K", | |
} | |
# RGB Colors | |
WHITE = (255, 255, 255) | |
BLACK = (0, 0, 0) | |
class Weather_Graphics: | |
def __init__(self, display, *, am_pm=True, celsius=True): | |
self.am_pm = am_pm | |
self.celsius = celsius | |
self.small_font = small_font | |
self.medium_font = medium_font | |
self.large_font = large_font | |
self.display = display | |
self._weather_icon = None | |
self._city_name = None | |
self._main_text = None | |
self._temperature = None | |
self._description = None | |
self._time_text = None | |
def display_weather(self, weather): | |
weather = json.loads(weather.decode("utf-8")) | |
# set the icon/background | |
self._weather_icon = ICON_MAP[weather["weather"][0]["icon"]] | |
city_name = weather["name"] + ", " + weather["sys"]["country"] | |
print(city_name) | |
self._city_name = city_name | |
main = weather["weather"][0]["main"] | |
print(main) | |
self._main_text = main | |
temperature = weather["main"]["temp"] - 273.15 # its...in kelvin | |
print(temperature) | |
if self.celsius: | |
self._temperature = "%d °C" % temperature | |
else: | |
self._temperature = "%d °F" % ((temperature * 9 / 5) + 32) | |
description = weather["weather"][0]["description"] | |
description = description[0].upper() + description[1:] | |
print(description) | |
self._description = description | |
# "thunderstorm with heavy drizzle" | |
self.update_time() | |
def update_time(self): | |
now = datetime.now() | |
self._time_text = now.strftime("%I:%M %p").lstrip("0").replace(" 0", " ") | |
self.update_display() | |
def update_display(self): | |
self.display.fill(Adafruit_EPD.WHITE) | |
image = Image.new("RGB", (self.display.width, self.display.height), color=WHITE) | |
draw = ImageDraw.Draw(image) | |
# Draw the Icon | |
(font_width, font_height) = icon_font.getsize(self._weather_icon) | |
draw.text( | |
( | |
self.display.width // 2 - font_width // 2, | |
self.display.height // 2 - font_height // 2 - 5, | |
), | |
self._weather_icon, | |
font=icon_font, | |
fill=BLACK, | |
) | |
# Draw the city | |
draw.text( | |
(5, 5), self._city_name, font=self.medium_font, fill=BLACK, | |
) | |
# Draw the time | |
(font_width, font_height) = medium_font.getsize(self._time_text) | |
draw.text( | |
(5, font_height * 2 - 5), | |
self._time_text, | |
font=self.medium_font, | |
fill=BLACK, | |
) | |
# Draw the main text | |
(font_width, font_height) = large_font.getsize(self._main_text) | |
draw.text( | |
(5, self.display.height - font_height * 2), | |
self._main_text, | |
font=self.large_font, | |
fill=BLACK, | |
) | |
# Draw the description | |
(font_width, font_height) = small_font.getsize(self._description) | |
draw.text( | |
(5, self.display.height - font_height - 5), | |
self._description, | |
font=self.small_font, | |
fill=BLACK, | |
) | |
# Draw the temperature | |
(font_width, font_height) = large_font.getsize(self._temperature) | |
draw.text( | |
( | |
self.display.width - font_width - 5, | |
self.display.height - font_height * 2, | |
), | |
self._temperature, | |
font=self.large_font, | |
fill=BLACK, | |
) | |
self.display.image(image) | |
self.display.display() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment