# 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")) except Exception as e: # pylint: disable=broad-except print( "Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds." ) pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) # Set up the URL for fetching time data DATA_SOURCE = "http://worldtimeapi.org/api/timezone/" + os.getenv("TIMEZONE") # Set up display a default image display = board.DISPLAY default_bitmap = displayio.OnDiskBitmap("/images/blanka-chan.bmp") default_tile_grid = displayio.TileGrid(default_bitmap, pixel_shader=default_bitmap.pixel_shader) group = displayio.Group() group.append(default_tile_grid) # Create label for displaying time time_label = bitmap_label.Label(terminalio.FONT, scale=5) time_label.anchor_point = (0.2, 0.5) time_label.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(time_label) # Show the main group on the display display.root_group = main_group current_background_image = "/images/blanka-chan.bmp" def set_background_image(filename): global current_background_image # pylint: disable=global-statement tile_bitmap = displayio.OnDiskBitmap(filename) new_tile_grid = displayio.TileGrid(tile_bitmap, pixel_shader=tile_bitmap.pixel_shader) group[0] = new_tile_grid current_background_image = filename def parse_time(datetime_str): # Extract the time part from the datetime string time_str = datetime_str.split("T")[1].split(".")[0] hour, minute, _ = map(int, time_str.split(":")) # Convert 24-hour format to 12-hour format and determine AM/PM period = "AM" if hour >= 12: period = "PM" if hour > 12: hour -= 12 elif hour == 0: hour = 12 return hour, minute, period while True: # Fetch time data from WorldTimeAPI response = requests.get(DATA_SOURCE) data = response.json() # Parse the time from the datetime string current_hour, current_minute, current_period = parse_time(data["datetime"]) # Display the time time_label.text = " {:2}{}\n :{:02}".format(current_hour, current_period, current_minute) # Switch between two images if current_background_image == "/images/blanka-chan.bmp": set_background_image("/images/blanka-chan-charged.bmp") else: set_background_image("/images/blanka-chan.bmp") time.sleep(5)
The file Arial-Bold-36.bdf in the project bundle was not used in the code above (the built-in terminal font is). You need not worry this file is used somewhere.
This code is doing several things:
- Connects to the internet via WiFi using your credentials
- Creates a label for displaying the current time
- Sets a background image and toggles between another background image
- Fetches and displays the current time from WorldTimeAPI
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." )
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())
Next, set up the URL for fetching time data.
DATA_SOURCE = "http://worldtimeapi.org/api/timezone/" + os.getenv("TIMEZONE")
Setting the background image
This chunk of code sets up the TFT's display to show a default image. First, access the TFT's display, then loads an image from the disk into a bitmap. This bitmap is then placed into a TileGrid for display purposes.
A new group is created, and the TileGrid is added to this group. This group can now be set as the root_group
on the display, allowing the image to be viewed.
display = board.DISPLAY default_bitmap = displayio.OnDiskBitmap("/images/blanka-chan.bmp") default_tile_grid = displayio.TileGrid(default_bitmap, pixel_shader=default_bitmap.pixel_shader) group = displayio.Group() group.append(default_tile_grid)
Setting up a label for displaying the current time
This part of the code creates a label to display the time and positions it in the center of the TFT's display. The label is created using a bitmap font, and its size is scaled by a factor of 5.
It then makes a main group to hold all display components, which includes the previously created image group and the time label. Lastly, it shows the main group on the TFT, displaying the image and the time label on the screen.
time_label = bitmap_label.Label(terminalio.FONT, scale=5) time_label.anchor_point = (0.2, 0.5) time_label.anchored_position = (display.width // 2, display.height // 2) main_group = displayio.Group() main_group.append(group) main_group.append(time_label) display.root_group = main_group
Creating a function to set the background image
The set_background_image(filename)
function is used to change the background image of the display. Here's how it works:
-
First, it takes in an input
filename
that will be used as the new background. -
Then uses the
global
keyword to declarecurrent_background_image
as a global variable, which means this function can access and modify the value ofcurrent_background_image
defined outside the function. -
It then creates a bitmap from the new image file using
displayio.OnDiskBitmap(filename)
. -
A new
TileGrid
is created from this bitmap. TheTileGrid
is a collection of graphical tiles that can be positioned in a grid and drawn on the display. -
The group's first element (the existing tile grid that displays the current background image) is replaced by this new tile grid. This effectively changes the background image on the display.
-
Finally, the
current_background_image
is updated with the new filename. This helps keep track of which image is currently being displayed.
def set_background_image(filename): global current_background_image # pylint: disable=global-statement tile_bitmap = displayio.OnDiskBitmap(filename) new_tile_grid = displayio.TileGrid(tile_bitmap, pixel_shader=tile_bitmap.pixel_shader) group[0] = new_tile_grid current_background_image = filename
Creating a function to parse the JSON time data
The parse_time(datetime_str)
function extracts and formats the time from the datetime string, typically in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ).
-
The function takes as input
datetime_str
, a string representing date and time. -
It first extracts the time part from the datetime string. In the ISO 8601 format, the date and time are separated by "T," and the seconds are followed by a ".". So, the function splits the string at "T" and "." and picks the second element from each resulting list, which gives the time string in HH:MM:SS format.
-
This time string is further split at ":", and the hour, minute, and second are converted to integers using the
map
function. -
It then converts the hour from a 24-hour format to a 12-hour format. If the hour is greater than or equal to 12, it's considered PM, and the hour is reduced by 12 for hours greater than 12. If the hour is 0, it's converted to 12.
-
The function returns the hour, minute, and period ("AM" or "PM"). The seconds are not used in this function, so they are discarded.
def parse_time(datetime_str): # Extract the time part from the datetime string time_str = datetime_str.split("T")[1].split(".")[0] hour, minute, _ = map(int, time_str.split(":")) # Convert 24-hour format to 12-hour format and determine AM/PM period = "AM" if hour >= 12: period = "PM" if hour > 12: hour -= 12 elif hour == 0: hour = 12 return hour, minute, period
The Main Loop
This part of the code runs in an infinite loop, continuously updating the time display and switching between two images on the TFT every five seconds. Here's a breakdown of this loop:
-
Fetch Time Data: First, make a GET request to the WorldTimeAPI using the specified URL assigned to the
DATA_SOURCE
variable earlier in the code. The API responds with the current date and time data in JSON format, then converted to a Python dictionary using thejson()
method. -
Parse Time: The function
parse_time
is called with the datetime string received from the API. This function returns the hour, minute, and period (AM/PM) after processing the input string. -
Display Time: The current time is formatted into a string and assigned to the
text
attribute of thetime_label
object, which updates the time display on the board. -
Switch Images: The code then checks the current background image file path. If it's set to the path of blanka-chan.bmp, it calls the
set_background_image
function to change the background image to blanka-chan-charged.bmp. If the current image is not blanka-chan.bmp, it switches the image back to blanka-chan.bmp. -
Sleep: The program pauses for 5 seconds using
time.sleep(5)
. After this pause, the loop starts over, getting the updated time from the API, parsing it, updating the display, and switching the image again.
while True: # Fetch time data from WorldTimeAPI response = requests.get(DATA_SOURCE) data = response.json() # Parse the time from the datetime string current_hour, current_minute, current_period = parse_time(data["datetime"]) # Display the time time_label.text = " {:2}{}\n :{:02}".format(current_hour, current_period, current_minute) # Switch between two images if current_background_image == "/images/blanka-chan.bmp": set_background_image("/images/blanka-chan-charged.bmp") else: set_background_image("/images/blanka-chan.bmp") time.sleep(5)
Text editor powered by tinymce.