Skip to content

Commit bf15ca1

Browse files
authored
Merge pull request #2355 from adafruit/weather_sprites
Adding code for qt py neopixel weather display
2 parents 933d03c + 63cc76e commit bf15ca1

File tree

2 files changed

+374
-0
lines changed

2 files changed

+374
-0
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
import os
5+
import ssl
6+
import time
7+
import board
8+
import wifi
9+
import socketpool
10+
import fontio
11+
import neopixel
12+
import simpleio
13+
from adafruit_display_text.bitmap_label import Label
14+
from adafruit_bitmap_font import bitmap_font
15+
from displayio import Bitmap
16+
from rainbowio import colorwheel
17+
18+
import adafruit_requests
19+
from weather_codes import weather_codes
20+
21+
# minimum expected temperature
22+
min_temp = 0
23+
# maximum expected temperature
24+
max_temp = 100
25+
# first daylight hour
26+
daytime_min = 7
27+
# last daylight hour
28+
daytime_max = 17
29+
# latitude
30+
lat = 42.36
31+
# longitude
32+
long = -71.06
33+
# temp unit for API request
34+
temperature_unit = "fahrenheit"
35+
# temp unit for display
36+
temp_unit = "F"
37+
38+
# API request to open-meteo
39+
weather_url = "https://api.open-meteo.com/v1/forecast?"
40+
# pass latitude and longitude
41+
weather_url += "latitude=%d&longitude=%d&timezone=auto" % (lat, long)
42+
# pass temperature_unit
43+
weather_url += "&current_weather=true&temperature_unit=%s&windspeed_unit=mph" % temperature_unit
44+
45+
# connect to SSID
46+
wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))
47+
48+
pool = socketpool.SocketPool(wifi.radio)
49+
requests = adafruit_requests.Session(pool, ssl.create_default_context())
50+
51+
# make the API request
52+
response = requests.get(weather_url)
53+
# packs the response into a JSON
54+
response_as_json = response.json()
55+
print()
56+
# prints the entire JSON
57+
print(response_as_json)
58+
print()
59+
# gets current weather code
60+
weather = int(response_as_json['current_weather']['weathercode'])
61+
print(weather)
62+
# gets temperature
63+
temp = response_as_json['current_weather']['temperature']
64+
temp_int = int(temp)
65+
print("%s°%s" % (temp, temp_unit))
66+
# gets time
67+
json_time = response_as_json['current_weather']['time']
68+
new_time = json_time.rsplit("T", 1)[-1]
69+
new_time = int(new_time[:2])
70+
print(new_time)
71+
72+
# font edit code by Jeff Epler
73+
tom_thumb = bitmap_font.load_font("tom-thumb.pcf", Bitmap)
74+
75+
_glyph_keys = ['bitmap', 'tile_index', 'width', 'height', 'dx', 'dy', 'shift_x', 'shift_y']
76+
def patch_glyph(base, **kw):
77+
d = {}
78+
for k in _glyph_keys:
79+
d[k] = kw.get(k, getattr(base, k))
80+
return fontio.Glyph(**d)
81+
82+
class PatchedFont:
83+
def __init__(self, base_font, patches):
84+
self.base_font = base_font
85+
self.patches = patches
86+
87+
def get_glyph(self, glyph):
88+
g = self.base_font.get_glyph(glyph)
89+
patch = self.patches.get(glyph)
90+
if patch is not None:
91+
#print("patching", repr(chr(glyph)), g)
92+
g = patch_glyph(g, **patch)
93+
#print("patched", g)
94+
return g
95+
96+
def get_bounding_box(self):
97+
return self.base_font.get_bounding_box()
98+
99+
font = PatchedFont(tom_thumb,
100+
{
101+
32: {'shift_x': 1, 'dx': 0},
102+
105: {'dx': 0, 'shift_x': 2},
103+
33: {'dx': 0, 'shift_x': 2},
104+
})
105+
106+
# temperature for scrolling text
107+
label = Label(text=" %s°%s " % (temp, temp_unit), font=font)
108+
bitmap = label.bitmap
109+
110+
# create 5x5 neopixels
111+
pixels = neopixel.NeoPixel(board.A3, 5*5, brightness=.08, auto_write=False)
112+
# count for pixels when drawing bitmaps
113+
count = 0
114+
# arrays to pack assets from weather_codes helper
115+
# weather condition code
116+
codes = []
117+
# bitmap for daytime
118+
day_images = []
119+
# bitmap for nighttime
120+
night_images = []
121+
122+
for i in weather_codes:
123+
codes.append(i['code'])
124+
day_images.append(i['day_img'])
125+
night_images.append(i['night_img'])
126+
127+
clock = time.monotonic()
128+
129+
# display current weather sprite & scroll temperature
130+
while True:
131+
# check every hour
132+
if (time.monotonic() - clock) > 3600:
133+
# make the API request
134+
response = requests.get(weather_url)
135+
# packs the response into a JSON
136+
response_as_json = response.json()
137+
print()
138+
# prints the entire JSON
139+
print(response_as_json)
140+
print()
141+
# current weather code
142+
weather = int(response_as_json['current_weather']['weathercode'])
143+
print(weather)
144+
# temperature
145+
temp = response_as_json['current_weather']['temperature']
146+
temp_int = int(temp)
147+
print("%s°%s" % (temp, temp_unit))
148+
# update scrolling text
149+
label.text = " %s°%s " % (temp, temp_unit)
150+
json_time = response_as_json['current_weather']['time']
151+
new_time = json_time.rsplit("T", 1)[-1]
152+
new_time = int(new_time[:2])
153+
print(new_time)
154+
clock = time.monotonic()
155+
# map color to temp range. colder temps are cool colors, warmer temps warm colors
156+
temp_color = simpleio.map_range(temp_int, min_temp, max_temp, 255, 0)
157+
# checks if it's day or night based on hour
158+
if new_time in range(daytime_min, daytime_max):
159+
img = day_images[weather]
160+
else:
161+
img = night_images[weather]
162+
# draw bitmap sprite
163+
for pixel in img:
164+
pixels[count] = pixel
165+
pixels.show()
166+
count+=1
167+
time.sleep(0.001)
168+
time.sleep(5)
169+
hue = 0
170+
count = 0
171+
# draw scrolling text
172+
for v in range(2):
173+
for i in range(bitmap.width):
174+
# Scoot the old text left by 1 pixel
175+
pixels[:20] = pixels[5:]
176+
# adjust color based on temperature
177+
color = colorwheel(temp_color)
178+
# Draw in the next line of text
179+
for y in range(5):
180+
# Select black or color depending on the bitmap pixel
181+
pixels[20+y] = color * bitmap[i,y]
182+
pixels.show()
183+
time.sleep(.1)
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
y = (255, 125, 0)
5+
o = (0, 0, 0)
6+
a = (0, 75, 125)
7+
w = (255, 255, 255)
8+
v = (127, 0, 255)
9+
b = (0, 0, 255)
10+
z = (0, 0, 25)
11+
g = (25, 25, 25)
12+
sun_bitmap = [
13+
y,a,y,a,y,
14+
a,y,y,y,a,
15+
y,y,y,y,y,
16+
a,y,y,y,a,
17+
y,a,y,a,y,
18+
]
19+
cloud_bitmap = [
20+
a,a,a,w,a,
21+
a,w,w,w,a,
22+
a,w,w,w,a,
23+
a,a,w,w,a,
24+
a,a,a,w,a,
25+
]
26+
partSun_bitmap = [
27+
a,w,w,w,a,
28+
a,w,w,w,w,
29+
y,y,w,w,a,
30+
a,y,y,w,a,
31+
y,a,y,a,a,
32+
]
33+
rain_bitmap = [
34+
z,z,v,z,b,
35+
v,v,v,b,z,
36+
v,v,v,z,b,
37+
z,v,v,b,z,
38+
z,z,v,z,b,
39+
]
40+
thunder_bitmap = [
41+
z,z,v,z,b,
42+
v,v,v,b,z,
43+
v,v,y,z,y,
44+
z,y,v,y,z,
45+
z,z,v,z,b,
46+
]
47+
snow_bitmap = [
48+
z,z,v,z,w,
49+
v,v,v,w,z,
50+
v,v,v,z,w,
51+
z,v,v,w,z,
52+
z,z,v,z,w,
53+
]
54+
night_bitmap = [
55+
y,g,g,y,g,
56+
g,g,y,g,g,
57+
g,y,g,g,y,
58+
y,y,y,g,g,
59+
g,y,g,g,g,
60+
]
61+
nightCloud_bitmap = [
62+
g,w,w,w,g,
63+
g,w,w,w,g,
64+
y,g,w,w,g,
65+
g,g,g,w,g,
66+
g,g,y,g,g,
67+
]
68+
nightRain_bitmap = [
69+
g,v,v,v,b,
70+
g,v,v,v,g,
71+
y,g,v,v,b,
72+
g,g,g,v,g,
73+
g,g,y,g,g,
74+
]
75+
nightThunder_bitmap = [
76+
g,v,v,v,b,
77+
g,v,v,v,g,
78+
g,g,y,v,y,
79+
v,y,g,y,b,
80+
v,v,b,g,g,
81+
]
82+
nightSnow_bitmap = [
83+
g,v,v,v,w,
84+
g,v,v,v,g,
85+
y,g,v,v,w,
86+
g,g,g,v,g,
87+
g,g,y,g,g,
88+
]
89+
90+
weather_codes = [
91+
{"code" : 0, "day_img" : sun_bitmap, "night_img" : night_bitmap},
92+
{"code" : 1, "day_img" : sun_bitmap, "night_img" : night_bitmap},
93+
{"code" : 2, "day_img" : sun_bitmap, "night_img" : night_bitmap},
94+
{"code" : 3, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
95+
{"code" : 4, "day_img" : partSun_bitmap, "night_img" : nightCloud_bitmap},
96+
{"code" : 5, "day_img" : partSun_bitmap, "night_img" : nightCloud_bitmap},
97+
{"code" : 6, "day_img" : partSun_bitmap, "night_img" : nightCloud_bitmap},
98+
{"code" : 7, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
99+
{"code" : 8, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
100+
{"code" : 9, "day_img" : partSun_bitmap, "night_img" : nightCloud_bitmap},
101+
{"code" : 10, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
102+
{"code" : 11, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
103+
{"code" : 12, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
104+
{"code" : 13, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap},
105+
{"code" : 14, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
106+
{"code" : 15, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
107+
{"code" : 16, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
108+
{"code" : 17, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap},
109+
{"code" : 18, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
110+
{"code" : 19, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap},
111+
{"code" : 20, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
112+
{"code" : 21, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
113+
{"code" : 22, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
114+
{"code" : 23, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
115+
{"code" : 24, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
116+
{"code" : 25, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
117+
{"code" : 26, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
118+
{"code" : 27, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
119+
{"code" : 28, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
120+
{"code" : 29, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap},
121+
{"code" : 30, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
122+
{"code" : 31, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
123+
{"code" : 32, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
124+
{"code" : 33, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
125+
{"code" : 34, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
126+
{"code" : 35, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
127+
{"code" : 36, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
128+
{"code" : 37, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
129+
{"code" : 38, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
130+
{"code" : 39, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
131+
{"code" : 40, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
132+
{"code" : 41, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
133+
{"code" : 42, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
134+
{"code" : 43, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
135+
{"code" : 44, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
136+
{"code" : 45, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
137+
{"code" : 46, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
138+
{"code" : 47, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
139+
{"code" : 48, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
140+
{"code" : 49, "day_img" : cloud_bitmap, "night_img" : nightCloud_bitmap},
141+
{"code" : 50, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
142+
{"code" : 51, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
143+
{"code" : 52, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
144+
{"code" : 53, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
145+
{"code" : 54, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
146+
{"code" : 55, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
147+
{"code" : 56, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
148+
{"code" : 57, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
149+
{"code" : 58, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
150+
{"code" : 59, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
151+
{"code" : 60, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
152+
{"code" : 61, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
153+
{"code" : 62, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
154+
{"code" : 63, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
155+
{"code" : 64, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
156+
{"code" : 65, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
157+
{"code" : 66, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
158+
{"code" : 67, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
159+
{"code" : 68, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
160+
{"code" : 69, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
161+
{"code" : 70, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
162+
{"code" : 71, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
163+
{"code" : 72, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
164+
{"code" : 73, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
165+
{"code" : 74, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
166+
{"code" : 75, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
167+
{"code" : 76, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
168+
{"code" : 77, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
169+
{"code" : 78, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
170+
{"code" : 79, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
171+
{"code" : 80, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
172+
{"code" : 81, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
173+
{"code" : 82, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
174+
{"code" : 83, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
175+
{"code" : 84, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
176+
{"code" : 85, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
177+
{"code" : 86, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
178+
{"code" : 87, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
179+
{"code" : 88, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
180+
{"code" : 89, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
181+
{"code" : 90, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
182+
{"code" : 91, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
183+
{"code" : 92, "day_img" : rain_bitmap, "night_img" : nightRain_bitmap},
184+
{"code" : 93, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
185+
{"code" : 94, "day_img" : snow_bitmap, "night_img" : nightSnow_bitmap},
186+
{"code" : 95, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap},
187+
{"code" : 96, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap},
188+
{"code" : 97, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap},
189+
{"code" : 98, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap},
190+
{"code" : 99, "day_img" : thunder_bitmap, "night_img" : nightThunder_bitmap}
191+
]

0 commit comments

Comments
 (0)