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
Time is Ticking
Finally, three separate ticks
timers are created for timekeeping in the loop.
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)
Text editor powered by tinymce.