Are you new to using CircuitPython? No worries, there is a full getting-started guide here.
Plug the device into your computer with a known good USB cable (not a charge-only cable). The device will appear to your computer in File Explorer or Finder (depending on your operating system) as a flash drive named CIRCUITPY. If the drive does not appear, you can install CircuitPython on your device and then return here.
Download the project files with the Download Project Bundle button below. Unzip the file and copy/paste the code.py and other project files to your CIRCUITPY drive using File Explorer or Finder (depending on your operating system).
Connect to WiFi
In order to communicate with the NTP servers your CircuitPython device needs to be connected to the internet. The easiest way to do this is to include your WiFi network credentials in a settings.toml file. If that file doesn't exist, then create it. Add these entries to the file and fill in the details for your own WiFi network. See this guide page for more info about creating the settings.toml file
CIRCUITPY_WIFI_SSID="network_name" CIRCUITPY_WIFI_PASSWORD="network_password"
Once that file is saved reboot your device and it will automatically connect to WiFi.
Drive Structure
After copying the files, your drive should look like the listing below. It can contain other files as well, but must contain these at a minimum.

Code
The code.py for the project is shown below.
There are 3 variables near the top which you can modify to change the clocks behavior.
-
UTC_OFFSET
- Set it to a negative or positive whole number to shift the time by relative to UTC. -
HOURLY_CHIME
- Set it toTrue
to enable the chime andFalse
to disable. -
ALARM_TIME
- Set it to None to disable the alarm, or a tuple containing hour and minute values for the alarm to go off at. These are in the local time after theUTC_OFFSET
has been applied.
# SPDX-FileCopyrightText: 2025 Tim Cocks # # SPDX-License-Identifier: MIT """ Cartoon Character Clock This project features an analog clock face rendered on a round display. The art and songs are inspired by an early 20th-century public domain cartoon. """ import os import time import board from adafruit_display_analogclock import AnalogClock from adafruit_gc9a01a import GC9A01A import rtc import socketpool import displayio from fourwire import FourWire import adafruit_ntp import wifi import audiobusio from audiomp3 import MP3Decoder # Set the desired offset from UTC time here. # US Eastern Standard Time is -5 # US Eastern Daylight Time is -4 UTC_OFFSET = -5 # enable or disable the hourly chime jingle HOURLY_CHIME = True # Set to a tuple containing hour and minute values to # enable the alarm e.g. 13, 25 will play the alarm at # 1:25 pm adjusted for the given UTC_OFFSET. ALARM_TIME = None #ALARM_TIME = (13, 25) # WiFi Setup # Get wifi AP credentials from a settings.toml file wifi_ssid = os.getenv("CIRCUITPY_WIFI_SSID") wifi_password = os.getenv("CIRCUITPY_WIFI_PASSWORD") if wifi_ssid is None: print("WiFi credentials are kept in settings.toml, please add them there!") raise ValueError("SSID not found in environment variables") try: wifi.radio.connect(wifi_ssid, wifi_password) except ConnectionError: print("Failed to connect to WiFi with provided credentials") raise # pylint: disable=unsubscriptable-object if HOURLY_CHIME or ALARM_TIME is not None: # Audio Setup audio = audiobusio.I2SOut(board.A2, board.A1, board.A0) songs = ["song_1.mp3", "song_2.mp3"] mp3 = open(songs[0], "rb") decoder = MP3Decoder(mp3) # Display Setup spi = board.SPI() tft_cs = board.TX tft_dc = board.RX displayio.release_displays() display_bus = FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=None) display = GC9A01A(display_bus, width=240, height=240) display.rotation = 90 # Sync time from NTP server pool = socketpool.SocketPool(wifi.radio) ntp = adafruit_ntp.NTP(pool, server="time.nist.gov", tz_offset=0, cache_seconds=3600) rtc.RTC().datetime = ntp.datetime # Initialize the clock face clockface = AnalogClock( "clock_short_hand.bmp", "clock_long_hand.bmp", (120, 120), 106, number_label_scale=2, background_color=0x989a97, background_img_file="clock_center.bmp", background_img_anchor_point=(0.5,0.5), background_img_anchored_position=(display.width//2, display.height//2 - 2) ) # set the clockface to show on the display display.root_group = clockface print(f"current time {time.localtime().tm_hour + UTC_OFFSET}:{time.localtime().tm_min}") cur_hour = time.localtime().tm_hour + UTC_OFFSET cur_minute = time.localtime().tm_min clockface.set_time(cur_hour, cur_minute) while True: # If we need to update the clock hands if cur_hour != time.localtime().tm_hour + UTC_OFFSET or cur_minute != time.localtime().tm_min: # store current values to comapre with next iteration cur_hour = time.localtime().tm_hour + UTC_OFFSET cur_minute = time.localtime().tm_min # update the clock face clockface.set_time(cur_hour, cur_minute) # if the hourly chime is enabled, and it's the top of the hour if HOURLY_CHIME and cur_minute == 0: # play the hour chime jingle audio.play(decoder) while audio.playing: pass # if the alarm is enabled and the current time is what # it was set to. if ALARM_TIME is not None and \ cur_hour == ALARM_TIME[0] and cur_minute == ALARM_TIME[1]: # open the alarm song file decoder.file = open("song_2.mp3", "rb") # play the alarm song twice for i in range(2): audio.play(decoder) while audio.playing: pass time.sleep(0.5) # re-open the hourly chime file decoder.file = open("song_1.mp3", "rb") time.sleep(3)
Page last edited February 12, 2025
Text editor powered by tinymce.