|
| 1 | +# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries |
| 2 | +# |
| 3 | +# SPDX-License-Identifier: MIT |
| 4 | + |
| 5 | +# Written by Liz Clark (Adafruit Industries) with OpenAI ChatGPT v4 Aug 3rd, 2023 build |
| 6 | +# https://help.openai.com/en/articles/6825453-chatgpt-release-notes |
| 7 | + |
| 8 | +# https://chat.openai.com/share/63cbe4c6-007f-4934-a458-a9c8a521620e |
| 9 | +# https://chat.openai.com/share/674c0f13-bc78-4d1e-be79-3bc777e29991 |
| 10 | + |
| 11 | +import time |
| 12 | +from math import pi, cos, sin |
| 13 | +import os |
| 14 | +import ssl |
| 15 | +import wifi |
| 16 | +import socketpool |
| 17 | +import adafruit_requests |
| 18 | +import board |
| 19 | +from adafruit_ticks import ticks_ms, ticks_add, ticks_diff |
| 20 | +import vectorio |
| 21 | +import displayio |
| 22 | +from adafruit_io.adafruit_io import IO_HTTP |
| 23 | +from jepler_udecimal import Decimal |
| 24 | +import keypad |
| 25 | +from adafruit_display_text import label |
| 26 | +from adafruit_bitmap_font import bitmap_font |
| 27 | +from adafruit_qualia.graphics import Graphics, Displays |
| 28 | + |
| 29 | +key = keypad.Keys((board.A0,), value_when_pressed=False, pull=True) |
| 30 | + |
| 31 | +timezone = -5 |
| 32 | + |
| 33 | +wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")) |
| 34 | +print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}") |
| 35 | + |
| 36 | +aio_username = os.getenv('ADAFRUIT_AIO_USERNAME') |
| 37 | +aio_key = os.getenv('ADAFRUIT_AIO_KEY') |
| 38 | + |
| 39 | +context = ssl.create_default_context() |
| 40 | +print("socketpool") |
| 41 | +pool = socketpool.SocketPool(wifi.radio) |
| 42 | +print("requests") |
| 43 | +requests = adafruit_requests.Session(pool, context) |
| 44 | +print("io") |
| 45 | +io = IO_HTTP(aio_username, aio_key, requests) |
| 46 | + |
| 47 | +earth_bitmap = displayio.OnDiskBitmap("/earth.bmp") |
| 48 | +mars_bitmap = displayio.OnDiskBitmap("/mars.bmp") |
| 49 | + |
| 50 | +graphics = Graphics(Displays.ROUND40, default_bg=None, auto_refresh=True) |
| 51 | + |
| 52 | +# Create a TileGrid to hold the bitmap |
| 53 | +earth_grid = displayio.TileGrid(earth_bitmap, pixel_shader=earth_bitmap.pixel_shader) |
| 54 | + |
| 55 | +# Create a Group to hold the TileGrid |
| 56 | +earth_group = displayio.Group() |
| 57 | + |
| 58 | +# Add the TileGrid to the Group |
| 59 | +earth_group.append(earth_grid) |
| 60 | + |
| 61 | +# Create a TileGrid to hold the bitmap |
| 62 | +mars_grid = displayio.TileGrid(mars_bitmap, pixel_shader=mars_bitmap.pixel_shader) |
| 63 | + |
| 64 | +def center(grid, bitmap): |
| 65 | + # Center the image |
| 66 | + grid.x -= (bitmap.width - graphics.display.width) // 2 |
| 67 | + grid.y -= (bitmap.height - graphics.display.height) // 2 |
| 68 | + |
| 69 | +center(mars_grid, mars_bitmap) |
| 70 | +center(earth_grid, earth_bitmap) |
| 71 | +# Create a Group to hold the TileGrid |
| 72 | +mars_group = displayio.Group() |
| 73 | + |
| 74 | +# Add the TileGrid to the Group |
| 75 | +mars_group.append(mars_grid) |
| 76 | + |
| 77 | +graphics.display.root_group = mars_group |
| 78 | + |
| 79 | +# Pointer using vectorio, first the hub |
| 80 | +pointer_pal = displayio.Palette(4) |
| 81 | +pointer_pal[0] = 0xff0000 |
| 82 | +pointer_pal[1] = 0x000000 |
| 83 | +pointer_pal[2] = 0x0000ff |
| 84 | +pointer_pal[3] = 0xffffff |
| 85 | +pointer_hub = vectorio.Circle(pixel_shader=pointer_pal, radius=16, x=0, y=0) |
| 86 | +pointer_hub.x = graphics.display.width // 2 |
| 87 | +pointer_hub.y = graphics.display.height // 2 |
| 88 | + |
| 89 | +# Pointer itself |
| 90 | +pw = 15 # pointer width |
| 91 | +ph = 200 # pointer height |
| 92 | +pointer_points = [(pw,0), (pw,-ph), (-pw,-ph), (-pw,0)] |
| 93 | +pointer = vectorio.Polygon(pixel_shader=pointer_pal, points=pointer_points, x=0,y=0) |
| 94 | +pointer.x = graphics.display.width // 2 |
| 95 | +pointer.y = graphics.display.height // 2 |
| 96 | +mars_group.append(pointer) |
| 97 | +earth_group.append(pointer) |
| 98 | +hw = 13 # pointer width |
| 99 | +hh = 150 # pointer height |
| 100 | +hour_points = [(hw,0), (hw,-hh), (-hw,-hh), (-hw,0)] |
| 101 | +hour_hand = vectorio.Polygon(pixel_shader=pointer_pal, points=hour_points, |
| 102 | + x=0, y=0, color_index=1) |
| 103 | +hour_hand.x = graphics.display.width // 2 |
| 104 | +hour_hand.y = graphics.display.height // 2 |
| 105 | +mars_group.append(hour_hand) |
| 106 | +earth_group.append(hour_hand) |
| 107 | + |
| 108 | +def calculate_number_position(number, center_x, center_y, radius): |
| 109 | + angle = (360 / 12) * (number - 3) # -3 adjusts the angle to start at 12 o'clock |
| 110 | + rad_angle = pi * angle / 180 |
| 111 | + if number >=8: |
| 112 | + x = int(center_x + cos(rad_angle) * radius-40) |
| 113 | + x = int(center_x + cos(rad_angle) * radius) |
| 114 | + y = int(center_y + sin(rad_angle) * radius) |
| 115 | + return x, y |
| 116 | + |
| 117 | +clock_face_numbers = {i: calculate_number_position(i, graphics.display.width // 2, |
| 118 | + graphics.display.height // 2, 300) for i in range(1, 13)} |
| 119 | + |
| 120 | +font_file = "/Roboto-Regular-47.pcf" |
| 121 | + |
| 122 | +for i in range(1, 13): |
| 123 | + mars_c = vectorio.Circle(pixel_shader=pointer_pal, radius=30, x=clock_face_numbers[i][0]+12, |
| 124 | + y=clock_face_numbers[i][1], color_index=1) |
| 125 | + earth_c = vectorio.Circle(pixel_shader=pointer_pal, radius=30, x=clock_face_numbers[i][0]+12, |
| 126 | + y=clock_face_numbers[i][1], color_index=3) |
| 127 | + if i >= 10: |
| 128 | + mars_c.x = mars_c.x + 14 |
| 129 | + earth_c.x = earth_c.x + 14 |
| 130 | + mars_group.append(mars_c) |
| 131 | + earth_group.append(earth_c) |
| 132 | + text = str(i) |
| 133 | + font = bitmap_font.load_font(font_file) |
| 134 | + |
| 135 | + mars_text = label.Label(font, text=text, color=0xFFFFFF) |
| 136 | + earth_text = label.Label(font, text=text, color=0x000000) |
| 137 | + mars_text.x = clock_face_numbers[i][0] |
| 138 | + mars_text.y = clock_face_numbers[i][1] |
| 139 | + earth_text.x = clock_face_numbers[i][0] |
| 140 | + earth_text.y = clock_face_numbers[i][1] |
| 141 | + mars_group.append(mars_text) |
| 142 | + earth_group.append(earth_text) |
| 143 | + |
| 144 | +mars_group.append(pointer_hub) |
| 145 | +earth_group.append(pointer_hub) |
| 146 | + |
| 147 | +def update_time(): |
| 148 | + print("time") |
| 149 | + now = io.receive_time() |
| 150 | + return now |
| 151 | + |
| 152 | +def convert_time(the_time): |
| 153 | + h = the_time[3] |
| 154 | + if h >= 12: |
| 155 | + h -= 12 |
| 156 | + a = "PM" |
| 157 | + else: |
| 158 | + a = "AM" |
| 159 | + if h == 0: |
| 160 | + h = 12 |
| 161 | + return h, a |
| 162 | + |
| 163 | +def mars_time(): |
| 164 | + dt = io.receive_time() |
| 165 | + print(dt) |
| 166 | + utc_offset = 3600 * abs(timezone) |
| 167 | + tai_offset = 37 |
| 168 | + millis = time.mktime(dt) |
| 169 | + unix_timestamp = millis + utc_offset |
| 170 | + |
| 171 | + # Convert to MSD |
| 172 | + msd = (unix_timestamp + tai_offset) / Decimal("88775.244147") + Decimal("34127.2954262") |
| 173 | + print(msd) |
| 174 | + # Convert MSD to MTC |
| 175 | + mtc = (msd % 1) * 24 |
| 176 | + mtc_hours = int(mtc) |
| 177 | + mtc_minutes = int((mtc * 60) % 60) |
| 178 | + mtc_seconds = int(((mtc * 3600)) % 60) |
| 179 | + |
| 180 | + print(f"Mars Time: {mtc_hours:02d}:{mtc_minutes:02d}:{mtc_seconds:02d}") |
| 181 | + return mtc_minutes, mtc_hours |
| 182 | + |
| 183 | +def time_angle(m, the_hour): |
| 184 | + if m == 15: |
| 185 | + m = 16 |
| 186 | + elif m == 45: |
| 187 | + m = 46 |
| 188 | + # Adjusted angle calculation for minute hand |
| 189 | + theta_minute = 360 - (m / 60) * 360 |
| 190 | + theta_hour = ((the_hour / 12) + (m / (12 * 60))) * 360 |
| 191 | + # Calculate coordinates for minute hand (mirrored) |
| 192 | + minute_x = -int(cos(pi * (theta_minute - 90) / 180) * ph) |
| 193 | + minute_y = int(sin(pi * (theta_minute + 90) / 180) * ph) |
| 194 | + hour_x = int(cos(pi * (theta_hour - 90) / 180) * hh) |
| 195 | + hour_y = int(sin(pi * (theta_hour + 90) / 180) * hh) |
| 196 | + pointer.points = [(pw, 0), (minute_x + 2, -minute_y), (minute_x - 2, -minute_y), (-pw, 0)] |
| 197 | + hour_hand.points = [(hw, 0), (hour_x + 2, -hour_y), (hour_x - 2, -hour_y), (-hw, 0)] |
| 198 | + |
| 199 | +clock_timer = 1 * 1000 |
| 200 | +clock_clock = ticks_ms() |
| 201 | +display_timer = 10 * 1000 |
| 202 | +display_clock = ticks_ms() |
| 203 | +mars_second = 88775 |
| 204 | +mars_clock = ticks_ms() |
| 205 | +clock = update_time() |
| 206 | +hour, am_pm = convert_time(clock) |
| 207 | +tick = clock[5] |
| 208 | +minute = clock[4] |
| 209 | +mars_min, mars_hour = mars_time() |
| 210 | +show_earth = True |
| 211 | + |
| 212 | +time_angle(minute, hour) |
| 213 | + |
| 214 | +while True: |
| 215 | + event = key.events.get() |
| 216 | + if event: |
| 217 | + if event.pressed: |
| 218 | + print("updating display") |
| 219 | + show_earth = not show_earth |
| 220 | + if show_earth: |
| 221 | + if pointer.color_index != 2: |
| 222 | + time_angle(minute, hour) |
| 223 | + graphics.display.root_group = earth_group |
| 224 | + pointer.color_index = 2 |
| 225 | + pointer_hub.color_index = 2 |
| 226 | + else: |
| 227 | + if pointer.color_index != 0: |
| 228 | + time_angle(mars_min, mars_hour) |
| 229 | + graphics.display.root_group = mars_group |
| 230 | + pointer.color_index = 0 |
| 231 | + pointer_hub.color_index = 0 |
| 232 | + if ticks_diff(ticks_ms(), clock_clock) >= clock_timer: |
| 233 | + tick += 1 |
| 234 | + if tick > 59: |
| 235 | + tick = 0 |
| 236 | + minute += 1 |
| 237 | + if minute > 59: |
| 238 | + clock = update_time() |
| 239 | + hour, am_pm = convert_time(clock) |
| 240 | + tick = clock[5] |
| 241 | + minute = clock[4] |
| 242 | + print(f"{hour}:{minute:02} {am_pm}") |
| 243 | + mars_min, mars_hour = mars_time() |
| 244 | + if show_earth: |
| 245 | + time_angle(minute, hour) |
| 246 | + else: |
| 247 | + time_angle(mars_min, mars_hour) |
| 248 | + clock_clock = ticks_add(clock_clock, clock_timer) |
0 commit comments