# SPDX-FileCopyrightText: 2023 Trevor Beaton for Adafruit Industries # # SPDX-License-Identifier: MIT import os import ssl import time import wifi import board import displayio import terminalio import socketpool import adafruit_requests from adafruit_display_text import bitmap_label # Initialize Wi-Fi connection try: wifi.radio.connect( os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD") ) print("Connected to %s!" % os.getenv("CIRCUITPY_WIFI_SSID")) # Wi-Fi connectivity fails with error messages, not specific errors, so this except is broad. except Exception as e: # pylint: disable=broad-except print( "Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds." ) # Create a socket pool and session object for making HTTP requests pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) # Set location and units for weather data UNITS = "metric" LOCATION = os.getenv("LOCATION") print("Getting weather for {}".format(LOCATION)) # Set up the URL for fetching weather data DATA_SOURCE = ( "http://api.openweathermap.org/data/2.5/weather?q=" + LOCATION + "&units=" + UNITS + "&mode=json" + "&appid=" + os.getenv("OPENWEATHER_KEY") ) # Define time interval between requests time_interval = 3000 # set the time interval to 30 minutes # Set up display a default image display = board.DISPLAY bitmap = displayio.OnDiskBitmap("/images/sunny.bmp") tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader) group = displayio.Group() group.append(tile_grid) # Create label for displaying temperature data text_area = bitmap_label.Label(terminalio.FONT, scale=3) text_area.anchor_point = (0.5, 0.5) text_area.anchored_position = (display.width // 2, display.height // 2) # Create main group to hold all display groups main_group = displayio.Group() main_group.append(group) main_group.append(text_area) # Show the main group on the display display.root_group = main_group # Define function to get the appropriate weather icon def get_weather_condition_icon(weather_condition): if "cloud" in weather_condition.lower(): return "/images/cloudy.bmp" elif "rain" in weather_condition.lower(): return "/images/rain.bmp" elif "snow" in weather_condition.lower(): return "/images/snowy.bmp" elif "clear" in weather_condition.lower(): return "/images/sunny.bmp" else: return "/images/sunny.bmp" # Define function to update the background image based on weather conditions def set_background(weather_condition, background_tile_grid): bitmap_path = get_weather_condition_icon(weather_condition) background_bitmap = displayio.OnDiskBitmap(bitmap_path) background_tile_grid.bitmap = background_bitmap # Main loop to continuously fetch and display weather data while True: # Fetch weather data from OpenWeatherMap API print("Fetching json from", DATA_SOURCE) response = requests.get(DATA_SOURCE) print(response.json()) # Extract temperature and weather condition data from API response current_temp = response.json()["main"]["temp"] max_temp = response.json()["main"]["temp_max"] min_temp = response.json()["main"]["temp_min"] current_weather_condition = response.json()["weather"][0]["main"] print("Weather condition: ", current_weather_condition) # Convert temperatures to Fahrenheit max_temp = (max_temp * 9 / 5) + 32 min_temp = (min_temp * 9 / 5) + 32 current_temp = (current_temp * 9 / 5) + 32 # Convert temperatures to Fahrenheit to Celsius # max_temp = (max_temp - 32) * 5/9 # min_temp = (min_temp - 32) * 5/9 # current_temp = (current_temp - 32) * 5/9 print("Current temperature: {:.1f} °F".format(current_temp)) # Update label for displaying temperature data text_area.text = "{}\n {:.0f}°F\nH:{:.0f}°F L:{:.0f}°F".format( LOCATION, round(current_temp), round(max_temp), round(min_temp)) # Update background image set_background(current_weather_condition, tile_grid) time.sleep(time_interval)
This code is doing several things:
- Connecting to the internet via WiFi using your credentials
- Create a label for displaying temperature data
- Set up the background image for the current weather condition
- Fetch weather data from OpenWeatherMap API
- Extract temperature and weather condition data from the API response
Connecting to WiFi
This code block connects to a WiFi network using the wifi.radio.connect
function and passes in the network’s SSID and password as arguments from your credentials. The values of the SSID and password are read from environment variables CIRCUITPY_WIFI_SSID
and CIRCUITPY_WIFI_PASSWORD.
try: wifi.radio.connect( os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD") ) print("Connected to %s!" % os.getenv("CIRCUITPY_WIFI_SSID")) except Exception as e: # pylint: disable=broad-except print( "Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds.")
If you haven't set up your credentials, go back here.
Creating a Socket Pool
The socketpool.SocketPool
function creates a pool of sockets for managing network connections. It takes the wifi.radio
object as an argument to allow for network communication over a WiFi connection.
You will also need to create session object for making HTTP requests using adafruit_requests.Session
.
pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context())
Set up the URL for fetching weather data
Set a variable for the DATA_SOURCE
, which will be used to query Open Weather Maps API next. This query will use the LOCATION
and UNITS
variables to form the request. The value of the OPENWEATHER_KEY
is read from the environment variable OPENWEATHER_KEY
located in your settings.toml file.
DATA_SOURCE = ( "http://api.openweathermap.org/data/2.5/weather?q=" + LOCATION + "&units=" + UNITS + "&mode=json" + "&appid=" + os.getenv("OPENWEATHER_KEY") )
Using this information, the code can then send a query to Open Weather Maps's API that looks something like this:
http://api.openweathermap.org/data/2.5/weather?q=New York, US&units=metric&mode=json&appid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Adding a time interval
Create a time_interval
variable to have a set time to have our weather station fetch weather data every 30 seconds.
# Define time interval between requests time_interval = 3000 # set the time interval to 30 minutes
Setting the background image
First, create a variable called display
that refers to the display object provided by the board module. This allows you to interact with the display hardware.
Then, create a variable called bitmap
that refers to the displayio.OnDiskBitmap()
function that creates a bitmap object from an image file stored on disk. You'll add the image file /images/sunny.bmp as a default image.
For the third line of code, create a tile_grid
variable that refers to the displayio.TileGrid()
function that creates a grid of tiles to display the bitmap on the display.
In the fourth line, create a group object that will have a reference to a Group
container that will append the tile_grid
. A Group is a container object that can hold multiple display objects and allows you to manipulate them as a single unit.
display = board.DISPLAY bitmap = displayio.OnDiskBitmap("/images/sunny.bmp") tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader) group = displayio.Group() group.append(tile_grid)
Setting up a label for displaying temperature data
Here you'll create a label for displaying temperature data, add it to a main group, and shows the main group on display.
Create a Label object called text_area
using a built-in font from the terminalio
module and sets its scale to 3 times the default size.
Next, set the label's anchor_point
and anchored_position
for the second and third lines. Set anchor_point
to the center of the Label using a tuple of (0.5, 0.5).
The anchored_position
is the position of the anchor point relative to the display. Set to the center of the display using the //
operator to divide the display width and height by 2.
text_area = bitmap_label.Label(terminalio.FONT, scale=3) text_area.anchor_point = (0.5, 0.5) text_area.anchored_position = (display.width // 2, display.height // 2)
Next, creates a display group called main_group
to hold both the tile_grid
and the Label. The Tile Grid and Label are added to the group using the append
method.
Finally, the last line in this code block shows the main group on the display by setting the root_group
property of the display object to the main_group
.
main_group = displayio.Group() main_group.append(group) main_group.append(text_area) # Show the main group on the display display.root_group = main_group
This function, called get_weather_condition_icon()
, takes a weather condition as input and returns a file path for the corresponding image to display its background image. We'll use this in the set_background
function.
def get_weather_condition_icon(weather_condition): if "cloud" in weather_condition.lower(): return "/images/cloudy.bmp" elif "rain" in weather_condition.lower(): return "/images/rain.bmp" elif "snow" in weather_condition.lower(): return "/images/snowy.bmp" elif "clear" in weather_condition.lower(): return "/images/sunny.bmp" else: return "/images/sunny.bmp"
This function called set_background()
takes two parameters: a weather condition and a tile grid for the background image. The function updates the background image based on the weather condition by replacing the bitmap used by the tile grid. These functions will be used in the main loop.
def set_background(weather_condition, background_tile_grid): bitmap_path = get_weather_condition_icon(weather_condition) background_bitmap = displayio.OnDiskBitmap(bitmap_path) background_tile_grid.bitmap = background_bitmap
The Main Loop
Fetching weather data from OpenWeatherMap API
This line of code sends a GET request to the OpenWeatherMap API using the requests
library and retrieves the response as a JSON object. I'll also print this JSON data to see what information is coming in from the console.
print("Fetching json from", DATA_SOURCE) response = requests.get(DATA_SOURCE) print(response.json())
These values are then converted from Kelvin to Fahrenheit, and the converted current temperature is printed to the console for debugging purposes.
current_temp = response.json()["main"]["temp"] max_temp = response.json()["main"]["temp_max"] min_temp = response.json()["main"]["temp_min"] current_weather_condition = response.json()["weather"][0]["main"] print("Weather condition: ", current_weather_condition) # Convert temperatures to Fahrenheit max_temp = (max_temp * 9 / 5) + 32 min_temp = (min_temp * 9 / 5) + 32 current_temp = (current_temp * 9 / 5) + 32 # Convert temperatures to Fahrenheit to Celsius # max_temp = (max_temp - 32) * 5/9 # min_temp = (min_temp - 32) * 5/9 # current_temp = (current_temp - 32) * 5/9 print("Current temperature: {:.1f} °F".format(current_temp))
Update label for displaying temperature data
text_area.text = "{}\n {:.0f}°F\nH:{:.0f}°F L:{:.0f}°F".format( LOCATION, round(current_temp), round(max_temp), round(min_temp))
Finally, the code sleeps for a fixed interval before fetching and displaying updated weather data.
set_background(current_weather_condition, tile_grid) time.sleep(time_interval)
Having Problems Getting Data?
If you have problems getting the data to display correctly, check your settings. The settings.toml file has the information noted here.
Text editor powered by tinymce.