watch.py

../_images/watch.png

Analog Watch Display

Analog Watch Display using jpg for the face and filled polygons for the hand Requires face_{width}x{height}.jpg in the same directory as this script. See the create_face.py script for creating a custom sized face image.

Previous version video: https://youtu.be/NItKb6umMc4

  1""""
  2watch.py
  3========
  4
  5.. figure:: /_static/watch.png
  6  :align: center
  7
  8  Analog Watch Display
  9
 10Analog Watch Display using jpg for the face and filled polygons for the hand Requires
 11face_{width}x{height}.jpg in the same directory as this script. See the create_face.py
 12script for creating a custom sized face image.
 13
 14Previous version video: https://youtu.be/NItKb6umMc4
 15"""
 16
 17import utime
 18import math
 19import gc9a01
 20import tft_config
 21
 22
 23tft = tft_config.config(1)
 24
 25
 26def hand_polygon(length, radius):
 27    """
 28    Returns a list of coordinates representing a hand polygon shape.
 29
 30    Args:
 31        length (int): The length of the hand polygon.
 32        radius (int): The radius of the hand polygon.
 33
 34    Returns:
 35        list: A list of (x, y) coordinates representing the hand polygon shape.
 36    """
 37
 38    return [
 39        (0, 0),
 40        (-radius, radius),
 41        (-radius, int(length * 0.3)),
 42        (-1, length),
 43        (1, length),
 44        (radius, int(length * 0.3)),
 45        (radius, radius),
 46        (0, 0),
 47    ]
 48
 49
 50def main():
 51    """
 52    Draw analog watch face and update time
 53    """
 54
 55    # enable display
 56    tft.init()
 57    width = tft.width()
 58    height = tft.height()
 59    radius = min(width, height)  # face is the smaller of the two
 60    ofs = (width - radius) // 2  # offset from the left to center the face
 61    center_x = radius // 2 + ofs - 1  # center of the face horizontally
 62    center_y = radius // 2 - 1  # center of the face vertically
 63
 64    # draw the watch face background
 65    face = f"face_{width}x{height}.jpg"
 66    tft.jpg(face, 0, 0, gc9a01.SLOW)
 67
 68    # create the polygons for the hour, minute and second hands
 69    # polygons must be closed convex polygons or bad things(tm) happen.
 70
 71    second_len = int(radius * 0.65 / 2)
 72    second_poly = hand_polygon(second_len, 2)
 73
 74    minute_len = int(radius * 0.6 / 2)
 75    minute_poly = hand_polygon(minute_len, 2)
 76
 77    hour_len = int(radius * 0.5 / 2)
 78    hour_poly = hand_polygon(hour_len, 3)
 79
 80    # constants for calculating hand angles.
 81    pi_div_6 = math.pi / 6
 82    pi_div_30 = math.pi / 30
 83    pi_div_360 = math.pi / 360
 84    pi_div_1800 = math.pi / 1800
 85    pi_div_2160 = math.pi / 2160
 86
 87    # initialize variables for the bounding rectangles for the
 88    # hour, minute and second hands. Calling bounding with True will
 89    # reset the bounds, calling with False will disable bounding
 90
 91    tft.bounding(True)
 92    hour_bound = tft.bounding(True)
 93    minute_bound = tft.bounding(True)
 94    second_bound = tft.bounding(True)
 95
 96    while True:
 97        # save the current time in seconds so we can determine when
 98        # when to update the display.
 99        last = utime.time()
100
101        # get the current hour, minute and second
102        _, _, _, hour, minute, second, _, _ = utime.localtime()
103
104        # constrain hours to 12 hour time
105        hour %= 12
106
107        # calculate the angle of the hour hand in radians
108        hour_ang = (hour * pi_div_6) + (minute * pi_div_360) + (second * pi_div_2160)
109
110        # calculate the angle of the minute hand in radians
111        minute_ang = (minute * pi_div_30) + (second * pi_div_1800)
112
113        # calculate the angle of the second hand on radians
114        second_ang = second * pi_div_30
115
116        # erase the bounding area of the last drawn hour hand
117        x1, y1, x2, y2 = hour_bound
118        tft.fill_rect(x1, y1, x2, y2, gc9a01.WHITE)
119
120        # erase the bounding area of the last drawn minute hand
121        x1, y1, x2, y2 = minute_bound
122        tft.fill_rect(x1, y1, x2, y2, gc9a01.WHITE)
123
124        # erase the bounding area of the last drawn second hand
125        x1, y1, x2, y2 = second_bound
126        tft.fill_rect(x1, y1, x2, y2, gc9a01.WHITE)
127
128        # draw the hub after erasing the bounding areas to reduce flickering
129        tft.fill_circle(center_x, center_y, 5, gc9a01.BLACK)
130
131        tft.bounding(True)  # clear bounding rectangle
132
133        # draw and fill the hour hand polygon rotated to hour_ang
134        tft.fill_polygon(hour_poly, center_x, center_y, gc9a01.BLACK, hour_ang)
135
136        # get the bounding rectangle of the hour_polygon as drawn and
137        # reset the bounding box for the next polygon
138        hour_bound = tft.bounding(True, True)
139
140        # draw and fill the minute hand polygon rotated to minute_ang
141        tft.fill_polygon(minute_poly, center_x, center_y, gc9a01.BLACK, minute_ang)
142
143        # get the bounding rectangle of the minute_polygon as drawn and
144        # reset the bounding box for the next polygon
145        minute_bound = tft.bounding(True, True)
146
147        # draw and fill the second hand polygon rotated to second_ang
148
149        tft.fill_polygon(second_poly, center_x, center_y, gc9a01.RED, second_ang)
150
151        # get the bounding rectangle of the second_polygon as drawn and
152        # reset the bounding box for the next polygon
153        second_bound = tft.bounding(True, True)
154
155        # draw the hub again to cover up the second hand
156        tft.fill_circle(center_x, center_y, 5, gc9a01.BLACK)
157
158        # wait until the current second changes
159        while last == utime.time():
160            utime.sleep_ms(50)
161
162
163main()