Before running these examples, be sure your Single Board Computer is connected to the internet, since a connection is required.

The included examples are all ports from previous projects for the PyPortal with associated learn guides. To use each of the examples, you will want to either go to the original Learn Guide click Download Project Zip or go to the original Project Files on GitHub.

While there are other examples besides the ones listed on this page, some of them make use of the some of the onboard sensors such as the light or temperature sensor, which is not present by default on the Pi. However, if you have a PiTFT with additional pins on the back, you could use a 26-pin or 40-pin Pi-Cobbler and connect additional sensors too.

If you are using an FT232H, you could may wire up some additional sensors to this board on some unused GPIO pins, though since the I2C pins are shared with the SPI pins, you won't be able to run and use I2C devices at the same time.

The following examples have been modified to work with the Raspberry Pi, which has a Linux Operating System and no built-in NeoPixel.

Secrets File

To run the examples, you will need to setup a secrets.py file in your script folder. You can copy it below if you don't already have one. Note that unlike the original PyPortal secrets file, you don't need to add your WiFi connection information because your board should already be on the internet.

# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it

secrets = {
    'timezone' : "America/New_York", # http://worldtimeapi.org/timezones
    'github_token' : 'fawfj23rakjnfawiefa',
    'hackaday_token' : 'h4xx0rs3kret',
    'openweather_token' : 'xxxxxxxxxxxxxxxxxxxxxxxx',
    }

PyPortal Startup Image and Sound

The PyPortal library looks to see if the PyPortal Startup Image and Wav file are in the folder that you are running the example from and plays them if they are there. You can download those here if you don't already have a copy. Be sure they are named pyportal_startup.bmp and pyportal_startup.wav.

When you start up each example, you should see the startup image displayed briefly and a hear sound played:

If you are using the 3.5" PiTFT, keep in mind that the display's resolution is higher than what examples were written for, so things may appear a little off.

Script changes for the 3.5" HX8357 TFT Display

To use the HX8357 display, we'll need to declare the display object in the example and pass it in since there's no way to detect which display is being used. In fact, it's likely that without modifying the code, the example would still run in a portion of the display with the unused part appearing as random noise.

The first thing you will need to add is some code to setup the display.

# Setup the Display
import board
import displayio
import adafruit_hx8357
displayio.release_displays()
spi = board.SPI()
tft_cs = board.CE0
tft_dc = board.D25

display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs)
display = adafruit_hx8357.HX8357(display_bus, width=480, height=320, backlight_pin=board.D18)

The other thing you will need to change is to pass in the already initialized display as an additional parameter to the PyPortal Initialization Function. So we will change this line:

pyportal = PyPortal(

to this:

pyportal = PyPortal(
    display=display,
If you see "AttributeError: module 'board' has no attribute 'NEOPIXEL'", make sure you are using the examples below rather than the original examples from the guides.

Bitcoin Example

The first example is the Bitcoin Price Display. This gets the current bitcoin value and displays it on the screen. You can get the resources for this example from the original PyPortal Bitcoin Example or from the original Learn Guide.

# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
This example will access the coindesk API, grab a number like bitcoin value in
USD and display it on a screen
If you can find something that spits out JSON data, we can display it!

You can find any resources in the associated Learn Guide at:
https://learn.adafruit.com/pyportal-bitcoin-value-display

Note: This library is designed to run on CPython and not CircuitPython.
"""
import os
import time
from adafruit_pyportal import PyPortal

# You can display in 'GBP', 'EUR' or 'USD'
CURRENCY = "USD"
# Set up where we'll be fetching data from
DATA_SOURCE = "https://api.coindesk.com/v1/bpi/currentprice.json"
DATA_LOCATION = ["bpi", CURRENCY, "rate_float"]


def text_transform(val):
    if CURRENCY == "USD":
        return "$%d" % val
    if CURRENCY == "EUR":
        return "‎€%d" % val
    if CURRENCY == "GBP":
        return "£%d" % val
    return "%d" % val


# the current working directory (where this file is)
try:
    cwd = os.path.dirname(os.path.realpath(__file__))
except AttributeError:
    cwd = ("/" + __file__).rsplit("/", 1)[0]

pyportal = PyPortal(
    url=DATA_SOURCE,
    json_path=DATA_LOCATION,
    default_bg=cwd + "/bitcoin_background.bmp",
    text_font=cwd + "/fonts/Arial-Bold-24-Complete.bdf",
    text_position=(195, 130),
    text_color=0x0,
    text_transform=text_transform,
)
pyportal.preload_font(b"$012345789")  # preload numbers
pyportal.preload_font((0x00A3, 0x20AC))  # preload gbp/euro symbol

while True:
    try:
        value = pyportal.fetch()
        print("Response is", value)
    except (ValueError, RuntimeError) as e:
        print("Some error occured, retrying! -", e)

    time.sleep(3 * 60)  # wait 3 minutes

Upload the secrets file, background image, font folder and the above example code to your Pi. If you are using a 3.5" display, be sure to make the necessary changes as well. After that, run the code:

python3 adafruit_blinka_pyportal_bitcoin.py

You should see something similar to the following:

NASA Image Example

The next example is the Image of the Day from NASA. This will get the current NASA image URL, download the image, resize it, and display it on the screen. You can get the resources for this example from the original PyPortal NASA Example or from the original Learn Guide.

# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
You can find any resources in the associated Learn Guide at:
https://learn.adafruit.com/pyportal-nasa-image-of-the-day-viewer

Note: This library is designed to run on CPython and not CircuitPython.
"""
import os
import time
from adafruit_pyportal import PyPortal

# Set up where we'll be fetching data from
DATA_SOURCE = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"
# There's a few different places we look for data in the photo of the day
IMAGE_LOCATION = ["url"]
TITLE_LOCATION = ["title"]
DATE_LOCATION = ["date"]

# the current working directory (where this file is)
try:
    cwd = os.path.dirname(os.path.realpath(__file__))
except AttributeError:
    cwd = ("/" + __file__).rsplit("/", 1)[0]

pyportal = PyPortal(
    url=DATA_SOURCE,
    json_path=(TITLE_LOCATION, DATE_LOCATION),
    default_bg=cwd + "/nasa_background.bmp",
    text_font=cwd + "/fonts/Arial-12.bdf",
    text_position=((5, 220), (5, 200)),
    text_color=(0xFFFFFF, 0xFFFFFF),
    text_maxlen=(50, 50),  # cut off characters
    image_json_path=IMAGE_LOCATION,
    image_resize=(320, 240),
    image_position=(0, 0),
    debug=True,
)

while True:
    response = None
    try:
        response = pyportal.fetch()
        print("Response is", response)
    except RuntimeError as e:
        print("Some error occured, retrying! -", e)

    time.sleep(30 * 60)  # 30 minutes till next check

Upload the secrets file, background image, font folder and the above example code to your Pi. If you are using a 3.5" display, be sure to make the necessary changes as well. After that, run the code:

python3 adafruit_blinka_pyportal_nasa.py

You should see the following background appear first:

Following that, it should download and display an image similar to this:

Quote Example

The next example is the Adafruit Quote display. This will go out and fetch a random quote from the Adafruit website and display it on the screen. You can get the resources for this example from the original PyPortal Quote Example or from the original Learn Guide.

# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
You can find any resources in the associated Learn Guide at:
https://learn.adafruit.com/pyportal-adafruit-quote-board

Note: This library is designed to run on CPython and not CircuitPython.
"""
import os
import time
from adafruit_pyportal import PyPortal

# Set up where we'll be fetching data from
DATA_SOURCE = "https://www.adafruit.com/api/quotes.php"
QUOTE_LOCATION = [0, "text"]
AUTHOR_LOCATION = [0, "author"]

# the current working directory (where this file is)
try:
    cwd = os.path.dirname(os.path.realpath(__file__))
except AttributeError:
    cwd = ("/" + __file__).rsplit("/", 1)[0]

pyportal = PyPortal(
    url=DATA_SOURCE,
    json_path=(QUOTE_LOCATION, AUTHOR_LOCATION),
    default_bg=cwd + "/quote_background.bmp",
    text_font=cwd + "/fonts/Arial-ItalicMT-17.bdf",
    text_position=((20, 120), (5, 210)),  # quote location  # author location
    text_color=(0xFFFFFF, 0x8080FF),  # quote text color  # author text color
    text_wrap=(35, 0),  # characters to wrap for quote  # no wrap for author
    text_maxlen=(180, 30),  # max text size for quote & author
)

# speed up projects with lots of text by preloading the font!
pyportal.preload_font()

while True:
    try:
        value = pyportal.fetch()
        print("Response is", value)
    except (ValueError, RuntimeError) as e:
        print("Some error occured, retrying! -", e)
    time.sleep(60)

Upload the secrets file, background image, font folder and the above example code to your Pi. If you are using a 3.5" display, be sure to make the necessary changes as well. After that, run the code:

python3 adafruit_blinka_pyportal_quote.py

You should see something similar to the following:

Discord Counter Example

The next example is the Adafruit Discord Online Counter display. This will go out and find the number of people currently online using the Adafruit Discord Server and display it on the screen. You can get the resources for this example from the original PyPortal Discord Example or from the original Learn Guide.

# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
This example will access shields.io API, grab the SVG graphic and then use
regular expression search to locate the number of online discord users, then
display it on a screen.
If you can find something that spits out text, we can display it!

You can find any resources in the associated Learn Guide at:
https://learn.adafruit.com/pyportal-discord-online-count

Note: This library is designed to run on CPython and not CircuitPython.
"""
import os
import time
from adafruit_pyportal import PyPortal

# Set up where we'll be fetching data from
DATA_SOURCE = "https://img.shields.io/discord/327254708534116352.svg"
# a regular expression for finding the data within the SVG xml text!
DATA_LOCATION = [r">([0-9]+ online)<"]

try:
    cwd = os.path.dirname(os.path.realpath(__file__))
except AttributeError:
    cwd = ("/" + __file__).rsplit("/", 1)[0]

pyportal = PyPortal(
    url=DATA_SOURCE,
    regexp_path=DATA_LOCATION,
    default_bg=cwd + "/discord_background.bmp",
    text_font=cwd + "/fonts/Collegiate-50.bdf",
    text_position=(70, 216),
    text_color=0x000000,
)

while True:
    try:
        value = pyportal.fetch()
        print("Response is", value)
    except RuntimeError as e:
        print("Some error occured, retrying! -", e)
    time.sleep(60)

Upload the secrets file, background image, font folder and the above example code to your Pi. If you are using a 3.5" display, be sure to make the necessary changes as well. After that, run the code:

python3 adafruit_blinka_pyportal_discord.py

You should see something similar to the following:

Open Weather Example

The last example is the Open Weather display. This will go out and find the weather conditions for the location that you have set and display it on the screen. This example is a little bit more complicated than the other examples. You can get the resources for this example from the original PyPortal Open Weather Example or from the original Learn Guide.

For this example, you will need to 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". Edit your secrets.py file and add the token into there.

Look for this line and be sure to change the LOCATION variable to your location:

# Use cityname, country code where countrycode is ISO3166 format.
# E.g. "New York, US" or "London, GB"
LOCATION = "Manhattan, US"

For this example, you will need 2 files. The main file:

# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
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

You can find any resources in the associated Learn Guide at:
https://learn.adafruit.com/pyportal-weather-station

Note: This library is designed to run on CPython and not CircuitPython.
"""
import os
import sys
import time
import adafruit_blinka_pyportal_openweather_graphics as openweather_graphics
from adafruit_pyportal import PyPortal

# the current working directory (where this file is)
try:
    cwd = os.path.dirname(os.path.realpath(__file__))
except AttributeError:
    cwd = ("/" + __file__).rsplit("/", 1)[0]

sys.path.append(cwd)

# 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

# Use cityname, country code where countrycode is ISO3166 format.
# E.g. "New York, US" or "London, GB"
LOCATION = "Manhattan, US"

# Set up where we'll be fetching data from
DATA_SOURCE = "http://api.openweathermap.org/data/2.5/weather?q=" + LOCATION
DATA_SOURCE += "&appid=" + secrets["openweather_token"]
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
DATA_LOCATION = []


# Initialize the pyportal object and let us know what data to fetch and where
# to display it
pyportal = PyPortal(url=DATA_SOURCE, json_path=DATA_LOCATION, default_bg=0x000000)

gfx = openweather_graphics.OpenWeather_Graphics(
    pyportal.splash, am_pm=True, celsius=False
)

localtile_refresh = None
weather_refresh = None
while True:
    # only query the online time once per hour (and on first run)
    if (not localtile_refresh) or (time.monotonic() - localtile_refresh) > 3600:
        try:
            print("Getting time from internet!")
            pyportal.get_local_time()
            localtile_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 = pyportal.fetch()
            print("Response is", value)
            gfx.display_weather(value)
            weather_refresh = time.monotonic()
        except RuntimeError as e:
            print("Some error occured, retrying! -", e)
            continue

    gfx.update_time()
    time.sleep(30)  # wait 30 seconds before updating anything again

And the Graphics handling file:

# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
This is a dependency of adafruit_blinka_pyportal_openweather.py and
should not be run directly.

Note: This library is designed to run on CPython and not CircuitPython.
"""

import os
import time
import json
import displayio
from adafruit_display_text.label import Label
from adafruit_bitmap_font import bitmap_font

try:
    cwd = os.path.dirname(os.path.realpath(__file__))
except AttributeError:
    cwd = ("/" + __file__).rsplit("/", 1)[0]

small_font = cwd + "/fonts/Arial-12.bdf"
medium_font = cwd + "/fonts/Arial-16.bdf"
large_font = cwd + "/fonts/Arial-Bold-24.bdf"

# pylint: disable=too-many-instance-attributes
class OpenWeather_Graphics(displayio.Group):
    def __init__(self, root_group, *, am_pm=True, celsius=True):
        super().__init__(max_size=2)
        self.am_pm = am_pm
        self.celsius = celsius

        root_group.append(self)
        self._icon_group = displayio.Group(max_size=1)
        self.append(self._icon_group)
        self._text_group = displayio.Group(max_size=5)
        self.append(self._text_group)

        self._icon_sprite = None
        self.set_icon(cwd + "/weather_background.bmp")

        self.small_font = bitmap_font.load_font(small_font)
        self.medium_font = bitmap_font.load_font(medium_font)
        self.large_font = bitmap_font.load_font(large_font)
        glyphs = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.: "
        self.small_font.load_glyphs(glyphs)
        self.medium_font.load_glyphs(glyphs)
        self.large_font.load_glyphs(glyphs)
        self.large_font.load_glyphs(("°",))  # a non-ascii character we need for sure
        self.city_text = None

        self.time_text = Label(self.medium_font, max_glyphs=8)
        self.time_text.x = 200
        self.time_text.y = 12
        self.time_text.color = 0xFFFFFF
        self._text_group.append(self.time_text)

        self.temp_text = Label(self.large_font, max_glyphs=6)
        self.temp_text.x = 200
        self.temp_text.y = 195
        self.temp_text.color = 0xFFFFFF
        self._text_group.append(self.temp_text)

        self.main_text = Label(self.large_font, max_glyphs=20)
        self.main_text.x = 10
        self.main_text.y = 195
        self.main_text.color = 0xFFFFFF
        self._text_group.append(self.main_text)

        self.description_text = Label(self.small_font, max_glyphs=60)
        self.description_text.x = 10
        self.description_text.y = 225
        self.description_text.color = 0xFFFFFF
        self._text_group.append(self.description_text)

    def display_weather(self, weather):
        weather = json.loads(weather)

        # set the icon/background
        weather_icon = weather["weather"][0]["icon"]
        self.set_icon(cwd + "/icons/" + weather_icon + ".bmp")

        city_name = weather["name"] + ", " + weather["sys"]["country"]
        print(city_name)
        if not self.city_text:
            self.city_text = Label(self.medium_font, text=city_name)
            self.city_text.x = 10
            self.city_text.y = 12
            self.city_text.color = 0xFFFFFF
            self._text_group.append(self.city_text)

        self.update_time()

        main_text = weather["weather"][0]["main"]
        print(main_text)
        self.main_text.text = main_text

        temperature = weather["main"]["temp"] - 273.15  # its...in kelvin
        print(temperature)
        if self.celsius:
            self.temp_text.text = "%d °C" % temperature
        else:
            self.temp_text.text = "%d °F" % ((temperature * 9 / 5) + 32)

        description = weather["weather"][0]["description"]
        description = description[0].upper() + description[1:]
        print(description)
        self.description_text.text = description
        # "thunderstorm with heavy drizzle"

    def update_time(self):
        """Fetch the time.localtime(), parse it out and update the display text"""
        now = time.localtime()
        hour = now[3]
        minute = now[4]
        format_str = "%d:%02d"
        if self.am_pm:
            if hour >= 12:
                hour -= 12
                format_str = format_str + " PM"
            else:
                format_str = format_str + " AM"
            if hour == 0:
                hour = 12
        time_str = format_str % (hour, minute)
        print(time_str)
        self.time_text.text = time_str

    def set_icon(self, filename):
        """The background image to a bitmap file.

        :param filename: The filename of the chosen icon

        """
        print("Set icon to ", filename)
        if self._icon_group:
            self._icon_group.pop()

        if not filename:
            return  # we're done, no icon desired
        with open(filename, "rb") as icon_file:
            icon = displayio.OnDiskBitmap(icon_file)
            self._icon_sprite = displayio.TileGrid(
                icon, pixel_shader=displayio.ColorConverter()
            )
            self._icon_group.append(self._icon_sprite)

Upload the updated secrets file, background image, font folder, icons folder and the above example files to your Pi. If you are using a 3.5" display, be sure to make the necessary changes as well. After that, run the code:

python3 adafruit_blinka_pyportal_openweather.py

You should see something similar to the following:

This guide was first published on Jul 09, 2020. It was last updated on Jul 09, 2020.

This page (Running Examples) was last updated on Sep 18, 2021.

Text editor powered by tinymce.