Once you've finished setting up your PyPortal with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
from os import getenv
import sys
import time
import board
from analogio import AnalogIn
from adafruit_pyportal import PyPortal
cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where this file is)
sys.path.append(cwd)
import openweather_graphics # pylint: disable=wrong-import-position
# Get WiFi details, ensure these are setup in settings.toml
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
if None in [ssid, password]:
raise RuntimeError(
"WiFi settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"at a minimum."
)
# Use cityname, country code where countrycode is ISO3166 format.
# E.g. "New York, US" or "London, GB"
LOCATION = "New York, 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=" + getenv('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,
status_neopixel=board.NEOPIXEL,
default_bg=0x000000)
display = board.DISPLAY
# rotate display for portrait orientation
display.rotation = 270
# instantiate the openweather_graphics class
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.root_group, am_pm=True, celsius=False)
# time keeping for refreshing screen icons and weather information
localtile_refresh = None
weather_refresh = None
# setup light sensor as an analog input
analogin = AnalogIn(board.LIGHT)
# analog scaling helper
def getVoltage(pin):
return (pin.value * 3.3) / 65536
# timer for updating onscreen clock
clock = time.monotonic()
# timer for keeping backlight on
light_clock = time.monotonic()
# timer for checking the light sensor
switch_clock = time.monotonic()
# variable to scale light sensor reading
ratio = 0
# storing last light sensor ratio reading
last_ratio = 0
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
# every 0.1 seconds check the light sensor value
if (time.monotonic() - switch_clock) > 0.1:
# read the light sensor and scale it 0 to 3.3
reading = getVoltage(analogin)
# calculate the % of light out of the maximum light
ratio = ((ratio + pow(1.0 - reading / 3.3, 4.0)) / 2.0)
# create a comparison ratio with the last reading
power_ratio = last_ratio + pow(last_ratio, 2.0)
# if the comparison ratio is less than 1
if power_ratio < 1:
# and the current ratio is larger
if ratio > power_ratio:
# turn on the backlight
pyportal.set_backlight(1)
light_clock = time.monotonic()
# otherwise (if in a darker room)
else:
# if there's a difference greater than 0.003
# between the current ratio and the last ratio
if ratio - last_ratio > 0.003:
# turn on the backlight
pyportal.set_backlight(1)
light_clock = time.monotonic()
# update last_ratio
last_ratio = ratio
switch_clock = time.monotonic()
# after 10 seconds, turn off the backlight
if (time.monotonic() - light_clock) > 10:
pyportal.set_backlight(0)
# every 30 seconds update time on screen
if (time.monotonic() - clock) > 30:
gfx.update_time()
clock = time.monotonic()
Upload the Code and Libraries to the PyPortal
After downloading the Project Bundle, plug your PyPortal into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the PyPortal's CIRCUITPY drive.
- lib folder
- fonts folder
- icons folder
- openweather_graphics.py
- code.py
Your PyPortal CIRCUITPY drive should look like this after copying the lib folder, fonts folder, icons folder, openweather_graphics.py file and the code.py file.
settings.toml
You will need to create and add a settings.toml file to your CIRCUITPY drive. Your settings.toml file will need to include the following information:
CIRCUITPY_WIFI_SSID="your-wifi-ssid" CIRCUITPY_WIFI_PASSWORD="your-wifi-password" ADAFRUIT_AIO_USERNAME="your-aio-username" ADAFRUIT_AIO_KEY="your-aio-key" openweather_token="YOUR-OPENWEATHER-TOKEN-HERE" timezone="America/New_York", # http://worldtimeapi.org/timezones
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".
You will also need your Adafruit IO username and key. Check out this guide for getting started with Adafruit IO if you haven't used Adafruit IO before.
How the CircuitPython Code Works
This code was adapted from the PyPortal Weather Station project. The code has been modified to rotate the display so that it is vertical and to turn on the display's backlight when it senses movement in front of the light sensor.
You'll want to update LOCATION at the top of the code to the location that you want to receive data for. Weather data is retrieved via the OpenWeatherMap API.
# Use cityname, country code where countrycode is ISO3166 format. # E.g. "New York, US" or "London, GB" LOCATION = "New York, 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 = []
The display is rotated using display.rotation. OpenWeather_Graphics() is a helper class from the openweather_graphics.py file. That file takes care of the graphics on screen and parsing the OpenWeatherMaps JSON feed to display the weather data.
# 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,
status_neopixel=board.NEOPIXEL,
default_bg=0x000000)
display = board.DISPLAY
# rotate display for portrait orientation
display.rotation = 270
# instantiate the openweather_graphics class
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.root_group, am_pm=True, celsius=False)
The light sensor is accessed with board.LIGHT. The getVoltage() helper is used to scale the analog input from 0 to 3.3.
# setup light sensor as an analog input
analogin = AnalogIn(board.LIGHT)
# analog scaling helper
def getVoltage(pin):
return (pin.value * 3.3) / 65536
In the loop, pyportal.get_local_time() is called every hour to get the internet time from Adafruit IO. The weather data feed is updated every 10 minutes with gfx.display_weather(value).
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
Every 0.1 seconds, the light sensor's value is read. The reading is scaled with getVoltage(). The code calculates what percentage out of the maximum amount of light is being read. This value is held in ratio. power_ratio holds the value of last_ratio plus last_ratio to the 2nd power.
power_ratio is used to compare the current ratio against to see if a substantial change in light value is being read by the sensor. This allows for the project to work in rooms with various levels of light.
# every 0.1 seconds check the light sensor value
if (time.monotonic() - switch_clock) > 0.1:
# read the light sensor and scale it 0 to 3.3
reading = getVoltage(analogin)
# calculate the % of light out of the maximum light
ratio = ((ratio + pow(1.0 - reading / 3.3, 4.0)) / 2.0)
# create a comparison ratio with the last reading
power_ratio = last_ratio + pow(last_ratio, 2.0)
If the ratio is larger than power_ratio when power_ratio is less than 1, then the PyPortal's backlight is turned on.
# if the comparison ratio is less than 1
if power_ratio < 1:
# and the current ratio is larger
if ratio > power_ratio:
# turn on the backlight
pyportal.set_backlight(1)
light_clock = time.monotonic()
If power_ratio is less than one, then the PyPortal's backlight is turned on when the difference between ratio and last_ratio is greater than 0.003.
# otherwise (if in a darker room)
else:
# if there's a difference greater than 0.003
# between the current ratio and the last ratio
if ratio - last_ratio > 0.003:
# turn on the backlight
pyportal.set_backlight(1)
light_clock = time.monotonic()
After 10 seconds, the PyPortal's backlight is turned off. Every 30 seconds, the clock time on screen is updated with gfx.update_time().
# after 10 seconds, turn off the backlight
if (time.monotonic() - light_clock) > 10:
pyportal.set_backlight(0)
# every 30 seconds update time on screen
if (time.monotonic() - clock) > 30:
gfx.update_time()
clock = time.monotonic()
Page last edited July 28, 2025
Text editor powered by tinymce.