diff --git a/NeoPixel_Sprite_Weather_Display/code.py b/NeoPixel_Sprite_Weather_Display/code.py new file mode 100644 index 000000000..354f60536 --- /dev/null +++ b/NeoPixel_Sprite_Weather_Display/code.py @@ -0,0 +1,183 @@ +# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +import os +import ssl +import time +import board +import wifi +import socketpool +import fontio +import neopixel +import simpleio +from adafruit_display_text.bitmap_label import Label +from adafruit_bitmap_font import bitmap_font +from displayio import Bitmap +from rainbowio import colorwheel + +import adafruit_requests +from weather_codes import weather_codes + +# minimum expected temperature +min_temp = 0 +# maximum expected temperature +max_temp = 100 +# first daylight hour +daytime_min = 7 +# last daylight hour +daytime_max = 17 +# latitude +lat = 42.36 +# longitude +long = -71.06 +# temp unit for API request +temperature_unit = "fahrenheit" +# temp unit for display +temp_unit = "F" + +# API request to open-meteo +weather_url = "https://api.open-meteo.com/v1/forecast?" +# pass latitude and longitude +weather_url += "latitude=%d&longitude=%d&timezone=auto" % (lat, long) +# pass temperature_unit +weather_url += "¤t_weather=true&temperature_unit=%s&windspeed_unit=mph" % temperature_unit + +# connect to SSID +wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD')) + +pool = socketpool.SocketPool(wifi.radio) +requests = adafruit_requests.Session(pool, ssl.create_default_context()) + +# make the API request +response = requests.get(weather_url) +# packs the response into a JSON +response_as_json = response.json() +print() +# prints the entire JSON +print(response_as_json) +print() +# gets current weather code +weather = int(response_as_json['current_weather']['weathercode']) +print(weather) +# gets temperature +temp = response_as_json['current_weather']['temperature'] +temp_int = int(temp) +print("%s°%s" % (temp, temp_unit)) +# gets time +json_time = response_as_json['current_weather']['time'] +new_time = json_time.rsplit("T", 1)[-1] +new_time = int(new_time[:2]) +print(new_time) + +# font edit code by Jeff Epler +tom_thumb = bitmap_font.load_font("tom-thumb.pcf", Bitmap) + +_glyph_keys = ['bitmap', 'tile_index', 'width', 'height', 'dx', 'dy', 'shift_x', 'shift_y'] +def patch_glyph(base, **kw): + d = {} + for k in _glyph_keys: + d[k] = kw.get(k, getattr(base, k)) + return fontio.Glyph(**d) + +class PatchedFont: + def __init__(self, base_font, patches): + self.base_font = base_font + self.patches = patches + + def get_glyph(self, glyph): + g = self.base_font.get_glyph(glyph) + patch = self.patches.get(glyph) + if patch is not None: + #print("patching", repr(chr(glyph)), g) + g = patch_glyph(g, **patch) + #print("patched", g) + return g + + def get_bounding_box(self): + return self.base_font.get_bounding_box() + +font = PatchedFont(tom_thumb, + { + 32: {'shift_x': 1, 'dx': 0}, + 105: {'dx': 0, 'shift_x': 2}, + 33: {'dx': 0, 'shift_x': 2}, + }) + +# temperature for scrolling text +label = Label(text=" %s°%s " % (temp, temp_unit), font=font) +bitmap = label.bitmap + +# create 5x5 neopixels +pixels = neopixel.NeoPixel(board.A3, 5*5, brightness=.08, auto_write=False) +# count for pixels when drawing bitmaps +count = 0 +# arrays to pack assets from weather_codes helper +# weather condition code +codes = [] +# bitmap for daytime +day_images = [] +# bitmap for nighttime +night_images = [] + +for i in weather_codes: + codes.append(i['code']) + day_images.append(i['day_img']) + night_images.append(i['night_img']) + +clock = time.monotonic() + +# display current weather sprite & scroll temperature +while True: + # check every hour + if (time.monotonic() - clock) > 3600: + # make the API request + response = requests.get(weather_url) + # packs the response into a JSON + response_as_json = response.json() + print() + # prints the entire JSON + print(response_as_json) + print() + # current weather code + weather = int(response_as_json['current_weather']['weathercode']) + print(weather) + # temperature + temp = response_as_json['current_weather']['temperature'] + temp_int = int(temp) + print("%s°%s" % (temp, temp_unit)) + # update scrolling text + label.text = " %s°%s " % (temp, temp_unit) + json_time = response_as_json['current_weather']['time'] + new_time = json_time.rsplit("T", 1)[-1] + new_time = int(new_time[:2]) + print(new_time) + clock = time.monotonic() + # map color to temp range. colder temps are cool colors, warmer temps warm colors + temp_color = simpleio.map_range(temp_int, min_temp, max_temp, 255, 0) + # checks if it's day or night based on hour + if new_time in range(daytime_min, daytime_max): + img = day_images[weather] + else: + img = night_images[weather] + # draw bitmap sprite + for pixel in img: + pixels[count] = pixel + pixels.show() + count+=1 + time.sleep(0.001) + time.sleep(5) + hue = 0 + count = 0 + # draw scrolling text + for v in range(2): + for i in range(bitmap.width): + # Scoot the old text left by 1 pixel + pixels[:20] = pixels[5:] + # adjust color based on temperature + color = colorwheel(temp_color) + # Draw in the next line of text + for y in range(5): + # Select black or color depending on the bitmap pixel + pixels[20+y] = color * bitmap[i,y] + pixels.show() + time.sleep(.1) diff --git a/NeoPixel_Sprite_Weather_Display/weather_codes.py b/NeoPixel_Sprite_Weather_Display/weather_codes.py new file mode 100644 index 000000000..a2a10065f --- /dev/null +++ b/NeoPixel_Sprite_Weather_Display/weather_codes.py @@ -0,0 +1,191 @@ +# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +y = (255, 125, 0) +o = (0, 0, 0) +a = (0, 75, 125) +w = (255, 255, 255) +v = (127, 0, 255) +b = (0, 0, 255) +z = (0, 0, 25) +g = (25, 25, 25) +sun_bitmap = [ + y,a,y,a,y, + a,y,y,y,a, + y,y,y,y,y, + a,y,y,y,a, + y,a,y,a,y, +] +cloud_bitmap = [ + a,a,a,w,a, + a,w,w,w,a, + a,w,w,w,a, + a,a,w,w,a, + a,a,a,w,a, +] +partSun_bitmap = [ + a,w,w,w,a, + a,w,w,w,w, + y,y,w,w,a, + a,y,y,w,a, + y,a,y,a,a, +] +rain_bitmap = [ + z,z,v,z,b, + v,v,v,b,z, + v,v,v,z,b, + z,v,v,b,z, + z,z,v,z,b, +] +thunder_bitmap = [ + z,z,v,z,b, + v,v,v,b,z, + v,v,y,z,y, + z,y,v,y,z, + z,z,v,z,b, +] +snow_bitmap = [ + z,z,v,z,w, + v,v,v,w,z, + v,v,v,z,w, + z,v,v,w,z, + z,z,v,z,w, +] +night_bitmap = [ + y,g,g,y,g, + g,g,y,g,g, + g,y,g,g,y, + y,y,y,g,g, + g,y,g,g,g, +] +nightCloud_bitmap = [ + g,w,w,w,g, + g,w,w,w,g, + y,g,w,w,g, + g,g,g,w,g, + g,g,y,g,g, +] +nightRain_bitmap = [ + g,v,v,v,b, + g,v,v,v,g, + y,g,v,v,b, + g,g,g,v,g, + g,g,y,g,g, +] +nightThunder_bitmap = [ + g,v,v,v,b, + g,v,v,v,g, + g,g,y,v,y, + v,y,g,y,b, + v,v,b,g,g, +] +nightSnow_bitmap = [ + g,v,v,v,w, + g,v,v,v,g, + y,g,v,v,w, + g,g,g,v,g, + g,g,y,g,g, +] + +weather_codes = [ +{"code" : 0, "day_img" : sun_bitmap, "night_img" : night_bitmap}, +{"code" : 1, "day_img" : sun_bitmap, "night_img" : night_bitmap}, +{"code" : 2, "day_img" : sun_bitmap, "night_img" : night_bitmap}, +{"code" : 3, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 4, "day_img" : partSun_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 5, "day_img" : partSun_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 6, "day_img" : partSun_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 7, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 8, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 9, "day_img" : partSun_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 10, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 11, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 12, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 13, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}, +{"code" : 14, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 15, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 16, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 17, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}, +{"code" : 18, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 19, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}, +{"code" : 20, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 21, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 22, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 23, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 24, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 25, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 26, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 27, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 28, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 29, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}, +{"code" : 30, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 31, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 32, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 33, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 34, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 35, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 36, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 37, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 38, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 39, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 40, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 41, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 42, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 43, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 44, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 45, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 46, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 47, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 48, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 49, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap}, +{"code" : 50, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 51, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 52, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 53, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 54, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 55, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 56, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 57, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 58, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 59, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 60, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 61, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 62, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 63, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 64, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 65, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 66, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 67, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 68, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 69, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 70, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 71, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 72, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 73, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 74, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 75, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 76, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 77, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 78, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 79, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 80, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 81, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 82, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 83, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 84, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 85, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 86, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 87, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 88, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 89, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 90, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 91, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 92, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap}, +{"code" : 93, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 94, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap}, +{"code" : 95, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}, +{"code" : 96, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}, +{"code" : 97, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}, +{"code" : 98, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}, +{"code" : 99, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap} +]