watch.py
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()