Adafruit IO Time Server

In order to get the precise time, our project will query the Adafruit IO Internet of Things service for the time. Adafruit IO is absolutely free to use, but you'll need to log in with your Adafruit account to use it. If you don't already have an Adafruit login, create one here.

If you haven't used Adafruit IO before, check out this guide for more info.

Once you have logged into your account, there are two pieces of information you'll need to place in your settings.toml file: Adafruit IO username, and Adafruit IO key. Head to and simply click the View AIO Key link on the left hand side of the Adafruit IO page to get this information.

Then, add them to the settings.toml file with a text editor, similar to this:

CIRCUITPY_WIFI_SSID = "your_wifi_ssid"
CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
AIO_USERNAME = "your_aio_username"
AIO_KEY = "your_aio_key"
If you get an error about no WiFi SSID or no aio_username, make a text file with the information above and save on the CIRCUITPY drive as

Install CircuitPython Code and Assets

In the embedded code element below, click on the Download Project Bundle button, and save the .zip archive file to your computer.

Then, uncompress the .zip file, it will unpack to a folder named PyPortal_CircuitPython_2020.

Copy the contents of the PyPortal_CircuitPython_2020 (, countdown_event.bmp, circuitpython_day_countdown_background.bmp, fonts directory) to your PyPortal's CIRCUITPY drive.

This is what the final contents of the CIRCUITPY drive will look like:

# SPDX-FileCopyrightText: 2019 Limor Fried for Adafruit Industries
# SPDX-License-Identifier: MIT

This example will figure out the current local time using the internet, and
then draw out a countdown clock until an event occurs!
Once the event is happening, a new graphic is shown
import time
import board
from adafruit_pyportal import PyPortal
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label

# The time of the thing!
# we'll make a python-friendly structure
event_time = time.struct_time((EVENT_YEAR, EVENT_MONTH, EVENT_DAY,
                               EVENT_HOUR, EVENT_MINUTE, 0,  # we don't track seconds
                               -1, -1, False))  # we dont know day of week/year or DST

# determine the current working directory
# needed so we know where to find files
cwd = ("/"+__file__).rsplit('/', 1)[0]
# Initialize the pyportal object and let us know what data to fetch and where
# to display it
pyportal = PyPortal(status_neopixel=board.NEOPIXEL,

big_font = bitmap_font.load_font(cwd+"/fonts/Helvetica-Bold-36.bdf")
big_font.load_glyphs(b'0123456789') # pre-load glyphs for fast printing
event_background = cwd+"/countdown_event.bmp"

days_position = (8, 207)
hours_position = (110, 207)
minutes_position = (220, 207)
text_color = 0xFFFFFF

text_areas = []
for pos in (days_position, hours_position, minutes_position):
    textarea = Label(big_font)
    textarea.x = pos[0]
    textarea.y = pos[1]
    textarea.color = text_color
refresh_time = None

while True:
    # only query the online time once per hour (and on first run)
    if (not refresh_time) or (time.monotonic() - refresh_time) > 3600:
            print("Getting time from internet!")
            refresh_time = time.monotonic()
        except RuntimeError as e:
            print("Some error occured, retrying! -", e)

    now = time.localtime()
    print("Current time:", now)
    remaining = time.mktime(event_time) - time.mktime(now)
    print("Time remaining (s):", remaining)
    if remaining < 0:
        # oh, its event time!
        while True:  # that's all folks
    secs_remaining = remaining % 60
    remaining //= 60
    mins_remaining = remaining % 60
    remaining //= 60
    hours_remaining = remaining % 24
    remaining //= 24
    days_remaining = remaining
    print("%d days, %d hours, %d minutes and %s seconds" %
          (days_remaining, hours_remaining, mins_remaining, secs_remaining))
    text_areas[0].text = '{:>2}'.format(days_remaining)  # set days textarea
    text_areas[1].text = '{:>2}'.format(hours_remaining) # set hours textarea
    text_areas[2].text = '{:>2}'.format(mins_remaining)  # set minutes textarea

    # update every 10 seconds
If you run into any errors, such as "ImportError: no module named `adafruit_display_text.label`" be sure to update your libraries to the latest release bundle!

This guide was first published on Jul 22, 2020. It was last updated on May 24, 2024.

This page (Code PyPortal with CircuitPython) was last updated on May 24, 2024.

Text editor powered by tinymce.