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 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 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 = "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 = [] # 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.splash, 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.
secrets.py
You will need to create and add a secrets.py file to your CIRCUITPY drive. Your secrets.py file will need to include the following information:
secrets = { 'ssid' : 'YOUR-SSID-HERE', 'password' : 'YOUR-SSID-PASSWORD-HERE', 'aio_username' : 'YOUR-AIO-USERNAME-HERE', 'aio_key' : 'YOUR-AIO-KEY-HERE', '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.splash, 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()
Text editor powered by tinymce.