|
| 1 | +''' |
| 2 | +sunrise.py |
| 3 | +This example is for Interstate 75 W, connected up to two chained 64 x 64 panels. |
| 4 | +It displays information from the Sunrise Sunset API: |
| 5 | +Find out more here - https://sunrise-sunset.org/api |
| 6 | +Also shows how to use a 16x16 animated sprite. |
| 7 | +''' |
| 8 | +import WIFI_CONFIG |
| 9 | +import time |
| 10 | +from math import radians, sin, cos |
| 11 | +from random import randint |
| 12 | +from interstate75 import Interstate75 |
| 13 | +from network_manager import NetworkManager |
| 14 | +import ntptime |
| 15 | +import urequests |
| 16 | +import uasyncio |
| 17 | +import machine |
| 18 | + |
| 19 | + |
| 20 | +# Helper class so that the different formats of time can be converted into one comparable format |
| 21 | +# Makes up for the lack of a datetime module in MicroPython |
| 22 | +class TimeObj: |
| 23 | + def __init__(self): |
| 24 | + self.secs = 0 |
| 25 | + self.mins = 0 |
| 26 | + self.hours = 0 |
| 27 | + self.PM = False |
| 28 | + |
| 29 | + # Returns time variables as a tuple (h, m, s) |
| 30 | + def get_time(self): |
| 31 | + pm_hrs = 0 |
| 32 | + if self.PM: |
| 33 | + pm_hrs = 12 |
| 34 | + return (self.hours + pm_hrs, self.mins, self.secs) |
| 35 | + |
| 36 | + # Returns time variables as a single string |
| 37 | + def get_str(self): |
| 38 | + h, m, s = self.get_time() |
| 39 | + return "{0:02d}:{1:02d}:{2:02d}".format(h, m, s) |
| 40 | + |
| 41 | + # Set time variables from the sunrise-sunset API |
| 42 | + def parse_api(self, apiStr): |
| 43 | + strsplit = apiStr.split() |
| 44 | + if strsplit[1] == 'PM': |
| 45 | + self.PM = True |
| 46 | + timesplit = strsplit[0].split(':') |
| 47 | + self.hours = int(timesplit[0]) |
| 48 | + self.mins = int(timesplit[1]) |
| 49 | + self.secs = int(timesplit[2]) |
| 50 | + |
| 51 | + # Set time variables form |
| 52 | + def parse_localtime(self, localtpl): |
| 53 | + yr, mo, dy, self.hours, self.mins, self.secs, un1, un2 = localtpl |
| 54 | + |
| 55 | + # Returns number of seconds since midnight |
| 56 | + def get_total_secs(self): |
| 57 | + seconds = 0 |
| 58 | + if self.PM: |
| 59 | + seconds += 43200 # seconds in the first 12 hours |
| 60 | + seconds += (self.hours * 3600) |
| 61 | + seconds += (self.mins * 60) |
| 62 | + seconds += self.secs |
| 63 | + return seconds |
| 64 | + |
| 65 | + |
| 66 | +# Instances of TimeObj helper class defined above |
| 67 | +sunrise_obj = TimeObj() |
| 68 | +sunset_obj = TimeObj() |
| 69 | +currenttime = TimeObj() |
| 70 | + |
| 71 | +# Coordinates for Sheffield-on-Sea, UK |
| 72 | +lng = -1.4659 |
| 73 | +lat = 53.3829 |
| 74 | +# Coordinates for LA, USA |
| 75 | +# lng = -118.2437 |
| 76 | +# lat = 34.0522 |
| 77 | +URL = 'https://api.sunrise-sunset.org/json?lat={0}&lng={1}&date=today'.format(lat, lng) |
| 78 | +rtc = machine.RTC() |
| 79 | + |
| 80 | +i75 = Interstate75(display=Interstate75.DISPLAY_INTERSTATE75_128X64) |
| 81 | +graphics = i75.display |
| 82 | +sys_status = "Starting" |
| 83 | +WIDTH, HEIGHT = graphics.get_bounds() |
| 84 | + |
| 85 | +BLACK = graphics.create_pen(0, 0, 0) |
| 86 | +WHITE = graphics.create_pen(200, 200, 200) |
| 87 | +HILLS_COLOUR_1 = graphics.create_pen(100, 100, 0) |
| 88 | +HILLS_COLOUR_2 = graphics.create_pen(100, 150, 0) |
| 89 | +HILLS_COLOUR_3 = graphics.create_pen(50, 150, 0) |
| 90 | +SUN_COLOUR = graphics.create_pen(255, 0, 0) |
| 91 | + |
| 92 | +# Generate hill heights |
| 93 | +hills1 = [randint(10, 18), randint(10, 18), randint(10, 18), randint(10, 18), randint(10, 18)] |
| 94 | +hills2 = [randint(10, 18), randint(10, 18)] |
| 95 | +hills3 = [randint(10, 18), randint(10, 18)] |
| 96 | + |
| 97 | +# Sprite information for sun icon |
| 98 | +sun = [ |
| 99 | + [ |
| 100 | + 0b0000000100000000, |
| 101 | + 0b0000000000000000, |
| 102 | + 0b0010011111001000, |
| 103 | + 0b0000100000100000, |
| 104 | + 0b0001000000010000, |
| 105 | + 0b0010000000001000, |
| 106 | + 0b0010010001001000, |
| 107 | + 0b0010000100001000, |
| 108 | + 0b0010000000001000, |
| 109 | + 0b0010000000001000, |
| 110 | + 0b0001001110010000, |
| 111 | + 0b0000100000100000, |
| 112 | + 0b0010011111001000, |
| 113 | + 0b0000000000000000, |
| 114 | + 0b0000000100000000, |
| 115 | + 0b0000000000000000 |
| 116 | + ], |
| 117 | + [ |
| 118 | + 0b0000000000000000, |
| 119 | + 0b0100000000000100, |
| 120 | + 0b0000011111000000, |
| 121 | + 0b0000100000100000, |
| 122 | + 0b0001000000010000, |
| 123 | + 0b0010010001001000, |
| 124 | + 0b0010010001001000, |
| 125 | + 0b1010000100001010, |
| 126 | + 0b0010000000001000, |
| 127 | + 0b0010010001001000, |
| 128 | + 0b0001001110010000, |
| 129 | + 0b0000100000100000, |
| 130 | + 0b0000011111000000, |
| 131 | + 0b0100000000000100, |
| 132 | + 0b0000000000000000, |
| 133 | + 0b0000000000000000 |
| 134 | + ], |
| 135 | + [ |
| 136 | + 0b0000000100000000, |
| 137 | + 0b0100000000000100, |
| 138 | + 0b0010011111001000, |
| 139 | + 0b0000100000100000, |
| 140 | + 0b0001000000010000, |
| 141 | + 0b0010010001001000, |
| 142 | + 0b0010010001001000, |
| 143 | + 0b1010000100001010, |
| 144 | + 0b0010000000001000, |
| 145 | + 0b0010010001001000, |
| 146 | + 0b0001001110010000, |
| 147 | + 0b0000100000100000, |
| 148 | + 0b0010011111001000, |
| 149 | + 0b0100000000000100, |
| 150 | + 0b0000000100000000, |
| 151 | + 0b0000000000000000 |
| 152 | + ] |
| 153 | +] |
| 154 | + |
| 155 | + |
| 156 | +def get_data(): |
| 157 | + # open the json file |
| 158 | + print(f'Requesting URL: {URL}') |
| 159 | + r = urequests.get(URL) |
| 160 | + # open the json data |
| 161 | + j = r.json() |
| 162 | + print('Data obtained!') |
| 163 | + r.close() |
| 164 | + return j |
| 165 | + |
| 166 | + |
| 167 | +def get_sunrise(): |
| 168 | + sun_json = get_data() |
| 169 | + sunrise = sun_json['results']['sunrise'] |
| 170 | + sunrise_obj.parse_api(sunrise) |
| 171 | + sunset = sun_json['results']['sunset'] |
| 172 | + sunset_obj.parse_api(sunset) |
| 173 | + |
| 174 | + |
| 175 | +def display_status(): |
| 176 | + global sys_status |
| 177 | + graphics.set_pen(BLACK) |
| 178 | + graphics.clear() |
| 179 | + graphics.set_pen(WHITE) |
| 180 | + graphics.text(sys_status, 2, 0, WIDTH, 1) |
| 181 | + i75.update(graphics) |
| 182 | + |
| 183 | + |
| 184 | +def status_handler(mode, status, ip): |
| 185 | + # reports wifi connection status |
| 186 | + global sys_status |
| 187 | + print(mode, status, ip) |
| 188 | + sys_status = 'Mode: {0} Connected: {1} IP: {2}'.format(mode, status, ip) |
| 189 | + display_status() |
| 190 | + i75.update(graphics) |
| 191 | + print('Connecting to wifi...') |
| 192 | + if status is not None: |
| 193 | + if status: |
| 194 | + print('Wifi connection successful!') |
| 195 | + else: |
| 196 | + print('Wifi connection failed!') |
| 197 | + |
| 198 | + |
| 199 | +def calc_circle_points(ori_x, ori_y, r, deg): |
| 200 | + rads = radians(deg - 180) |
| 201 | + x = int(cos(rads) * r) + ori_x |
| 202 | + y = int(sin(rads) * r) + ori_y |
| 203 | + return x, y |
| 204 | + |
| 205 | + |
| 206 | +def to_seconds(hour, mins, secs, isPM=False): |
| 207 | + seconds = 0 |
| 208 | + if isPM: |
| 209 | + seconds += 43200 # Seconds in the first 12 hours |
| 210 | + seconds += (hour * 3600) |
| 211 | + seconds += (mins * 60) |
| 212 | + seconds += secs |
| 213 | + return seconds |
| 214 | + |
| 215 | + |
| 216 | +def draw_hills(): |
| 217 | + graphics.set_pen(HILLS_COLOUR_1) |
| 218 | + for x in range(5): |
| 219 | + graphics.circle(30 * x, 64, hills1[x]) |
| 220 | + graphics.set_pen(HILLS_COLOUR_2) |
| 221 | + for x in range(2): |
| 222 | + graphics.circle(60 * x + 15, 64, hills2[x]) |
| 223 | + graphics.set_pen(HILLS_COLOUR_3) |
| 224 | + for x in range(2): |
| 225 | + graphics.circle(60 * x + 30 + 15, 64, hills3[x]) |
| 226 | + |
| 227 | + |
| 228 | +def draw_text(): |
| 229 | + graphics.set_pen(WHITE) |
| 230 | + graphics.text("Sun Rise-Set API demo", 2, 0, WIDTH, 1) |
| 231 | + graphics.text("Sunrise: {0}".format(sunrise_obj.get_str()), 2, 8, WIDTH, 1) |
| 232 | + graphics.text("Sunset: {0}".format(sunset_obj.get_str()), 2, 16, WIDTH, 1) |
| 233 | + graphics.text("{0}".format(currenttime.get_str()), 2, 24, WIDTH, 1) |
| 234 | + |
| 235 | + |
| 236 | +def draw_sun(sunrise, sunset, time, cycle): |
| 237 | + global SUN_COLOUR |
| 238 | + sunlight_range = sunset - sunrise |
| 239 | + angle = int((180 / sunlight_range) * (time - sunrise)) % 360 |
| 240 | + pos_x, pos_y = calc_circle_points(64 - 16, 54, 50, angle) |
| 241 | + graphics.set_pen(SUN_COLOUR) |
| 242 | + if angle > 180: |
| 243 | + SUN_COLOUR = graphics.create_pen(0, 0, 0) |
| 244 | + elif angle < 90: |
| 245 | + r = 255 |
| 246 | + g = int(((angle / 100) * 90)) |
| 247 | + b = 0 |
| 248 | + SUN_COLOUR = graphics.create_pen(r, g, b) |
| 249 | + elif angle > 90: |
| 250 | + r = 255 |
| 251 | + g = int(100 - (((angle - 90) / 100) * 90)) |
| 252 | + b = 0 |
| 253 | + SUN_COLOUR = graphics.create_pen(r, g, b) |
| 254 | + for y in range(16): |
| 255 | + for x in range(16): |
| 256 | + if sun[cycle][y] & (1 << x): |
| 257 | + graphics.pixel(x + pos_x, int((y + pos_y))) |
| 258 | + |
| 259 | + |
| 260 | +try: |
| 261 | + network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) |
| 262 | + uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) |
| 263 | +except Exception as e: |
| 264 | + print(f'Wifi connection failed! {e}') |
| 265 | + |
| 266 | +get_sunrise() |
| 267 | +ntptime.settime() |
| 268 | +currenttime.parse_localtime(time.localtime()) |
| 269 | + |
| 270 | +count = 0 # Counter for animation |
| 271 | + |
| 272 | +while True: |
| 273 | + # Update current time class instance from RTC |
| 274 | + currenttime.parse_localtime(time.localtime()) |
| 275 | + count += 1 |
| 276 | + |
| 277 | + graphics.set_pen(BLACK) |
| 278 | + graphics.clear() |
| 279 | + |
| 280 | + # Uncomment for the animation |
| 281 | + # draw_sun(0, 180, count % 180, count % 3) |
| 282 | + draw_sun(sunrise_obj.get_total_secs(), sunset_obj.get_total_secs(), currenttime.get_total_secs(), count % 3) |
| 283 | + draw_hills() |
| 284 | + draw_text() |
| 285 | + i75.update(graphics) |
| 286 | + time.sleep(0.2) |
0 commit comments