Once you've finished setting up your Feather ESP32-S3 TFT 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 to your computer as a zipped folder.
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import time
import wifi
import board
import displayio
import socketpool
import microcontroller
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import bitmap_label
import adafruit_ntp
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
timezone = -4
# The time of the thing!
EVENT_YEAR = 2024
EVENT_MONTH = 8
EVENT_DAY = 16
EVENT_HOUR = 0
EVENT_MINUTE = 0
# 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
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, tz_offset=timezone, cache_seconds=3600)
display = board.DISPLAY
group = displayio.Group()
font = bitmap_font.load_font("/Helvetica-Bold-16.pcf")
blinka_bitmap = displayio.OnDiskBitmap("/cpday_tft.bmp")
blinka_grid = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_bitmap.pixel_shader)
scrolling_label = bitmap_label.Label(font, text=" ", y=display.height - 13)
group.append(blinka_grid)
group.append(scrolling_label)
display.root_group = group
display.auto_refresh = False
refresh_clock = ticks_ms()
refresh_timer = 3600 * 1000
clock_clock = ticks_ms()
clock_timer = 1000
scroll_clock = ticks_ms()
scroll_timer = 50
first_run = True
while True:
# only query the online time once per hour (and on first run)
if ticks_diff(ticks_ms(), refresh_clock) >= refresh_timer or first_run:
try:
print("Getting time from internet!")
now = ntp.datetime
print(now)
total_seconds = time.mktime(now)
first_run = False
refresh_clock = ticks_add(refresh_clock, refresh_timer)
except Exception as e: # pylint: disable=broad-except
print("Some error occured, retrying! -", e)
time.sleep(2)
microcontroller.reset()
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
remaining = time.mktime(event_time) - total_seconds
secs_remaining = remaining % 60
remaining //= 60
mins_remaining = remaining % 60
remaining //= 60
hours_remaining = remaining % 24
remaining //= 24
days_remaining = remaining
scrolling_label.text = (f"{days_remaining} DAYS, {hours_remaining} HOURS," +
f"{mins_remaining} MINUTES & {secs_remaining} SECONDS")
total_seconds += 1
clock_clock = ticks_add(clock_clock, clock_timer)
if ticks_diff(ticks_ms(), scroll_clock) >= scroll_timer:
scrolling_label.x -= 1
if scrolling_label.x < -(scrolling_label.width + 5):
scrolling_label.x = display.width + 2
display.refresh()
scroll_clock = ticks_add(scroll_clock, scroll_timer)
Upload the Code and Libraries to the Feather ESP32-S3 TFT
After downloading the Project Bundle, plug your Feather ESP32-S3 TFT 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 Feather ESP32-S3 TFT's CIRCUITPY drive.
- lib folder
- code.py
- cpday_tft.bmp
- Helvetica-Bold-16.pcf
Your Feather ESP32-S3 TFT CIRCUITPY drive should look like this after copying the lib folder, cpday_tft.bmp file, Helvetica-Bold-16.pcf file and the code.py file.
Add Your settings.toml File
As of CircuitPython 8.0.0, there is support for Environment Variables. Environment variables are stored in a settings.toml file. Similar to secrets.py, the settings.toml file separates your sensitive information from your main code.py file. Add your settings.toml file as described in the Create Your settings.toml File page earlier in this guide. You'll need to include your CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD.
CIRCUITPY_WIFI_SSID = "your-ssid-here" CIRCUITPY_WIFI_PASSWORD = "your-ssid-password-here"
How the CircuitPython Code Works
At the top of the code, you'll edit timezone to reflect your UTC timezone offset for your location. The event time is also setup. In this case, it's August 16, 2024 at midnight.
timezone = -4
# The time of the thing!
EVENT_YEAR = 2024
EVENT_MONTH = 8
EVENT_DAY = 16
EVENT_HOUR = 0
EVENT_MINUTE = 0
# 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
WiFi and NTP
WiFi is setup along with an NTP instance. NTP is a networking protocol for clock synchronization and will take care of the timing for this project. Your timezone is passed to the NTP instance to reflect the time in your location.
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, tz_offset=timezone, cache_seconds=3600)
Graphics
Next are the display objects for the built-in TFT display on the Feather. This takes care of the background bitmap graphic and text element.
display = board.DISPLAY
group = displayio.Group()
font = bitmap_font.load_font("/Helvetica-Bold-16.pcf")
blinka_bitmap = displayio.OnDiskBitmap("/cpday_tft.bmp")
blinka_grid = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_bitmap.pixel_shader)
scrolling_label = bitmap_label.Label(font, text=" ", y=display.height - 13)
group.append(blinka_grid)
group.append(scrolling_label)
display.root_group = group
display.auto_refresh = False
refresh_clock = ticks_ms() refresh_timer = 3600 * 1000 clock_clock = ticks_ms() clock_timer = 1000 scroll_clock = ticks_ms() scroll_timer = 50
The Loop
In the loop, the time is fetched from the NTP server every hour and stored in now. now is converted to seconds using time.mktime(now). This lets you calculate how much time is remaining until the event.
if ticks_diff(ticks_ms(), refresh_clock) >= refresh_timer or first_run:
try:
print("Getting time from internet!")
now = ntp.datetime
print(now)
total_seconds = time.mktime(now)
first_run = False
refresh_clock = ticks_add(refresh_clock, refresh_timer)
except RuntimeError as e:
print("Some error occured, retrying! -", e)
continue
The time is kept by the microcontroller in between polling the NTP server. Every second, 1 second is added to the total_seconds value tracking the current time. remaining stores the total seconds remaining until the event. This is converted to days, hours, minutes and seconds. These values are added to the scrolling text on the TFT.
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
remaining = time.mktime(event_time) - total_seconds
secs_remaining = remaining % 60
remaining //= 60
mins_remaining = remaining % 60
remaining //= 60
hours_remaining = remaining % 24
remaining //= 24
days_remaining = remaining
my_scrolling_label.text = f"{days_remaining} Days, {hours_remaining} Hours, {mins_remaining} Minutes and {secs_remaining} Seconds"
total_seconds += 1
clock_clock = ticks_add(clock_clock, clock_timer)
The last timer is used to scroll the text by moving the x coordinate of the text by 2 pixels. When the text is offscreen, its x coordinate is reset to start scrolling across again.
if ticks_diff(ticks_ms(), scroll_clock) >= scroll_timer:
my_scrolling_label.x -= 2
if my_scrolling_label.x < -(my_scrolling_label.width + 5):
my_scrolling_label.x = display.width + 2
display.refresh()
scroll_clock = ticks_add(scroll_clock, scroll_timer)
Page last edited January 21, 2025
Text editor powered by tinymce.