Skip to content

Commit e722883

Browse files
authored
Merge branch 'master' into frank
2 parents 06c4dee + ec740ac commit e722883

File tree

3 files changed

+365
-1
lines changed

3 files changed

+365
-1
lines changed

EInk_Bonnet_Weather_Station/weather_graphics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def __init__(self, display, *, am_pm=True, celsius=True):
5959
self._time_text = None
6060

6161
def display_weather(self, weather):
62-
weather = json.loads(weather)
62+
weather = json.loads(weather.decode("utf-8"))
6363

6464
# set the icon/background
6565
self._weather_icon = ICON_MAP[weather["weather"][0]["icon"]]

QT_Py_Timer/QT_Py_Activity_Timer.py

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import time
2+
import board
3+
import adafruit_lis3dh
4+
import neopixel
5+
6+
# Length of each activity, in minutes.
7+
activity_one = 120
8+
activity_two = 15
9+
10+
# Length of time, in minutes, before timer begins Red Alert blinking.
11+
red_alert_delay = 2
12+
13+
# The color will fade from color_begin to color_complete. This is a gentle indicator of how much
14+
# time has passed. Set to any two colors.
15+
color_begin = (0, 255, 0) # Green. Set to any color to begin your countdown.
16+
color_complete = (255, 0, 0) # Red. Set to any color to end your countdown.
17+
led_brightness = 0.2 # Set to a number between 0.0 and 1.0 to set LED brightness.
18+
19+
# Increase or decrease this to change the speed of the Red Alert blinking in seconds.
20+
red_alert_blink_speed = 0.5
21+
22+
# These are the thresholds the z-axis values must exceed for the orientation to be considered
23+
# "up" or "down". These values are valid if the LIS3DH breakout is mounted flat inside the timer
24+
# assembly. If you want the option to have the timer on an angle while timing, you can calibrate
25+
# these values by uncommenting the print(z) in orientation() to see the z-axis values and update
26+
# the thresholds to match the desired range.
27+
down_threshold = -8
28+
up_threshold = 8
29+
30+
# Number of LEDs. Default is 32 (2 x rings of 16 each). If you use a different form of NeoPixels,
31+
# update this to match the total number of pixels.
32+
number_of_pixels = 32
33+
34+
# Set up accelerometer and LEDs.
35+
lis3dh = adafruit_lis3dh.LIS3DH_I2C(board.I2C())
36+
pixels = neopixel.NeoPixel(board.A3, number_of_pixels, brightness=led_brightness)
37+
pixels.fill(0) # Turn off the LEDs if they're on.
38+
39+
STATE_IDLE = "Idle"
40+
STATE_TIMING = "Timing"
41+
STATE_TIMING_COMPLETE = "Timing complete"
42+
RED_ALERT = "Red Alert"
43+
44+
45+
def gradient(color_one, color_two, blend_weight):
46+
"""
47+
Blend between two colors with a given ratio.
48+
49+
:param color_one: first color, as an (r,g,b) tuple
50+
:param color_two: second color, as an (r,g,b) tuple
51+
:param blend_weight: Blend weight (ratio) of second color, 0.0 to 1.0
52+
"""
53+
if blend_weight < 0.0:
54+
blend_weight = 0.0
55+
elif blend_weight > 1.0:
56+
blend_weight = 1.0
57+
initial_weight = 1.0 - blend_weight
58+
return (int(color_one[0] * initial_weight + color_two[0] * blend_weight),
59+
int(color_one[1] * initial_weight + color_two[1] * blend_weight),
60+
int(color_one[2] * initial_weight + color_two[2] * blend_weight))
61+
62+
63+
# pylint: disable=global-statement
64+
def set_state(state):
65+
global current_state, state_changed
66+
current_state = state
67+
state_changed = time.monotonic()
68+
print("Current state:", current_state) # Print the current state.
69+
70+
71+
def orientation():
72+
"""Determines orientation based on z-axis values. Thresholds set above."""
73+
_, _, z = lis3dh.acceleration
74+
# print(z) # Uncomment to print the z-axis value. Use to calibrate thresholds if desired.
75+
if z < down_threshold:
76+
return 'down'
77+
if z > up_threshold:
78+
return 'up'
79+
return 'side'
80+
81+
82+
def orientation_debounced():
83+
"""
84+
Debounces the orientation changes. For a new orientation to be registered, the timer must
85+
be in that orientation for the duration of the delay.
86+
"""
87+
delay = 0.2
88+
initial_time = time.monotonic()
89+
initial_orientation = orientation()
90+
while True:
91+
new_orientation = orientation()
92+
if new_orientation != initial_orientation:
93+
initial_time = time.monotonic()
94+
initial_orientation = new_orientation
95+
continue
96+
if time.monotonic() - initial_time > delay:
97+
return new_orientation
98+
99+
100+
def state_from_orientation():
101+
"""Determines the state based on orientation."""
102+
global current_orientation
103+
new_orientation = orientation_debounced()
104+
if new_orientation != current_orientation:
105+
if new_orientation == 'side':
106+
set_state(STATE_IDLE)
107+
current_orientation = orientation_debounced()
108+
return
109+
set_state(STATE_TIMING)
110+
current_orientation = orientation_debounced()
111+
112+
113+
def idle():
114+
"""The idle state."""
115+
pixels.fill(0)
116+
state_from_orientation()
117+
118+
119+
def timing():
120+
"""The timing active state."""
121+
state_from_orientation()
122+
123+
if current_orientation == 'up':
124+
activity_duration = activity_one * 60 # Converts seconds to minutes.
125+
elif current_orientation == 'down':
126+
activity_duration = activity_two * 60 # Converts seconds to minutes.
127+
else:
128+
return
129+
130+
elapsed = time.monotonic() - state_changed
131+
132+
if elapsed >= activity_duration:
133+
set_state(STATE_TIMING_COMPLETE)
134+
return
135+
136+
blend = (elapsed / activity_duration)
137+
pixels.fill(gradient(color_begin, color_complete, blend))
138+
139+
140+
def timing_complete():
141+
"""The timing complete state."""
142+
pixels.fill(color_complete)
143+
144+
state_from_orientation()
145+
146+
elapsed = time.monotonic() - state_changed
147+
148+
if elapsed >= (red_alert_delay * 60): # Converts seconds to minutes.
149+
set_state(RED_ALERT)
150+
return
151+
152+
153+
blink_is_on = False
154+
155+
156+
def red_alert():
157+
"""The Red Alert state."""
158+
global blink_is_on
159+
elapsed = time.monotonic() - state_changed
160+
if elapsed >= red_alert_blink_speed:
161+
set_state(RED_ALERT)
162+
blink_is_on = not blink_is_on
163+
if blink_is_on:
164+
pixels.fill(color_complete)
165+
else:
166+
pixels.fill(0)
167+
168+
state_from_orientation()
169+
170+
171+
current_orientation = orientation_debounced() # Get the initial orientation.
172+
state_changed = time.monotonic() # Set an initial timestamp.
173+
current_state = STATE_IDLE # Set initial state to idle.
174+
175+
print("Start state:", current_state) # Print the starting state.
176+
177+
while True:
178+
if current_state == STATE_IDLE:
179+
idle()
180+
if current_state == STATE_TIMING:
181+
timing()
182+
if current_state == STATE_TIMING_COMPLETE:
183+
timing_complete()
184+
if current_state == RED_ALERT:
185+
red_alert()
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import time
2+
import board
3+
import adafruit_lis3dh
4+
import neopixel
5+
6+
# The interval you would like to be reminded to drink water, in minutes.
7+
hydration_reminder = 60
8+
9+
# Length of time, in minutes, before timer begins Red Alert blinking.
10+
red_alert_delay = 2
11+
12+
# The color will fade from color_begin to color_complete. This is a gentle indicator of how much
13+
# time has passed. Set to any two colors.
14+
color_begin = (0, 0, 255) # Blue. Set to any color to begin your countdown.
15+
color_complete = (255, 255, 255) # White. Set to any color to end your countdown.
16+
led_brightness = 0.2 # Set to a number between 0.0 and 1.0 to set LED brightness.
17+
18+
# Increase or decrease this to change the speed of the Red Alert blinking in seconds.
19+
red_alert_blink_speed = 0.5
20+
21+
# These are the thresholds the z-axis values must exceed for the orientation to be considered
22+
# "up" or "down". These values are valid if the LIS3DH breakout is mounted horizontally inside the
23+
# timer assembly. If you want the option to have the timer on an angle while timing, you can
24+
# calibrate these values by uncommenting the print(z) in orientation() to see the z-axis values and
25+
# update the thresholds to match the desired range.
26+
down_threshold = -8
27+
up_threshold = 8
28+
29+
# Number of LEDs. Default is 32 (2 x rings of 16 each). If you use a different form of NeoPixels,
30+
# update this to match the total number of pixels.
31+
number_of_pixels = 32
32+
33+
# Set up accelerometer and LEDs.
34+
lis3dh = adafruit_lis3dh.LIS3DH_I2C(board.I2C())
35+
pixels = neopixel.NeoPixel(board.A3, number_of_pixels, brightness=led_brightness)
36+
pixels.fill(0) # Turn off the LEDs if they're on.
37+
38+
STATE_IDLE = "Idle"
39+
STATE_TIMING = "Timing"
40+
STATE_TIMING_COMPLETE = "Timing complete"
41+
RED_ALERT = "Red Alert"
42+
43+
44+
def gradient(color_one, color_two, blend_weight):
45+
"""
46+
Blend between two colors with a given ratio.
47+
48+
:param color_one: first color, as an (r,g,b) tuple
49+
:param color_two: second color, as an (r,g,b) tuple
50+
:param blend_weight: Blend weight (ratio) of second color, 0.0 to 1.0
51+
"""
52+
if blend_weight < 0.0:
53+
blend_weight = 0.0
54+
elif blend_weight > 1.0:
55+
blend_weight = 1.0
56+
initial_weight = 1.0 - blend_weight
57+
return (int(color_one[0] * initial_weight + color_two[0] * blend_weight),
58+
int(color_one[1] * initial_weight + color_two[1] * blend_weight),
59+
int(color_one[2] * initial_weight + color_two[2] * blend_weight))
60+
61+
62+
# pylint: disable=global-statement
63+
def set_state(state):
64+
global current_state, state_changed
65+
current_state = state
66+
state_changed = time.monotonic()
67+
print("Current state:", current_state) # Print the current state.
68+
69+
70+
def orientation():
71+
"""Determines orientation based on z-axis values. Thresholds set above."""
72+
_, _, z = lis3dh.acceleration
73+
# print(z) # Uncomment to print the z-axis value. Use to calibrate thresholds if desired.
74+
if z < down_threshold:
75+
return 'down'
76+
if z > up_threshold:
77+
return 'up'
78+
return 'side'
79+
80+
81+
def orientation_debounced():
82+
"""
83+
Debounces the orientation changes. For a new orientation to be registered, the timer must
84+
be in that orientation for the duration of the delay.
85+
"""
86+
delay = 0.2
87+
initial_time = time.monotonic()
88+
initial_orientation = orientation()
89+
while True:
90+
new_orientation = orientation()
91+
if new_orientation != initial_orientation:
92+
initial_time = time.monotonic()
93+
initial_orientation = new_orientation
94+
continue
95+
if time.monotonic() - initial_time > delay:
96+
return new_orientation
97+
98+
99+
def state_from_orientation():
100+
"""Determines the state based on orientation."""
101+
global current_orientation
102+
new_orientation = orientation_debounced()
103+
if new_orientation != current_orientation:
104+
if new_orientation == 'side':
105+
set_state(STATE_IDLE)
106+
current_orientation = orientation_debounced()
107+
return
108+
set_state(STATE_TIMING)
109+
current_orientation = orientation_debounced()
110+
111+
112+
def idle():
113+
"""The idle state."""
114+
pixels.fill(0)
115+
state_from_orientation()
116+
117+
118+
def timing():
119+
"""The timing active state."""
120+
state_from_orientation()
121+
122+
activity_duration = hydration_reminder * 60 # Converts minutes to seconds.
123+
124+
elapsed = time.monotonic() - state_changed
125+
126+
if elapsed >= activity_duration:
127+
set_state(STATE_TIMING_COMPLETE)
128+
return
129+
130+
blend = (elapsed / activity_duration)
131+
pixels.fill(gradient(color_begin, color_complete, blend))
132+
133+
134+
def timing_complete():
135+
"""The timing complete state."""
136+
pixels.fill(color_complete)
137+
138+
state_from_orientation()
139+
140+
elapsed = time.monotonic() - state_changed
141+
142+
if elapsed >= (red_alert_delay * 60): # Converts minutes to seconds.
143+
set_state(RED_ALERT)
144+
return
145+
146+
147+
blink_is_on = False
148+
149+
150+
def red_alert():
151+
"""The Red Alert state."""
152+
global blink_is_on
153+
elapsed = time.monotonic() - state_changed
154+
if elapsed >= red_alert_blink_speed:
155+
set_state(RED_ALERT)
156+
blink_is_on = not blink_is_on
157+
if blink_is_on:
158+
pixels.fill(color_complete)
159+
else:
160+
pixels.fill(0)
161+
162+
state_from_orientation()
163+
164+
165+
current_orientation = orientation_debounced() # Get the initial orientation.
166+
state_changed = time.monotonic() # Set an initial timestamp.
167+
current_state = STATE_IDLE # Set initial state to idle.
168+
169+
print("Start state:", current_state) # Print the starting state.
170+
171+
while True:
172+
if current_state == STATE_IDLE:
173+
idle()
174+
if current_state == STATE_TIMING:
175+
timing()
176+
if current_state == STATE_TIMING_COMPLETE:
177+
timing_complete()
178+
if current_state == RED_ALERT:
179+
red_alert()

0 commit comments

Comments
 (0)